MagickCore  6.9.12-90
Convert, Edit, Or Compose Bitmap Images
 All Data Structures
display.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % DDDD IIIII SSSSS PPPP L AAA Y Y %
7 % D D I SS P P L A A Y Y %
8 % D D I SSS PPPP L AAAAA Y %
9 % D D I SS P L A A Y %
10 % DDDD IIIII SSSSS P LLLLL A A Y %
11 % %
12 % %
13 % MagickCore Methods to Interactively Display and Edit an Image %
14 % %
15 % Software Design %
16 % Cristy %
17 % July 1992 %
18 % %
19 % %
20 % Copyright 1999 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
22 % %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
25 % %
26 % https://imagemagick.org/script/license.php %
27 % %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
33 % %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 
39 /*
40  Include declarations.
41 */
42 #include "magick/studio.h"
43 #include "magick/artifact.h"
44 #include "magick/attribute.h"
45 #include "magick/blob.h"
46 #include "magick/cache.h"
47 #include "magick/channel.h"
48 #include "magick/client.h"
49 #include "magick/color.h"
50 #include "magick/colorspace.h"
51 #include "magick/composite.h"
52 #include "magick/constitute.h"
53 #include "magick/decorate.h"
54 #include "magick/delegate.h"
55 #include "magick/display.h"
56 #include "magick/display-private.h"
57 #include "magick/distort.h"
58 #include "magick/draw.h"
59 #include "magick/effect.h"
60 #include "magick/enhance.h"
61 #include "magick/exception.h"
62 #include "magick/exception-private.h"
63 #include "magick/fx.h"
64 #include "magick/geometry.h"
65 #include "magick/image.h"
66 #include "magick/image-private.h"
67 #include "magick/list.h"
68 #include "magick/locale-private.h"
69 #include "magick/log.h"
70 #include "magick/magick.h"
71 #include "magick/memory_.h"
72 #include "magick/monitor.h"
73 #include "magick/monitor-private.h"
74 #include "magick/montage.h"
75 #include "magick/nt-base-private.h"
76 #include "magick/option.h"
77 #include "magick/paint.h"
78 #include "magick/pixel.h"
79 #include "magick/pixel-private.h"
80 #include "magick/property.h"
81 #include "magick/quantum.h"
82 #include "magick/resize.h"
83 #include "magick/resource_.h"
84 #include "magick/shear.h"
85 #include "magick/segment.h"
86 #include "magick/statistic.h"
87 #include "magick/string_.h"
88 #include "magick/string-private.h"
89 #include "magick/timer-private.h"
90 #include "magick/transform.h"
91 #include "magick/threshold.h"
92 #include "magick/utility.h"
93 #include "magick/utility-private.h"
94 #include "magick/version.h"
95 #include "magick/visual-effects.h"
96 #include "magick/widget.h"
97 #include "magick/xwindow-private.h"
98 
99 #if defined(MAGICKCORE_X11_DELEGATE)
100 /*
101  Define declarations.
102 */
103 #define MaxColors MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
104 
105 /*
106  Constant declarations.
107 */
108 static const unsigned char
109  HighlightBitmap[8] =
110  {
111  0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
112  },
113  OpaqueBitmap[8] =
114  {
115  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
116  },
117  ShadowBitmap[8] =
118  {
119  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
120  };
121 
122 /*
123  Help widget declarations.
124 */
125 static const char
126  ImageAnnotateHelp[] =
127  {
128  "In annotate mode, the Command widget has these options:\n"
129  "\n"
130  " Font Name\n"
131  " fixed\n"
132  " variable\n"
133  " 5x8\n"
134  " 6x10\n"
135  " 7x13bold\n"
136  " 8x13bold\n"
137  " 9x15bold\n"
138  " 10x20\n"
139  " 12x24\n"
140  " Browser...\n"
141  " Font Color\n"
142  " black\n"
143  " blue\n"
144  " cyan\n"
145  " green\n"
146  " gray\n"
147  " red\n"
148  " magenta\n"
149  " yellow\n"
150  " white\n"
151  " transparent\n"
152  " Browser...\n"
153  " Font Color\n"
154  " black\n"
155  " blue\n"
156  " cyan\n"
157  " green\n"
158  " gray\n"
159  " red\n"
160  " magenta\n"
161  " yellow\n"
162  " white\n"
163  " transparent\n"
164  " Browser...\n"
165  " Rotate Text\n"
166  " -90\n"
167  " -45\n"
168  " -30\n"
169  " 0\n"
170  " 30\n"
171  " 45\n"
172  " 90\n"
173  " 180\n"
174  " Dialog...\n"
175  " Help\n"
176  " Dismiss\n"
177  "\n"
178  "Choose a font name from the Font Name sub-menu. Additional\n"
179  "font names can be specified with the font browser. You can\n"
180  "change the menu names by setting the X resources font1\n"
181  "through font9.\n"
182  "\n"
183  "Choose a font color from the Font Color sub-menu.\n"
184  "Additional font colors can be specified with the color\n"
185  "browser. You can change the menu colors by setting the X\n"
186  "resources pen1 through pen9.\n"
187  "\n"
188  "If you select the color browser and press Grab, you can\n"
189  "choose the font color by moving the pointer to the desired\n"
190  "color on the screen and press any button.\n"
191  "\n"
192  "If you choose to rotate the text, choose Rotate Text from the\n"
193  "menu and select an angle. Typically you will only want to\n"
194  "rotate one line of text at a time. Depending on the angle you\n"
195  "choose, subsequent lines may end up overwriting each other.\n"
196  "\n"
197  "Choosing a font and its color is optional. The default font\n"
198  "is fixed and the default color is black. However, you must\n"
199  "choose a location to begin entering text and press button 1.\n"
200  "An underscore character will appear at the location of the\n"
201  "pointer. The cursor changes to a pencil to indicate you are\n"
202  "in text mode. To exit immediately, press Dismiss.\n"
203  "\n"
204  "In text mode, any key presses will display the character at\n"
205  "the location of the underscore and advance the underscore\n"
206  "cursor. Enter your text and once completed press Apply to\n"
207  "finish your image annotation. To correct errors press BACK\n"
208  "SPACE. To delete an entire line of text, press DELETE. Any\n"
209  "text that exceeds the boundaries of the image window is\n"
210  "automagically continued onto the next line.\n"
211  "\n"
212  "The actual color you request for the font is saved in the\n"
213  "image. However, the color that appears in your image window\n"
214  "may be different. For example, on a monochrome screen the\n"
215  "text will appear black or white even if you choose the color\n"
216  "red as the font color. However, the image saved to a file\n"
217  "with -write is written with red lettering. To assure the\n"
218  "correct color text in the final image, any PseudoClass image\n"
219  "is promoted to DirectClass (see miff(5)). To force a\n"
220  "PseudoClass image to remain PseudoClass, use -colors.\n"
221  },
222  ImageChopHelp[] =
223  {
224  "In chop mode, the Command widget has these options:\n"
225  "\n"
226  " Direction\n"
227  " horizontal\n"
228  " vertical\n"
229  " Help\n"
230  " Dismiss\n"
231  "\n"
232  "If the you choose the horizontal direction (this the\n"
233  "default), the area of the image between the two horizontal\n"
234  "endpoints of the chop line is removed. Otherwise, the area\n"
235  "of the image between the two vertical endpoints of the chop\n"
236  "line is removed.\n"
237  "\n"
238  "Select a location within the image window to begin your chop,\n"
239  "press and hold any button. Next, move the pointer to\n"
240  "another location in the image. As you move a line will\n"
241  "connect the initial location and the pointer. When you\n"
242  "release the button, the area within the image to chop is\n"
243  "determined by which direction you choose from the Command\n"
244  "widget.\n"
245  "\n"
246  "To cancel the image chopping, move the pointer back to the\n"
247  "starting point of the line and release the button.\n"
248  },
249  ImageColorEditHelp[] =
250  {
251  "In color edit mode, the Command widget has these options:\n"
252  "\n"
253  " Method\n"
254  " point\n"
255  " replace\n"
256  " floodfill\n"
257  " filltoborder\n"
258  " reset\n"
259  " Pixel Color\n"
260  " black\n"
261  " blue\n"
262  " cyan\n"
263  " green\n"
264  " gray\n"
265  " red\n"
266  " magenta\n"
267  " yellow\n"
268  " white\n"
269  " Browser...\n"
270  " Border Color\n"
271  " black\n"
272  " blue\n"
273  " cyan\n"
274  " green\n"
275  " gray\n"
276  " red\n"
277  " magenta\n"
278  " yellow\n"
279  " white\n"
280  " Browser...\n"
281  " Fuzz\n"
282  " 0%\n"
283  " 2%\n"
284  " 5%\n"
285  " 10%\n"
286  " 15%\n"
287  " Dialog...\n"
288  " Undo\n"
289  " Help\n"
290  " Dismiss\n"
291  "\n"
292  "Choose a color editing method from the Method sub-menu\n"
293  "of the Command widget. The point method recolors any pixel\n"
294  "selected with the pointer until the button is released. The\n"
295  "replace method recolors any pixel that matches the color of\n"
296  "the pixel you select with a button press. Floodfill recolors\n"
297  "any pixel that matches the color of the pixel you select with\n"
298  "a button press and is a neighbor. Whereas filltoborder recolors\n"
299  "any neighbor pixel that is not the border color. Finally reset\n"
300  "changes the entire image to the designated color.\n"
301  "\n"
302  "Next, choose a pixel color from the Pixel Color sub-menu.\n"
303  "Additional pixel colors can be specified with the color\n"
304  "browser. You can change the menu colors by setting the X\n"
305  "resources pen1 through pen9.\n"
306  "\n"
307  "Now press button 1 to select a pixel within the image window\n"
308  "to change its color. Additional pixels may be recolored as\n"
309  "prescribed by the method you choose.\n"
310  "\n"
311  "If the Magnify widget is mapped, it can be helpful in positioning\n"
312  "your pointer within the image (refer to button 2).\n"
313  "\n"
314  "The actual color you request for the pixels is saved in the\n"
315  "image. However, the color that appears in your image window\n"
316  "may be different. For example, on a monochrome screen the\n"
317  "pixel will appear black or white even if you choose the\n"
318  "color red as the pixel color. However, the image saved to a\n"
319  "file with -write is written with red pixels. To assure the\n"
320  "correct color text in the final image, any PseudoClass image\n"
321  "is promoted to DirectClass (see miff(5)). To force a\n"
322  "PseudoClass image to remain PseudoClass, use -colors.\n"
323  },
324  ImageCompositeHelp[] =
325  {
326  "First a widget window is displayed requesting you to enter an\n"
327  "image name. Press Composite, Grab or type a file name.\n"
328  "Press Cancel if you choose not to create a composite image.\n"
329  "When you choose Grab, move the pointer to the desired window\n"
330  "and press any button.\n"
331  "\n"
332  "If the Composite image does not have any matte information,\n"
333  "you are informed and the file browser is displayed again.\n"
334  "Enter the name of a mask image. The image is typically\n"
335  "grayscale and the same size as the composite image. If the\n"
336  "image is not grayscale, it is converted to grayscale and the\n"
337  "resulting intensities are used as matte information.\n"
338  "\n"
339  "A small window appears showing the location of the cursor in\n"
340  "the image window. You are now in composite mode. To exit\n"
341  "immediately, press Dismiss. In composite mode, the Command\n"
342  "widget has these options:\n"
343  "\n"
344  " Operators\n"
345  " Over\n"
346  " In\n"
347  " Out\n"
348  " Atop\n"
349  " Xor\n"
350  " Plus\n"
351  " Minus\n"
352  " Add\n"
353  " Subtract\n"
354  " Difference\n"
355  " Multiply\n"
356  " Bumpmap\n"
357  " Copy\n"
358  " CopyRed\n"
359  " CopyGreen\n"
360  " CopyBlue\n"
361  " CopyOpacity\n"
362  " Clear\n"
363  " Dissolve\n"
364  " Displace\n"
365  " Help\n"
366  " Dismiss\n"
367  "\n"
368  "Choose a composite operation from the Operators sub-menu of\n"
369  "the Command widget. How each operator behaves is described\n"
370  "below. Image window is the image currently displayed on\n"
371  "your X server and image is the image obtained with the File\n"
372  "Browser widget.\n"
373  "\n"
374  "Over The result is the union of the two image shapes,\n"
375  " with image obscuring image window in the region of\n"
376  " overlap.\n"
377  "\n"
378  "In The result is simply image cut by the shape of\n"
379  " image window. None of the image data of image\n"
380  " window is in the result.\n"
381  "\n"
382  "Out The resulting image is image with the shape of\n"
383  " image window cut out.\n"
384  "\n"
385  "Atop The result is the same shape as 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 } CommandType;
1407 
1408 typedef enum
1409 {
1410  AnnotateNameCommand,
1411  AnnotateFontColorCommand,
1412  AnnotateBackgroundColorCommand,
1413  AnnotateRotateCommand,
1414  AnnotateHelpCommand,
1415  AnnotateDismissCommand,
1416  TextHelpCommand,
1417  TextApplyCommand,
1418  ChopDirectionCommand,
1419  ChopHelpCommand,
1420  ChopDismissCommand,
1421  HorizontalChopCommand,
1422  VerticalChopCommand,
1423  ColorEditMethodCommand,
1424  ColorEditColorCommand,
1425  ColorEditBorderCommand,
1426  ColorEditFuzzCommand,
1427  ColorEditUndoCommand,
1428  ColorEditHelpCommand,
1429  ColorEditDismissCommand,
1430  CompositeOperatorsCommand,
1431  CompositeDissolveCommand,
1432  CompositeDisplaceCommand,
1433  CompositeHelpCommand,
1434  CompositeDismissCommand,
1435  CropHelpCommand,
1436  CropDismissCommand,
1437  RectifyCopyCommand,
1438  RectifyHelpCommand,
1439  RectifyDismissCommand,
1440  DrawElementCommand,
1441  DrawColorCommand,
1442  DrawStippleCommand,
1443  DrawWidthCommand,
1444  DrawUndoCommand,
1445  DrawHelpCommand,
1446  DrawDismissCommand,
1447  MatteEditMethod,
1448  MatteEditBorderCommand,
1449  MatteEditFuzzCommand,
1450  MatteEditValueCommand,
1451  MatteEditUndoCommand,
1452  MatteEditHelpCommand,
1453  MatteEditDismissCommand,
1454  PasteOperatorsCommand,
1455  PasteHelpCommand,
1456  PasteDismissCommand,
1457  RotateColorCommand,
1458  RotateDirectionCommand,
1459  RotateCropCommand,
1460  RotateSharpenCommand,
1461  RotateHelpCommand,
1462  RotateDismissCommand,
1463  HorizontalRotateCommand,
1464  VerticalRotateCommand,
1465  TileLoadCommand,
1466  TileNextCommand,
1467  TileFormerCommand,
1468  TileDeleteCommand,
1469  TileUpdateCommand
1470 } ModeType;
1471 
1472 /*
1473  Stipples.
1474 */
1475 #define BricksWidth 20
1476 #define BricksHeight 20
1477 #define DiagonalWidth 16
1478 #define DiagonalHeight 16
1479 #define HighlightWidth 8
1480 #define HighlightHeight 8
1481 #define OpaqueWidth 8
1482 #define OpaqueHeight 8
1483 #define ScalesWidth 16
1484 #define ScalesHeight 16
1485 #define ShadowWidth 8
1486 #define ShadowHeight 8
1487 #define VerticalWidth 16
1488 #define VerticalHeight 16
1489 #define WavyWidth 16
1490 #define WavyHeight 16
1491 
1492 /*
1493  Constant declaration.
1494 */
1495 static const int
1496  RoiDelta = 8;
1497 
1498 static const unsigned char
1499  BricksBitmap[] =
1500  {
1501  0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1502  0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1503  0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1504  0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1505  0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1506  },
1507  DiagonalBitmap[] =
1508  {
1509  0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1510  0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1511  0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1512  },
1513  ScalesBitmap[] =
1514  {
1515  0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1516  0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1517  0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1518  },
1519  VerticalBitmap[] =
1520  {
1521  0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1522  0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1523  0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1524  },
1525  WavyBitmap[] =
1526  {
1527  0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1528  0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1529  0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1530  };
1531 
1532 /*
1533  Function prototypes.
1534 */
1535 static CommandType
1536  XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1537  const MagickStatusType,KeySym,Image **);
1538 
1539 static Image
1540  *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1541  Image **),
1542  *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1543  *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *),
1544  *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *);
1545 
1546 static MagickBooleanType
1547  XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *),
1548  XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1549  XChopImage(Display *,XResourceInfo *,XWindows *,Image **),
1550  XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode),
1551  XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **),
1552  XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1553  XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *),
1554  XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *),
1555  XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1556  XPasteImage(Display *,XResourceInfo *,XWindows *,Image *),
1557  XPrintImage(Display *,XResourceInfo *,XWindows *,Image *),
1558  XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **),
1559  XROIImage(Display *,XResourceInfo *,XWindows *,Image **),
1560  XSaveImage(Display *,XResourceInfo *,XWindows *,Image *),
1561  XTrimImage(Display *,XResourceInfo *,XWindows *,Image *);
1562 
1563 static void
1564  XDrawPanRectangle(Display *,XWindows *),
1565  XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **),
1566  XMagnifyImage(Display *,XWindows *,XEvent *),
1567  XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *),
1568  XPanImage(Display *,XWindows *,XEvent *),
1569  XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1570  const KeySym),
1571  XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1572  XScreenEvent(Display *,XWindows *,XEvent *),
1573  XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1574 
1575 /*
1576 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1577 % %
1578 % %
1579 % %
1580 % D i s p l a y I m a g e s %
1581 % %
1582 % %
1583 % %
1584 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1585 %
1586 % DisplayImages() displays an image sequence to any X window screen. It
1587 % returns a value other than 0 if successful. Check the exception member
1588 % of image to determine the reason for any failure.
1589 %
1590 % The format of the DisplayImages method is:
1591 %
1592 % MagickBooleanType DisplayImages(const ImageInfo *image_info,
1593 % Image *images)
1594 %
1595 % A description of each parameter follows:
1596 %
1597 % o image_info: the image info.
1598 %
1599 % o image: the image.
1600 %
1601 */
1602 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1603  Image *images)
1604 {
1605  char
1606  *argv[1];
1607 
1608  Display
1609  *display;
1610 
1611  Image
1612  *image;
1613 
1614  ssize_t
1615  i;
1616 
1617  size_t
1618  state;
1619 
1620  XrmDatabase
1621  resource_database;
1622 
1623  XResourceInfo
1624  resource_info;
1625 
1626  assert(image_info != (const ImageInfo *) NULL);
1627  assert(image_info->signature == MagickCoreSignature);
1628  assert(images != (Image *) NULL);
1629  assert(images->signature == MagickCoreSignature);
1630  if (IsEventLogging() != MagickFalse)
1631  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1632  display=XOpenDisplay(image_info->server_name);
1633  if (display == (Display *) NULL)
1634  {
1635  (void) ThrowMagickException(&images->exception,GetMagickModule(),
1636  XServerError,"UnableToOpenXServer","`%s'",XDisplayName(
1637  image_info->server_name));
1638  return(MagickFalse);
1639  }
1640  if (images->exception.severity != UndefinedException)
1641  CatchException(&images->exception);
1642  (void) XSetErrorHandler(XError);
1643  resource_database=XGetResourceDatabase(display,GetClientName());
1644  (void) memset(&resource_info,0,sizeof(resource_info));
1645  XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1646  if (image_info->page != (char *) NULL)
1647  resource_info.image_geometry=AcquireString(image_info->page);
1648  resource_info.immutable=MagickTrue;
1649  argv[0]=AcquireString(GetClientName());
1650  state=DefaultState;
1651  for (i=0; (state & ExitState) == 0; i++)
1652  {
1653  if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1654  break;
1655  image=GetImageFromList(images,i % GetImageListLength(images));
1656  (void) XDisplayImage(display,&resource_info,argv,1,&image,&state);
1657  }
1658  (void) SetErrorHandler((ErrorHandler) NULL);
1659  (void) SetWarningHandler((WarningHandler) NULL);
1660  argv[0]=DestroyString(argv[0]);
1661  XDestroyResourceInfo(&resource_info);
1662  if (images->exception.severity != UndefinedException)
1663  return(MagickFalse);
1664  return(MagickTrue);
1665 }
1666 
1667 /*
1668 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1669 % %
1670 % %
1671 % %
1672 % R e m o t e D i s p l a y C o m m a n d %
1673 % %
1674 % %
1675 % %
1676 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1677 %
1678 % RemoteDisplayCommand() encourages a remote display program to display the
1679 % specified image filename.
1680 %
1681 % The format of the RemoteDisplayCommand method is:
1682 %
1683 % MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1684 % const char *window,const char *filename,ExceptionInfo *exception)
1685 %
1686 % A description of each parameter follows:
1687 %
1688 % o image_info: the image info.
1689 %
1690 % o window: Specifies the name or id of an X window.
1691 %
1692 % o filename: the name of the image filename to display.
1693 %
1694 % o exception: return any errors or warnings in this structure.
1695 %
1696 */
1697 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1698  const char *window,const char *filename,ExceptionInfo *exception)
1699 {
1700  Display
1701  *display;
1702 
1703  MagickStatusType
1704  status;
1705 
1706  assert(image_info != (const ImageInfo *) NULL);
1707  assert(image_info->signature == MagickCoreSignature);
1708  assert(filename != (char *) NULL);
1709  if (IsEventLogging() != MagickFalse)
1710  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1711  display=XOpenDisplay(image_info->server_name);
1712  if (display == (Display *) NULL)
1713  {
1714  (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1715  "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1716  return(MagickFalse);
1717  }
1718  (void) XSetErrorHandler(XError);
1719  status=XRemoteCommand(display,window,filename);
1720  (void) XCloseDisplay(display);
1721  return(status != 0 ? MagickTrue : MagickFalse);
1722 }
1723 
1724 /*
1725 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1726 % %
1727 % %
1728 % %
1729 + X A n n o t a t e E d i t I m a g e %
1730 % %
1731 % %
1732 % %
1733 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1734 %
1735 % XAnnotateEditImage() annotates the image with text.
1736 %
1737 % The format of the XAnnotateEditImage method is:
1738 %
1739 % MagickBooleanType XAnnotateEditImage(Display *display,
1740 % XResourceInfo *resource_info,XWindows *windows,Image *image)
1741 %
1742 % A description of each parameter follows:
1743 %
1744 % o display: Specifies a connection to an X server; returned from
1745 % XOpenDisplay.
1746 %
1747 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1748 %
1749 % o windows: Specifies a pointer to a XWindows structure.
1750 %
1751 % o image: the image; returned from ReadImage.
1752 %
1753 */
1754 
1755 static MagickBooleanType XAnnotateEditImage(Display *display,
1756  XResourceInfo *resource_info,XWindows *windows,Image *image)
1757 {
1758  const char
1759  *const AnnotateMenu[] =
1760  {
1761  "Font Name",
1762  "Font Color",
1763  "Box Color",
1764  "Rotate Text",
1765  "Help",
1766  "Dismiss",
1767  (char *) NULL
1768  },
1769  *const TextMenu[] =
1770  {
1771  "Help",
1772  "Apply",
1773  (char *) NULL
1774  };
1775 
1776  static const ModeType
1777  AnnotateCommands[] =
1778  {
1779  AnnotateNameCommand,
1780  AnnotateFontColorCommand,
1781  AnnotateBackgroundColorCommand,
1782  AnnotateRotateCommand,
1783  AnnotateHelpCommand,
1784  AnnotateDismissCommand
1785  },
1786  TextCommands[] =
1787  {
1788  TextHelpCommand,
1789  TextApplyCommand
1790  };
1791 
1792  static MagickBooleanType
1793  transparent_box = MagickTrue,
1794  transparent_pen = MagickFalse;
1795 
1796  static MagickRealType
1797  degrees = 0.0;
1798 
1799  static unsigned int
1800  box_id = MaxNumberPens-2,
1801  font_id = 0,
1802  pen_id = 0;
1803 
1804  char
1805  command[MaxTextExtent],
1806  text[MaxTextExtent];
1807 
1808  const char
1809  *ColorMenu[MaxNumberPens+1];
1810 
1811  Cursor
1812  cursor;
1813 
1814  GC
1815  annotate_context;
1816 
1817  int
1818  id,
1819  pen_number,
1820  status,
1821  x,
1822  y;
1823 
1824  KeySym
1825  key_symbol;
1826 
1827  char
1828  *p;
1829 
1830  ssize_t
1831  i;
1832 
1833  unsigned int
1834  height,
1835  width;
1836 
1837  size_t
1838  state;
1839 
1840  XAnnotateInfo
1841  *annotate_info,
1842  *previous_info;
1843 
1844  XColor
1845  color;
1846 
1847  XFontStruct
1848  *font_info;
1849 
1850  XEvent
1851  event,
1852  text_event;
1853 
1854  /*
1855  Map Command widget.
1856  */
1857  (void) CloneString(&windows->command.name,"Annotate");
1858  windows->command.data=4;
1859  (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1860  (void) XMapRaised(display,windows->command.id);
1861  XClientMessage(display,windows->image.id,windows->im_protocols,
1862  windows->im_update_widget,CurrentTime);
1863  /*
1864  Track pointer until button 1 is pressed.
1865  */
1866  XQueryPosition(display,windows->image.id,&x,&y);
1867  (void) XSelectInput(display,windows->image.id,
1868  windows->image.attributes.event_mask | PointerMotionMask);
1869  cursor=XCreateFontCursor(display,XC_left_side);
1870  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1871  state=DefaultState;
1872  do
1873  {
1874  if (windows->info.mapped != MagickFalse)
1875  {
1876  /*
1877  Display pointer position.
1878  */
1879  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1880  x+windows->image.x,y+windows->image.y);
1881  XInfoWidget(display,windows,text);
1882  }
1883  /*
1884  Wait for next event.
1885  */
1886  XScreenEvent(display,windows,&event);
1887  if (event.xany.window == windows->command.id)
1888  {
1889  /*
1890  Select a command from the Command widget.
1891  */
1892  id=XCommandWidget(display,windows,AnnotateMenu,&event);
1893  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1894  if (id < 0)
1895  continue;
1896  switch (AnnotateCommands[id])
1897  {
1898  case AnnotateNameCommand:
1899  {
1900  const char
1901  *FontMenu[MaxNumberFonts];
1902 
1903  int
1904  font_number;
1905 
1906  /*
1907  Initialize menu selections.
1908  */
1909  for (i=0; i < MaxNumberFonts; i++)
1910  FontMenu[i]=resource_info->font_name[i];
1911  FontMenu[MaxNumberFonts-2]="Browser...";
1912  FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1913  /*
1914  Select a font name from the pop-up menu.
1915  */
1916  font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1917  (const char **) FontMenu,command);
1918  if (font_number < 0)
1919  break;
1920  if (font_number == (MaxNumberFonts-2))
1921  {
1922  static char
1923  font_name[MaxTextExtent] = "fixed";
1924 
1925  /*
1926  Select a font name from a browser.
1927  */
1928  resource_info->font_name[font_number]=font_name;
1929  XFontBrowserWidget(display,windows,"Select",font_name);
1930  if (*font_name == '\0')
1931  break;
1932  }
1933  /*
1934  Initialize font info.
1935  */
1936  font_info=XLoadQueryFont(display,resource_info->font_name[
1937  font_number]);
1938  if (font_info == (XFontStruct *) NULL)
1939  {
1940  XNoticeWidget(display,windows,"Unable to load font:",
1941  resource_info->font_name[font_number]);
1942  break;
1943  }
1944  font_id=(unsigned int) font_number;
1945  (void) XFreeFont(display,font_info);
1946  break;
1947  }
1948  case AnnotateFontColorCommand:
1949  {
1950  /*
1951  Initialize menu selections.
1952  */
1953  for (i=0; i < (int) (MaxNumberPens-2); i++)
1954  ColorMenu[i]=resource_info->pen_colors[i];
1955  ColorMenu[MaxNumberPens-2]="transparent";
1956  ColorMenu[MaxNumberPens-1]="Browser...";
1957  ColorMenu[MaxNumberPens]=(const char *) NULL;
1958  /*
1959  Select a pen color from the pop-up menu.
1960  */
1961  pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
1962  (const char **) ColorMenu,command);
1963  if (pen_number < 0)
1964  break;
1965  transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
1966  MagickFalse;
1967  if (transparent_pen != MagickFalse)
1968  break;
1969  if (pen_number == (MaxNumberPens-1))
1970  {
1971  static char
1972  color_name[MaxTextExtent] = "gray";
1973 
1974  /*
1975  Select a pen color from a dialog.
1976  */
1977  resource_info->pen_colors[pen_number]=color_name;
1978  XColorBrowserWidget(display,windows,"Select",color_name);
1979  if (*color_name == '\0')
1980  break;
1981  }
1982  /*
1983  Set pen color.
1984  */
1985  (void) XParseColor(display,windows->map_info->colormap,
1986  resource_info->pen_colors[pen_number],&color);
1987  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
1988  (unsigned int) MaxColors,&color);
1989  windows->pixel_info->pen_colors[pen_number]=color;
1990  pen_id=(unsigned int) pen_number;
1991  break;
1992  }
1993  case AnnotateBackgroundColorCommand:
1994  {
1995  /*
1996  Initialize menu selections.
1997  */
1998  for (i=0; i < (int) (MaxNumberPens-2); i++)
1999  ColorMenu[i]=resource_info->pen_colors[i];
2000  ColorMenu[MaxNumberPens-2]="transparent";
2001  ColorMenu[MaxNumberPens-1]="Browser...";
2002  ColorMenu[MaxNumberPens]=(const char *) NULL;
2003  /*
2004  Select a pen color from the pop-up menu.
2005  */
2006  pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2007  (const char **) ColorMenu,command);
2008  if (pen_number < 0)
2009  break;
2010  transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2011  MagickFalse;
2012  if (transparent_box != MagickFalse)
2013  break;
2014  if (pen_number == (MaxNumberPens-1))
2015  {
2016  static char
2017  color_name[MaxTextExtent] = "gray";
2018 
2019  /*
2020  Select a pen color from a dialog.
2021  */
2022  resource_info->pen_colors[pen_number]=color_name;
2023  XColorBrowserWidget(display,windows,"Select",color_name);
2024  if (*color_name == '\0')
2025  break;
2026  }
2027  /*
2028  Set pen color.
2029  */
2030  (void) XParseColor(display,windows->map_info->colormap,
2031  resource_info->pen_colors[pen_number],&color);
2032  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2033  (unsigned int) MaxColors,&color);
2034  windows->pixel_info->pen_colors[pen_number]=color;
2035  box_id=(unsigned int) pen_number;
2036  break;
2037  }
2038  case AnnotateRotateCommand:
2039  {
2040  int
2041  entry;
2042 
2043  const char
2044  *const RotateMenu[] =
2045  {
2046  "-90",
2047  "-45",
2048  "-30",
2049  "0",
2050  "30",
2051  "45",
2052  "90",
2053  "180",
2054  "Dialog...",
2055  (char *) NULL,
2056  };
2057 
2058  static char
2059  angle[MaxTextExtent] = "30.0";
2060 
2061  /*
2062  Select a command from the pop-up menu.
2063  */
2064  entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2065  command);
2066  if (entry < 0)
2067  break;
2068  if (entry != 8)
2069  {
2070  degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2071  break;
2072  }
2073  (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2074  angle);
2075  if (*angle == '\0')
2076  break;
2077  degrees=StringToDouble(angle,(char **) NULL);
2078  break;
2079  }
2080  case AnnotateHelpCommand:
2081  {
2082  XTextViewHelp(display,resource_info,windows,MagickFalse,
2083  "Help Viewer - Image Annotation",ImageAnnotateHelp);
2084  break;
2085  }
2086  case AnnotateDismissCommand:
2087  {
2088  /*
2089  Prematurely exit.
2090  */
2091  state|=EscapeState;
2092  state|=ExitState;
2093  break;
2094  }
2095  default:
2096  break;
2097  }
2098  continue;
2099  }
2100  switch (event.type)
2101  {
2102  case ButtonPress:
2103  {
2104  if (event.xbutton.button != Button1)
2105  break;
2106  if (event.xbutton.window != windows->image.id)
2107  break;
2108  /*
2109  Change to text entering mode.
2110  */
2111  x=event.xbutton.x;
2112  y=event.xbutton.y;
2113  state|=ExitState;
2114  break;
2115  }
2116  case ButtonRelease:
2117  break;
2118  case Expose:
2119  break;
2120  case KeyPress:
2121  {
2122  if (event.xkey.window != windows->image.id)
2123  break;
2124  /*
2125  Respond to a user key press.
2126  */
2127  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2128  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2129  switch ((int) key_symbol)
2130  {
2131  case XK_Escape:
2132  case XK_F20:
2133  {
2134  /*
2135  Prematurely exit.
2136  */
2137  state|=EscapeState;
2138  state|=ExitState;
2139  break;
2140  }
2141  case XK_F1:
2142  case XK_Help:
2143  {
2144  XTextViewHelp(display,resource_info,windows,MagickFalse,
2145  "Help Viewer - Image Annotation",ImageAnnotateHelp);
2146  break;
2147  }
2148  default:
2149  {
2150  (void) XBell(display,0);
2151  break;
2152  }
2153  }
2154  break;
2155  }
2156  case MotionNotify:
2157  {
2158  /*
2159  Map and unmap Info widget as cursor crosses its boundaries.
2160  */
2161  x=event.xmotion.x;
2162  y=event.xmotion.y;
2163  if (windows->info.mapped != MagickFalse)
2164  {
2165  if ((x < (int) (windows->info.x+windows->info.width)) &&
2166  (y < (int) (windows->info.y+windows->info.height)))
2167  (void) XWithdrawWindow(display,windows->info.id,
2168  windows->info.screen);
2169  }
2170  else
2171  if ((x > (int) (windows->info.x+windows->info.width)) ||
2172  (y > (int) (windows->info.y+windows->info.height)))
2173  (void) XMapWindow(display,windows->info.id);
2174  break;
2175  }
2176  default:
2177  break;
2178  }
2179  } while ((state & ExitState) == 0);
2180  (void) XSelectInput(display,windows->image.id,
2181  windows->image.attributes.event_mask);
2182  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2183  if ((state & EscapeState) != 0)
2184  return(MagickTrue);
2185  /*
2186  Set font info and check boundary conditions.
2187  */
2188  font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2189  if (font_info == (XFontStruct *) NULL)
2190  {
2191  XNoticeWidget(display,windows,"Unable to load font:",
2192  resource_info->font_name[font_id]);
2193  font_info=windows->font_info;
2194  }
2195  if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2196  x=(int) windows->image.width-font_info->max_bounds.width;
2197  if (y < (int) (font_info->ascent+font_info->descent))
2198  y=(int) font_info->ascent+font_info->descent;
2199  if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2200  ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2201  return(MagickFalse);
2202  /*
2203  Initialize annotate structure.
2204  */
2205  annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2206  if (annotate_info == (XAnnotateInfo *) NULL)
2207  return(MagickFalse);
2208  XGetAnnotateInfo(annotate_info);
2209  annotate_info->x=x;
2210  annotate_info->y=y;
2211  if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2212  annotate_info->stencil=OpaqueStencil;
2213  else
2214  if (transparent_box == MagickFalse)
2215  annotate_info->stencil=BackgroundStencil;
2216  else
2217  annotate_info->stencil=ForegroundStencil;
2218  annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2219  annotate_info->degrees=degrees;
2220  annotate_info->font_info=font_info;
2221  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2222  windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2223  sizeof(*annotate_info->text));
2224  if (annotate_info->text == (char *) NULL)
2225  return(MagickFalse);
2226  /*
2227  Create cursor and set graphic context.
2228  */
2229  cursor=XCreateFontCursor(display,XC_pencil);
2230  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2231  annotate_context=windows->image.annotate_context;
2232  (void) XSetFont(display,annotate_context,font_info->fid);
2233  (void) XSetBackground(display,annotate_context,
2234  windows->pixel_info->pen_colors[box_id].pixel);
2235  (void) XSetForeground(display,annotate_context,
2236  windows->pixel_info->pen_colors[pen_id].pixel);
2237  /*
2238  Begin annotating the image with text.
2239  */
2240  (void) CloneString(&windows->command.name,"Text");
2241  windows->command.data=0;
2242  (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2243  state=DefaultState;
2244  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2245  text_event.xexpose.width=(int) font_info->max_bounds.width;
2246  text_event.xexpose.height=font_info->max_bounds.ascent+
2247  font_info->max_bounds.descent;
2248  p=annotate_info->text;
2249  do
2250  {
2251  /*
2252  Display text cursor.
2253  */
2254  *p='\0';
2255  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2256  /*
2257  Wait for next event.
2258  */
2259  XScreenEvent(display,windows,&event);
2260  if (event.xany.window == windows->command.id)
2261  {
2262  /*
2263  Select a command from the Command widget.
2264  */
2265  (void) XSetBackground(display,annotate_context,
2266  windows->pixel_info->background_color.pixel);
2267  (void) XSetForeground(display,annotate_context,
2268  windows->pixel_info->foreground_color.pixel);
2269  id=XCommandWidget(display,windows,AnnotateMenu,&event);
2270  (void) XSetBackground(display,annotate_context,
2271  windows->pixel_info->pen_colors[box_id].pixel);
2272  (void) XSetForeground(display,annotate_context,
2273  windows->pixel_info->pen_colors[pen_id].pixel);
2274  if (id < 0)
2275  continue;
2276  switch (TextCommands[id])
2277  {
2278  case TextHelpCommand:
2279  {
2280  XTextViewHelp(display,resource_info,windows,MagickFalse,
2281  "Help Viewer - Image Annotation",ImageAnnotateHelp);
2282  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2283  break;
2284  }
2285  case TextApplyCommand:
2286  {
2287  /*
2288  Finished annotating.
2289  */
2290  annotate_info->width=(unsigned int) XTextWidth(font_info,
2291  annotate_info->text,(int) strlen(annotate_info->text));
2292  XRefreshWindow(display,&windows->image,&text_event);
2293  state|=ExitState;
2294  break;
2295  }
2296  default:
2297  break;
2298  }
2299  continue;
2300  }
2301  /*
2302  Erase text cursor.
2303  */
2304  text_event.xexpose.x=x;
2305  text_event.xexpose.y=y-font_info->max_bounds.ascent;
2306  (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2307  (unsigned int) text_event.xexpose.width,(unsigned int)
2308  text_event.xexpose.height,MagickFalse);
2309  XRefreshWindow(display,&windows->image,&text_event);
2310  switch (event.type)
2311  {
2312  case ButtonPress:
2313  {
2314  if (event.xbutton.window != windows->image.id)
2315  break;
2316  if (event.xbutton.button == Button2)
2317  {
2318  /*
2319  Request primary selection.
2320  */
2321  (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2322  windows->image.id,CurrentTime);
2323  break;
2324  }
2325  break;
2326  }
2327  case Expose:
2328  {
2329  if (event.xexpose.count == 0)
2330  {
2331  XAnnotateInfo
2332  *text_info;
2333 
2334  /*
2335  Refresh Image window.
2336  */
2337  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2338  text_info=annotate_info;
2339  while (text_info != (XAnnotateInfo *) NULL)
2340  {
2341  if (annotate_info->stencil == ForegroundStencil)
2342  (void) XDrawString(display,windows->image.id,annotate_context,
2343  text_info->x,text_info->y,text_info->text,
2344  (int) strlen(text_info->text));
2345  else
2346  (void) XDrawImageString(display,windows->image.id,
2347  annotate_context,text_info->x,text_info->y,text_info->text,
2348  (int) strlen(text_info->text));
2349  text_info=text_info->previous;
2350  }
2351  (void) XDrawString(display,windows->image.id,annotate_context,
2352  x,y,"_",1);
2353  }
2354  break;
2355  }
2356  case KeyPress:
2357  {
2358  int
2359  length;
2360 
2361  if (event.xkey.window != windows->image.id)
2362  break;
2363  /*
2364  Respond to a user key press.
2365  */
2366  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2367  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2368  *(command+length)='\0';
2369  if (((event.xkey.state & ControlMask) != 0) ||
2370  ((event.xkey.state & Mod1Mask) != 0))
2371  state|=ModifierState;
2372  if ((state & ModifierState) != 0)
2373  switch ((int) key_symbol)
2374  {
2375  case XK_u:
2376  case XK_U:
2377  {
2378  key_symbol=DeleteCommand;
2379  break;
2380  }
2381  default:
2382  break;
2383  }
2384  switch ((int) key_symbol)
2385  {
2386  case XK_BackSpace:
2387  {
2388  /*
2389  Erase one character.
2390  */
2391  if (p == annotate_info->text)
2392  {
2393  if (annotate_info->previous == (XAnnotateInfo *) NULL)
2394  break;
2395  else
2396  {
2397  /*
2398  Go to end of the previous line of text.
2399  */
2400  annotate_info=annotate_info->previous;
2401  p=annotate_info->text;
2402  x=annotate_info->x+annotate_info->width;
2403  y=annotate_info->y;
2404  if (annotate_info->width != 0)
2405  p+=strlen(annotate_info->text);
2406  break;
2407  }
2408  }
2409  p--;
2410  x-=XTextWidth(font_info,p,1);
2411  text_event.xexpose.x=x;
2412  text_event.xexpose.y=y-font_info->max_bounds.ascent;
2413  XRefreshWindow(display,&windows->image,&text_event);
2414  break;
2415  }
2416  case XK_bracketleft:
2417  {
2418  key_symbol=XK_Escape;
2419  break;
2420  }
2421  case DeleteCommand:
2422  {
2423  /*
2424  Erase the entire line of text.
2425  */
2426  while (p != annotate_info->text)
2427  {
2428  p--;
2429  x-=XTextWidth(font_info,p,1);
2430  text_event.xexpose.x=x;
2431  XRefreshWindow(display,&windows->image,&text_event);
2432  }
2433  break;
2434  }
2435  case XK_Escape:
2436  case XK_F20:
2437  {
2438  /*
2439  Finished annotating.
2440  */
2441  annotate_info->width=(unsigned int) XTextWidth(font_info,
2442  annotate_info->text,(int) strlen(annotate_info->text));
2443  XRefreshWindow(display,&windows->image,&text_event);
2444  state|=ExitState;
2445  break;
2446  }
2447  default:
2448  {
2449  /*
2450  Draw a single character on the Image window.
2451  */
2452  if ((state & ModifierState) != 0)
2453  break;
2454  if (*command == '\0')
2455  break;
2456  *p=(*command);
2457  if (annotate_info->stencil == ForegroundStencil)
2458  (void) XDrawString(display,windows->image.id,annotate_context,
2459  x,y,p,1);
2460  else
2461  (void) XDrawImageString(display,windows->image.id,
2462  annotate_context,x,y,p,1);
2463  x+=XTextWidth(font_info,p,1);
2464  p++;
2465  if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2466  break;
2467  magick_fallthrough;
2468  }
2469  case XK_Return:
2470  case XK_KP_Enter:
2471  {
2472  /*
2473  Advance to the next line of text.
2474  */
2475  *p='\0';
2476  annotate_info->width=(unsigned int) XTextWidth(font_info,
2477  annotate_info->text,(int) strlen(annotate_info->text));
2478  if (annotate_info->next != (XAnnotateInfo *) NULL)
2479  {
2480  /*
2481  Line of text already exists.
2482  */
2483  annotate_info=annotate_info->next;
2484  x=annotate_info->x;
2485  y=annotate_info->y;
2486  p=annotate_info->text;
2487  break;
2488  }
2489  annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2490  sizeof(*annotate_info->next));
2491  if (annotate_info->next == (XAnnotateInfo *) NULL)
2492  return(MagickFalse);
2493  *annotate_info->next=(*annotate_info);
2494  annotate_info->next->previous=annotate_info;
2495  annotate_info=annotate_info->next;
2496  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2497  windows->image.width/MagickMax((ssize_t)
2498  font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2499  if (annotate_info->text == (char *) NULL)
2500  return(MagickFalse);
2501  annotate_info->y+=annotate_info->height;
2502  if (annotate_info->y > (int) windows->image.height)
2503  annotate_info->y=(int) annotate_info->height;
2504  annotate_info->next=(XAnnotateInfo *) NULL;
2505  x=annotate_info->x;
2506  y=annotate_info->y;
2507  p=annotate_info->text;
2508  break;
2509  }
2510  }
2511  break;
2512  }
2513  case KeyRelease:
2514  {
2515  /*
2516  Respond to a user key release.
2517  */
2518  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2519  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2520  state&=(~ModifierState);
2521  break;
2522  }
2523  case SelectionNotify:
2524  {
2525  Atom
2526  type;
2527 
2528  int
2529  format;
2530 
2531  unsigned char
2532  *data;
2533 
2534  unsigned long
2535  after,
2536  length;
2537 
2538  /*
2539  Obtain response from primary selection.
2540  */
2541  if (event.xselection.property == (Atom) None)
2542  break;
2543  status=XGetWindowProperty(display,event.xselection.requestor,
2544  event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2545  &type,&format,&length,&after,&data);
2546  if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2547  (length == 0))
2548  break;
2549  /*
2550  Annotate Image window with primary selection.
2551  */
2552  for (i=0; i < (ssize_t) length; i++)
2553  {
2554  if ((char) data[i] != '\n')
2555  {
2556  /*
2557  Draw a single character on the Image window.
2558  */
2559  *p=(char) data[i];
2560  (void) XDrawString(display,windows->image.id,annotate_context,
2561  x,y,p,1);
2562  x+=XTextWidth(font_info,p,1);
2563  p++;
2564  if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2565  continue;
2566  }
2567  /*
2568  Advance to the next line of text.
2569  */
2570  *p='\0';
2571  annotate_info->width=(unsigned int) XTextWidth(font_info,
2572  annotate_info->text,(int) strlen(annotate_info->text));
2573  if (annotate_info->next != (XAnnotateInfo *) NULL)
2574  {
2575  /*
2576  Line of text already exists.
2577  */
2578  annotate_info=annotate_info->next;
2579  x=annotate_info->x;
2580  y=annotate_info->y;
2581  p=annotate_info->text;
2582  continue;
2583  }
2584  annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2585  sizeof(*annotate_info->next));
2586  if (annotate_info->next == (XAnnotateInfo *) NULL)
2587  return(MagickFalse);
2588  *annotate_info->next=(*annotate_info);
2589  annotate_info->next->previous=annotate_info;
2590  annotate_info=annotate_info->next;
2591  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2592  windows->image.width/MagickMax((ssize_t)
2593  font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2594  if (annotate_info->text == (char *) NULL)
2595  return(MagickFalse);
2596  annotate_info->y+=annotate_info->height;
2597  if (annotate_info->y > (int) windows->image.height)
2598  annotate_info->y=(int) annotate_info->height;
2599  annotate_info->next=(XAnnotateInfo *) NULL;
2600  x=annotate_info->x;
2601  y=annotate_info->y;
2602  p=annotate_info->text;
2603  }
2604  (void) XFree((void *) data);
2605  break;
2606  }
2607  default:
2608  break;
2609  }
2610  } while ((state & ExitState) == 0);
2611  (void) XFreeCursor(display,cursor);
2612  /*
2613  Annotation is relative to image configuration.
2614  */
2615  width=(unsigned int) image->columns;
2616  height=(unsigned int) image->rows;
2617  x=0;
2618  y=0;
2619  if (windows->image.crop_geometry != (char *) NULL)
2620  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2621  /*
2622  Initialize annotated image.
2623  */
2624  XSetCursorState(display,windows,MagickTrue);
2625  XCheckRefreshWindows(display,windows);
2626  while (annotate_info != (XAnnotateInfo *) NULL)
2627  {
2628  if (annotate_info->width == 0)
2629  {
2630  /*
2631  No text on this line-- go to the next line of text.
2632  */
2633  previous_info=annotate_info->previous;
2634  annotate_info->text=(char *)
2635  RelinquishMagickMemory(annotate_info->text);
2636  annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2637  annotate_info=previous_info;
2638  continue;
2639  }
2640  /*
2641  Determine pixel index for box and pen color.
2642  */
2643  windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2644  if (windows->pixel_info->colors != 0)
2645  for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2646  if (windows->pixel_info->pixels[i] ==
2647  windows->pixel_info->pen_colors[box_id].pixel)
2648  {
2649  windows->pixel_info->box_index=(unsigned short) i;
2650  break;
2651  }
2652  windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2653  if (windows->pixel_info->colors != 0)
2654  for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2655  if (windows->pixel_info->pixels[i] ==
2656  windows->pixel_info->pen_colors[pen_id].pixel)
2657  {
2658  windows->pixel_info->pen_index=(unsigned short) i;
2659  break;
2660  }
2661  /*
2662  Define the annotate geometry string.
2663  */
2664  annotate_info->x=(int)
2665  width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2666  annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2667  windows->image.y)/windows->image.ximage->height;
2668  (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2669  "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2670  height*annotate_info->height/windows->image.ximage->height,
2671  annotate_info->x+x,annotate_info->y+y);
2672  /*
2673  Annotate image with text.
2674  */
2675  status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
2676  if (status == 0)
2677  return(MagickFalse);
2678  /*
2679  Free up memory.
2680  */
2681  previous_info=annotate_info->previous;
2682  annotate_info->text=DestroyString(annotate_info->text);
2683  annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2684  annotate_info=previous_info;
2685  }
2686  (void) XSetForeground(display,annotate_context,
2687  windows->pixel_info->foreground_color.pixel);
2688  (void) XSetBackground(display,annotate_context,
2689  windows->pixel_info->background_color.pixel);
2690  (void) XSetFont(display,annotate_context,windows->font_info->fid);
2691  XSetCursorState(display,windows,MagickFalse);
2692  (void) XFreeFont(display,font_info);
2693  /*
2694  Update image configuration.
2695  */
2696  XConfigureImageColormap(display,resource_info,windows,image);
2697  (void) XConfigureImage(display,resource_info,windows,image);
2698  return(MagickTrue);
2699 }
2700 
2701 /*
2702 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2703 % %
2704 % %
2705 % %
2706 + X B a c k g r o u n d I m a g e %
2707 % %
2708 % %
2709 % %
2710 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2711 %
2712 % XBackgroundImage() displays the image in the background of a window.
2713 %
2714 % The format of the XBackgroundImage method is:
2715 %
2716 % MagickBooleanType XBackgroundImage(Display *display,
2717 % XResourceInfo *resource_info,XWindows *windows,Image **image)
2718 %
2719 % A description of each parameter follows:
2720 %
2721 % o display: Specifies a connection to an X server; returned from
2722 % XOpenDisplay.
2723 %
2724 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2725 %
2726 % o windows: Specifies a pointer to a XWindows structure.
2727 %
2728 % o image: the image.
2729 %
2730 */
2731 static MagickBooleanType XBackgroundImage(Display *display,
2732  XResourceInfo *resource_info,XWindows *windows,Image **image)
2733 {
2734 #define BackgroundImageTag "Background/Image"
2735 
2736  int
2737  status;
2738 
2739  static char
2740  window_id[MaxTextExtent] = "root";
2741 
2742  XResourceInfo
2743  background_resources;
2744 
2745  /*
2746  Put image in background.
2747  */
2748  status=XDialogWidget(display,windows,"Background",
2749  "Enter window id (id 0x00 selects window with pointer):",window_id);
2750  if (*window_id == '\0')
2751  return(MagickFalse);
2752  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
2753  XInfoWidget(display,windows,BackgroundImageTag);
2754  XSetCursorState(display,windows,MagickTrue);
2755  XCheckRefreshWindows(display,windows);
2756  background_resources=(*resource_info);
2757  background_resources.window_id=window_id;
2758  background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2759  status=XDisplayBackgroundImage(display,&background_resources,*image);
2760  if (status != MagickFalse)
2761  XClientMessage(display,windows->image.id,windows->im_protocols,
2762  windows->im_retain_colors,CurrentTime);
2763  XSetCursorState(display,windows,MagickFalse);
2764  (void) XMagickCommand(display,resource_info,windows,UndoCommand,image);
2765  return(MagickTrue);
2766 }
2767 
2768 /*
2769 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2770 % %
2771 % %
2772 % %
2773 + X C h o p I m a g e %
2774 % %
2775 % %
2776 % %
2777 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2778 %
2779 % XChopImage() chops the X image.
2780 %
2781 % The format of the XChopImage method is:
2782 %
2783 % MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2784 % XWindows *windows,Image **image)
2785 %
2786 % A description of each parameter follows:
2787 %
2788 % o display: Specifies a connection to an X server; returned from
2789 % XOpenDisplay.
2790 %
2791 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2792 %
2793 % o windows: Specifies a pointer to a XWindows structure.
2794 %
2795 % o image: the image.
2796 %
2797 */
2798 static MagickBooleanType XChopImage(Display *display,
2799  XResourceInfo *resource_info,XWindows *windows,Image **image)
2800 {
2801  const char
2802  *const ChopMenu[] =
2803  {
2804  "Direction",
2805  "Help",
2806  "Dismiss",
2807  (char *) NULL
2808  };
2809 
2810  static ModeType
2811  direction = HorizontalChopCommand;
2812 
2813  static const ModeType
2814  ChopCommands[] =
2815  {
2816  ChopDirectionCommand,
2817  ChopHelpCommand,
2818  ChopDismissCommand
2819  },
2820  DirectionCommands[] =
2821  {
2822  HorizontalChopCommand,
2823  VerticalChopCommand
2824  };
2825 
2826  char
2827  text[MaxTextExtent];
2828 
2829  Image
2830  *chop_image;
2831 
2832  int
2833  id,
2834  x,
2835  y;
2836 
2837  MagickRealType
2838  scale_factor;
2839 
2841  chop_info;
2842 
2843  unsigned int
2844  distance,
2845  height,
2846  width;
2847 
2848  size_t
2849  state;
2850 
2851  XEvent
2852  event;
2853 
2854  XSegment
2855  segment_info;
2856 
2857  /*
2858  Map Command widget.
2859  */
2860  (void) CloneString(&windows->command.name,"Chop");
2861  windows->command.data=1;
2862  (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2863  (void) XMapRaised(display,windows->command.id);
2864  XClientMessage(display,windows->image.id,windows->im_protocols,
2865  windows->im_update_widget,CurrentTime);
2866  /*
2867  Track pointer until button 1 is pressed.
2868  */
2869  XQueryPosition(display,windows->image.id,&x,&y);
2870  (void) XSelectInput(display,windows->image.id,
2871  windows->image.attributes.event_mask | PointerMotionMask);
2872  state=DefaultState;
2873  (void) memset(&segment_info,0,sizeof(segment_info));
2874  do
2875  {
2876  if (windows->info.mapped != MagickFalse)
2877  {
2878  /*
2879  Display pointer position.
2880  */
2881  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2882  x+windows->image.x,y+windows->image.y);
2883  XInfoWidget(display,windows,text);
2884  }
2885  /*
2886  Wait for next event.
2887  */
2888  XScreenEvent(display,windows,&event);
2889  if (event.xany.window == windows->command.id)
2890  {
2891  /*
2892  Select a command from the Command widget.
2893  */
2894  id=XCommandWidget(display,windows,ChopMenu,&event);
2895  if (id < 0)
2896  continue;
2897  switch (ChopCommands[id])
2898  {
2899  case ChopDirectionCommand:
2900  {
2901  char
2902  command[MaxTextExtent];
2903 
2904  const char
2905  *const Directions[] =
2906  {
2907  "horizontal",
2908  "vertical",
2909  (char *) NULL,
2910  };
2911 
2912  /*
2913  Select a command from the pop-up menu.
2914  */
2915  id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2916  if (id >= 0)
2917  direction=DirectionCommands[id];
2918  break;
2919  }
2920  case ChopHelpCommand:
2921  {
2922  XTextViewHelp(display,resource_info,windows,MagickFalse,
2923  "Help Viewer - Image Chop",ImageChopHelp);
2924  break;
2925  }
2926  case ChopDismissCommand:
2927  {
2928  /*
2929  Prematurely exit.
2930  */
2931  state|=EscapeState;
2932  state|=ExitState;
2933  break;
2934  }
2935  default:
2936  break;
2937  }
2938  continue;
2939  }
2940  switch (event.type)
2941  {
2942  case ButtonPress:
2943  {
2944  if (event.xbutton.button != Button1)
2945  break;
2946  if (event.xbutton.window != windows->image.id)
2947  break;
2948  /*
2949  User has committed to start point of chopping line.
2950  */
2951  segment_info.x1=(short int) event.xbutton.x;
2952  segment_info.x2=(short int) event.xbutton.x;
2953  segment_info.y1=(short int) event.xbutton.y;
2954  segment_info.y2=(short int) event.xbutton.y;
2955  state|=ExitState;
2956  break;
2957  }
2958  case ButtonRelease:
2959  break;
2960  case Expose:
2961  break;
2962  case KeyPress:
2963  {
2964  char
2965  command[MaxTextExtent];
2966 
2967  KeySym
2968  key_symbol;
2969 
2970  if (event.xkey.window != windows->image.id)
2971  break;
2972  /*
2973  Respond to a user key press.
2974  */
2975  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2976  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2977  switch ((int) key_symbol)
2978  {
2979  case XK_Escape:
2980  case XK_F20:
2981  {
2982  /*
2983  Prematurely exit.
2984  */
2985  state|=EscapeState;
2986  state|=ExitState;
2987  break;
2988  }
2989  case XK_F1:
2990  case XK_Help:
2991  {
2992  (void) XSetFunction(display,windows->image.highlight_context,
2993  GXcopy);
2994  XTextViewHelp(display,resource_info,windows,MagickFalse,
2995  "Help Viewer - Image Chop",ImageChopHelp);
2996  (void) XSetFunction(display,windows->image.highlight_context,
2997  GXinvert);
2998  break;
2999  }
3000  default:
3001  {
3002  (void) XBell(display,0);
3003  break;
3004  }
3005  }
3006  break;
3007  }
3008  case MotionNotify:
3009  {
3010  /*
3011  Map and unmap Info widget as text cursor crosses its boundaries.
3012  */
3013  x=event.xmotion.x;
3014  y=event.xmotion.y;
3015  if (windows->info.mapped != MagickFalse)
3016  {
3017  if ((x < (int) (windows->info.x+windows->info.width)) &&
3018  (y < (int) (windows->info.y+windows->info.height)))
3019  (void) XWithdrawWindow(display,windows->info.id,
3020  windows->info.screen);
3021  }
3022  else
3023  if ((x > (int) (windows->info.x+windows->info.width)) ||
3024  (y > (int) (windows->info.y+windows->info.height)))
3025  (void) XMapWindow(display,windows->info.id);
3026  }
3027  }
3028  } while ((state & ExitState) == 0);
3029  (void) XSelectInput(display,windows->image.id,
3030  windows->image.attributes.event_mask);
3031  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3032  if ((state & EscapeState) != 0)
3033  return(MagickTrue);
3034  /*
3035  Draw line as pointer moves until the mouse button is released.
3036  */
3037  chop_info.width=0;
3038  chop_info.height=0;
3039  chop_info.x=0;
3040  chop_info.y=0;
3041  distance=0;
3042  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3043  state=DefaultState;
3044  do
3045  {
3046  if (distance > 9)
3047  {
3048  /*
3049  Display info and draw chopping line.
3050  */
3051  if (windows->info.mapped == MagickFalse)
3052  (void) XMapWindow(display,windows->info.id);
3053  (void) FormatLocaleString(text,MaxTextExtent,
3054  " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3055  chop_info.height,(double) chop_info.x,(double) chop_info.y);
3056  XInfoWidget(display,windows,text);
3057  XHighlightLine(display,windows->image.id,
3058  windows->image.highlight_context,&segment_info);
3059  }
3060  else
3061  if (windows->info.mapped != MagickFalse)
3062  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3063  /*
3064  Wait for next event.
3065  */
3066  XScreenEvent(display,windows,&event);
3067  if (distance > 9)
3068  XHighlightLine(display,windows->image.id,
3069  windows->image.highlight_context,&segment_info);
3070  switch (event.type)
3071  {
3072  case ButtonPress:
3073  {
3074  segment_info.x2=(short int) event.xmotion.x;
3075  segment_info.y2=(short int) event.xmotion.y;
3076  break;
3077  }
3078  case ButtonRelease:
3079  {
3080  /*
3081  User has committed to chopping line.
3082  */
3083  segment_info.x2=(short int) event.xbutton.x;
3084  segment_info.y2=(short int) event.xbutton.y;
3085  state|=ExitState;
3086  break;
3087  }
3088  case Expose:
3089  break;
3090  case MotionNotify:
3091  {
3092  segment_info.x2=(short int) event.xmotion.x;
3093  segment_info.y2=(short int) event.xmotion.y;
3094  }
3095  default:
3096  break;
3097  }
3098  /*
3099  Check boundary conditions.
3100  */
3101  if (segment_info.x2 < 0)
3102  segment_info.x2=0;
3103  else
3104  if (segment_info.x2 > windows->image.ximage->width)
3105  segment_info.x2=windows->image.ximage->width;
3106  if (segment_info.y2 < 0)
3107  segment_info.y2=0;
3108  else
3109  if (segment_info.y2 > windows->image.ximage->height)
3110  segment_info.y2=windows->image.ximage->height;
3111  distance=(unsigned int)
3112  (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3113  ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3114  /*
3115  Compute chopping geometry.
3116  */
3117  if (direction == HorizontalChopCommand)
3118  {
3119  chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3120  chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3121  chop_info.height=0;
3122  chop_info.y=0;
3123  if (segment_info.x1 > (int) segment_info.x2)
3124  {
3125  chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3126  chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3127  }
3128  }
3129  else
3130  {
3131  chop_info.width=0;
3132  chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3133  chop_info.x=0;
3134  chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3135  if (segment_info.y1 > segment_info.y2)
3136  {
3137  chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3138  chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3139  }
3140  }
3141  } while ((state & ExitState) == 0);
3142  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3143  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3144  if (distance <= 9)
3145  return(MagickTrue);
3146  /*
3147  Image chopping is relative to image configuration.
3148  */
3149  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
3150  XSetCursorState(display,windows,MagickTrue);
3151  XCheckRefreshWindows(display,windows);
3152  windows->image.window_changes.width=windows->image.ximage->width-
3153  (unsigned int) chop_info.width;
3154  windows->image.window_changes.height=windows->image.ximage->height-
3155  (unsigned int) chop_info.height;
3156  width=(unsigned int) (*image)->columns;
3157  height=(unsigned int) (*image)->rows;
3158  x=0;
3159  y=0;
3160  if (windows->image.crop_geometry != (char *) NULL)
3161  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3162  scale_factor=(MagickRealType) width/windows->image.ximage->width;
3163  chop_info.x+=x;
3164  chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3165  chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3166  scale_factor=(MagickRealType) height/windows->image.ximage->height;
3167  chop_info.y+=y;
3168  chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3169  chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3170  /*
3171  Chop image.
3172  */
3173  chop_image=ChopImage(*image,&chop_info,&(*image)->exception);
3174  XSetCursorState(display,windows,MagickFalse);
3175  if (chop_image == (Image *) NULL)
3176  return(MagickFalse);
3177  *image=DestroyImage(*image);
3178  *image=chop_image;
3179  /*
3180  Update image configuration.
3181  */
3182  XConfigureImageColormap(display,resource_info,windows,*image);
3183  (void) XConfigureImage(display,resource_info,windows,*image);
3184  return(MagickTrue);
3185 }
3186 
3187 /*
3188 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3189 % %
3190 % %
3191 % %
3192 + X C o l o r E d i t I m a g e %
3193 % %
3194 % %
3195 % %
3196 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3197 %
3198 % XColorEditImage() allows the user to interactively change the color of one
3199 % pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3200 %
3201 % The format of the XColorEditImage method is:
3202 %
3203 % MagickBooleanType XColorEditImage(Display *display,
3204 % XResourceInfo *resource_info,XWindows *windows,Image **image)
3205 %
3206 % A description of each parameter follows:
3207 %
3208 % o display: Specifies a connection to an X server; returned from
3209 % XOpenDisplay.
3210 %
3211 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3212 %
3213 % o windows: Specifies a pointer to a XWindows structure.
3214 %
3215 % o image: the image; returned from ReadImage.
3216 %
3217 */
3218 
3219 
3220 static MagickBooleanType XColorEditImage(Display *display,
3221  XResourceInfo *resource_info,XWindows *windows,Image **image)
3222 {
3223  const char
3224  *const ColorEditMenu[] =
3225  {
3226  "Method",
3227  "Pixel Color",
3228  "Border Color",
3229  "Fuzz",
3230  "Undo",
3231  "Help",
3232  "Dismiss",
3233  (char *) NULL
3234  };
3235 
3236  static const ModeType
3237  ColorEditCommands[] =
3238  {
3239  ColorEditMethodCommand,
3240  ColorEditColorCommand,
3241  ColorEditBorderCommand,
3242  ColorEditFuzzCommand,
3243  ColorEditUndoCommand,
3244  ColorEditHelpCommand,
3245  ColorEditDismissCommand
3246  };
3247 
3248  static PaintMethod
3249  method = PointMethod;
3250 
3251  static unsigned int
3252  pen_id = 0;
3253 
3254  static XColor
3255  border_color = { 0, 0, 0, 0, 0, 0 };
3256 
3257  char
3258  command[MaxTextExtent] = "",
3259  text[MaxTextExtent] = "";
3260 
3261  Cursor
3262  cursor;
3263 
3265  *exception;
3266 
3267  int
3268  entry,
3269  id,
3270  x,
3271  x_offset,
3272  y,
3273  y_offset;
3274 
3275  PixelPacket
3276  *q;
3277 
3278  ssize_t
3279  i;
3280 
3281  unsigned int
3282  height,
3283  width;
3284 
3285  size_t
3286  state;
3287 
3288  XColor
3289  color;
3290 
3291  XEvent
3292  event;
3293 
3294  /*
3295  Map Command widget.
3296  */
3297  (void) CloneString(&windows->command.name,"Color Edit");
3298  windows->command.data=4;
3299  (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3300  (void) XMapRaised(display,windows->command.id);
3301  XClientMessage(display,windows->image.id,windows->im_protocols,
3302  windows->im_update_widget,CurrentTime);
3303  /*
3304  Make cursor.
3305  */
3306  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3307  resource_info->background_color,resource_info->foreground_color);
3308  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3309  /*
3310  Track pointer until button 1 is pressed.
3311  */
3312  XQueryPosition(display,windows->image.id,&x,&y);
3313  (void) XSelectInput(display,windows->image.id,
3314  windows->image.attributes.event_mask | PointerMotionMask);
3315  state=DefaultState;
3316  do
3317  {
3318  if (windows->info.mapped != MagickFalse)
3319  {
3320  /*
3321  Display pointer position.
3322  */
3323  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3324  x+windows->image.x,y+windows->image.y);
3325  XInfoWidget(display,windows,text);
3326  }
3327  /*
3328  Wait for next event.
3329  */
3330  XScreenEvent(display,windows,&event);
3331  if (event.xany.window == windows->command.id)
3332  {
3333  /*
3334  Select a command from the Command widget.
3335  */
3336  id=XCommandWidget(display,windows,ColorEditMenu,&event);
3337  if (id < 0)
3338  {
3339  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3340  continue;
3341  }
3342  switch (ColorEditCommands[id])
3343  {
3344  case ColorEditMethodCommand:
3345  {
3346  char
3347  **methods;
3348 
3349  /*
3350  Select a method from the pop-up menu.
3351  */
3352  methods=(char **) GetCommandOptions(MagickMethodOptions);
3353  if (methods == (char **) NULL)
3354  break;
3355  entry=XMenuWidget(display,windows,ColorEditMenu[id],
3356  (const char **) methods,command);
3357  if (entry >= 0)
3358  method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3359  MagickFalse,methods[entry]);
3360  methods=DestroyStringList(methods);
3361  break;
3362  }
3363  case ColorEditColorCommand:
3364  {
3365  const char
3366  *ColorMenu[MaxNumberPens];
3367 
3368  int
3369  pen_number;
3370 
3371  /*
3372  Initialize menu selections.
3373  */
3374  for (i=0; i < (int) (MaxNumberPens-2); i++)
3375  ColorMenu[i]=resource_info->pen_colors[i];
3376  ColorMenu[MaxNumberPens-2]="Browser...";
3377  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3378  /*
3379  Select a pen color from the pop-up menu.
3380  */
3381  pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3382  (const char **) ColorMenu,command);
3383  if (pen_number < 0)
3384  break;
3385  if (pen_number == (MaxNumberPens-2))
3386  {
3387  static char
3388  color_name[MaxTextExtent] = "gray";
3389 
3390  /*
3391  Select a pen color from a dialog.
3392  */
3393  resource_info->pen_colors[pen_number]=color_name;
3394  XColorBrowserWidget(display,windows,"Select",color_name);
3395  if (*color_name == '\0')
3396  break;
3397  }
3398  /*
3399  Set pen color.
3400  */
3401  (void) XParseColor(display,windows->map_info->colormap,
3402  resource_info->pen_colors[pen_number],&color);
3403  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3404  (unsigned int) MaxColors,&color);
3405  windows->pixel_info->pen_colors[pen_number]=color;
3406  pen_id=(unsigned int) pen_number;
3407  break;
3408  }
3409  case ColorEditBorderCommand:
3410  {
3411  const char
3412  *ColorMenu[MaxNumberPens];
3413 
3414  int
3415  pen_number;
3416 
3417  /*
3418  Initialize menu selections.
3419  */
3420  for (i=0; i < (int) (MaxNumberPens-2); i++)
3421  ColorMenu[i]=resource_info->pen_colors[i];
3422  ColorMenu[MaxNumberPens-2]="Browser...";
3423  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3424  /*
3425  Select a pen color from the pop-up menu.
3426  */
3427  pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3428  (const char **) ColorMenu,command);
3429  if (pen_number < 0)
3430  break;
3431  if (pen_number == (MaxNumberPens-2))
3432  {
3433  static char
3434  color_name[MaxTextExtent] = "gray";
3435 
3436  /*
3437  Select a pen color from a dialog.
3438  */
3439  resource_info->pen_colors[pen_number]=color_name;
3440  XColorBrowserWidget(display,windows,"Select",color_name);
3441  if (*color_name == '\0')
3442  break;
3443  }
3444  /*
3445  Set border color.
3446  */
3447  (void) XParseColor(display,windows->map_info->colormap,
3448  resource_info->pen_colors[pen_number],&border_color);
3449  break;
3450  }
3451  case ColorEditFuzzCommand:
3452  {
3453  static char
3454  fuzz[MaxTextExtent];
3455 
3456  static const char
3457  *FuzzMenu[] =
3458  {
3459  "0%",
3460  "2%",
3461  "5%",
3462  "10%",
3463  "15%",
3464  "Dialog...",
3465  (char *) NULL,
3466  };
3467 
3468  /*
3469  Select a command from the pop-up menu.
3470  */
3471  entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3472  command);
3473  if (entry < 0)
3474  break;
3475  if (entry != 5)
3476  {
3477  (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3478  QuantumRange+1.0);
3479  break;
3480  }
3481  (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3482  (void) XDialogWidget(display,windows,"Ok",
3483  "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3484  if (*fuzz == '\0')
3485  break;
3486  (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3487  (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3488  1.0);
3489  break;
3490  }
3491  case ColorEditUndoCommand:
3492  {
3493  (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3494  image);
3495  break;
3496  }
3497  case ColorEditHelpCommand:
3498  default:
3499  {
3500  XTextViewHelp(display,resource_info,windows,MagickFalse,
3501  "Help Viewer - Image Annotation",ImageColorEditHelp);
3502  break;
3503  }
3504  case ColorEditDismissCommand:
3505  {
3506  /*
3507  Prematurely exit.
3508  */
3509  state|=EscapeState;
3510  state|=ExitState;
3511  break;
3512  }
3513  }
3514  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3515  continue;
3516  }
3517  switch (event.type)
3518  {
3519  case ButtonPress:
3520  {
3521  if (event.xbutton.button != Button1)
3522  break;
3523  if ((event.xbutton.window != windows->image.id) &&
3524  (event.xbutton.window != windows->magnify.id))
3525  break;
3526  /*
3527  exit loop.
3528  */
3529  x=event.xbutton.x;
3530  y=event.xbutton.y;
3531  (void) XMagickCommand(display,resource_info,windows,
3532  SaveToUndoBufferCommand,image);
3533  state|=UpdateConfigurationState;
3534  break;
3535  }
3536  case ButtonRelease:
3537  {
3538  if (event.xbutton.button != Button1)
3539  break;
3540  if ((event.xbutton.window != windows->image.id) &&
3541  (event.xbutton.window != windows->magnify.id))
3542  break;
3543  /*
3544  Update colormap information.
3545  */
3546  x=event.xbutton.x;
3547  y=event.xbutton.y;
3548  XConfigureImageColormap(display,resource_info,windows,*image);
3549  (void) XConfigureImage(display,resource_info,windows,*image);
3550  XInfoWidget(display,windows,text);
3551  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3552  state&=(~UpdateConfigurationState);
3553  break;
3554  }
3555  case Expose:
3556  break;
3557  case KeyPress:
3558  {
3559  KeySym
3560  key_symbol;
3561 
3562  if (event.xkey.window == windows->magnify.id)
3563  {
3564  Window
3565  window;
3566 
3567  window=windows->magnify.id;
3568  while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3569  }
3570  if (event.xkey.window != windows->image.id)
3571  break;
3572  /*
3573  Respond to a user key press.
3574  */
3575  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3576  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3577  switch ((int) key_symbol)
3578  {
3579  case XK_Escape:
3580  case XK_F20:
3581  {
3582  /*
3583  Prematurely exit.
3584  */
3585  state|=ExitState;
3586  break;
3587  }
3588  case XK_F1:
3589  case XK_Help:
3590  {
3591  XTextViewHelp(display,resource_info,windows,MagickFalse,
3592  "Help Viewer - Image Annotation",ImageColorEditHelp);
3593  break;
3594  }
3595  default:
3596  {
3597  (void) XBell(display,0);
3598  break;
3599  }
3600  }
3601  break;
3602  }
3603  case MotionNotify:
3604  {
3605  /*
3606  Map and unmap Info widget as cursor crosses its boundaries.
3607  */
3608  x=event.xmotion.x;
3609  y=event.xmotion.y;
3610  if (windows->info.mapped != MagickFalse)
3611  {
3612  if ((x < (int) (windows->info.x+windows->info.width)) &&
3613  (y < (int) (windows->info.y+windows->info.height)))
3614  (void) XWithdrawWindow(display,windows->info.id,
3615  windows->info.screen);
3616  }
3617  else
3618  if ((x > (int) (windows->info.x+windows->info.width)) ||
3619  (y > (int) (windows->info.y+windows->info.height)))
3620  (void) XMapWindow(display,windows->info.id);
3621  break;
3622  }
3623  default:
3624  break;
3625  }
3626  if (event.xany.window == windows->magnify.id)
3627  {
3628  x=windows->magnify.x-windows->image.x;
3629  y=windows->magnify.y-windows->image.y;
3630  }
3631  x_offset=x;
3632  y_offset=y;
3633  if ((state & UpdateConfigurationState) != 0)
3634  {
3635  CacheView
3636  *image_view;
3637 
3638  int
3639  x,
3640  y;
3641 
3642  /*
3643  Pixel edit is relative to image configuration.
3644  */
3645  (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3646  MagickTrue);
3647  color=windows->pixel_info->pen_colors[pen_id];
3648  XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3649  width=(unsigned int) (*image)->columns;
3650  height=(unsigned int) (*image)->rows;
3651  x=0;
3652  y=0;
3653  if (windows->image.crop_geometry != (char *) NULL)
3654  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3655  &width,&height);
3656  x_offset=(int)
3657  (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3658  y_offset=(int)
3659  (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3660  if ((x_offset < 0) || (y_offset < 0))
3661  continue;
3662  if ((x_offset >= (int) (*image)->columns) ||
3663  (y_offset >= (int) (*image)->rows))
3664  continue;
3665  exception=(&(*image)->exception);
3666  image_view=AcquireAuthenticCacheView(*image,exception);
3667  switch (method)
3668  {
3669  case PointMethod:
3670  default:
3671  {
3672  /*
3673  Update color information using point algorithm.
3674  */
3675  if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3676  return(MagickFalse);
3677  q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3678  (ssize_t)y_offset,1,1,exception);
3679  if (q == (PixelPacket *) NULL)
3680  break;
3681  q->red=ScaleShortToQuantum(color.red);
3682  q->green=ScaleShortToQuantum(color.green);
3683  q->blue=ScaleShortToQuantum(color.blue);
3684  (void) SyncCacheViewAuthenticPixels(image_view,
3685  &(*image)->exception);
3686  break;
3687  }
3688  case ReplaceMethod:
3689  {
3690  PixelPacket
3691  target;
3692 
3693  /*
3694  Update color information using replace algorithm.
3695  */
3696  (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3697  (ssize_t) y_offset,&target,&(*image)->exception);
3698  if ((*image)->storage_class == DirectClass)
3699  {
3700  for (y=0; y < (int) (*image)->rows; y++)
3701  {
3702  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3703  (*image)->columns,1,exception);
3704  if (q == (PixelPacket *) NULL)
3705  break;
3706  for (x=0; x < (int) (*image)->columns; x++)
3707  {
3708  if (IsColorSimilar(*image,q,&target) != MagickFalse)
3709  {
3710  q->red=ScaleShortToQuantum(color.red);
3711  q->green=ScaleShortToQuantum(color.green);
3712  q->blue=ScaleShortToQuantum(color.blue);
3713  }
3714  q++;
3715  }
3716  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3717  break;
3718  }
3719  }
3720  else
3721  {
3722  for (i=0; i < (ssize_t) (*image)->colors; i++)
3723  if (IsColorSimilar(*image,(*image)->colormap+i,&target) != MagickFalse)
3724  {
3725  (*image)->colormap[i].red=ScaleShortToQuantum(color.red);
3726  (*image)->colormap[i].green=ScaleShortToQuantum(
3727  color.green);
3728  (*image)->colormap[i].blue=ScaleShortToQuantum(
3729  color.blue);
3730  }
3731  (void) SyncImage(*image);
3732  }
3733  break;
3734  }
3735  case FloodfillMethod:
3736  case FillToBorderMethod:
3737  {
3738  DrawInfo
3739  *draw_info;
3740 
3742  target;
3743 
3744  /*
3745  Update color information using floodfill algorithm.
3746  */
3747  (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
3748  (ssize_t) y_offset,&target,exception);
3749  if (method == FillToBorderMethod)
3750  {
3751  target.red=(MagickRealType)
3752  ScaleShortToQuantum(border_color.red);
3753  target.green=(MagickRealType)
3754  ScaleShortToQuantum(border_color.green);
3755  target.blue=(MagickRealType)
3756  ScaleShortToQuantum(border_color.blue);
3757  }
3758  draw_info=CloneDrawInfo(resource_info->image_info,
3759  (DrawInfo *) NULL);
3760  (void) QueryColorDatabase(resource_info->pen_colors[pen_id],
3761  &draw_info->fill,exception);
3762  (void) FloodfillPaintImage(*image,DefaultChannels,draw_info,&target,
3763  (ssize_t) x_offset,(ssize_t) y_offset,
3764  method == FloodfillMethod ? MagickFalse : MagickTrue);
3765  draw_info=DestroyDrawInfo(draw_info);
3766  break;
3767  }
3768  case ResetMethod:
3769  {
3770  /*
3771  Update color information using reset algorithm.
3772  */
3773  if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3774  return(MagickFalse);
3775  for (y=0; y < (int) (*image)->rows; y++)
3776  {
3777  q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3778  (*image)->columns,1,exception);
3779  if (q == (PixelPacket *) NULL)
3780  break;
3781  for (x=0; x < (int) (*image)->columns; x++)
3782  {
3783  q->red=ScaleShortToQuantum(color.red);
3784  q->green=ScaleShortToQuantum(color.green);
3785  q->blue=ScaleShortToQuantum(color.blue);
3786  q++;
3787  }
3788  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3789  break;
3790  }
3791  break;
3792  }
3793  }
3794  image_view=DestroyCacheView(image_view);
3795  state&=(~UpdateConfigurationState);
3796  }
3797  } while ((state & ExitState) == 0);
3798  (void) XSelectInput(display,windows->image.id,
3799  windows->image.attributes.event_mask);
3800  XSetCursorState(display,windows,MagickFalse);
3801  (void) XFreeCursor(display,cursor);
3802  return(MagickTrue);
3803 }
3804 
3805 /*
3806 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3807 % %
3808 % %
3809 % %
3810 + X C o m p o s i t e I m a g e %
3811 % %
3812 % %
3813 % %
3814 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3815 %
3816 % XCompositeImage() requests an image name from the user, reads the image and
3817 % composites it with the X window image at a location the user chooses with
3818 % the pointer.
3819 %
3820 % The format of the XCompositeImage method is:
3821 %
3822 % MagickBooleanType XCompositeImage(Display *display,
3823 % XResourceInfo *resource_info,XWindows *windows,Image *image)
3824 %
3825 % A description of each parameter follows:
3826 %
3827 % o display: Specifies a connection to an X server; returned from
3828 % XOpenDisplay.
3829 %
3830 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3831 %
3832 % o windows: Specifies a pointer to a XWindows structure.
3833 %
3834 % o image: the image; returned from ReadImage.
3835 %
3836 */
3837 static MagickBooleanType XCompositeImage(Display *display,
3838  XResourceInfo *resource_info,XWindows *windows,Image *image)
3839 {
3840  const char
3841  *const CompositeMenu[] =
3842  {
3843  "Operators",
3844  "Dissolve",
3845  "Displace",
3846  "Help",
3847  "Dismiss",
3848  (char *) NULL
3849  };
3850 
3851  static char
3852  displacement_geometry[MaxTextExtent] = "30x30",
3853  filename[MaxTextExtent] = "\0";
3854 
3855  static CompositeOperator
3856  compose = CopyCompositeOp;
3857 
3858  static const ModeType
3859  CompositeCommands[] =
3860  {
3861  CompositeOperatorsCommand,
3862  CompositeDissolveCommand,
3863  CompositeDisplaceCommand,
3864  CompositeHelpCommand,
3865  CompositeDismissCommand
3866  };
3867 
3868  char
3869  text[MaxTextExtent];
3870 
3871  Cursor
3872  cursor;
3873 
3874  Image
3875  *composite_image;
3876 
3877  int
3878  entry,
3879  id,
3880  x,
3881  y;
3882 
3883  MagickRealType
3884  blend,
3885  scale_factor;
3886 
3888  highlight_info,
3889  composite_info;
3890 
3891  unsigned int
3892  height,
3893  width;
3894 
3895  size_t
3896  state;
3897 
3898  XEvent
3899  event;
3900 
3901  /*
3902  Request image file name from user.
3903  */
3904  XFileBrowserWidget(display,windows,"Composite",filename);
3905  if (*filename == '\0')
3906  return(MagickTrue);
3907  /*
3908  Read image.
3909  */
3910  XSetCursorState(display,windows,MagickTrue);
3911  XCheckRefreshWindows(display,windows);
3912  (void) CopyMagickString(resource_info->image_info->filename,filename,
3913  MaxTextExtent);
3914  composite_image=ReadImage(resource_info->image_info,&image->exception);
3915  CatchException(&image->exception);
3916  XSetCursorState(display,windows,MagickFalse);
3917  if (composite_image == (Image *) NULL)
3918  return(MagickFalse);
3919  /*
3920  Map Command widget.
3921  */
3922  (void) CloneString(&windows->command.name,"Composite");
3923  windows->command.data=1;
3924  (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3925  (void) XMapRaised(display,windows->command.id);
3926  XClientMessage(display,windows->image.id,windows->im_protocols,
3927  windows->im_update_widget,CurrentTime);
3928  /*
3929  Track pointer until button 1 is pressed.
3930  */
3931  XQueryPosition(display,windows->image.id,&x,&y);
3932  (void) XSelectInput(display,windows->image.id,
3933  windows->image.attributes.event_mask | PointerMotionMask);
3934  composite_info.x=(ssize_t) windows->image.x+x;
3935  composite_info.y=(ssize_t) windows->image.y+y;
3936  composite_info.width=0;
3937  composite_info.height=0;
3938  cursor=XCreateFontCursor(display,XC_ul_angle);
3939  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3940  blend=0.0;
3941  state=DefaultState;
3942  do
3943  {
3944  if (windows->info.mapped != MagickFalse)
3945  {
3946  /*
3947  Display pointer position.
3948  */
3949  (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
3950  (long) composite_info.x,(long) composite_info.y);
3951  XInfoWidget(display,windows,text);
3952  }
3953  highlight_info=composite_info;
3954  highlight_info.x=composite_info.x-windows->image.x;
3955  highlight_info.y=composite_info.y-windows->image.y;
3956  XHighlightRectangle(display,windows->image.id,
3957  windows->image.highlight_context,&highlight_info);
3958  /*
3959  Wait for next event.
3960  */
3961  XScreenEvent(display,windows,&event);
3962  XHighlightRectangle(display,windows->image.id,
3963  windows->image.highlight_context,&highlight_info);
3964  if (event.xany.window == windows->command.id)
3965  {
3966  /*
3967  Select a command from the Command widget.
3968  */
3969  id=XCommandWidget(display,windows,CompositeMenu,&event);
3970  if (id < 0)
3971  continue;
3972  switch (CompositeCommands[id])
3973  {
3974  case CompositeOperatorsCommand:
3975  {
3976  char
3977  command[MaxTextExtent],
3978  **operators;
3979 
3980  /*
3981  Select a command from the pop-up menu.
3982  */
3983  operators=GetCommandOptions(MagickComposeOptions);
3984  if (operators == (char **) NULL)
3985  break;
3986  entry=XMenuWidget(display,windows,CompositeMenu[id],
3987  (const char **) operators,command);
3988  if (entry >= 0)
3989  compose=(CompositeOperator) ParseCommandOption(
3990  MagickComposeOptions,MagickFalse,operators[entry]);
3991  operators=DestroyStringList(operators);
3992  break;
3993  }
3994  case CompositeDissolveCommand:
3995  {
3996  static char
3997  factor[MaxTextExtent] = "20.0";
3998 
3999  /*
4000  Dissolve the two images a given percent.
4001  */
4002  (void) XSetFunction(display,windows->image.highlight_context,
4003  GXcopy);
4004  (void) XDialogWidget(display,windows,"Dissolve",
4005  "Enter the blend factor (0.0 - 99.9%):",factor);
4006  (void) XSetFunction(display,windows->image.highlight_context,
4007  GXinvert);
4008  if (*factor == '\0')
4009  break;
4010  blend=StringToDouble(factor,(char **) NULL);
4011  compose=DissolveCompositeOp;
4012  break;
4013  }
4014  case CompositeDisplaceCommand:
4015  {
4016  /*
4017  Get horizontal and vertical scale displacement geometry.
4018  */
4019  (void) XSetFunction(display,windows->image.highlight_context,
4020  GXcopy);
4021  (void) XDialogWidget(display,windows,"Displace",
4022  "Enter the horizontal and vertical scale:",displacement_geometry);
4023  (void) XSetFunction(display,windows->image.highlight_context,
4024  GXinvert);
4025  if (*displacement_geometry == '\0')
4026  break;
4027  compose=DisplaceCompositeOp;
4028  break;
4029  }
4030  case CompositeHelpCommand:
4031  {
4032  (void) XSetFunction(display,windows->image.highlight_context,
4033  GXcopy);
4034  XTextViewHelp(display,resource_info,windows,MagickFalse,
4035  "Help Viewer - Image Composite",ImageCompositeHelp);
4036  (void) XSetFunction(display,windows->image.highlight_context,
4037  GXinvert);
4038  break;
4039  }
4040  case CompositeDismissCommand:
4041  {
4042  /*
4043  Prematurely exit.
4044  */
4045  state|=EscapeState;
4046  state|=ExitState;
4047  break;
4048  }
4049  default:
4050  break;
4051  }
4052  continue;
4053  }
4054  switch (event.type)
4055  {
4056  case ButtonPress:
4057  {
4058  if (resource_info->debug != MagickFalse)
4059  (void) LogMagickEvent(X11Event,GetMagickModule(),
4060  "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4061  event.xbutton.button,event.xbutton.x,event.xbutton.y);
4062  if (event.xbutton.button != Button1)
4063  break;
4064  if (event.xbutton.window != windows->image.id)
4065  break;
4066  /*
4067  Change cursor.
4068  */
4069  composite_info.width=composite_image->columns;
4070  composite_info.height=composite_image->rows;
4071  (void) XCheckDefineCursor(display,windows->image.id,cursor);
4072  composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4073  composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4074  break;
4075  }
4076  case ButtonRelease:
4077  {
4078  if (resource_info->debug != MagickFalse)
4079  (void) LogMagickEvent(X11Event,GetMagickModule(),
4080  "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4081  event.xbutton.button,event.xbutton.x,event.xbutton.y);
4082  if (event.xbutton.button != Button1)
4083  break;
4084  if (event.xbutton.window != windows->image.id)
4085  break;
4086  if ((composite_info.width != 0) && (composite_info.height != 0))
4087  {
4088  /*
4089  User has selected the location of the composite image.
4090  */
4091  composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4092  composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4093  state|=ExitState;
4094  }
4095  break;
4096  }
4097  case Expose:
4098  break;
4099  case KeyPress:
4100  {
4101  char
4102  command[MaxTextExtent];
4103 
4104  KeySym
4105  key_symbol;
4106 
4107  int
4108  length;
4109 
4110  if (event.xkey.window != windows->image.id)
4111  break;
4112  /*
4113  Respond to a user key press.
4114  */
4115  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4116  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4117  *(command+length)='\0';
4118  if (resource_info->debug != MagickFalse)
4119  (void) LogMagickEvent(X11Event,GetMagickModule(),
4120  "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4121  switch ((int) key_symbol)
4122  {
4123  case XK_Escape:
4124  case XK_F20:
4125  {
4126  /*
4127  Prematurely exit.
4128  */
4129  composite_image=DestroyImage(composite_image);
4130  state|=EscapeState;
4131  state|=ExitState;
4132  break;
4133  }
4134  case XK_F1:
4135  case XK_Help:
4136  {
4137  (void) XSetFunction(display,windows->image.highlight_context,
4138  GXcopy);
4139  XTextViewHelp(display,resource_info,windows,MagickFalse,
4140  "Help Viewer - Image Composite",ImageCompositeHelp);
4141  (void) XSetFunction(display,windows->image.highlight_context,
4142  GXinvert);
4143  break;
4144  }
4145  default:
4146  {
4147  (void) XBell(display,0);
4148  break;
4149  }
4150  }
4151  break;
4152  }
4153  case MotionNotify:
4154  {
4155  /*
4156  Map and unmap Info widget as text cursor crosses its boundaries.
4157  */
4158  x=event.xmotion.x;
4159  y=event.xmotion.y;
4160  if (windows->info.mapped != MagickFalse)
4161  {
4162  if ((x < (int) (windows->info.x+windows->info.width)) &&
4163  (y < (int) (windows->info.y+windows->info.height)))
4164  (void) XWithdrawWindow(display,windows->info.id,
4165  windows->info.screen);
4166  }
4167  else
4168  if ((x > (int) (windows->info.x+windows->info.width)) ||
4169  (y > (int) (windows->info.y+windows->info.height)))
4170  (void) XMapWindow(display,windows->info.id);
4171  composite_info.x=(ssize_t) windows->image.x+x;
4172  composite_info.y=(ssize_t) windows->image.y+y;
4173  break;
4174  }
4175  default:
4176  {
4177  if (resource_info->debug != MagickFalse)
4178  (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4179  event.type);
4180  break;
4181  }
4182  }
4183  } while ((state & ExitState) == 0);
4184  (void) XSelectInput(display,windows->image.id,
4185  windows->image.attributes.event_mask);
4186  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4187  XSetCursorState(display,windows,MagickFalse);
4188  (void) XFreeCursor(display,cursor);
4189  if ((state & EscapeState) != 0)
4190  return(MagickTrue);
4191  /*
4192  Image compositing is relative to image configuration.
4193  */
4194  XSetCursorState(display,windows,MagickTrue);
4195  XCheckRefreshWindows(display,windows);
4196  width=(unsigned int) image->columns;
4197  height=(unsigned int) image->rows;
4198  x=0;
4199  y=0;
4200  if (windows->image.crop_geometry != (char *) NULL)
4201  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4202  scale_factor=(MagickRealType) width/windows->image.ximage->width;
4203  composite_info.x+=x;
4204  composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4205  composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4206  scale_factor=(MagickRealType) height/windows->image.ximage->height;
4207  composite_info.y+=y;
4208  composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4209  composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4210  if ((composite_info.width != composite_image->columns) ||
4211  (composite_info.height != composite_image->rows))
4212  {
4213  Image
4214  *resize_image;
4215 
4216  /*
4217  Scale composite image.
4218  */
4219  resize_image=ResizeImage(composite_image,composite_info.width,
4220  composite_info.height,composite_image->filter,composite_image->blur,
4221  &image->exception);
4222  composite_image=DestroyImage(composite_image);
4223  if (resize_image == (Image *) NULL)
4224  {
4225  XSetCursorState(display,windows,MagickFalse);
4226  return(MagickFalse);
4227  }
4228  composite_image=resize_image;
4229  }
4230  if (compose == DisplaceCompositeOp)
4231  (void) SetImageArtifact(composite_image,"compose:args",
4232  displacement_geometry);
4233  if (blend != 0.0)
4234  {
4235  CacheView
4236  *image_view;
4237 
4239  *exception;
4240 
4241  int
4242  y;
4243 
4244  Quantum
4245  opacity;
4246 
4247  int
4248  x;
4249 
4250  PixelPacket
4251  *q;
4252 
4253  /*
4254  Create mattes for blending.
4255  */
4256  (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel);
4257  opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4258  ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4259  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
4260  return(MagickFalse);
4261  image->matte=MagickTrue;
4262  exception=(&image->exception);
4263  image_view=AcquireAuthenticCacheView(image,exception);
4264  for (y=0; y < (int) image->rows; y++)
4265  {
4266  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4267  exception);
4268  if (q == (PixelPacket *) NULL)
4269  break;
4270  for (x=0; x < (int) image->columns; x++)
4271  {
4272  q->opacity=opacity;
4273  q++;
4274  }
4275  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4276  break;
4277  }
4278  image_view=DestroyCacheView(image_view);
4279  }
4280  /*
4281  Composite image with X Image window.
4282  */
4283  (void) CompositeImage(image,compose,composite_image,composite_info.x,
4284  composite_info.y);
4285  composite_image=DestroyImage(composite_image);
4286  XSetCursorState(display,windows,MagickFalse);
4287  /*
4288  Update image configuration.
4289  */
4290  XConfigureImageColormap(display,resource_info,windows,image);
4291  (void) XConfigureImage(display,resource_info,windows,image);
4292  return(MagickTrue);
4293 }
4294 
4295 /*
4296 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4297 % %
4298 % %
4299 % %
4300 + X C o n f i g u r e I m a g e %
4301 % %
4302 % %
4303 % %
4304 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4305 %
4306 % XConfigureImage() creates a new X image. It also notifies the window
4307 % manager of the new image size and configures the transient widows.
4308 %
4309 % The format of the XConfigureImage method is:
4310 %
4311 % MagickBooleanType XConfigureImage(Display *display,
4312 % XResourceInfo *resource_info,XWindows *windows,Image *image)
4313 %
4314 % A description of each parameter follows:
4315 %
4316 % o display: Specifies a connection to an X server; returned from
4317 % XOpenDisplay.
4318 %
4319 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4320 %
4321 % o windows: Specifies a pointer to a XWindows structure.
4322 %
4323 % o image: the image.
4324 %
4325 %
4326 */
4327 static MagickBooleanType XConfigureImage(Display *display,
4328  XResourceInfo *resource_info,XWindows *windows,Image *image)
4329 {
4330  char
4331  geometry[MaxTextExtent];
4332 
4333  MagickStatusType
4334  status;
4335 
4336  size_t
4337  mask,
4338  height,
4339  width;
4340 
4341  ssize_t
4342  x,
4343  y;
4344 
4345  XSizeHints
4346  *size_hints;
4347 
4348  XWindowChanges
4349  window_changes;
4350 
4351  /*
4352  Dismiss if window dimensions are zero.
4353  */
4354  width=(unsigned int) windows->image.window_changes.width;
4355  height=(unsigned int) windows->image.window_changes.height;
4356  if (resource_info->debug != MagickFalse)
4357  (void) LogMagickEvent(X11Event,GetMagickModule(),
4358  "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4359  windows->image.ximage->height,(double) width,(double) height);
4360  if ((width*height) == 0)
4361  return(MagickTrue);
4362  x=0;
4363  y=0;
4364  /*
4365  Resize image to fit Image window dimensions.
4366  */
4367  XSetCursorState(display,windows,MagickTrue);
4368  (void) XFlush(display);
4369  if (((int) width != windows->image.ximage->width) ||
4370  ((int) height != windows->image.ximage->height))
4371  image->taint=MagickTrue;
4372  windows->magnify.x=(int)
4373  width*windows->magnify.x/windows->image.ximage->width;
4374  windows->magnify.y=(int)
4375  height*windows->magnify.y/windows->image.ximage->height;
4376  windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4377  windows->image.y=(int)
4378  (height*windows->image.y/windows->image.ximage->height);
4379  status=XMakeImage(display,resource_info,&windows->image,image,
4380  (unsigned int) width,(unsigned int) height);
4381  if (status == MagickFalse)
4382  XNoticeWidget(display,windows,"Unable to configure X image:",
4383  windows->image.name);
4384  /*
4385  Notify window manager of the new configuration.
4386  */
4387  if (resource_info->image_geometry != (char *) NULL)
4388  (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4389  resource_info->image_geometry);
4390  else
4391  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4392  XDisplayWidth(display,windows->image.screen),
4393  XDisplayHeight(display,windows->image.screen));
4394  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4395  window_changes.width=(int) width;
4396  if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4397  window_changes.width=XDisplayWidth(display,windows->image.screen);
4398  window_changes.height=(int) height;
4399  if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4400  window_changes.height=XDisplayHeight(display,windows->image.screen);
4401  mask=(size_t) (CWWidth | CWHeight);
4402  if (resource_info->backdrop)
4403  {
4404  mask|=CWX | CWY;
4405  window_changes.x=(int)
4406  ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4407  window_changes.y=(int)
4408  ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4409  }
4410  (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4411  (unsigned int) mask,&window_changes);
4412  (void) XClearWindow(display,windows->image.id);
4413  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4414  /*
4415  Update Magnify window configuration.
4416  */
4417  if (windows->magnify.mapped != MagickFalse)
4418  XMakeMagnifyImage(display,windows);
4419  windows->pan.crop_geometry=windows->image.crop_geometry;
4420  XBestIconSize(display,&windows->pan,image);
4421  while (((windows->pan.width << 1) < MaxIconSize) &&
4422  ((windows->pan.height << 1) < MaxIconSize))
4423  {
4424  windows->pan.width<<=1;
4425  windows->pan.height<<=1;
4426  }
4427  if (windows->pan.geometry != (char *) NULL)
4428  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4429  &windows->pan.width,&windows->pan.height);
4430  window_changes.width=(int) windows->pan.width;
4431  window_changes.height=(int) windows->pan.height;
4432  size_hints=XAllocSizeHints();
4433  if (size_hints != (XSizeHints *) NULL)
4434  {
4435  /*
4436  Set new size hints.
4437  */
4438  size_hints->flags=PSize | PMinSize | PMaxSize;
4439  size_hints->width=window_changes.width;
4440  size_hints->height=window_changes.height;
4441  size_hints->min_width=size_hints->width;
4442  size_hints->min_height=size_hints->height;
4443  size_hints->max_width=size_hints->width;
4444  size_hints->max_height=size_hints->height;
4445  (void) XSetNormalHints(display,windows->pan.id,size_hints);
4446  (void) XFree((void *) size_hints);
4447  }
4448  (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4449  (unsigned int) (CWWidth | CWHeight),&window_changes);
4450  /*
4451  Update icon window configuration.
4452  */
4453  windows->icon.crop_geometry=windows->image.crop_geometry;
4454  XBestIconSize(display,&windows->icon,image);
4455  window_changes.width=(int) windows->icon.width;
4456  window_changes.height=(int) windows->icon.height;
4457  (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4458  (unsigned int) (CWWidth | CWHeight),&window_changes);
4459  XSetCursorState(display,windows,MagickFalse);
4460  return(status != 0 ? MagickTrue : MagickFalse);
4461 }
4462 
4463 /*
4464 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4465 % %
4466 % %
4467 % %
4468 + X C r o p I m a g e %
4469 % %
4470 % %
4471 % %
4472 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4473 %
4474 % XCropImage() allows the user to select a region of the image and crop, copy,
4475 % or cut it. For copy or cut, the image can subsequently be composited onto
4476 % the image with XPasteImage.
4477 %
4478 % The format of the XCropImage method is:
4479 %
4480 % MagickBooleanType XCropImage(Display *display,
4481 % XResourceInfo *resource_info,XWindows *windows,Image *image,
4482 % const ClipboardMode mode)
4483 %
4484 % A description of each parameter follows:
4485 %
4486 % o display: Specifies a connection to an X server; returned from
4487 % XOpenDisplay.
4488 %
4489 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4490 %
4491 % o windows: Specifies a pointer to a XWindows structure.
4492 %
4493 % o image: the image; returned from ReadImage.
4494 %
4495 % o mode: This unsigned value specified whether the image should be
4496 % cropped, copied, or cut.
4497 %
4498 */
4499 static MagickBooleanType XCropImage(Display *display,
4500  XResourceInfo *resource_info,XWindows *windows,Image *image,
4501  const ClipboardMode mode)
4502 {
4503  static const char
4504  *CropModeMenu[] =
4505  {
4506  "Help",
4507  "Dismiss",
4508  (char *) NULL
4509  },
4510  *RectifyModeMenu[] =
4511  {
4512  "Crop",
4513  "Help",
4514  "Dismiss",
4515  (char *) NULL
4516  };
4517 
4518  static const ModeType
4519  CropCommands[] =
4520  {
4521  CropHelpCommand,
4522  CropDismissCommand
4523  },
4524  RectifyCommands[] =
4525  {
4526  RectifyCopyCommand,
4527  RectifyHelpCommand,
4528  RectifyDismissCommand
4529  };
4530 
4531  CacheView
4532  *image_view;
4533 
4534  char
4535  command[MaxTextExtent],
4536  text[MaxTextExtent];
4537 
4538  Cursor
4539  cursor;
4540 
4542  *exception;
4543 
4544  int
4545  id,
4546  x,
4547  y;
4548 
4549  KeySym
4550  key_symbol;
4551 
4552  Image
4553  *crop_image;
4554 
4555  MagickRealType
4556  scale_factor;
4557 
4559  crop_info,
4560  highlight_info;
4561 
4562  PixelPacket
4563  *q;
4564 
4565  unsigned int
4566  height,
4567  width;
4568 
4569  size_t
4570  state;
4571 
4572  XEvent
4573  event;
4574 
4575  /*
4576  Map Command widget.
4577  */
4578  switch (mode)
4579  {
4580  case CopyMode:
4581  {
4582  (void) CloneString(&windows->command.name,"Copy");
4583  break;
4584  }
4585  case CropMode:
4586  {
4587  (void) CloneString(&windows->command.name,"Crop");
4588  break;
4589  }
4590  case CutMode:
4591  {
4592  (void) CloneString(&windows->command.name,"Cut");
4593  break;
4594  }
4595  }
4596  RectifyModeMenu[0]=windows->command.name;
4597  windows->command.data=0;
4598  (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4599  (void) XMapRaised(display,windows->command.id);
4600  XClientMessage(display,windows->image.id,windows->im_protocols,
4601  windows->im_update_widget,CurrentTime);
4602  /*
4603  Track pointer until button 1 is pressed.
4604  */
4605  XQueryPosition(display,windows->image.id,&x,&y);
4606  (void) XSelectInput(display,windows->image.id,
4607  windows->image.attributes.event_mask | PointerMotionMask);
4608  crop_info.x=(ssize_t) windows->image.x+x;
4609  crop_info.y=(ssize_t) windows->image.y+y;
4610  crop_info.width=0;
4611  crop_info.height=0;
4612  cursor=XCreateFontCursor(display,XC_fleur);
4613  state=DefaultState;
4614  do
4615  {
4616  if (windows->info.mapped != MagickFalse)
4617  {
4618  /*
4619  Display pointer position.
4620  */
4621  (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4622  (long) crop_info.x,(long) crop_info.y);
4623  XInfoWidget(display,windows,text);
4624  }
4625  /*
4626  Wait for next event.
4627  */
4628  XScreenEvent(display,windows,&event);
4629  if (event.xany.window == windows->command.id)
4630  {
4631  /*
4632  Select a command from the Command widget.
4633  */
4634  id=XCommandWidget(display,windows,CropModeMenu,&event);
4635  if (id < 0)
4636  continue;
4637  switch (CropCommands[id])
4638  {
4639  case CropHelpCommand:
4640  {
4641  switch (mode)
4642  {
4643  case CopyMode:
4644  {
4645  XTextViewHelp(display,resource_info,windows,MagickFalse,
4646  "Help Viewer - Image Copy",ImageCopyHelp);
4647  break;
4648  }
4649  case CropMode:
4650  {
4651  XTextViewHelp(display,resource_info,windows,MagickFalse,
4652  "Help Viewer - Image Crop",ImageCropHelp);
4653  break;
4654  }
4655  case CutMode:
4656  {
4657  XTextViewHelp(display,resource_info,windows,MagickFalse,
4658  "Help Viewer - Image Cut",ImageCutHelp);
4659  break;
4660  }
4661  }
4662  break;
4663  }
4664  case CropDismissCommand:
4665  {
4666  /*
4667  Prematurely exit.
4668  */
4669  state|=EscapeState;
4670  state|=ExitState;
4671  break;
4672  }
4673  default:
4674  break;
4675  }
4676  continue;
4677  }
4678  switch (event.type)
4679  {
4680  case ButtonPress:
4681  {
4682  if (event.xbutton.button != Button1)
4683  break;
4684  if (event.xbutton.window != windows->image.id)
4685  break;
4686  /*
4687  Note first corner of cropping rectangle-- exit loop.
4688  */
4689  (void) XCheckDefineCursor(display,windows->image.id,cursor);
4690  crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4691  crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4692  state|=ExitState;
4693  break;
4694  }
4695  case ButtonRelease:
4696  break;
4697  case Expose:
4698  break;
4699  case KeyPress:
4700  {
4701  if (event.xkey.window != windows->image.id)
4702  break;
4703  /*
4704  Respond to a user key press.
4705  */
4706  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4707  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4708  switch ((int) key_symbol)
4709  {
4710  case XK_Escape:
4711  case XK_F20:
4712  {
4713  /*
4714  Prematurely exit.
4715  */
4716  state|=EscapeState;
4717  state|=ExitState;
4718  break;
4719  }
4720  case XK_F1:
4721  case XK_Help:
4722  {
4723  switch (mode)
4724  {
4725  case CopyMode:
4726  {
4727  XTextViewHelp(display,resource_info,windows,MagickFalse,
4728  "Help Viewer - Image Copy",ImageCopyHelp);
4729  break;
4730  }
4731  case CropMode:
4732  {
4733  XTextViewHelp(display,resource_info,windows,MagickFalse,
4734  "Help Viewer - Image Crop",ImageCropHelp);
4735  break;
4736  }
4737  case CutMode:
4738  {
4739  XTextViewHelp(display,resource_info,windows,MagickFalse,
4740  "Help Viewer - Image Cut",ImageCutHelp);
4741  break;
4742  }
4743  }
4744  break;
4745  }
4746  default:
4747  {
4748  (void) XBell(display,0);
4749  break;
4750  }
4751  }
4752  break;
4753  }
4754  case MotionNotify:
4755  {
4756  if (event.xmotion.window != windows->image.id)
4757  break;
4758  /*
4759  Map and unmap Info widget as text cursor crosses its boundaries.
4760  */
4761  x=event.xmotion.x;
4762  y=event.xmotion.y;
4763  if (windows->info.mapped != MagickFalse)
4764  {
4765  if ((x < (int) (windows->info.x+windows->info.width)) &&
4766  (y < (int) (windows->info.y+windows->info.height)))
4767  (void) XWithdrawWindow(display,windows->info.id,
4768  windows->info.screen);
4769  }
4770  else
4771  if ((x > (int) (windows->info.x+windows->info.width)) ||
4772  (y > (int) (windows->info.y+windows->info.height)))
4773  (void) XMapWindow(display,windows->info.id);
4774  crop_info.x=(ssize_t) windows->image.x+x;
4775  crop_info.y=(ssize_t) windows->image.y+y;
4776  break;
4777  }
4778  default:
4779  break;
4780  }
4781  } while ((state & ExitState) == 0);
4782  (void) XSelectInput(display,windows->image.id,
4783  windows->image.attributes.event_mask);
4784  if ((state & EscapeState) != 0)
4785  {
4786  /*
4787  User want to exit without cropping.
4788  */
4789  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4790  (void) XFreeCursor(display,cursor);
4791  return(MagickTrue);
4792  }
4793  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4794  do
4795  {
4796  /*
4797  Size rectangle as pointer moves until the mouse button is released.
4798  */
4799  x=(int) crop_info.x;
4800  y=(int) crop_info.y;
4801  crop_info.width=0;
4802  crop_info.height=0;
4803  state=DefaultState;
4804  do
4805  {
4806  highlight_info=crop_info;
4807  highlight_info.x=crop_info.x-windows->image.x;
4808  highlight_info.y=crop_info.y-windows->image.y;
4809  if ((highlight_info.width > 3) && (highlight_info.height > 3))
4810  {
4811  /*
4812  Display info and draw cropping rectangle.
4813  */
4814  if (windows->info.mapped == MagickFalse)
4815  (void) XMapWindow(display,windows->info.id);
4816  (void) FormatLocaleString(text,MaxTextExtent,
4817  " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4818  crop_info.height,(double) crop_info.x,(double) crop_info.y);
4819  XInfoWidget(display,windows,text);
4820  XHighlightRectangle(display,windows->image.id,
4821  windows->image.highlight_context,&highlight_info);
4822  }
4823  else
4824  if (windows->info.mapped != MagickFalse)
4825  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4826  /*
4827  Wait for next event.
4828  */
4829  XScreenEvent(display,windows,&event);
4830  if ((highlight_info.width > 3) && (highlight_info.height > 3))
4831  XHighlightRectangle(display,windows->image.id,
4832  windows->image.highlight_context,&highlight_info);
4833  switch (event.type)
4834  {
4835  case ButtonPress:
4836  {
4837  crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4838  crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4839  break;
4840  }
4841  case ButtonRelease:
4842  {
4843  /*
4844  User has committed to cropping rectangle.
4845  */
4846  crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4847  crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4848  XSetCursorState(display,windows,MagickFalse);
4849  state|=ExitState;
4850  windows->command.data=0;
4851  (void) XCommandWidget(display,windows,RectifyModeMenu,
4852  (XEvent *) NULL);
4853  break;
4854  }
4855  case Expose:
4856  break;
4857  case MotionNotify:
4858  {
4859  crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4860  crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4861  }
4862  default:
4863  break;
4864  }
4865  if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4866  ((state & ExitState) != 0))
4867  {
4868  /*
4869  Check boundary conditions.
4870  */
4871  if (crop_info.x < 0)
4872  crop_info.x=0;
4873  else
4874  if (crop_info.x > (ssize_t) windows->image.ximage->width)
4875  crop_info.x=(ssize_t) windows->image.ximage->width;
4876  if ((int) crop_info.x < x)
4877  crop_info.width=(unsigned int) (x-crop_info.x);
4878  else
4879  {
4880  crop_info.width=(unsigned int) (crop_info.x-x);
4881  crop_info.x=(ssize_t) x;
4882  }
4883  if (crop_info.y < 0)
4884  crop_info.y=0;
4885  else
4886  if (crop_info.y > (ssize_t) windows->image.ximage->height)
4887  crop_info.y=(ssize_t) windows->image.ximage->height;
4888  if ((int) crop_info.y < y)
4889  crop_info.height=(unsigned int) (y-crop_info.y);
4890  else
4891  {
4892  crop_info.height=(unsigned int) (crop_info.y-y);
4893  crop_info.y=(ssize_t) y;
4894  }
4895  }
4896  } while ((state & ExitState) == 0);
4897  /*
4898  Wait for user to grab a corner of the rectangle or press return.
4899  */
4900  state=DefaultState;
4901  (void) XMapWindow(display,windows->info.id);
4902  do
4903  {
4904  if (windows->info.mapped != MagickFalse)
4905  {
4906  /*
4907  Display pointer position.
4908  */
4909  (void) FormatLocaleString(text,MaxTextExtent,
4910  " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4911  crop_info.height,(double) crop_info.x,(double) crop_info.y);
4912  XInfoWidget(display,windows,text);
4913  }
4914  highlight_info=crop_info;
4915  highlight_info.x=crop_info.x-windows->image.x;
4916  highlight_info.y=crop_info.y-windows->image.y;
4917  if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4918  {
4919  state|=EscapeState;
4920  state|=ExitState;
4921  break;
4922  }
4923  XHighlightRectangle(display,windows->image.id,
4924  windows->image.highlight_context,&highlight_info);
4925  XScreenEvent(display,windows,&event);
4926  if (event.xany.window == windows->command.id)
4927  {
4928  /*
4929  Select a command from the Command widget.
4930  */
4931  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4932  id=XCommandWidget(display,windows,RectifyModeMenu,&event);
4933  (void) XSetFunction(display,windows->image.highlight_context,
4934  GXinvert);
4935  XHighlightRectangle(display,windows->image.id,
4936  windows->image.highlight_context,&highlight_info);
4937  if (id >= 0)
4938  switch (RectifyCommands[id])
4939  {
4940  case RectifyCopyCommand:
4941  {
4942  state|=ExitState;
4943  break;
4944  }
4945  case RectifyHelpCommand:
4946  {
4947  (void) XSetFunction(display,windows->image.highlight_context,
4948  GXcopy);
4949  switch (mode)
4950  {
4951  case CopyMode:
4952  {
4953  XTextViewHelp(display,resource_info,windows,MagickFalse,
4954  "Help Viewer - Image Copy",ImageCopyHelp);
4955  break;
4956  }
4957  case CropMode:
4958  {
4959  XTextViewHelp(display,resource_info,windows,MagickFalse,
4960  "Help Viewer - Image Crop",ImageCropHelp);
4961  break;
4962  }
4963  case CutMode:
4964  {
4965  XTextViewHelp(display,resource_info,windows,MagickFalse,
4966  "Help Viewer - Image Cut",ImageCutHelp);
4967  break;
4968  }
4969  }
4970  (void) XSetFunction(display,windows->image.highlight_context,
4971  GXinvert);
4972  break;
4973  }
4974  case RectifyDismissCommand:
4975  {
4976  /*
4977  Prematurely exit.
4978  */
4979  state|=EscapeState;
4980  state|=ExitState;
4981  break;
4982  }
4983  default:
4984  break;
4985  }
4986  continue;
4987  }
4988  XHighlightRectangle(display,windows->image.id,
4989  windows->image.highlight_context,&highlight_info);
4990  switch (event.type)
4991  {
4992  case ButtonPress:
4993  {
4994  if (event.xbutton.button != Button1)
4995  break;
4996  if (event.xbutton.window != windows->image.id)
4997  break;
4998  x=windows->image.x+event.xbutton.x;
4999  y=windows->image.y+event.xbutton.y;
5000  if ((x < (int) (crop_info.x+RoiDelta)) &&
5001  (x > (int) (crop_info.x-RoiDelta)) &&
5002  (y < (int) (crop_info.y+RoiDelta)) &&
5003  (y > (int) (crop_info.y-RoiDelta)))
5004  {
5005  crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5006  crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5007  state|=UpdateConfigurationState;
5008  break;
5009  }
5010  if ((x < (int) (crop_info.x+RoiDelta)) &&
5011  (x > (int) (crop_info.x-RoiDelta)) &&
5012  (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5013  (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5014  {
5015  crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5016  state|=UpdateConfigurationState;
5017  break;
5018  }
5019  if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5020  (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5021  (y < (int) (crop_info.y+RoiDelta)) &&
5022  (y > (int) (crop_info.y-RoiDelta)))
5023  {
5024  crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5025  state|=UpdateConfigurationState;
5026  break;
5027  }
5028  if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5029  (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5030  (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5031  (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5032  {
5033  state|=UpdateConfigurationState;
5034  break;
5035  }
5036  magick_fallthrough;
5037  }
5038  case ButtonRelease:
5039  {
5040  if (event.xbutton.window == windows->pan.id)
5041  if ((highlight_info.x != crop_info.x-windows->image.x) ||
5042  (highlight_info.y != crop_info.y-windows->image.y))
5043  XHighlightRectangle(display,windows->image.id,
5044  windows->image.highlight_context,&highlight_info);
5045  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5046  event.xbutton.time);
5047  break;
5048  }
5049  case Expose:
5050  {
5051  if (event.xexpose.window == windows->image.id)
5052  if (event.xexpose.count == 0)
5053  {
5054  event.xexpose.x=(int) highlight_info.x;
5055  event.xexpose.y=(int) highlight_info.y;
5056  event.xexpose.width=(int) highlight_info.width;
5057  event.xexpose.height=(int) highlight_info.height;
5058  XRefreshWindow(display,&windows->image,&event);
5059  }
5060  if (event.xexpose.window == windows->info.id)
5061  if (event.xexpose.count == 0)
5062  XInfoWidget(display,windows,text);
5063  break;
5064  }
5065  case KeyPress:
5066  {
5067  if (event.xkey.window != windows->image.id)
5068  break;
5069  /*
5070  Respond to a user key press.
5071  */
5072  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5073  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5074  switch ((int) key_symbol)
5075  {
5076  case XK_Escape:
5077  case XK_F20:
5078  {
5079  state|=EscapeState;
5080  magick_fallthrough;
5081  }
5082  case XK_Return:
5083  {
5084  state|=ExitState;
5085  break;
5086  }
5087  case XK_Home:
5088  case XK_KP_Home:
5089  {
5090  crop_info.x=(ssize_t) (windows->image.width/2L-crop_info.width/
5091  2L);
5092  crop_info.y=(ssize_t) (windows->image.height/2L-crop_info.height/
5093  2L);
5094  break;
5095  }
5096  case XK_Left:
5097  case XK_KP_Left:
5098  {
5099  crop_info.x--;
5100  break;
5101  }
5102  case XK_Up:
5103  case XK_KP_Up:
5104  case XK_Next:
5105  {
5106  crop_info.y--;
5107  break;
5108  }
5109  case XK_Right:
5110  case XK_KP_Right:
5111  {
5112  crop_info.x++;
5113  break;
5114  }
5115  case XK_Prior:
5116  case XK_Down:
5117  case XK_KP_Down:
5118  {
5119  crop_info.y++;
5120  break;
5121  }
5122  case XK_F1:
5123  case XK_Help:
5124  {
5125  (void) XSetFunction(display,windows->image.highlight_context,
5126  GXcopy);
5127  switch (mode)
5128  {
5129  case CopyMode:
5130  {
5131  XTextViewHelp(display,resource_info,windows,MagickFalse,
5132  "Help Viewer - Image Copy",ImageCopyHelp);
5133  break;
5134  }
5135  case CropMode:
5136  {
5137  XTextViewHelp(display,resource_info,windows,MagickFalse,
5138  "Help Viewer - Image Cropg",ImageCropHelp);
5139  break;
5140  }
5141  case CutMode:
5142  {
5143  XTextViewHelp(display,resource_info,windows,MagickFalse,
5144  "Help Viewer - Image Cutg",ImageCutHelp);
5145  break;
5146  }
5147  }
5148  (void) XSetFunction(display,windows->image.highlight_context,
5149  GXinvert);
5150  break;
5151  }
5152  default:
5153  {
5154  (void) XBell(display,0);
5155  break;
5156  }
5157  }
5158  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5159  event.xkey.time);
5160  break;
5161  }
5162  case KeyRelease:
5163  break;
5164  case MotionNotify:
5165  {
5166  if (event.xmotion.window != windows->image.id)
5167  break;
5168  /*
5169  Map and unmap Info widget as text cursor crosses its boundaries.
5170  */
5171  x=event.xmotion.x;
5172  y=event.xmotion.y;
5173  if (windows->info.mapped != MagickFalse)
5174  {
5175  if ((x < (int) (windows->info.x+windows->info.width)) &&
5176  (y < (int) (windows->info.y+windows->info.height)))
5177  (void) XWithdrawWindow(display,windows->info.id,
5178  windows->info.screen);
5179  }
5180  else
5181  if ((x > (int) (windows->info.x+windows->info.width)) ||
5182  (y > (int) (windows->info.y+windows->info.height)))
5183  (void) XMapWindow(display,windows->info.id);
5184  crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5185  crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5186  break;
5187  }
5188  case SelectionRequest:
5189  {
5190  XSelectionEvent
5191  notify;
5192 
5193  XSelectionRequestEvent
5194  *request;
5195 
5196  /*
5197  Set primary selection.
5198  */
5199  (void) FormatLocaleString(text,MaxTextExtent,
5200  "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5201  crop_info.height,(double) crop_info.x,(double) crop_info.y);
5202  request=(&(event.xselectionrequest));
5203  (void) XChangeProperty(request->display,request->requestor,
5204  request->property,request->target,8,PropModeReplace,
5205  (unsigned char *) text,(int) strlen(text));
5206  notify.type=SelectionNotify;
5207  notify.display=request->display;
5208  notify.requestor=request->requestor;
5209  notify.selection=request->selection;
5210  notify.target=request->target;
5211  notify.time=request->time;
5212  if (request->property == None)
5213  notify.property=request->target;
5214  else
5215  notify.property=request->property;
5216  (void) XSendEvent(request->display,request->requestor,False,0,
5217  (XEvent *) &notify);
5218  }
5219  default:
5220  break;
5221  }
5222  if ((state & UpdateConfigurationState) != 0)
5223  {
5224  (void) XPutBackEvent(display,&event);
5225  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5226  break;
5227  }
5228  } while ((state & ExitState) == 0);
5229  } while ((state & ExitState) == 0);
5230  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5231  XSetCursorState(display,windows,MagickFalse);
5232  if ((state & EscapeState) != 0)
5233  return(MagickTrue);
5234  if (mode == CropMode)
5235  if (((int) crop_info.width != windows->image.ximage->width) ||
5236  ((int) crop_info.height != windows->image.ximage->height))
5237  {
5238  /*
5239  Reconfigure Image window as defined by cropping rectangle.
5240  */
5241  XSetCropGeometry(display,windows,&crop_info,image);
5242  windows->image.window_changes.width=(int) crop_info.width;
5243  windows->image.window_changes.height=(int) crop_info.height;
5244  (void) XConfigureImage(display,resource_info,windows,image);
5245  return(MagickTrue);
5246  }
5247  /*
5248  Copy image before applying image transforms.
5249  */
5250  XSetCursorState(display,windows,MagickTrue);
5251  XCheckRefreshWindows(display,windows);
5252  width=(unsigned int) image->columns;
5253  height=(unsigned int) image->rows;
5254  x=0;
5255  y=0;
5256  if (windows->image.crop_geometry != (char *) NULL)
5257  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5258  scale_factor=(MagickRealType) width/windows->image.ximage->width;
5259  crop_info.x+=x;
5260  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5261  crop_info.x+=image->page.x;
5262  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5263  scale_factor=(MagickRealType) height/windows->image.ximage->height;
5264  crop_info.y+=y;
5265  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5266  crop_info.y+=image->page.y;
5267  crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5268  crop_image=CropImage(image,&crop_info,&image->exception);
5269  XSetCursorState(display,windows,MagickFalse);
5270  if (crop_image == (Image *) NULL)
5271  return(MagickFalse);
5272  if (resource_info->copy_image != (Image *) NULL)
5273  resource_info->copy_image=DestroyImage(resource_info->copy_image);
5274  resource_info->copy_image=crop_image;
5275  if (mode == CopyMode)
5276  {
5277  (void) XConfigureImage(display,resource_info,windows,image);
5278  return(MagickTrue);
5279  }
5280  /*
5281  Cut image.
5282  */
5283  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
5284  return(MagickFalse);
5285  image->matte=MagickTrue;
5286  exception=(&image->exception);
5287  image_view=AcquireAuthenticCacheView(image,exception);
5288  for (y=0; y < (int) crop_info.height; y++)
5289  {
5290  q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5291  crop_info.width,1,exception);
5292  if (q == (PixelPacket *) NULL)
5293  break;
5294  for (x=0; x < (int) crop_info.width; x++)
5295  {
5296  q->opacity=(Quantum) TransparentOpacity;
5297  q++;
5298  }
5299  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5300  break;
5301  }
5302  image_view=DestroyCacheView(image_view);
5303  /*
5304  Update image configuration.
5305  */
5306  XConfigureImageColormap(display,resource_info,windows,image);
5307  (void) XConfigureImage(display,resource_info,windows,image);
5308  return(MagickTrue);
5309 }
5310 
5311 /*
5312 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5313 % %
5314 % %
5315 % %
5316 + X D r a w I m a g e %
5317 % %
5318 % %
5319 % %
5320 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5321 %
5322 % XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5323 % the image.
5324 %
5325 % The format of the XDrawEditImage method is:
5326 %
5327 % MagickBooleanType XDrawEditImage(Display *display,
5328 % XResourceInfo *resource_info,XWindows *windows,Image **image)
5329 %
5330 % A description of each parameter follows:
5331 %
5332 % o display: Specifies a connection to an X server; returned from
5333 % XOpenDisplay.
5334 %
5335 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5336 %
5337 % o windows: Specifies a pointer to a XWindows structure.
5338 %
5339 % o image: the image.
5340 %
5341 */
5342 static MagickBooleanType XDrawEditImage(Display *display,
5343  XResourceInfo *resource_info,XWindows *windows,Image **image)
5344 {
5345  const char
5346  *const DrawMenu[] =
5347  {
5348  "Element",
5349  "Color",
5350  "Stipple",
5351  "Width",
5352  "Undo",
5353  "Help",
5354  "Dismiss",
5355  (char *) NULL
5356  };
5357 
5358  static ElementType
5359  element = PointElement;
5360 
5361  static const ModeType
5362  DrawCommands[] =
5363  {
5364  DrawElementCommand,
5365  DrawColorCommand,
5366  DrawStippleCommand,
5367  DrawWidthCommand,
5368  DrawUndoCommand,
5369  DrawHelpCommand,
5370  DrawDismissCommand
5371  };
5372 
5373  static Pixmap
5374  stipple = (Pixmap) NULL;
5375 
5376  static unsigned int
5377  pen_id = 0,
5378  line_width = 1;
5379 
5380  char
5381  command[MaxTextExtent],
5382  text[MaxTextExtent];
5383 
5384  Cursor
5385  cursor;
5386 
5387  int
5388  entry,
5389  id,
5390  number_coordinates,
5391  x,
5392  y;
5393 
5394  MagickRealType
5395  degrees;
5396 
5397  MagickStatusType
5398  status;
5399 
5401  rectangle_info;
5402 
5403  int
5404  i;
5405 
5406  unsigned int
5407  distance,
5408  height,
5409  max_coordinates,
5410  width;
5411 
5412  size_t
5413  state;
5414 
5415  Window
5416  root_window;
5417 
5418  XDrawInfo
5419  draw_info;
5420 
5421  XEvent
5422  event;
5423 
5424  XPoint
5425  *coordinate_info;
5426 
5427  XSegment
5428  line_info;
5429 
5430  /*
5431  Allocate polygon info.
5432  */
5433  max_coordinates=2048;
5434  coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5435  sizeof(*coordinate_info));
5436  if (coordinate_info == (XPoint *) NULL)
5437  {
5438  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
5439  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5440  return(MagickFalse);
5441  }
5442  /*
5443  Map Command widget.
5444  */
5445  (void) CloneString(&windows->command.name,"Draw");
5446  windows->command.data=4;
5447  (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5448  (void) XMapRaised(display,windows->command.id);
5449  XClientMessage(display,windows->image.id,windows->im_protocols,
5450  windows->im_update_widget,CurrentTime);
5451  /*
5452  Wait for first button press.
5453  */
5454  root_window=XRootWindow(display,XDefaultScreen(display));
5455  draw_info.stencil=OpaqueStencil;
5456  status=MagickTrue;
5457  cursor=XCreateFontCursor(display,XC_tcross);
5458  for ( ; ; )
5459  {
5460  XQueryPosition(display,windows->image.id,&x,&y);
5461  (void) XSelectInput(display,windows->image.id,
5462  windows->image.attributes.event_mask | PointerMotionMask);
5463  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5464  state=DefaultState;
5465  do
5466  {
5467  if (windows->info.mapped != MagickFalse)
5468  {
5469  /*
5470  Display pointer position.
5471  */
5472  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5473  x+windows->image.x,y+windows->image.y);
5474  XInfoWidget(display,windows,text);
5475  }
5476  /*
5477  Wait for next event.
5478  */
5479  XScreenEvent(display,windows,&event);
5480  if (event.xany.window == windows->command.id)
5481  {
5482  /*
5483  Select a command from the Command widget.
5484  */
5485  id=XCommandWidget(display,windows,DrawMenu,&event);
5486  if (id < 0)
5487  continue;
5488  switch (DrawCommands[id])
5489  {
5490  case DrawElementCommand:
5491  {
5492  const char
5493  *const Elements[] =
5494  {
5495  "point",
5496  "line",
5497  "rectangle",
5498  "fill rectangle",
5499  "circle",
5500  "fill circle",
5501  "ellipse",
5502  "fill ellipse",
5503  "polygon",
5504  "fill polygon",
5505  (char *) NULL,
5506  };
5507 
5508  /*
5509  Select a command from the pop-up menu.
5510  */
5511  element=(ElementType) (XMenuWidget(display,windows,
5512  DrawMenu[id],Elements,command)+1);
5513  break;
5514  }
5515  case DrawColorCommand:
5516  {
5517  const char
5518  *ColorMenu[MaxNumberPens+1];
5519 
5520  int
5521  pen_number;
5522 
5523  MagickBooleanType
5524  transparent;
5525 
5526  XColor
5527  color;
5528 
5529  /*
5530  Initialize menu selections.
5531  */
5532  for (i=0; i < (int) (MaxNumberPens-2); i++)
5533  ColorMenu[i]=resource_info->pen_colors[i];
5534  ColorMenu[MaxNumberPens-2]="transparent";
5535  ColorMenu[MaxNumberPens-1]="Browser...";
5536  ColorMenu[MaxNumberPens]=(char *) NULL;
5537  /*
5538  Select a pen color from the pop-up menu.
5539  */
5540  pen_number=XMenuWidget(display,windows,DrawMenu[id],
5541  (const char **) ColorMenu,command);
5542  if (pen_number < 0)
5543  break;
5544  transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5545  MagickFalse;
5546  if (transparent != MagickFalse)
5547  {
5548  draw_info.stencil=TransparentStencil;
5549  break;
5550  }
5551  if (pen_number == (MaxNumberPens-1))
5552  {
5553  static char
5554  color_name[MaxTextExtent] = "gray";
5555 
5556  /*
5557  Select a pen color from a dialog.
5558  */
5559  resource_info->pen_colors[pen_number]=color_name;
5560  XColorBrowserWidget(display,windows,"Select",color_name);
5561  if (*color_name == '\0')
5562  break;
5563  }
5564  /*
5565  Set pen color.
5566  */
5567  (void) XParseColor(display,windows->map_info->colormap,
5568  resource_info->pen_colors[pen_number],&color);
5569  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5570  (unsigned int) MaxColors,&color);
5571  windows->pixel_info->pen_colors[pen_number]=color;
5572  pen_id=(unsigned int) pen_number;
5573  draw_info.stencil=OpaqueStencil;
5574  break;
5575  }
5576  case DrawStippleCommand:
5577  {
5578  const char
5579  *StipplesMenu[] =
5580  {
5581  "Brick",
5582  "Diagonal",
5583  "Scales",
5584  "Vertical",
5585  "Wavy",
5586  "Translucent",
5587  "Opaque",
5588  (char *) NULL,
5589  (char *) NULL,
5590  };
5591 
5592  Image
5593  *stipple_image;
5594 
5595  ImageInfo
5596  *image_info;
5597 
5598  int
5599  status;
5600 
5601  static char
5602  filename[MaxTextExtent] = "\0";
5603 
5604  /*
5605  Select a command from the pop-up menu.
5606  */
5607  StipplesMenu[7]="Open...";
5608  entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5609  command);
5610  if (entry < 0)
5611  break;
5612  if (stipple != (Pixmap) NULL)
5613  (void) XFreePixmap(display,stipple);
5614  stipple=(Pixmap) NULL;
5615  if (entry != 7)
5616  {
5617  switch (entry)
5618  {
5619  case 0:
5620  {
5621  stipple=XCreateBitmapFromData(display,root_window,
5622  (char *) BricksBitmap,BricksWidth,BricksHeight);
5623  break;
5624  }
5625  case 1:
5626  {
5627  stipple=XCreateBitmapFromData(display,root_window,
5628  (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5629  break;
5630  }
5631  case 2:
5632  {
5633  stipple=XCreateBitmapFromData(display,root_window,
5634  (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5635  break;
5636  }
5637  case 3:
5638  {
5639  stipple=XCreateBitmapFromData(display,root_window,
5640  (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5641  break;
5642  }
5643  case 4:
5644  {
5645  stipple=XCreateBitmapFromData(display,root_window,
5646  (char *) WavyBitmap,WavyWidth,WavyHeight);
5647  break;
5648  }
5649  case 5:
5650  {
5651  stipple=XCreateBitmapFromData(display,root_window,
5652  (char *) HighlightBitmap,HighlightWidth,
5653  HighlightHeight);
5654  break;
5655  }
5656  case 6:
5657  default:
5658  {
5659  stipple=XCreateBitmapFromData(display,root_window,
5660  (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5661  break;
5662  }
5663  }
5664  break;
5665  }
5666  XFileBrowserWidget(display,windows,"Stipple",filename);
5667  if (*filename == '\0')
5668  break;
5669  /*
5670  Read image.
5671  */
5672  XSetCursorState(display,windows,MagickTrue);
5673  XCheckRefreshWindows(display,windows);
5674  image_info=AcquireImageInfo();
5675  (void) CopyMagickString(image_info->filename,filename,
5676  MaxTextExtent);
5677  stipple_image=ReadImage(image_info,&(*image)->exception);
5678  CatchException(&(*image)->exception);
5679  XSetCursorState(display,windows,MagickFalse);
5680  if (stipple_image == (Image *) NULL)
5681  break;
5682  (void) AcquireUniqueFileResource(filename);
5683  (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5684  "xbm:%s",filename);
5685  (void) WriteImage(image_info,stipple_image);
5686  stipple_image=DestroyImage(stipple_image);
5687  image_info=DestroyImageInfo(image_info);
5688  status=XReadBitmapFile(display,root_window,filename,&width,
5689  &height,&stipple,&x,&y);
5690  (void) RelinquishUniqueFileResource(filename);
5691  if ((status != BitmapSuccess) != 0)
5692  XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5693  filename);
5694  break;
5695  }
5696  case DrawWidthCommand:
5697  {
5698  const char
5699  *const WidthsMenu[] =
5700  {
5701  "1",
5702  "2",
5703  "4",
5704  "8",
5705  "16",
5706  "Dialog...",
5707  (char *) NULL,
5708  };
5709 
5710  static char
5711  width[MaxTextExtent] = "0";
5712 
5713  /*
5714  Select a command from the pop-up menu.
5715  */
5716  entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5717  command);
5718  if (entry < 0)
5719  break;
5720  if (entry != 5)
5721  {
5722  line_width=(unsigned int) StringToUnsignedLong(
5723  WidthsMenu[entry]);
5724  break;
5725  }
5726  (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5727  width);
5728  if (*width == '\0')
5729  break;
5730  line_width=(unsigned int) StringToUnsignedLong(width);
5731  break;
5732  }
5733  case DrawUndoCommand:
5734  {
5735  (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5736  image);
5737  break;
5738  }
5739  case DrawHelpCommand:
5740  {
5741  XTextViewHelp(display,resource_info,windows,MagickFalse,
5742  "Help Viewer - Image Rotation",ImageDrawHelp);
5743  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5744  break;
5745  }
5746  case DrawDismissCommand:
5747  {
5748  /*
5749  Prematurely exit.
5750  */
5751  state|=EscapeState;
5752  state|=ExitState;
5753  break;
5754  }
5755  default:
5756  break;
5757  }
5758  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5759  continue;
5760  }
5761  switch (event.type)
5762  {
5763  case ButtonPress:
5764  {
5765  if (event.xbutton.button != Button1)
5766  break;
5767  if (event.xbutton.window != windows->image.id)
5768  break;
5769  /*
5770  exit loop.
5771  */
5772  x=event.xbutton.x;
5773  y=event.xbutton.y;
5774  state|=ExitState;
5775  break;
5776  }
5777  case ButtonRelease:
5778  break;
5779  case Expose:
5780  break;
5781  case KeyPress:
5782  {
5783  KeySym
5784  key_symbol;
5785 
5786  if (event.xkey.window != windows->image.id)
5787  break;
5788  /*
5789  Respond to a user key press.
5790  */
5791  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5792  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5793  switch ((int) key_symbol)
5794  {
5795  case XK_Escape:
5796  case XK_F20:
5797  {
5798  /*
5799  Prematurely exit.
5800  */
5801  state|=EscapeState;
5802  state|=ExitState;
5803  break;
5804  }
5805  case XK_F1:
5806  case XK_Help:
5807  {
5808  XTextViewHelp(display,resource_info,windows,MagickFalse,
5809  "Help Viewer - Image Rotation",ImageDrawHelp);
5810  break;
5811  }
5812  default:
5813  {
5814  (void) XBell(display,0);
5815  break;
5816  }
5817  }
5818  break;
5819  }
5820  case MotionNotify:
5821  {
5822  /*
5823  Map and unmap Info widget as text cursor crosses its boundaries.
5824  */
5825  x=event.xmotion.x;
5826  y=event.xmotion.y;
5827  if (windows->info.mapped != MagickFalse)
5828  {
5829  if ((x < (int) (windows->info.x+windows->info.width)) &&
5830  (y < (int) (windows->info.y+windows->info.height)))
5831  (void) XWithdrawWindow(display,windows->info.id,
5832  windows->info.screen);
5833  }
5834  else
5835  if ((x > (int) (windows->info.x+windows->info.width)) ||
5836  (y > (int) (windows->info.y+windows->info.height)))
5837  (void) XMapWindow(display,windows->info.id);
5838  break;
5839  }
5840  }
5841  } while ((state & ExitState) == 0);
5842  (void) XSelectInput(display,windows->image.id,
5843  windows->image.attributes.event_mask);
5844  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5845  if ((state & EscapeState) != 0)
5846  break;
5847  /*
5848  Draw element as pointer moves until the button is released.
5849  */
5850  distance=0;
5851  degrees=0.0;
5852  line_info.x1=x;
5853  line_info.y1=y;
5854  line_info.x2=x;
5855  line_info.y2=y;
5856  rectangle_info.x=(ssize_t) x;
5857  rectangle_info.y=(ssize_t) y;
5858  rectangle_info.width=0;
5859  rectangle_info.height=0;
5860  number_coordinates=1;
5861  coordinate_info->x=x;
5862  coordinate_info->y=y;
5863  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5864  state=DefaultState;
5865  do
5866  {
5867  switch (element)
5868  {
5869  case PointElement:
5870  default:
5871  {
5872  if (number_coordinates > 1)
5873  {
5874  (void) XDrawLines(display,windows->image.id,
5875  windows->image.highlight_context,coordinate_info,
5876  number_coordinates,CoordModeOrigin);
5877  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5878  coordinate_info[number_coordinates-1].x,
5879  coordinate_info[number_coordinates-1].y);
5880  XInfoWidget(display,windows,text);
5881  }
5882  break;
5883  }
5884  case LineElement:
5885  {
5886  if (distance > 9)
5887  {
5888  /*
5889  Display angle of the line.
5890  */
5891  degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5892  line_info.y1),(double) (line_info.x2-line_info.x1)));
5893  (void) FormatLocaleString(text,MaxTextExtent," %g",
5894  (double) degrees);
5895  XInfoWidget(display,windows,text);
5896  XHighlightLine(display,windows->image.id,
5897  windows->image.highlight_context,&line_info);
5898  }
5899  else
5900  if (windows->info.mapped != MagickFalse)
5901  (void) XWithdrawWindow(display,windows->info.id,
5902  windows->info.screen);
5903  break;
5904  }
5905  case RectangleElement:
5906  case FillRectangleElement:
5907  {
5908  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5909  {
5910  /*
5911  Display info and draw drawing rectangle.
5912  */
5913  (void) FormatLocaleString(text,MaxTextExtent,
5914  " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5915  (double) rectangle_info.height,(double) rectangle_info.x,
5916  (double) rectangle_info.y);
5917  XInfoWidget(display,windows,text);
5918  XHighlightRectangle(display,windows->image.id,
5919  windows->image.highlight_context,&rectangle_info);
5920  }
5921  else
5922  if (windows->info.mapped != MagickFalse)
5923  (void) XWithdrawWindow(display,windows->info.id,
5924  windows->info.screen);
5925  break;
5926  }
5927  case CircleElement:
5928  case FillCircleElement:
5929  case EllipseElement:
5930  case FillEllipseElement:
5931  {
5932  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5933  {
5934  /*
5935  Display info and draw drawing rectangle.
5936  */
5937  (void) FormatLocaleString(text,MaxTextExtent,
5938  " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5939  (double) rectangle_info.height,(double) rectangle_info.x,
5940  (double) rectangle_info.y);
5941  XInfoWidget(display,windows,text);
5942  XHighlightEllipse(display,windows->image.id,
5943  windows->image.highlight_context,&rectangle_info);
5944  }
5945  else
5946  if (windows->info.mapped != MagickFalse)
5947  (void) XWithdrawWindow(display,windows->info.id,
5948  windows->info.screen);
5949  break;
5950  }
5951  case PolygonElement:
5952  case FillPolygonElement:
5953  {
5954  if (number_coordinates > 1)
5955  (void) XDrawLines(display,windows->image.id,
5956  windows->image.highlight_context,coordinate_info,
5957  number_coordinates,CoordModeOrigin);
5958  if (distance > 9)
5959  {
5960  /*
5961  Display angle of the line.
5962  */
5963  degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5964  line_info.y1),(double) (line_info.x2-line_info.x1)));
5965  (void) FormatLocaleString(text,MaxTextExtent," %g",
5966  (double) degrees);
5967  XInfoWidget(display,windows,text);
5968  XHighlightLine(display,windows->image.id,
5969  windows->image.highlight_context,&line_info);
5970  }
5971  else
5972  if (windows->info.mapped != MagickFalse)
5973  (void) XWithdrawWindow(display,windows->info.id,
5974  windows->info.screen);
5975  break;
5976  }
5977  }
5978  /*
5979  Wait for next event.
5980  */
5981  XScreenEvent(display,windows,&event);
5982  switch (element)
5983  {
5984  case PointElement:
5985  default:
5986  {
5987  if (number_coordinates > 1)
5988  (void) XDrawLines(display,windows->image.id,
5989  windows->image.highlight_context,coordinate_info,
5990  number_coordinates,CoordModeOrigin);
5991  break;
5992  }
5993  case LineElement:
5994  {
5995  if (distance > 9)
5996  XHighlightLine(display,windows->image.id,
5997  windows->image.highlight_context,&line_info);
5998  break;
5999  }
6000  case RectangleElement:
6001  case FillRectangleElement:
6002  {
6003  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6004  XHighlightRectangle(display,windows->image.id,
6005  windows->image.highlight_context,&rectangle_info);
6006  break;
6007  }
6008  case CircleElement:
6009  case FillCircleElement:
6010  case EllipseElement:
6011  case FillEllipseElement:
6012  {
6013  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6014  XHighlightEllipse(display,windows->image.id,
6015  windows->image.highlight_context,&rectangle_info);
6016  break;
6017  }
6018  case PolygonElement:
6019  case FillPolygonElement:
6020  {
6021  if (number_coordinates > 1)
6022  (void) XDrawLines(display,windows->image.id,
6023  windows->image.highlight_context,coordinate_info,
6024  number_coordinates,CoordModeOrigin);
6025  if (distance > 9)
6026  XHighlightLine(display,windows->image.id,
6027  windows->image.highlight_context,&line_info);
6028  break;
6029  }
6030  }
6031  switch (event.type)
6032  {
6033  case ButtonPress:
6034  break;
6035  case ButtonRelease:
6036  {
6037  /*
6038  User has committed to element.
6039  */
6040  line_info.x2=event.xbutton.x;
6041  line_info.y2=event.xbutton.y;
6042  rectangle_info.x=(ssize_t) event.xbutton.x;
6043  rectangle_info.y=(ssize_t) event.xbutton.y;
6044  coordinate_info[number_coordinates].x=event.xbutton.x;
6045  coordinate_info[number_coordinates].y=event.xbutton.y;
6046  if (((element != PolygonElement) &&
6047  (element != FillPolygonElement)) || (distance <= 9))
6048  {
6049  state|=ExitState;
6050  break;
6051  }
6052  number_coordinates++;
6053  if (number_coordinates < (int) max_coordinates)
6054  {
6055  line_info.x1=event.xbutton.x;
6056  line_info.y1=event.xbutton.y;
6057  break;
6058  }
6059  max_coordinates<<=1;
6060  coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6061  max_coordinates,sizeof(*coordinate_info));
6062  if (coordinate_info == (XPoint *) NULL)
6063  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6064  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6065  break;
6066  }
6067  case Expose:
6068  break;
6069  case MotionNotify:
6070  {
6071  if (event.xmotion.window != windows->image.id)
6072  break;
6073  if (element != PointElement)
6074  {
6075  line_info.x2=event.xmotion.x;
6076  line_info.y2=event.xmotion.y;
6077  rectangle_info.x=(ssize_t) event.xmotion.x;
6078  rectangle_info.y=(ssize_t) event.xmotion.y;
6079  break;
6080  }
6081  coordinate_info[number_coordinates].x=event.xbutton.x;
6082  coordinate_info[number_coordinates].y=event.xbutton.y;
6083  number_coordinates++;
6084  if (number_coordinates < (int) max_coordinates)
6085  break;
6086  max_coordinates<<=1;
6087  coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6088  max_coordinates,sizeof(*coordinate_info));
6089  if (coordinate_info == (XPoint *) NULL)
6090  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6091  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6092  break;
6093  }
6094  default:
6095  break;
6096  }
6097  /*
6098  Check boundary conditions.
6099  */
6100  if (line_info.x2 < 0)
6101  line_info.x2=0;
6102  else
6103  if (line_info.x2 > (int) windows->image.width)
6104  line_info.x2=(short) windows->image.width;
6105  if (line_info.y2 < 0)
6106  line_info.y2=0;
6107  else
6108  if (line_info.y2 > (int) windows->image.height)
6109  line_info.y2=(short) windows->image.height;
6110  distance=(unsigned int)
6111  (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6112  ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6113  if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6114  ((state & ExitState) != 0))
6115  {
6116  if (rectangle_info.x < 0)
6117  rectangle_info.x=0;
6118  else
6119  if (rectangle_info.x > (ssize_t) windows->image.width)
6120  rectangle_info.x=(ssize_t) windows->image.width;
6121  if ((int) rectangle_info.x < x)
6122  rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6123  else
6124  {
6125  rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6126  rectangle_info.x=(ssize_t) x;
6127  }
6128  if (rectangle_info.y < 0)
6129  rectangle_info.y=0;
6130  else
6131  if (rectangle_info.y > (ssize_t) windows->image.height)
6132  rectangle_info.y=(ssize_t) windows->image.height;
6133  if ((int) rectangle_info.y < y)
6134  rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6135  else
6136  {
6137  rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6138  rectangle_info.y=(ssize_t) y;
6139  }
6140  }
6141  } while ((state & ExitState) == 0);
6142  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6143  if ((element == PointElement) || (element == PolygonElement) ||
6144  (element == FillPolygonElement))
6145  {
6146  /*
6147  Determine polygon bounding box.
6148  */
6149  rectangle_info.x=(ssize_t) coordinate_info->x;
6150  rectangle_info.y=(ssize_t) coordinate_info->y;
6151  x=coordinate_info->x;
6152  y=coordinate_info->y;
6153  for (i=1; i < number_coordinates; i++)
6154  {
6155  if (coordinate_info[i].x > x)
6156  x=coordinate_info[i].x;
6157  if (coordinate_info[i].y > y)
6158  y=coordinate_info[i].y;
6159  if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6160  rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6161  if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6162  rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6163  }
6164  rectangle_info.width=(size_t) (x-rectangle_info.x);
6165  rectangle_info.height=(size_t) (y-rectangle_info.y);
6166  for (i=0; i < number_coordinates; i++)
6167  {
6168  coordinate_info[i].x-=rectangle_info.x;
6169  coordinate_info[i].y-=rectangle_info.y;
6170  }
6171  }
6172  else
6173  if (distance <= 9)
6174  continue;
6175  else
6176  if ((element == RectangleElement) ||
6177  (element == CircleElement) || (element == EllipseElement))
6178  {
6179  rectangle_info.width--;
6180  rectangle_info.height--;
6181  }
6182  /*
6183  Drawing is relative to image configuration.
6184  */
6185  draw_info.x=(int) rectangle_info.x;
6186  draw_info.y=(int) rectangle_info.y;
6187  (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6188  image);
6189  width=(unsigned int) (*image)->columns;
6190  height=(unsigned int) (*image)->rows;
6191  x=0;
6192  y=0;
6193  if (windows->image.crop_geometry != (char *) NULL)
6194  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6195  draw_info.x+=windows->image.x-(line_width/2);
6196  if (draw_info.x < 0)
6197  draw_info.x=0;
6198  draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6199  draw_info.y+=windows->image.y-(line_width/2);
6200  if (draw_info.y < 0)
6201  draw_info.y=0;
6202  draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6203  draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6204  if (draw_info.width > (unsigned int) (*image)->columns)
6205  draw_info.width=(unsigned int) (*image)->columns;
6206  draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6207  if (draw_info.height > (unsigned int) (*image)->rows)
6208  draw_info.height=(unsigned int) (*image)->rows;
6209  (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6210  width*draw_info.width/windows->image.ximage->width,
6211  height*draw_info.height/windows->image.ximage->height,
6212  draw_info.x+x,draw_info.y+y);
6213  /*
6214  Initialize drawing attributes.
6215  */
6216  draw_info.degrees=0.0;
6217  draw_info.element=element;
6218  draw_info.stipple=stipple;
6219  draw_info.line_width=line_width;
6220  draw_info.line_info=line_info;
6221  if (line_info.x1 > (int) (line_width/2))
6222  draw_info.line_info.x1=(short) line_width/2;
6223  if (line_info.y1 > (int) (line_width/2))
6224  draw_info.line_info.y1=(short) line_width/2;
6225  draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6226  draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6227  if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6228  {
6229  draw_info.line_info.x2=(-draw_info.line_info.x2);
6230  draw_info.line_info.y2=(-draw_info.line_info.y2);
6231  }
6232  if (draw_info.line_info.x2 < 0)
6233  {
6234  draw_info.line_info.x2=(-draw_info.line_info.x2);
6235  Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6236  }
6237  if (draw_info.line_info.y2 < 0)
6238  {
6239  draw_info.line_info.y2=(-draw_info.line_info.y2);
6240  Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6241  }
6242  draw_info.rectangle_info=rectangle_info;
6243  if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6244  draw_info.rectangle_info.x=(ssize_t) line_width/2;
6245  if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6246  draw_info.rectangle_info.y=(ssize_t) line_width/2;
6247  draw_info.number_coordinates=(unsigned int) number_coordinates;
6248  draw_info.coordinate_info=coordinate_info;
6249  windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6250  /*
6251  Draw element on image.
6252  */
6253  XSetCursorState(display,windows,MagickTrue);
6254  XCheckRefreshWindows(display,windows);
6255  status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6256  XSetCursorState(display,windows,MagickFalse);
6257  /*
6258  Update image colormap and return to image drawing.
6259  */
6260  XConfigureImageColormap(display,resource_info,windows,*image);
6261  (void) XConfigureImage(display,resource_info,windows,*image);
6262  }
6263  XSetCursorState(display,windows,MagickFalse);
6264  coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6265  return(status != 0 ? MagickTrue : MagickFalse);
6266 }
6267 
6268 /*
6269 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6270 % %
6271 % %
6272 % %
6273 + X D r a w P a n R e c t a n g l e %
6274 % %
6275 % %
6276 % %
6277 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6278 %
6279 % XDrawPanRectangle() draws a rectangle in the pan window. The pan window
6280 % displays a zoom image and the rectangle shows which portion of the image is
6281 % displayed in the Image window.
6282 %
6283 % The format of the XDrawPanRectangle method is:
6284 %
6285 % XDrawPanRectangle(Display *display,XWindows *windows)
6286 %
6287 % A description of each parameter follows:
6288 %
6289 % o display: Specifies a connection to an X server; returned from
6290 % XOpenDisplay.
6291 %
6292 % o windows: Specifies a pointer to a XWindows structure.
6293 %
6294 */
6295 static void XDrawPanRectangle(Display *display,XWindows *windows)
6296 {
6297  MagickRealType
6298  scale_factor;
6299 
6301  highlight_info;
6302 
6303  /*
6304  Determine dimensions of the panning rectangle.
6305  */
6306  scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6307  highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6308  highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6309  scale_factor=(MagickRealType)
6310  windows->pan.height/windows->image.ximage->height;
6311  highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6312  highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6313  /*
6314  Display the panning rectangle.
6315  */
6316  (void) XClearWindow(display,windows->pan.id);
6317  XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6318  &highlight_info);
6319 }
6320 
6321 /*
6322 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6323 % %
6324 % %
6325 % %
6326 + X I m a g e C a c h e %
6327 % %
6328 % %
6329 % %
6330 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6331 %
6332 % XImageCache() handles the creation, manipulation, and destruction of the
6333 % image cache (undo and redo buffers).
6334 %
6335 % The format of the XImageCache method is:
6336 %
6337 % void XImageCache(Display *display,XResourceInfo *resource_info,
6338 % XWindows *windows,const CommandType command,Image **image)
6339 %
6340 % A description of each parameter follows:
6341 %
6342 % o display: Specifies a connection to an X server; returned from
6343 % XOpenDisplay.
6344 %
6345 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6346 %
6347 % o windows: Specifies a pointer to a XWindows structure.
6348 %
6349 % o command: Specifies a command to perform.
6350 %
6351 % o image: the image; XImageCache may transform the image and return a new
6352 % image pointer.
6353 %
6354 */
6355 static void XImageCache(Display *display,XResourceInfo *resource_info,
6356  XWindows *windows,const CommandType command,Image **image)
6357 {
6358  Image
6359  *cache_image;
6360 
6361  static Image
6362  *redo_image = (Image *) NULL,
6363  *undo_image = (Image *) NULL;
6364 
6365  switch (command)
6366  {
6367  case FreeBuffersCommand:
6368  {
6369  /*
6370  Free memory from the undo and redo cache.
6371  */
6372  while (undo_image != (Image *) NULL)
6373  {
6374  cache_image=undo_image;
6375  undo_image=GetPreviousImageInList(undo_image);
6376  cache_image->list=DestroyImage(cache_image->list);
6377  cache_image=DestroyImage(cache_image);
6378  }
6379  undo_image=NewImageList();
6380  if (redo_image != (Image *) NULL)
6381  redo_image=DestroyImage(redo_image);
6382  redo_image=NewImageList();
6383  return;
6384  }
6385  case UndoCommand:
6386  {
6387  char
6388  image_geometry[MaxTextExtent];
6389 
6390  /*
6391  Undo the last image transformation.
6392  */
6393  if (undo_image == (Image *) NULL)
6394  {
6395  (void) XBell(display,0);
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 % CommandType 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 CommandType 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] = "";
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  if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6654  {
6655  *delta='\0';
6656  resource_info->quantum=1;
6657  }
6658  last_symbol=key_symbol;
6659  delta[strlen(delta)+1]='\0';
6660  delta[strlen(delta)]=Digits[key_symbol-XK_0];
6661  resource_info->quantum=StringToLong(delta);
6662  return(NullCommand);
6663  }
6664  last_symbol=key_symbol;
6665  if (resource_info->immutable)
6666  {
6667  /*
6668  Virtual image window has a restricted command set.
6669  */
6670  switch (key_symbol)
6671  {
6672  case XK_question:
6673  return(InfoCommand);
6674  case XK_p:
6675  case XK_Print:
6676  return(PrintCommand);
6677  case XK_space:
6678  return(NextCommand);
6679  case XK_q:
6680  case XK_Escape:
6681  return(QuitCommand);
6682  default:
6683  break;
6684  }
6685  return(NullCommand);
6686  }
6687  switch ((int) key_symbol)
6688  {
6689  case XK_o:
6690  {
6691  if ((state & ControlMask) == 0)
6692  break;
6693  return(OpenCommand);
6694  }
6695  case XK_space:
6696  return(NextCommand);
6697  case XK_BackSpace:
6698  return(FormerCommand);
6699  case XK_s:
6700  {
6701  if ((state & Mod1Mask) != 0)
6702  return(SwirlCommand);
6703  if ((state & ControlMask) == 0)
6704  return(ShearCommand);
6705  return(SaveCommand);
6706  }
6707  case XK_p:
6708  case XK_Print:
6709  {
6710  if ((state & Mod1Mask) != 0)
6711  return(OilPaintCommand);
6712  if ((state & Mod4Mask) != 0)
6713  return(ColorCommand);
6714  if ((state & ControlMask) == 0)
6715  return(NullCommand);
6716  return(PrintCommand);
6717  }
6718  case XK_d:
6719  {
6720  if ((state & Mod4Mask) != 0)
6721  return(DrawCommand);
6722  if ((state & ControlMask) == 0)
6723  return(NullCommand);
6724  return(DeleteCommand);
6725  }
6726  case XK_Select:
6727  {
6728  if ((state & ControlMask) == 0)
6729  return(NullCommand);
6730  return(SelectCommand);
6731  }
6732  case XK_n:
6733  {
6734  if ((state & ControlMask) == 0)
6735  return(NullCommand);
6736  return(NewCommand);
6737  }
6738  case XK_q:
6739  case XK_Escape:
6740  return(QuitCommand);
6741  case XK_z:
6742  case XK_Undo:
6743  {
6744  if ((state & ControlMask) == 0)
6745  return(NullCommand);
6746  return(UndoCommand);
6747  }
6748  case XK_r:
6749  case XK_Redo:
6750  {
6751  if ((state & ControlMask) == 0)
6752  return(RollCommand);
6753  return(RedoCommand);
6754  }
6755  case XK_x:
6756  {
6757  if ((state & ControlMask) == 0)
6758  return(NullCommand);
6759  return(CutCommand);
6760  }
6761  case XK_c:
6762  {
6763  if ((state & Mod1Mask) != 0)
6764  return(CharcoalDrawCommand);
6765  if ((state & ControlMask) == 0)
6766  return(CropCommand);
6767  return(CopyCommand);
6768  }
6769  case XK_v:
6770  case XK_Insert:
6771  {
6772  if ((state & Mod4Mask) != 0)
6773  return(CompositeCommand);
6774  if ((state & ControlMask) == 0)
6775  return(FlipCommand);
6776  return(PasteCommand);
6777  }
6778  case XK_less:
6779  return(HalfSizeCommand);
6780  case XK_minus:
6781  return(OriginalSizeCommand);
6782  case XK_greater:
6783  return(DoubleSizeCommand);
6784  case XK_percent:
6785  return(ResizeCommand);
6786  case XK_at:
6787  return(RefreshCommand);
6788  case XK_bracketleft:
6789  return(ChopCommand);
6790  case XK_h:
6791  return(FlopCommand);
6792  case XK_slash:
6793  return(RotateRightCommand);
6794  case XK_backslash:
6795  return(RotateLeftCommand);
6796  case XK_asterisk:
6797  return(RotateCommand);
6798  case XK_t:
6799  return(TrimCommand);
6800  case XK_H:
6801  return(HueCommand);
6802  case XK_S:
6803  return(SaturationCommand);
6804  case XK_L:
6805  return(BrightnessCommand);
6806  case XK_G:
6807  return(GammaCommand);
6808  case XK_C:
6809  return(SpiffCommand);
6810  case XK_Z:
6811  return(DullCommand);
6812  case XK_N:
6813  return(NormalizeCommand);
6814  case XK_equal:
6815  return(EqualizeCommand);
6816  case XK_asciitilde:
6817  return(NegateCommand);
6818  case XK_period:
6819  return(GrayscaleCommand);
6820  case XK_numbersign:
6821  return(QuantizeCommand);
6822  case XK_F2:
6823  return(DespeckleCommand);
6824  case XK_F3:
6825  return(EmbossCommand);
6826  case XK_F4:
6827  return(ReduceNoiseCommand);
6828  case XK_F5:
6829  return(AddNoiseCommand);
6830  case XK_F6:
6831  return(SharpenCommand);
6832  case XK_F7:
6833  return(BlurCommand);
6834  case XK_F8:
6835  return(ThresholdCommand);
6836  case XK_F9:
6837  return(EdgeDetectCommand);
6838  case XK_F10:
6839  return(SpreadCommand);
6840  case XK_F11:
6841  return(ShadeCommand);
6842  case XK_F12:
6843  return(RaiseCommand);
6844  case XK_F13:
6845  return(SegmentCommand);
6846  case XK_i:
6847  {
6848  if ((state & Mod1Mask) == 0)
6849  return(NullCommand);
6850  return(ImplodeCommand);
6851  }
6852  case XK_w:
6853  {
6854  if ((state & Mod1Mask) == 0)
6855  return(NullCommand);
6856  return(WaveCommand);
6857  }
6858  case XK_m:
6859  {
6860  if ((state & Mod4Mask) == 0)
6861  return(NullCommand);
6862  return(MatteCommand);
6863  }
6864  case XK_b:
6865  {
6866  if ((state & Mod4Mask) == 0)
6867  return(NullCommand);
6868  return(AddBorderCommand);
6869  }
6870  case XK_f:
6871  {
6872  if ((state & Mod4Mask) == 0)
6873  return(NullCommand);
6874  return(AddFrameCommand);
6875  }
6876  case XK_exclam:
6877  {
6878  if ((state & Mod4Mask) == 0)
6879  return(NullCommand);
6880  return(CommentCommand);
6881  }
6882  case XK_a:
6883  {
6884  if ((state & Mod1Mask) != 0)
6885  return(ApplyCommand);
6886  if ((state & Mod4Mask) != 0)
6887  return(AnnotateCommand);
6888  if ((state & ControlMask) == 0)
6889  return(NullCommand);
6890  return(RegionOfInterestCommand);
6891  }
6892  case XK_question:
6893  return(InfoCommand);
6894  case XK_plus:
6895  return(ZoomCommand);
6896  case XK_P:
6897  {
6898  if ((state & ShiftMask) == 0)
6899  return(NullCommand);
6900  return(ShowPreviewCommand);
6901  }
6902  case XK_Execute:
6903  return(LaunchCommand);
6904  case XK_F1:
6905  return(HelpCommand);
6906  case XK_Find:
6907  return(BrowseDocumentationCommand);
6908  case XK_Menu:
6909  {
6910  (void) XMapRaised(display,windows->command.id);
6911  return(NullCommand);
6912  }
6913  case XK_Next:
6914  case XK_Prior:
6915  case XK_Home:
6916  case XK_KP_Home:
6917  {
6918  XTranslateImage(display,windows,*image,key_symbol);
6919  return(NullCommand);
6920  }
6921  case XK_Up:
6922  case XK_KP_Up:
6923  case XK_Down:
6924  case XK_KP_Down:
6925  case XK_Left:
6926  case XK_KP_Left:
6927  case XK_Right:
6928  case XK_KP_Right:
6929  {
6930  if ((state & Mod1Mask) != 0)
6931  {
6933  crop_info;
6934 
6935  /*
6936  Trim one pixel from edge of image.
6937  */
6938  crop_info.x=0;
6939  crop_info.y=0;
6940  crop_info.width=(size_t) windows->image.ximage->width;
6941  crop_info.height=(size_t) windows->image.ximage->height;
6942  if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
6943  {
6944  if (resource_info->quantum >= (int) crop_info.height)
6945  resource_info->quantum=(int) crop_info.height-1;
6946  crop_info.height-=resource_info->quantum;
6947  }
6948  if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
6949  {
6950  if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
6951  resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
6952  crop_info.y+=resource_info->quantum;
6953  crop_info.height-=resource_info->quantum;
6954  }
6955  if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
6956  {
6957  if (resource_info->quantum >= (int) crop_info.width)
6958  resource_info->quantum=(int) crop_info.width-1;
6959  crop_info.width-=resource_info->quantum;
6960  }
6961  if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
6962  {
6963  if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
6964  resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
6965  crop_info.x+=resource_info->quantum;
6966  crop_info.width-=resource_info->quantum;
6967  }
6968  if ((int) (windows->image.x+windows->image.width) >
6969  (int) crop_info.width)
6970  windows->image.x=(int) (crop_info.width-windows->image.width);
6971  if ((int) (windows->image.y+windows->image.height) >
6972  (int) crop_info.height)
6973  windows->image.y=(int) (crop_info.height-windows->image.height);
6974  XSetCropGeometry(display,windows,&crop_info,*image);
6975  windows->image.window_changes.width=(int) crop_info.width;
6976  windows->image.window_changes.height=(int) crop_info.height;
6977  (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
6978  (void) XConfigureImage(display,resource_info,windows,*image);
6979  return(NullCommand);
6980  }
6981  XTranslateImage(display,windows,*image,key_symbol);
6982  return(NullCommand);
6983  }
6984  default:
6985  return(NullCommand);
6986  }
6987  return(NullCommand);
6988 }
6989 
6990 /*
6991 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6992 % %
6993 % %
6994 % %
6995 + X M a g i c k C o m m a n d %
6996 % %
6997 % %
6998 % %
6999 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7000 %
7001 % XMagickCommand() makes a transform to the image or Image window as
7002 % specified by a user menu button or keyboard command.
7003 %
7004 % The format of the XMagickCommand method is:
7005 %
7006 % Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7007 % XWindows *windows,const CommandType command,Image **image)
7008 %
7009 % A description of each parameter follows:
7010 %
7011 % o nexus: Method XMagickCommand returns an image when the
7012 % user chooses 'Load Image' from the command menu. Otherwise a null
7013 % image is returned.
7014 %
7015 % o display: Specifies a connection to an X server; returned from
7016 % XOpenDisplay.
7017 %
7018 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7019 %
7020 % o windows: Specifies a pointer to a XWindows structure.
7021 %
7022 % o command: Specifies a command to perform.
7023 %
7024 % o image: the image; XMagickCommand
7025 % may transform the image and return a new image pointer.
7026 %
7027 */
7028 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7029  XWindows *windows,const CommandType command,Image **image)
7030 {
7031  char
7032  filename[MaxTextExtent],
7033  geometry[MaxTextExtent],
7034  modulate_factors[MaxTextExtent];
7035 
7036  GeometryInfo
7037  geometry_info;
7038 
7039  Image
7040  *nexus;
7041 
7042  ImageInfo
7043  *image_info;
7044 
7045  int
7046  x,
7047  y;
7048 
7049  MagickStatusType
7050  flags,
7051  status;
7052 
7053  QuantizeInfo
7054  quantize_info;
7055 
7057  page_geometry;
7058 
7059  int
7060  i;
7061 
7062  static char
7063  color[MaxTextExtent] = "gray";
7064 
7065  unsigned int
7066  height,
7067  width;
7068 
7069  /*
7070  Process user command.
7071  */
7072  XCheckRefreshWindows(display,windows);
7073  XImageCache(display,resource_info,windows,command,image);
7074  nexus=NewImageList();
7075  windows->image.window_changes.width=windows->image.ximage->width;
7076  windows->image.window_changes.height=windows->image.ximage->height;
7077  image_info=CloneImageInfo(resource_info->image_info);
7078  SetGeometryInfo(&geometry_info);
7079  GetQuantizeInfo(&quantize_info);
7080  switch (command)
7081  {
7082  case OpenCommand:
7083  {
7084  /*
7085  Load image.
7086  */
7087  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7088  break;
7089  }
7090  case NextCommand:
7091  {
7092  /*
7093  Display next image.
7094  */
7095  for (i=0; i < resource_info->quantum; i++)
7096  XClientMessage(display,windows->image.id,windows->im_protocols,
7097  windows->im_next_image,CurrentTime);
7098  break;
7099  }
7100  case FormerCommand:
7101  {
7102  /*
7103  Display former image.
7104  */
7105  for (i=0; i < resource_info->quantum; i++)
7106  XClientMessage(display,windows->image.id,windows->im_protocols,
7107  windows->im_former_image,CurrentTime);
7108  break;
7109  }
7110  case SelectCommand:
7111  {
7112  int
7113  status;
7114 
7115  /*
7116  Select image.
7117  */
7118  if (*resource_info->home_directory == '\0')
7119  (void) CopyMagickString(resource_info->home_directory,".",
7120  MaxTextExtent);
7121  status=chdir(resource_info->home_directory);
7122  if (status == -1)
7123  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
7124  FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
7125  nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7126  break;
7127  }
7128  case SaveCommand:
7129  {
7130  /*
7131  Save image.
7132  */
7133  status=XSaveImage(display,resource_info,windows,*image);
7134  if (status == MagickFalse)
7135  {
7136  char
7137  message[MaxTextExtent];
7138 
7139  (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7140  (*image)->exception.reason != (char *) NULL ?
7141  (*image)->exception.reason : "",
7142  (*image)->exception.description != (char *) NULL ?
7143  (*image)->exception.description : "");
7144  XNoticeWidget(display,windows,"Unable to save file:",message);
7145  break;
7146  }
7147  break;
7148  }
7149  case PrintCommand:
7150  {
7151  /*
7152  Print image.
7153  */
7154  status=XPrintImage(display,resource_info,windows,*image);
7155  if (status == MagickFalse)
7156  {
7157  char
7158  message[MaxTextExtent];
7159 
7160  (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7161  (*image)->exception.reason != (char *) NULL ?
7162  (*image)->exception.reason : "",
7163  (*image)->exception.description != (char *) NULL ?
7164  (*image)->exception.description : "");
7165  XNoticeWidget(display,windows,"Unable to print file:",message);
7166  break;
7167  }
7168  break;
7169  }
7170  case DeleteCommand:
7171  {
7172  static char
7173  filename[MaxTextExtent] = "\0";
7174 
7175  /*
7176  Delete image file.
7177  */
7178  XFileBrowserWidget(display,windows,"Delete",filename);
7179  if (*filename == '\0')
7180  break;
7181  status=ShredFile(filename);
7182  status|=remove_utf8(filename);
7183  if (status != MagickFalse)
7184  XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7185  break;
7186  }
7187  case NewCommand:
7188  {
7189  int
7190  status;
7191 
7192  static char
7193  color[MaxTextExtent] = "gray",
7194  geometry[MaxTextExtent] = "640x480";
7195 
7196  static const char
7197  *format = "gradient";
7198 
7199  /*
7200  Query user for canvas geometry.
7201  */
7202  status=XDialogWidget(display,windows,"New","Enter image geometry:",
7203  geometry);
7204  if (*geometry == '\0')
7205  break;
7206  if (status == 0)
7207  format="xc";
7208  XColorBrowserWidget(display,windows,"Select",color);
7209  if (*color == '\0')
7210  break;
7211  /*
7212  Create canvas.
7213  */
7214  (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7215  "%s:%s",format,color);
7216  (void) CloneString(&image_info->size,geometry);
7217  nexus=ReadImage(image_info,&(*image)->exception);
7218  CatchException(&(*image)->exception);
7219  XClientMessage(display,windows->image.id,windows->im_protocols,
7220  windows->im_next_image,CurrentTime);
7221  break;
7222  }
7223  case VisualDirectoryCommand:
7224  {
7225  /*
7226  Visual Image directory.
7227  */
7228  nexus=XVisualDirectoryImage(display,resource_info,windows);
7229  break;
7230  }
7231  case QuitCommand:
7232  {
7233  /*
7234  exit program.
7235  */
7236  if (resource_info->confirm_exit == MagickFalse)
7237  XClientMessage(display,windows->image.id,windows->im_protocols,
7238  windows->im_exit,CurrentTime);
7239  else
7240  {
7241  int
7242  status;
7243 
7244  /*
7245  Confirm program exit.
7246  */
7247  status=XConfirmWidget(display,windows,"Do you really want to exit",
7248  resource_info->client_name);
7249  if (status > 0)
7250  XClientMessage(display,windows->image.id,windows->im_protocols,
7251  windows->im_exit,CurrentTime);
7252  }
7253  break;
7254  }
7255  case CutCommand:
7256  {
7257  /*
7258  Cut image.
7259  */
7260  (void) XCropImage(display,resource_info,windows,*image,CutMode);
7261  break;
7262  }
7263  case CopyCommand:
7264  {
7265  /*
7266  Copy image.
7267  */
7268  (void) XCropImage(display,resource_info,windows,*image,CopyMode);
7269  break;
7270  }
7271  case PasteCommand:
7272  {
7273  /*
7274  Paste image.
7275  */
7276  status=XPasteImage(display,resource_info,windows,*image);
7277  if (status == MagickFalse)
7278  {
7279  XNoticeWidget(display,windows,"Unable to paste X image",
7280  (*image)->filename);
7281  break;
7282  }
7283  break;
7284  }
7285  case HalfSizeCommand:
7286  {
7287  /*
7288  Half image size.
7289  */
7290  windows->image.window_changes.width=windows->image.ximage->width/2;
7291  windows->image.window_changes.height=windows->image.ximage->height/2;
7292  (void) XConfigureImage(display,resource_info,windows,*image);
7293  break;
7294  }
7295  case OriginalSizeCommand:
7296  {
7297  /*
7298  Original image size.
7299  */
7300  windows->image.window_changes.width=(int) (*image)->columns;
7301  windows->image.window_changes.height=(int) (*image)->rows;
7302  (void) XConfigureImage(display,resource_info,windows,*image);
7303  break;
7304  }
7305  case DoubleSizeCommand:
7306  {
7307  /*
7308  Double the image size.
7309  */
7310  windows->image.window_changes.width=windows->image.ximage->width << 1;
7311  windows->image.window_changes.height=windows->image.ximage->height << 1;
7312  (void) XConfigureImage(display,resource_info,windows,*image);
7313  break;
7314  }
7315  case ResizeCommand:
7316  {
7317  int
7318  status;
7319 
7320  size_t
7321  height,
7322  width;
7323 
7324  ssize_t
7325  x,
7326  y;
7327 
7328  /*
7329  Resize image.
7330  */
7331  width=(size_t) windows->image.ximage->width;
7332  height=(size_t) windows->image.ximage->height;
7333  x=0;
7334  y=0;
7335  (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7336  (double) width,(double) height);
7337  status=XDialogWidget(display,windows,"Resize",
7338  "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7339  if (*geometry == '\0')
7340  break;
7341  if (status == 0)
7342  (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7343  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7344  windows->image.window_changes.width=(int) width;
7345  windows->image.window_changes.height=(int) height;
7346  (void) XConfigureImage(display,resource_info,windows,*image);
7347  break;
7348  }
7349  case ApplyCommand:
7350  {
7351  char
7352  image_geometry[MaxTextExtent];
7353 
7354  if ((windows->image.crop_geometry == (char *) NULL) &&
7355  ((int) (*image)->columns == windows->image.ximage->width) &&
7356  ((int) (*image)->rows == windows->image.ximage->height))
7357  break;
7358  /*
7359  Apply size transforms to image.
7360  */
7361  XSetCursorState(display,windows,MagickTrue);
7362  XCheckRefreshWindows(display,windows);
7363  /*
7364  Crop and/or scale displayed image.
7365  */
7366  (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7367  windows->image.ximage->width,windows->image.ximage->height);
7368  (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7369  if (windows->image.crop_geometry != (char *) NULL)
7370  windows->image.crop_geometry=(char *)
7371  RelinquishMagickMemory(windows->image.crop_geometry);
7372  windows->image.x=0;
7373  windows->image.y=0;
7374  XConfigureImageColormap(display,resource_info,windows,*image);
7375  (void) XConfigureImage(display,resource_info,windows,*image);
7376  break;
7377  }
7378  case RefreshCommand:
7379  {
7380  (void) XConfigureImage(display,resource_info,windows,*image);
7381  break;
7382  }
7383  case RestoreCommand:
7384  {
7385  /*
7386  Restore Image window to its original size.
7387  */
7388  if ((windows->image.width == (unsigned int) (*image)->columns) &&
7389  (windows->image.height == (unsigned int) (*image)->rows) &&
7390  (windows->image.crop_geometry == (char *) NULL))
7391  {
7392  (void) XBell(display,0);
7393  break;
7394  }
7395  windows->image.window_changes.width=(int) (*image)->columns;
7396  windows->image.window_changes.height=(int) (*image)->rows;
7397  if (windows->image.crop_geometry != (char *) NULL)
7398  {
7399  windows->image.crop_geometry=(char *)
7400  RelinquishMagickMemory(windows->image.crop_geometry);
7401  windows->image.crop_geometry=(char *) NULL;
7402  windows->image.x=0;
7403  windows->image.y=0;
7404  }
7405  XConfigureImageColormap(display,resource_info,windows,*image);
7406  (void) XConfigureImage(display,resource_info,windows,*image);
7407  break;
7408  }
7409  case CropCommand:
7410  {
7411  /*
7412  Crop image.
7413  */
7414  (void) XCropImage(display,resource_info,windows,*image,CropMode);
7415  break;
7416  }
7417  case ChopCommand:
7418  {
7419  /*
7420  Chop image.
7421  */
7422  status=XChopImage(display,resource_info,windows,image);
7423  if (status == MagickFalse)
7424  {
7425  XNoticeWidget(display,windows,"Unable to cut X image",
7426  (*image)->filename);
7427  break;
7428  }
7429  break;
7430  }
7431  case FlopCommand:
7432  {
7433  Image
7434  *flop_image;
7435 
7436  /*
7437  Flop image scanlines.
7438  */
7439  XSetCursorState(display,windows,MagickTrue);
7440  XCheckRefreshWindows(display,windows);
7441  flop_image=FlopImage(*image,&(*image)->exception);
7442  if (flop_image != (Image *) NULL)
7443  {
7444  *image=DestroyImage(*image);
7445  *image=flop_image;
7446  }
7447  CatchException(&(*image)->exception);
7448  XSetCursorState(display,windows,MagickFalse);
7449  if (windows->image.crop_geometry != (char *) NULL)
7450  {
7451  /*
7452  Flop crop geometry.
7453  */
7454  width=(unsigned int) (*image)->columns;
7455  height=(unsigned int) (*image)->rows;
7456  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7457  &width,&height);
7458  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7459  "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7460  }
7461  if (windows->image.orphan != MagickFalse)
7462  break;
7463  (void) XConfigureImage(display,resource_info,windows,*image);
7464  break;
7465  }
7466  case FlipCommand:
7467  {
7468  Image
7469  *flip_image;
7470 
7471  /*
7472  Flip image scanlines.
7473  */
7474  XSetCursorState(display,windows,MagickTrue);
7475  XCheckRefreshWindows(display,windows);
7476  flip_image=FlipImage(*image,&(*image)->exception);
7477  if (flip_image != (Image *) NULL)
7478  {
7479  *image=DestroyImage(*image);
7480  *image=flip_image;
7481  }
7482  CatchException(&(*image)->exception);
7483  XSetCursorState(display,windows,MagickFalse);
7484  if (windows->image.crop_geometry != (char *) NULL)
7485  {
7486  /*
7487  Flip crop geometry.
7488  */
7489  width=(unsigned int) (*image)->columns;
7490  height=(unsigned int) (*image)->rows;
7491  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7492  &width,&height);
7493  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7494  "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7495  }
7496  if (windows->image.orphan != MagickFalse)
7497  break;
7498  (void) XConfigureImage(display,resource_info,windows,*image);
7499  break;
7500  }
7501  case RotateRightCommand:
7502  {
7503  /*
7504  Rotate image 90 degrees clockwise.
7505  */
7506  status=XRotateImage(display,resource_info,windows,90.0,image);
7507  if (status == MagickFalse)
7508  {
7509  XNoticeWidget(display,windows,"Unable to rotate X image",
7510  (*image)->filename);
7511  break;
7512  }
7513  break;
7514  }
7515  case RotateLeftCommand:
7516  {
7517  /*
7518  Rotate image 90 degrees counter-clockwise.
7519  */
7520  status=XRotateImage(display,resource_info,windows,-90.0,image);
7521  if (status == MagickFalse)
7522  {
7523  XNoticeWidget(display,windows,"Unable to rotate X image",
7524  (*image)->filename);
7525  break;
7526  }
7527  break;
7528  }
7529  case RotateCommand:
7530  {
7531  /*
7532  Rotate image.
7533  */
7534  status=XRotateImage(display,resource_info,windows,0.0,image);
7535  if (status == MagickFalse)
7536  {
7537  XNoticeWidget(display,windows,"Unable to rotate X image",
7538  (*image)->filename);
7539  break;
7540  }
7541  break;
7542  }
7543  case ShearCommand:
7544  {
7545  Image
7546  *shear_image;
7547 
7548  static char
7549  geometry[MaxTextExtent] = "45.0x45.0";
7550 
7551  /*
7552  Query user for shear color and geometry.
7553  */
7554  XColorBrowserWidget(display,windows,"Select",color);
7555  if (*color == '\0')
7556  break;
7557  (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7558  geometry);
7559  if (*geometry == '\0')
7560  break;
7561  /*
7562  Shear image.
7563  */
7564  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7565  XSetCursorState(display,windows,MagickTrue);
7566  XCheckRefreshWindows(display,windows);
7567  (void) QueryColorDatabase(color,&(*image)->background_color,
7568  &(*image)->exception);
7569  flags=ParseGeometry(geometry,&geometry_info);
7570  if ((flags & SigmaValue) == 0)
7571  geometry_info.sigma=geometry_info.rho;
7572  shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7573  &(*image)->exception);
7574  if (shear_image != (Image *) NULL)
7575  {
7576  *image=DestroyImage(*image);
7577  *image=shear_image;
7578  }
7579  CatchException(&(*image)->exception);
7580  XSetCursorState(display,windows,MagickFalse);
7581  if (windows->image.orphan != MagickFalse)
7582  break;
7583  windows->image.window_changes.width=(int) (*image)->columns;
7584  windows->image.window_changes.height=(int) (*image)->rows;
7585  XConfigureImageColormap(display,resource_info,windows,*image);
7586  (void) XConfigureImage(display,resource_info,windows,*image);
7587  break;
7588  }
7589  case RollCommand:
7590  {
7591  Image
7592  *roll_image;
7593 
7594  static char
7595  geometry[MaxTextExtent] = "+2+2";
7596 
7597  /*
7598  Query user for the roll geometry.
7599  */
7600  (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7601  geometry);
7602  if (*geometry == '\0')
7603  break;
7604  /*
7605  Roll image.
7606  */
7607  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7608  XSetCursorState(display,windows,MagickTrue);
7609  XCheckRefreshWindows(display,windows);
7610  (void) ParsePageGeometry(*image,geometry,&page_geometry,
7611  &(*image)->exception);
7612  roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7613  &(*image)->exception);
7614  if (roll_image != (Image *) NULL)
7615  {
7616  *image=DestroyImage(*image);
7617  *image=roll_image;
7618  }
7619  CatchException(&(*image)->exception);
7620  XSetCursorState(display,windows,MagickFalse);
7621  if (windows->image.orphan != MagickFalse)
7622  break;
7623  windows->image.window_changes.width=(int) (*image)->columns;
7624  windows->image.window_changes.height=(int) (*image)->rows;
7625  XConfigureImageColormap(display,resource_info,windows,*image);
7626  (void) XConfigureImage(display,resource_info,windows,*image);
7627  break;
7628  }
7629  case TrimCommand:
7630  {
7631  static char
7632  fuzz[MaxTextExtent];
7633 
7634  /*
7635  Query user for the fuzz factor.
7636  */
7637  (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7638  (*image)->fuzz/(QuantumRange+1.0));
7639  (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7640  if (*fuzz == '\0')
7641  break;
7642  (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7643  /*
7644  Trim image.
7645  */
7646  status=XTrimImage(display,resource_info,windows,*image);
7647  if (status == MagickFalse)
7648  {
7649  XNoticeWidget(display,windows,"Unable to trim X image",
7650  (*image)->filename);
7651  break;
7652  }
7653  break;
7654  }
7655  case HueCommand:
7656  {
7657  static char
7658  hue_percent[MaxTextExtent] = "110";
7659 
7660  /*
7661  Query user for percent hue change.
7662  */
7663  (void) XDialogWidget(display,windows,"Apply",
7664  "Enter percent change in image hue (0-200):",hue_percent);
7665  if (*hue_percent == '\0')
7666  break;
7667  /*
7668  Vary the image hue.
7669  */
7670  XSetCursorState(display,windows,MagickTrue);
7671  XCheckRefreshWindows(display,windows);
7672  (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7673  (void) ConcatenateMagickString(modulate_factors,hue_percent,
7674  MaxTextExtent);
7675  (void) ModulateImage(*image,modulate_factors);
7676  XSetCursorState(display,windows,MagickFalse);
7677  if (windows->image.orphan != MagickFalse)
7678  break;
7679  XConfigureImageColormap(display,resource_info,windows,*image);
7680  (void) XConfigureImage(display,resource_info,windows,*image);
7681  break;
7682  }
7683  case SaturationCommand:
7684  {
7685  static char
7686  saturation_percent[MaxTextExtent] = "110";
7687 
7688  /*
7689  Query user for percent saturation change.
7690  */
7691  (void) XDialogWidget(display,windows,"Apply",
7692  "Enter percent change in color saturation (0-200):",saturation_percent);
7693  if (*saturation_percent == '\0')
7694  break;
7695  /*
7696  Vary color saturation.
7697  */
7698  XSetCursorState(display,windows,MagickTrue);
7699  XCheckRefreshWindows(display,windows);
7700  (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7701  (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7702  MaxTextExtent);
7703  (void) ModulateImage(*image,modulate_factors);
7704  XSetCursorState(display,windows,MagickFalse);
7705  if (windows->image.orphan != MagickFalse)
7706  break;
7707  XConfigureImageColormap(display,resource_info,windows,*image);
7708  (void) XConfigureImage(display,resource_info,windows,*image);
7709  break;
7710  }
7711  case BrightnessCommand:
7712  {
7713  static char
7714  brightness_percent[MaxTextExtent] = "110";
7715 
7716  /*
7717  Query user for percent brightness change.
7718  */
7719  (void) XDialogWidget(display,windows,"Apply",
7720  "Enter percent change in color brightness (0-200):",brightness_percent);
7721  if (*brightness_percent == '\0')
7722  break;
7723  /*
7724  Vary the color brightness.
7725  */
7726  XSetCursorState(display,windows,MagickTrue);
7727  XCheckRefreshWindows(display,windows);
7728  (void) CopyMagickString(modulate_factors,brightness_percent,
7729  MaxTextExtent);
7730  (void) ModulateImage(*image,modulate_factors);
7731  XSetCursorState(display,windows,MagickFalse);
7732  if (windows->image.orphan != MagickFalse)
7733  break;
7734  XConfigureImageColormap(display,resource_info,windows,*image);
7735  (void) XConfigureImage(display,resource_info,windows,*image);
7736  break;
7737  }
7738  case GammaCommand:
7739  {
7740  static char
7741  factor[MaxTextExtent] = "1.6";
7742 
7743  /*
7744  Query user for gamma value.
7745  */
7746  (void) XDialogWidget(display,windows,"Gamma",
7747  "Enter gamma value (e.g. 1.0,1.0,1.6):",factor);
7748  if (*factor == '\0')
7749  break;
7750  /*
7751  Gamma correct image.
7752  */
7753  XSetCursorState(display,windows,MagickTrue);
7754  XCheckRefreshWindows(display,windows);
7755  (void) GammaImage(*image,factor);
7756  XSetCursorState(display,windows,MagickFalse);
7757  if (windows->image.orphan != MagickFalse)
7758  break;
7759  XConfigureImageColormap(display,resource_info,windows,*image);
7760  (void) XConfigureImage(display,resource_info,windows,*image);
7761  break;
7762  }
7763  case SpiffCommand:
7764  {
7765  /*
7766  Sharpen the image contrast.
7767  */
7768  XSetCursorState(display,windows,MagickTrue);
7769  XCheckRefreshWindows(display,windows);
7770  (void) ContrastImage(*image,MagickTrue);
7771  XSetCursorState(display,windows,MagickFalse);
7772  if (windows->image.orphan != MagickFalse)
7773  break;
7774  XConfigureImageColormap(display,resource_info,windows,*image);
7775  (void) XConfigureImage(display,resource_info,windows,*image);
7776  break;
7777  }
7778  case DullCommand:
7779  {
7780  /*
7781  Dull the image contrast.
7782  */
7783  XSetCursorState(display,windows,MagickTrue);
7784  XCheckRefreshWindows(display,windows);
7785  (void) ContrastImage(*image,MagickFalse);
7786  XSetCursorState(display,windows,MagickFalse);
7787  if (windows->image.orphan != MagickFalse)
7788  break;
7789  XConfigureImageColormap(display,resource_info,windows,*image);
7790  (void) XConfigureImage(display,resource_info,windows,*image);
7791  break;
7792  }
7793  case ContrastStretchCommand:
7794  {
7795  double
7796  black_point,
7797  white_point;
7798 
7799  static char
7800  levels[MaxTextExtent] = "1%";
7801 
7802  /*
7803  Query user for gamma value.
7804  */
7805  (void) XDialogWidget(display,windows,"Contrast Stretch",
7806  "Enter black and white points:",levels);
7807  if (*levels == '\0')
7808  break;
7809  /*
7810  Contrast stretch image.
7811  */
7812  XSetCursorState(display,windows,MagickTrue);
7813  XCheckRefreshWindows(display,windows);
7814  flags=ParseGeometry(levels,&geometry_info);
7815  black_point=geometry_info.rho;
7816  white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7817  if ((flags & PercentValue) != 0)
7818  {
7819  black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7820  white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7821  }
7822  white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7823  (void) ContrastStretchImageChannel(*image,DefaultChannels,black_point,
7824  white_point);
7825  XSetCursorState(display,windows,MagickFalse);
7826  if (windows->image.orphan != MagickFalse)
7827  break;
7828  XConfigureImageColormap(display,resource_info,windows,*image);
7829  (void) XConfigureImage(display,resource_info,windows,*image);
7830  break;
7831  }
7832  case SigmoidalContrastCommand:
7833  {
7834  static char
7835  levels[MaxTextExtent] = "3x50%";
7836 
7837  /*
7838  Query user for gamma value.
7839  */
7840  (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7841  "Enter contrast and midpoint:",levels);
7842  if (*levels == '\0')
7843  break;
7844  /*
7845  Contrast stretch image.
7846  */
7847  XSetCursorState(display,windows,MagickTrue);
7848  XCheckRefreshWindows(display,windows);
7849  (void) SigmoidalContrastImage(*image,MagickTrue,levels);
7850  XSetCursorState(display,windows,MagickFalse);
7851  if (windows->image.orphan != MagickFalse)
7852  break;
7853  XConfigureImageColormap(display,resource_info,windows,*image);
7854  (void) XConfigureImage(display,resource_info,windows,*image);
7855  break;
7856  }
7857  case NormalizeCommand:
7858  {
7859  /*
7860  Perform histogram normalization on the image.
7861  */
7862  XSetCursorState(display,windows,MagickTrue);
7863  XCheckRefreshWindows(display,windows);
7864  (void) NormalizeImage(*image);
7865  XSetCursorState(display,windows,MagickFalse);
7866  if (windows->image.orphan != MagickFalse)
7867  break;
7868  XConfigureImageColormap(display,resource_info,windows,*image);
7869  (void) XConfigureImage(display,resource_info,windows,*image);
7870  break;
7871  }
7872  case EqualizeCommand:
7873  {
7874  /*
7875  Perform histogram equalization on the image.
7876  */
7877  XSetCursorState(display,windows,MagickTrue);
7878  XCheckRefreshWindows(display,windows);
7879  (void) EqualizeImage(*image);
7880  XSetCursorState(display,windows,MagickFalse);
7881  if (windows->image.orphan != MagickFalse)
7882  break;
7883  XConfigureImageColormap(display,resource_info,windows,*image);
7884  (void) XConfigureImage(display,resource_info,windows,*image);
7885  break;
7886  }
7887  case NegateCommand:
7888  {
7889  /*
7890  Negate colors in image.
7891  */
7892  XSetCursorState(display,windows,MagickTrue);
7893  XCheckRefreshWindows(display,windows);
7894  (void) NegateImage(*image,MagickFalse);
7895  XSetCursorState(display,windows,MagickFalse);
7896  if (windows->image.orphan != MagickFalse)
7897  break;
7898  XConfigureImageColormap(display,resource_info,windows,*image);
7899  (void) XConfigureImage(display,resource_info,windows,*image);
7900  break;
7901  }
7902  case GrayscaleCommand:
7903  {
7904  /*
7905  Convert image to grayscale.
7906  */
7907  XSetCursorState(display,windows,MagickTrue);
7908  XCheckRefreshWindows(display,windows);
7909  (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7910  GrayscaleType : GrayscaleMatteType);
7911  XSetCursorState(display,windows,MagickFalse);
7912  if (windows->image.orphan != MagickFalse)
7913  break;
7914  XConfigureImageColormap(display,resource_info,windows,*image);
7915  (void) XConfigureImage(display,resource_info,windows,*image);
7916  break;
7917  }
7918  case MapCommand:
7919  {
7920  Image
7921  *affinity_image;
7922 
7923  static char
7924  filename[MaxTextExtent] = "\0";
7925 
7926  /*
7927  Request image file name from user.
7928  */
7929  XFileBrowserWidget(display,windows,"Map",filename);
7930  if (*filename == '\0')
7931  break;
7932  /*
7933  Map image.
7934  */
7935  XSetCursorState(display,windows,MagickTrue);
7936  XCheckRefreshWindows(display,windows);
7937  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
7938  affinity_image=ReadImage(image_info,&(*image)->exception);
7939  if (affinity_image != (Image *) NULL)
7940  {
7941  (void) RemapImage(&quantize_info,*image,affinity_image);
7942  affinity_image=DestroyImage(affinity_image);
7943  }
7944  CatchException(&(*image)->exception);
7945  XSetCursorState(display,windows,MagickFalse);
7946  if (windows->image.orphan != MagickFalse)
7947  break;
7948  XConfigureImageColormap(display,resource_info,windows,*image);
7949  (void) XConfigureImage(display,resource_info,windows,*image);
7950  break;
7951  }
7952  case QuantizeCommand:
7953  {
7954  int
7955  status;
7956 
7957  static char
7958  colors[MaxTextExtent] = "256";
7959 
7960  /*
7961  Query user for maximum number of colors.
7962  */
7963  status=XDialogWidget(display,windows,"Quantize",
7964  "Maximum number of colors:",colors);
7965  if (*colors == '\0')
7966  break;
7967  /*
7968  Color reduce the image.
7969  */
7970  XSetCursorState(display,windows,MagickTrue);
7971  XCheckRefreshWindows(display,windows);
7972  quantize_info.number_colors=StringToUnsignedLong(colors);
7973  quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
7974  (void) QuantizeImage(&quantize_info,*image);
7975  XSetCursorState(display,windows,MagickFalse);
7976  if (windows->image.orphan != MagickFalse)
7977  break;
7978  XConfigureImageColormap(display,resource_info,windows,*image);
7979  (void) XConfigureImage(display,resource_info,windows,*image);
7980  break;
7981  }
7982  case DespeckleCommand:
7983  {
7984  Image
7985  *despeckle_image;
7986 
7987  /*
7988  Despeckle image.
7989  */
7990  XSetCursorState(display,windows,MagickTrue);
7991  XCheckRefreshWindows(display,windows);
7992  despeckle_image=DespeckleImage(*image,&(*image)->exception);
7993  if (despeckle_image != (Image *) NULL)
7994  {
7995  *image=DestroyImage(*image);
7996  *image=despeckle_image;
7997  }
7998  CatchException(&(*image)->exception);
7999  XSetCursorState(display,windows,MagickFalse);
8000  if (windows->image.orphan != MagickFalse)
8001  break;
8002  XConfigureImageColormap(display,resource_info,windows,*image);
8003  (void) XConfigureImage(display,resource_info,windows,*image);
8004  break;
8005  }
8006  case EmbossCommand:
8007  {
8008  Image
8009  *emboss_image;
8010 
8011  static char
8012  radius[MaxTextExtent] = "0.0x1.0";
8013 
8014  /*
8015  Query user for emboss radius.
8016  */
8017  (void) XDialogWidget(display,windows,"Emboss",
8018  "Enter the emboss radius and standard deviation:",radius);
8019  if (*radius == '\0')
8020  break;
8021  /*
8022  Reduce noise in the image.
8023  */
8024  XSetCursorState(display,windows,MagickTrue);
8025  XCheckRefreshWindows(display,windows);
8026  flags=ParseGeometry(radius,&geometry_info);
8027  if ((flags & SigmaValue) == 0)
8028  geometry_info.sigma=1.0;
8029  emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8030  &(*image)->exception);
8031  if (emboss_image != (Image *) NULL)
8032  {
8033  *image=DestroyImage(*image);
8034  *image=emboss_image;
8035  }
8036  CatchException(&(*image)->exception);
8037  XSetCursorState(display,windows,MagickFalse);
8038  if (windows->image.orphan != MagickFalse)
8039  break;
8040  XConfigureImageColormap(display,resource_info,windows,*image);
8041  (void) XConfigureImage(display,resource_info,windows,*image);
8042  break;
8043  }
8044  case ReduceNoiseCommand:
8045  {
8046  Image
8047  *noise_image;
8048 
8049  static char
8050  radius[MaxTextExtent] = "0";
8051 
8052  /*
8053  Query user for noise radius.
8054  */
8055  (void) XDialogWidget(display,windows,"Reduce Noise",
8056  "Enter the noise radius:",radius);
8057  if (*radius == '\0')
8058  break;
8059  /*
8060  Reduce noise in the image.
8061  */
8062  XSetCursorState(display,windows,MagickTrue);
8063  XCheckRefreshWindows(display,windows);
8064  flags=ParseGeometry(radius,&geometry_info);
8065  noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8066  geometry_info.rho,(size_t) geometry_info.rho,&(*image)->exception);
8067  if (noise_image != (Image *) NULL)
8068  {
8069  *image=DestroyImage(*image);
8070  *image=noise_image;
8071  }
8072  CatchException(&(*image)->exception);
8073  XSetCursorState(display,windows,MagickFalse);
8074  if (windows->image.orphan != MagickFalse)
8075  break;
8076  XConfigureImageColormap(display,resource_info,windows,*image);
8077  (void) XConfigureImage(display,resource_info,windows,*image);
8078  break;
8079  }
8080  case AddNoiseCommand:
8081  {
8082  char
8083  **noises;
8084 
8085  Image
8086  *noise_image;
8087 
8088  static char
8089  noise_type[MaxTextExtent] = "Gaussian";
8090 
8091  /*
8092  Add noise to the image.
8093  */
8094  noises=GetCommandOptions(MagickNoiseOptions);
8095  if (noises == (char **) NULL)
8096  break;
8097  XListBrowserWidget(display,windows,&windows->widget,
8098  (const char **) noises,"Add Noise",
8099  "Select a type of noise to add to your image:",noise_type);
8100  noises=DestroyStringList(noises);
8101  if (*noise_type == '\0')
8102  break;
8103  XSetCursorState(display,windows,MagickTrue);
8104  XCheckRefreshWindows(display,windows);
8105  noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8106  MagickNoiseOptions,MagickFalse,noise_type),&(*image)->exception);
8107  if (noise_image != (Image *) NULL)
8108  {
8109  *image=DestroyImage(*image);
8110  *image=noise_image;
8111  }
8112  CatchException(&(*image)->exception);
8113  XSetCursorState(display,windows,MagickFalse);
8114  if (windows->image.orphan != MagickFalse)
8115  break;
8116  XConfigureImageColormap(display,resource_info,windows,*image);
8117  (void) XConfigureImage(display,resource_info,windows,*image);
8118  break;
8119  }
8120  case SharpenCommand:
8121  {
8122  Image
8123  *sharp_image;
8124 
8125  static char
8126  radius[MaxTextExtent] = "0.0x1.0";
8127 
8128  /*
8129  Query user for sharpen radius.
8130  */
8131  (void) XDialogWidget(display,windows,"Sharpen",
8132  "Enter the sharpen radius and standard deviation:",radius);
8133  if (*radius == '\0')
8134  break;
8135  /*
8136  Sharpen image scanlines.
8137  */
8138  XSetCursorState(display,windows,MagickTrue);
8139  XCheckRefreshWindows(display,windows);
8140  flags=ParseGeometry(radius,&geometry_info);
8141  sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8142  &(*image)->exception);
8143  if (sharp_image != (Image *) NULL)
8144  {
8145  *image=DestroyImage(*image);
8146  *image=sharp_image;
8147  }
8148  CatchException(&(*image)->exception);
8149  XSetCursorState(display,windows,MagickFalse);
8150  if (windows->image.orphan != MagickFalse)
8151  break;
8152  XConfigureImageColormap(display,resource_info,windows,*image);
8153  (void) XConfigureImage(display,resource_info,windows,*image);
8154  break;
8155  }
8156  case BlurCommand:
8157  {
8158  Image
8159  *blur_image;
8160 
8161  static char
8162  radius[MaxTextExtent] = "0.0x1.0";
8163 
8164  /*
8165  Query user for blur radius.
8166  */
8167  (void) XDialogWidget(display,windows,"Blur",
8168  "Enter the blur radius and standard deviation:",radius);
8169  if (*radius == '\0')
8170  break;
8171  /*
8172  Blur an image.
8173  */
8174  XSetCursorState(display,windows,MagickTrue);
8175  XCheckRefreshWindows(display,windows);
8176  flags=ParseGeometry(radius,&geometry_info);
8177  blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8178  &(*image)->exception);
8179  if (blur_image != (Image *) NULL)
8180  {
8181  *image=DestroyImage(*image);
8182  *image=blur_image;
8183  }
8184  CatchException(&(*image)->exception);
8185  XSetCursorState(display,windows,MagickFalse);
8186  if (windows->image.orphan != MagickFalse)
8187  break;
8188  XConfigureImageColormap(display,resource_info,windows,*image);
8189  (void) XConfigureImage(display,resource_info,windows,*image);
8190  break;
8191  }
8192  case ThresholdCommand:
8193  {
8194  double
8195  threshold;
8196 
8197  static char
8198  factor[MaxTextExtent] = "128";
8199 
8200  /*
8201  Query user for threshold value.
8202  */
8203  (void) XDialogWidget(display,windows,"Threshold",
8204  "Enter threshold value:",factor);
8205  if (*factor == '\0')
8206  break;
8207  /*
8208  Gamma correct image.
8209  */
8210  XSetCursorState(display,windows,MagickTrue);
8211  XCheckRefreshWindows(display,windows);
8212  threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8213  (void) BilevelImage(*image,threshold);
8214  XSetCursorState(display,windows,MagickFalse);
8215  if (windows->image.orphan != MagickFalse)
8216  break;
8217  XConfigureImageColormap(display,resource_info,windows,*image);
8218  (void) XConfigureImage(display,resource_info,windows,*image);
8219  break;
8220  }
8221  case EdgeDetectCommand:
8222  {
8223  Image
8224  *edge_image;
8225 
8226  static char
8227  radius[MaxTextExtent] = "0";
8228 
8229  /*
8230  Query user for edge factor.
8231  */
8232  (void) XDialogWidget(display,windows,"Detect Edges",
8233  "Enter the edge detect radius:",radius);
8234  if (*radius == '\0')
8235  break;
8236  /*
8237  Detect edge in image.
8238  */
8239  XSetCursorState(display,windows,MagickTrue);
8240  XCheckRefreshWindows(display,windows);
8241  flags=ParseGeometry(radius,&geometry_info);
8242  edge_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8243  if (edge_image != (Image *) NULL)
8244  {
8245  *image=DestroyImage(*image);
8246  *image=edge_image;
8247  }
8248  CatchException(&(*image)->exception);
8249  XSetCursorState(display,windows,MagickFalse);
8250  if (windows->image.orphan != MagickFalse)
8251  break;
8252  XConfigureImageColormap(display,resource_info,windows,*image);
8253  (void) XConfigureImage(display,resource_info,windows,*image);
8254  break;
8255  }
8256  case SpreadCommand:
8257  {
8258  Image
8259  *spread_image;
8260 
8261  static char
8262  amount[MaxTextExtent] = "2";
8263 
8264  /*
8265  Query user for spread amount.
8266  */
8267  (void) XDialogWidget(display,windows,"Spread",
8268  "Enter the displacement amount:",amount);
8269  if (*amount == '\0')
8270  break;
8271  /*
8272  Displace image pixels by a random amount.
8273  */
8274  XSetCursorState(display,windows,MagickTrue);
8275  XCheckRefreshWindows(display,windows);
8276  flags=ParseGeometry(amount,&geometry_info);
8277  spread_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8278  if (spread_image != (Image *) NULL)
8279  {
8280  *image=DestroyImage(*image);
8281  *image=spread_image;
8282  }
8283  CatchException(&(*image)->exception);
8284  XSetCursorState(display,windows,MagickFalse);
8285  if (windows->image.orphan != MagickFalse)
8286  break;
8287  XConfigureImageColormap(display,resource_info,windows,*image);
8288  (void) XConfigureImage(display,resource_info,windows,*image);
8289  break;
8290  }
8291  case ShadeCommand:
8292  {
8293  Image
8294  *shade_image;
8295 
8296  int
8297  status;
8298 
8299  static char
8300  geometry[MaxTextExtent] = "30x30";
8301 
8302  /*
8303  Query user for the shade geometry.
8304  */
8305  status=XDialogWidget(display,windows,"Shade",
8306  "Enter the azimuth and elevation of the light source:",geometry);
8307  if (*geometry == '\0')
8308  break;
8309  /*
8310  Shade image pixels.
8311  */
8312  XSetCursorState(display,windows,MagickTrue);
8313  XCheckRefreshWindows(display,windows);
8314  flags=ParseGeometry(geometry,&geometry_info);
8315  if ((flags & SigmaValue) == 0)
8316  geometry_info.sigma=1.0;
8317  shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8318  geometry_info.rho,geometry_info.sigma,&(*image)->exception);
8319  if (shade_image != (Image *) NULL)
8320  {
8321  *image=DestroyImage(*image);
8322  *image=shade_image;
8323  }
8324  CatchException(&(*image)->exception);
8325  XSetCursorState(display,windows,MagickFalse);
8326  if (windows->image.orphan != MagickFalse)
8327  break;
8328  XConfigureImageColormap(display,resource_info,windows,*image);
8329  (void) XConfigureImage(display,resource_info,windows,*image);
8330  break;
8331  }
8332  case RaiseCommand:
8333  {
8334  static char
8335  bevel_width[MaxTextExtent] = "10";
8336 
8337  /*
8338  Query user for bevel width.
8339  */
8340  (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8341  if (*bevel_width == '\0')
8342  break;
8343  /*
8344  Raise an image.
8345  */
8346  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8347  XSetCursorState(display,windows,MagickTrue);
8348  XCheckRefreshWindows(display,windows);
8349  (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8350  &(*image)->exception);
8351  (void) RaiseImage(*image,&page_geometry,MagickTrue);
8352  XSetCursorState(display,windows,MagickFalse);
8353  if (windows->image.orphan != MagickFalse)
8354  break;
8355  XConfigureImageColormap(display,resource_info,windows,*image);
8356  (void) XConfigureImage(display,resource_info,windows,*image);
8357  break;
8358  }
8359  case SegmentCommand:
8360  {
8361  static char
8362  threshold[MaxTextExtent] = "1.0x1.5";
8363 
8364  /*
8365  Query user for smoothing threshold.
8366  */
8367  (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8368  threshold);
8369  if (*threshold == '\0')
8370  break;
8371  /*
8372  Segment an image.
8373  */
8374  XSetCursorState(display,windows,MagickTrue);
8375  XCheckRefreshWindows(display,windows);
8376  flags=ParseGeometry(threshold,&geometry_info);
8377  if ((flags & SigmaValue) == 0)
8378  geometry_info.sigma=1.0;
8379  (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8380  geometry_info.sigma);
8381  XSetCursorState(display,windows,MagickFalse);
8382  if (windows->image.orphan != MagickFalse)
8383  break;
8384  XConfigureImageColormap(display,resource_info,windows,*image);
8385  (void) XConfigureImage(display,resource_info,windows,*image);
8386  break;
8387  }
8388  case SepiaToneCommand:
8389  {
8390  double
8391  threshold;
8392 
8393  Image
8394  *sepia_image;
8395 
8396  static char
8397  factor[MaxTextExtent] = "80%";
8398 
8399  /*
8400  Query user for sepia-tone factor.
8401  */
8402  (void) XDialogWidget(display,windows,"Sepia Tone",
8403  "Enter the sepia tone factor (0 - 99.9%):",factor);
8404  if (*factor == '\0')
8405  break;
8406  /*
8407  Sepia tone image pixels.
8408  */
8409  XSetCursorState(display,windows,MagickTrue);
8410  XCheckRefreshWindows(display,windows);
8411  threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8412  sepia_image=SepiaToneImage(*image,threshold,&(*image)->exception);
8413  if (sepia_image != (Image *) NULL)
8414  {
8415  *image=DestroyImage(*image);
8416  *image=sepia_image;
8417  }
8418  CatchException(&(*image)->exception);
8419  XSetCursorState(display,windows,MagickFalse);
8420  if (windows->image.orphan != MagickFalse)
8421  break;
8422  XConfigureImageColormap(display,resource_info,windows,*image);
8423  (void) XConfigureImage(display,resource_info,windows,*image);
8424  break;
8425  }
8426  case SolarizeCommand:
8427  {
8428  double
8429  threshold;
8430 
8431  static char
8432  factor[MaxTextExtent] = "60%";
8433 
8434  /*
8435  Query user for solarize factor.
8436  */
8437  (void) XDialogWidget(display,windows,"Solarize",
8438  "Enter the solarize factor (0 - 99.9%):",factor);
8439  if (*factor == '\0')
8440  break;
8441  /*
8442  Solarize image pixels.
8443  */
8444  XSetCursorState(display,windows,MagickTrue);
8445  XCheckRefreshWindows(display,windows);
8446  threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8447  (void) SolarizeImage(*image,threshold);
8448  XSetCursorState(display,windows,MagickFalse);
8449  if (windows->image.orphan != MagickFalse)
8450  break;
8451  XConfigureImageColormap(display,resource_info,windows,*image);
8452  (void) XConfigureImage(display,resource_info,windows,*image);
8453  break;
8454  }
8455  case SwirlCommand:
8456  {
8457  Image
8458  *swirl_image;
8459 
8460  static char
8461  degrees[MaxTextExtent] = "60";
8462 
8463  /*
8464  Query user for swirl angle.
8465  */
8466  (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8467  degrees);
8468  if (*degrees == '\0')
8469  break;
8470  /*
8471  Swirl image pixels about the center.
8472  */
8473  XSetCursorState(display,windows,MagickTrue);
8474  XCheckRefreshWindows(display,windows);
8475  flags=ParseGeometry(degrees,&geometry_info);
8476  swirl_image=SwirlImage(*image,geometry_info.rho,&(*image)->exception);
8477  if (swirl_image != (Image *) NULL)
8478  {
8479  *image=DestroyImage(*image);
8480  *image=swirl_image;
8481  }
8482  CatchException(&(*image)->exception);
8483  XSetCursorState(display,windows,MagickFalse);
8484  if (windows->image.orphan != MagickFalse)
8485  break;
8486  XConfigureImageColormap(display,resource_info,windows,*image);
8487  (void) XConfigureImage(display,resource_info,windows,*image);
8488  break;
8489  }
8490  case ImplodeCommand:
8491  {
8492  Image
8493  *implode_image;
8494 
8495  static char
8496  factor[MaxTextExtent] = "0.3";
8497 
8498  /*
8499  Query user for implode factor.
8500  */
8501  (void) XDialogWidget(display,windows,"Implode",
8502  "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8503  if (*factor == '\0')
8504  break;
8505  /*
8506  Implode image pixels about the center.
8507  */
8508  XSetCursorState(display,windows,MagickTrue);
8509  XCheckRefreshWindows(display,windows);
8510  flags=ParseGeometry(factor,&geometry_info);
8511  implode_image=ImplodeImage(*image,geometry_info.rho,&(*image)->exception);
8512  if (implode_image != (Image *) NULL)
8513  {
8514  *image=DestroyImage(*image);
8515  *image=implode_image;
8516  }
8517  CatchException(&(*image)->exception);
8518  XSetCursorState(display,windows,MagickFalse);
8519  if (windows->image.orphan != MagickFalse)
8520  break;
8521  XConfigureImageColormap(display,resource_info,windows,*image);
8522  (void) XConfigureImage(display,resource_info,windows,*image);
8523  break;
8524  }
8525  case VignetteCommand:
8526  {
8527  Image
8528  *vignette_image;
8529 
8530  static char
8531  geometry[MaxTextExtent] = "0x20";
8532 
8533  /*
8534  Query user for the vignette geometry.
8535  */
8536  (void) XDialogWidget(display,windows,"Vignette",
8537  "Enter the radius, sigma, and x and y offsets:",geometry);
8538  if (*geometry == '\0')
8539  break;
8540  /*
8541  Soften the edges of the image in vignette style
8542  */
8543  XSetCursorState(display,windows,MagickTrue);
8544  XCheckRefreshWindows(display,windows);
8545  flags=ParseGeometry(geometry,&geometry_info);
8546  if ((flags & SigmaValue) == 0)
8547  geometry_info.sigma=1.0;
8548  if ((flags & XiValue) == 0)
8549  geometry_info.xi=0.1*(*image)->columns;
8550  if ((flags & PsiValue) == 0)
8551  geometry_info.psi=0.1*(*image)->rows;
8552  vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8553  (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8554  0.5),&(*image)->exception);
8555  if (vignette_image != (Image *) NULL)
8556  {
8557  *image=DestroyImage(*image);
8558  *image=vignette_image;
8559  }
8560  CatchException(&(*image)->exception);
8561  XSetCursorState(display,windows,MagickFalse);
8562  if (windows->image.orphan != MagickFalse)
8563  break;
8564  XConfigureImageColormap(display,resource_info,windows,*image);
8565  (void) XConfigureImage(display,resource_info,windows,*image);
8566  break;
8567  }
8568  case WaveCommand:
8569  {
8570  Image
8571  *wave_image;
8572 
8573  static char
8574  geometry[MaxTextExtent] = "25x150";
8575 
8576  /*
8577  Query user for the wave geometry.
8578  */
8579  (void) XDialogWidget(display,windows,"Wave",
8580  "Enter the amplitude and length of the wave:",geometry);
8581  if (*geometry == '\0')
8582  break;
8583  /*
8584  Alter an image along a sine wave.
8585  */
8586  XSetCursorState(display,windows,MagickTrue);
8587  XCheckRefreshWindows(display,windows);
8588  flags=ParseGeometry(geometry,&geometry_info);
8589  if ((flags & SigmaValue) == 0)
8590  geometry_info.sigma=1.0;
8591  wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8592  &(*image)->exception);
8593  if (wave_image != (Image *) NULL)
8594  {
8595  *image=DestroyImage(*image);
8596  *image=wave_image;
8597  }
8598  CatchException(&(*image)->exception);
8599  XSetCursorState(display,windows,MagickFalse);
8600  if (windows->image.orphan != MagickFalse)
8601  break;
8602  XConfigureImageColormap(display,resource_info,windows,*image);
8603  (void) XConfigureImage(display,resource_info,windows,*image);
8604  break;
8605  }
8606  case OilPaintCommand:
8607  {
8608  Image
8609  *paint_image;
8610 
8611  static char
8612  radius[MaxTextExtent] = "0";
8613 
8614  /*
8615  Query user for circular neighborhood radius.
8616  */
8617  (void) XDialogWidget(display,windows,"Oil Paint",
8618  "Enter the mask radius:",radius);
8619  if (*radius == '\0')
8620  break;
8621  /*
8622  OilPaint image scanlines.
8623  */
8624  XSetCursorState(display,windows,MagickTrue);
8625  XCheckRefreshWindows(display,windows);
8626  flags=ParseGeometry(radius,&geometry_info);
8627  paint_image=OilPaintImage(*image,geometry_info.rho,&(*image)->exception);
8628  if (paint_image != (Image *) NULL)
8629  {
8630  *image=DestroyImage(*image);
8631  *image=paint_image;
8632  }
8633  CatchException(&(*image)->exception);
8634  XSetCursorState(display,windows,MagickFalse);
8635  if (windows->image.orphan != MagickFalse)
8636  break;
8637  XConfigureImageColormap(display,resource_info,windows,*image);
8638  (void) XConfigureImage(display,resource_info,windows,*image);
8639  break;
8640  }
8641  case CharcoalDrawCommand:
8642  {
8643  Image
8644  *charcoal_image;
8645 
8646  static char
8647  radius[MaxTextExtent] = "0x1";
8648 
8649  /*
8650  Query user for charcoal radius.
8651  */
8652  (void) XDialogWidget(display,windows,"Charcoal Draw",
8653  "Enter the charcoal radius and sigma:",radius);
8654  if (*radius == '\0')
8655  break;
8656  /*
8657  Charcoal the image.
8658  */
8659  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8660  XSetCursorState(display,windows,MagickTrue);
8661  XCheckRefreshWindows(display,windows);
8662  flags=ParseGeometry(radius,&geometry_info);
8663  if ((flags & SigmaValue) == 0)
8664  geometry_info.sigma=geometry_info.rho;
8665  charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8666  &(*image)->exception);
8667  if (charcoal_image != (Image *) NULL)
8668  {
8669  *image=DestroyImage(*image);
8670  *image=charcoal_image;
8671  }
8672  CatchException(&(*image)->exception);
8673  XSetCursorState(display,windows,MagickFalse);
8674  if (windows->image.orphan != MagickFalse)
8675  break;
8676  XConfigureImageColormap(display,resource_info,windows,*image);
8677  (void) XConfigureImage(display,resource_info,windows,*image);
8678  break;
8679  }
8680  case AnnotateCommand:
8681  {
8682  /*
8683  Annotate the image with text.
8684  */
8685  status=XAnnotateEditImage(display,resource_info,windows,*image);
8686  if (status == MagickFalse)
8687  {
8688  XNoticeWidget(display,windows,"Unable to annotate X image",
8689  (*image)->filename);
8690  break;
8691  }
8692  break;
8693  }
8694  case DrawCommand:
8695  {
8696  /*
8697  Draw image.
8698  */
8699  status=XDrawEditImage(display,resource_info,windows,image);
8700  if (status == MagickFalse)
8701  {
8702  XNoticeWidget(display,windows,"Unable to draw on the X image",
8703  (*image)->filename);
8704  break;
8705  }
8706  break;
8707  }
8708  case ColorCommand:
8709  {
8710  /*
8711  Color edit.
8712  */
8713  status=XColorEditImage(display,resource_info,windows,image);
8714  if (status == MagickFalse)
8715  {
8716  XNoticeWidget(display,windows,"Unable to pixel edit X image",
8717  (*image)->filename);
8718  break;
8719  }
8720  break;
8721  }
8722  case MatteCommand:
8723  {
8724  /*
8725  Matte edit.
8726  */
8727  status=XMatteEditImage(display,resource_info,windows,image);
8728  if (status == MagickFalse)
8729  {
8730  XNoticeWidget(display,windows,"Unable to matte edit X image",
8731  (*image)->filename);
8732  break;
8733  }
8734  break;
8735  }
8736  case CompositeCommand:
8737  {
8738  /*
8739  Composite image.
8740  */
8741  status=XCompositeImage(display,resource_info,windows,*image);
8742  if (status == MagickFalse)
8743  {
8744  XNoticeWidget(display,windows,"Unable to composite X image",
8745  (*image)->filename);
8746  break;
8747  }
8748  break;
8749  }
8750  case AddBorderCommand:
8751  {
8752  Image
8753  *border_image;
8754 
8755  static char
8756  geometry[MaxTextExtent] = "6x6";
8757 
8758  /*
8759  Query user for border color and geometry.
8760  */
8761  XColorBrowserWidget(display,windows,"Select",color);
8762  if (*color == '\0')
8763  break;
8764  (void) XDialogWidget(display,windows,"Add Border",
8765  "Enter border geometry:",geometry);
8766  if (*geometry == '\0')
8767  break;
8768  /*
8769  Add a border to the image.
8770  */
8771  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8772  XSetCursorState(display,windows,MagickTrue);
8773  XCheckRefreshWindows(display,windows);
8774  (void) QueryColorDatabase(color,&(*image)->border_color,
8775  &(*image)->exception);
8776  (void) ParsePageGeometry(*image,geometry,&page_geometry,
8777  &(*image)->exception);
8778  border_image=BorderImage(*image,&page_geometry,&(*image)->exception);
8779  if (border_image != (Image *) NULL)
8780  {
8781  *image=DestroyImage(*image);
8782  *image=border_image;
8783  }
8784  CatchException(&(*image)->exception);
8785  XSetCursorState(display,windows,MagickFalse);
8786  if (windows->image.orphan != MagickFalse)
8787  break;
8788  windows->image.window_changes.width=(int) (*image)->columns;
8789  windows->image.window_changes.height=(int) (*image)->rows;
8790  XConfigureImageColormap(display,resource_info,windows,*image);
8791  (void) XConfigureImage(display,resource_info,windows,*image);
8792  break;
8793  }
8794  case AddFrameCommand:
8795  {
8796  FrameInfo
8797  frame_info;
8798 
8799  Image
8800  *frame_image;
8801 
8802  static char
8803  geometry[MaxTextExtent] = "6x6";
8804 
8805  /*
8806  Query user for frame color and geometry.
8807  */
8808  XColorBrowserWidget(display,windows,"Select",color);
8809  if (*color == '\0')
8810  break;
8811  (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8812  geometry);
8813  if (*geometry == '\0')
8814  break;
8815  /*
8816  Surround image with an ornamental border.
8817  */
8818  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8819  XSetCursorState(display,windows,MagickTrue);
8820  XCheckRefreshWindows(display,windows);
8821  (void) QueryColorDatabase(color,&(*image)->matte_color,
8822  &(*image)->exception);
8823  (void) ParsePageGeometry(*image,geometry,&page_geometry,
8824  &(*image)->exception);
8825  frame_info.width=page_geometry.width;
8826  frame_info.height=page_geometry.height;
8827  frame_info.outer_bevel=page_geometry.x;
8828  frame_info.inner_bevel=page_geometry.y;
8829  frame_info.x=(ssize_t) frame_info.width;
8830  frame_info.y=(ssize_t) frame_info.height;
8831  frame_info.width=(*image)->columns+2*frame_info.width;
8832  frame_info.height=(*image)->rows+2*frame_info.height;
8833  frame_image=FrameImage(*image,&frame_info,&(*image)->exception);
8834  if (frame_image != (Image *) NULL)
8835  {
8836  *image=DestroyImage(*image);
8837  *image=frame_image;
8838  }
8839  CatchException(&(*image)->exception);
8840  XSetCursorState(display,windows,MagickFalse);
8841  if (windows->image.orphan != MagickFalse)
8842  break;
8843  windows->image.window_changes.width=(int) (*image)->columns;
8844  windows->image.window_changes.height=(int) (*image)->rows;
8845  XConfigureImageColormap(display,resource_info,windows,*image);
8846  (void) XConfigureImage(display,resource_info,windows,*image);
8847  break;
8848  }
8849  case CommentCommand:
8850  {
8851  const char
8852  *value;
8853 
8854  FILE
8855  *file;
8856 
8857  int
8858  unique_file;
8859 
8860  /*
8861  Edit image comment.
8862  */
8863  unique_file=AcquireUniqueFileResource(image_info->filename);
8864  if (unique_file == -1)
8865  {
8866  XNoticeWidget(display,windows,"Unable to edit image comment",
8867  image_info->filename);
8868  break;
8869  }
8870  value=GetImageProperty(*image,"comment");
8871  if (value == (char *) NULL)
8872  unique_file=close(unique_file)-1;
8873  else
8874  {
8875  const char
8876  *p;
8877 
8878  file=fdopen(unique_file,"w");
8879  if (file == (FILE *) NULL)
8880  {
8881  XNoticeWidget(display,windows,"Unable to edit image comment",
8882  image_info->filename);
8883  break;
8884  }
8885  for (p=value; *p != '\0'; p++)
8886  (void) fputc((int) *p,file);
8887  (void) fputc('\n',file);
8888  (void) fclose(file);
8889  }
8890  XSetCursorState(display,windows,MagickTrue);
8891  XCheckRefreshWindows(display,windows);
8892  status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8893  &(*image)->exception);
8894  if (status == MagickFalse)
8895  XNoticeWidget(display,windows,"Unable to edit image comment",
8896  (char *) NULL);
8897  else
8898  {
8899  char
8900  *comment;
8901 
8902  comment=FileToString(image_info->filename,~0UL,&(*image)->exception);
8903  if (comment != (char *) NULL)
8904  {
8905  (void) SetImageProperty(*image,"comment",comment);
8906  (*image)->taint=MagickTrue;
8907  }
8908  }
8909  (void) RelinquishUniqueFileResource(image_info->filename);
8910  XSetCursorState(display,windows,MagickFalse);
8911  break;
8912  }
8913  case LaunchCommand:
8914  {
8915  /*
8916  Launch program.
8917  */
8918  XSetCursorState(display,windows,MagickTrue);
8919  XCheckRefreshWindows(display,windows);
8920  (void) AcquireUniqueFilename(filename);
8921  (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
8922  filename);
8923  status=WriteImage(image_info,*image);
8924  if (status == MagickFalse)
8925  XNoticeWidget(display,windows,"Unable to launch image editor",
8926  (char *) NULL);
8927  else
8928  {
8929  nexus=ReadImage(resource_info->image_info,&(*image)->exception);
8930  CatchException(&(*image)->exception);
8931  XClientMessage(display,windows->image.id,windows->im_protocols,
8932  windows->im_next_image,CurrentTime);
8933  }
8934  (void) RelinquishUniqueFileResource(filename);
8935  XSetCursorState(display,windows,MagickFalse);
8936  break;
8937  }
8938  case RegionOfInterestCommand:
8939  {
8940  /*
8941  Apply an image processing technique to a region of interest.
8942  */
8943  (void) XROIImage(display,resource_info,windows,image);
8944  break;
8945  }
8946  case InfoCommand:
8947  break;
8948  case ZoomCommand:
8949  {
8950  /*
8951  Zoom image.
8952  */
8953  if (windows->magnify.mapped != MagickFalse)
8954  (void) XRaiseWindow(display,windows->magnify.id);
8955  else
8956  {
8957  /*
8958  Make magnify image.
8959  */
8960  XSetCursorState(display,windows,MagickTrue);
8961  (void) XMapRaised(display,windows->magnify.id);
8962  XSetCursorState(display,windows,MagickFalse);
8963  }
8964  break;
8965  }
8966  case ShowPreviewCommand:
8967  {
8968  char
8969  **previews;
8970 
8971  Image
8972  *preview_image;
8973 
8974  static char
8975  preview_type[MaxTextExtent] = "Gamma";
8976 
8977  /*
8978  Select preview type from menu.
8979  */
8980  previews=GetCommandOptions(MagickPreviewOptions);
8981  if (previews == (char **) NULL)
8982  break;
8983  XListBrowserWidget(display,windows,&windows->widget,
8984  (const char **) previews,"Preview",
8985  "Select an enhancement, effect, or F/X:",preview_type);
8986  previews=DestroyStringList(previews);
8987  if (*preview_type == '\0')
8988  break;
8989  /*
8990  Show image preview.
8991  */
8992  XSetCursorState(display,windows,MagickTrue);
8993  XCheckRefreshWindows(display,windows);
8994  image_info->preview_type=(PreviewType)
8995  ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
8996  image_info->group=(ssize_t) windows->image.id;
8997  (void) DeleteImageProperty(*image,"label");
8998  (void) SetImageProperty(*image,"label","Preview");
8999  (void) AcquireUniqueFilename(filename);
9000  (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9001  filename);
9002  status=WriteImage(image_info,*image);
9003  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9004  preview_image=ReadImage(image_info,&(*image)->exception);
9005  (void) RelinquishUniqueFileResource(filename);
9006  if (preview_image == (Image *) NULL)
9007  break;
9008  (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9009  filename);
9010  status=WriteImage(image_info,preview_image);
9011  preview_image=DestroyImage(preview_image);
9012  if (status == MagickFalse)
9013  XNoticeWidget(display,windows,"Unable to show image preview",
9014  (*image)->filename);
9015  XDelay(display,1500);
9016  XSetCursorState(display,windows,MagickFalse);
9017  break;
9018  }
9019  case ShowHistogramCommand:
9020  {
9021  Image
9022  *histogram_image;
9023 
9024  /*
9025  Show image histogram.
9026  */
9027  XSetCursorState(display,windows,MagickTrue);
9028  XCheckRefreshWindows(display,windows);
9029  image_info->group=(ssize_t) windows->image.id;
9030  (void) DeleteImageProperty(*image,"label");
9031  (void) SetImageProperty(*image,"label","Histogram");
9032  (void) AcquireUniqueFilename(filename);
9033  (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9034  filename);
9035  status=WriteImage(image_info,*image);
9036  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9037  histogram_image=ReadImage(image_info,&(*image)->exception);
9038  (void) RelinquishUniqueFileResource(filename);
9039  if (histogram_image == (Image *) NULL)
9040  break;
9041  (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9042  "show:%s",filename);
9043  status=WriteImage(image_info,histogram_image);
9044  histogram_image=DestroyImage(histogram_image);
9045  if (status == MagickFalse)
9046  XNoticeWidget(display,windows,"Unable to show histogram",
9047  (*image)->filename);
9048  XDelay(display,1500);
9049  XSetCursorState(display,windows,MagickFalse);
9050  break;
9051  }
9052  case ShowMatteCommand:
9053  {
9054  Image
9055  *matte_image;
9056 
9057  if ((*image)->matte == MagickFalse)
9058  {
9059  XNoticeWidget(display,windows,
9060  "Image does not have any matte information",(*image)->filename);
9061  break;
9062  }
9063  /*
9064  Show image matte.
9065  */
9066  XSetCursorState(display,windows,MagickTrue);
9067  XCheckRefreshWindows(display,windows);
9068  image_info->group=(ssize_t) windows->image.id;
9069  (void) DeleteImageProperty(*image,"label");
9070  (void) SetImageProperty(*image,"label","Matte");
9071  (void) AcquireUniqueFilename(filename);
9072  (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9073  filename);
9074  status=WriteImage(image_info,*image);
9075  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9076  matte_image=ReadImage(image_info,&(*image)->exception);
9077  (void) RelinquishUniqueFileResource(filename);
9078  if (matte_image == (Image *) NULL)
9079  break;
9080  (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9081  filename);
9082  status=WriteImage(image_info,matte_image);
9083  matte_image=DestroyImage(matte_image);
9084  if (status == MagickFalse)
9085  XNoticeWidget(display,windows,"Unable to show matte",
9086  (*image)->filename);
9087  XDelay(display,1500);
9088  XSetCursorState(display,windows,MagickFalse);
9089  break;
9090  }
9091  case BackgroundCommand:
9092  {
9093  /*
9094  Background image.
9095  */
9096  status=XBackgroundImage(display,resource_info,windows,image);
9097  if (status == MagickFalse)
9098  break;
9099  nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9100  if (nexus != (Image *) NULL)
9101  XClientMessage(display,windows->image.id,windows->im_protocols,
9102  windows->im_next_image,CurrentTime);
9103  break;
9104  }
9105  case SlideShowCommand:
9106  {
9107  static char
9108  delay[MaxTextExtent] = "5";
9109 
9110  /*
9111  Display next image after pausing.
9112  */
9113  (void) XDialogWidget(display,windows,"Slide Show",
9114  "Pause how many 1/100ths of a second between images:",delay);
9115  if (*delay == '\0')
9116  break;
9117  resource_info->delay=StringToUnsignedLong(delay);
9118  XClientMessage(display,windows->image.id,windows->im_protocols,
9119  windows->im_next_image,CurrentTime);
9120  break;
9121  }
9122  case PreferencesCommand:
9123  {
9124  /*
9125  Set user preferences.
9126  */
9127  status=XPreferencesWidget(display,resource_info,windows);
9128  if (status == MagickFalse)
9129  break;
9130  nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9131  if (nexus != (Image *) NULL)
9132  XClientMessage(display,windows->image.id,windows->im_protocols,
9133  windows->im_next_image,CurrentTime);
9134  break;
9135  }
9136  case HelpCommand:
9137  {
9138  /*
9139  User requested help.
9140  */
9141  XTextViewHelp(display,resource_info,windows,MagickFalse,
9142  "Help Viewer - Display",DisplayHelp);
9143  break;
9144  }
9145  case BrowseDocumentationCommand:
9146  {
9147  Atom
9148  mozilla_atom;
9149 
9150  Window
9151  mozilla_window,
9152  root_window;
9153 
9154  /*
9155  Browse the ImageMagick documentation.
9156  */
9157  root_window=XRootWindow(display,XDefaultScreen(display));
9158  mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9159  mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9160  if (mozilla_window != (Window) NULL)
9161  {
9162  char
9163  command[MaxTextExtent];
9164 
9165  /*
9166  Display documentation using Netscape remote control.
9167  */
9168  (void) FormatLocaleString(command,MaxTextExtent,
9169  "openurl(%s,new-tab)",MagickAuthoritativeURL);
9170  mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9171  (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9172  8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9173  XSetCursorState(display,windows,MagickFalse);
9174  break;
9175  }
9176  XSetCursorState(display,windows,MagickTrue);
9177  XCheckRefreshWindows(display,windows);
9178  status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9179  &(*image)->exception);
9180  if (status == MagickFalse)
9181  XNoticeWidget(display,windows,"Unable to browse documentation",
9182  (char *) NULL);
9183  XDelay(display,1500);
9184  XSetCursorState(display,windows,MagickFalse);
9185  break;
9186  }
9187  case VersionCommand:
9188  {
9189  XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9190  GetMagickCopyright());
9191  break;
9192  }
9193  case SaveToUndoBufferCommand:
9194  break;
9195  default:
9196  {
9197  (void) XBell(display,0);
9198  break;
9199  }
9200  }
9201  image_info=DestroyImageInfo(image_info);
9202  return(nexus);
9203 }
9204 
9205 /*
9206 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9207 % %
9208 % %
9209 % %
9210 + X M a g n i f y I m a g e %
9211 % %
9212 % %
9213 % %
9214 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9215 %
9216 % XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9217 % The magnified portion is displayed in a separate window.
9218 %
9219 % The format of the XMagnifyImage method is:
9220 %
9221 % void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9222 %
9223 % A description of each parameter follows:
9224 %
9225 % o display: Specifies a connection to an X server; returned from
9226 % XOpenDisplay.
9227 %
9228 % o windows: Specifies a pointer to a XWindows structure.
9229 %
9230 % o event: Specifies a pointer to a XEvent structure. If it is NULL,
9231 % the entire image is refreshed.
9232 %
9233 */
9234 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9235 {
9236  char
9237  text[MaxTextExtent];
9238 
9239  int
9240  x,
9241  y;
9242 
9243  size_t
9244  state;
9245 
9246  /*
9247  Update magnified image until the mouse button is released.
9248  */
9249  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9250  state=DefaultState;
9251  x=event->xbutton.x;
9252  y=event->xbutton.y;
9253  windows->magnify.x=(int) windows->image.x+x;
9254  windows->magnify.y=(int) windows->image.y+y;
9255  do
9256  {
9257  /*
9258  Map and unmap Info widget as text cursor crosses its boundaries.
9259  */
9260  if (windows->info.mapped != MagickFalse)
9261  {
9262  if ((x < (int) (windows->info.x+windows->info.width)) &&
9263  (y < (int) (windows->info.y+windows->info.height)))
9264  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9265  }
9266  else
9267  if ((x > (int) (windows->info.x+windows->info.width)) ||
9268  (y > (int) (windows->info.y+windows->info.height)))
9269  (void) XMapWindow(display,windows->info.id);
9270  if (windows->info.mapped != MagickFalse)
9271  {
9272  /*
9273  Display pointer position.
9274  */
9275  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9276  windows->magnify.x,windows->magnify.y);
9277  XInfoWidget(display,windows,text);
9278  }
9279  /*
9280  Wait for next event.
9281  */
9282  XScreenEvent(display,windows,event);
9283  switch (event->type)
9284  {
9285  case ButtonPress:
9286  break;
9287  case ButtonRelease:
9288  {
9289  /*
9290  User has finished magnifying image.
9291  */
9292  x=event->xbutton.x;
9293  y=event->xbutton.y;
9294  state|=ExitState;
9295  break;
9296  }
9297  case Expose:
9298  break;
9299  case MotionNotify:
9300  {
9301  x=event->xmotion.x;
9302  y=event->xmotion.y;
9303  break;
9304  }
9305  default:
9306  break;
9307  }
9308  /*
9309  Check boundary conditions.
9310  */
9311  if (x < 0)
9312  x=0;
9313  else
9314  if (x >= (int) windows->image.width)
9315  x=(int) windows->image.width-1;
9316  if (y < 0)
9317  y=0;
9318  else
9319  if (y >= (int) windows->image.height)
9320  y=(int) windows->image.height-1;
9321  } while ((state & ExitState) == 0);
9322  /*
9323  Display magnified image.
9324  */
9325  XSetCursorState(display,windows,MagickFalse);
9326 }
9327 
9328 /*
9329 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9330 % %
9331 % %
9332 % %
9333 + X M a g n i f y W i n d o w C o m m a n d %
9334 % %
9335 % %
9336 % %
9337 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9338 %
9339 % XMagnifyWindowCommand() moves the image within an Magnify window by one
9340 % pixel as specified by the key symbol.
9341 %
9342 % The format of the XMagnifyWindowCommand method is:
9343 %
9344 % void XMagnifyWindowCommand(Display *display,XWindows *windows,
9345 % const MagickStatusType state,const KeySym key_symbol)
9346 %
9347 % A description of each parameter follows:
9348 %
9349 % o display: Specifies a connection to an X server; returned from
9350 % XOpenDisplay.
9351 %
9352 % o windows: Specifies a pointer to a XWindows structure.
9353 %
9354 % o state: key mask.
9355 %
9356 % o key_symbol: Specifies a KeySym which indicates which side of the image
9357 % to trim.
9358 %
9359 */
9360 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9361  const MagickStatusType state,const KeySym key_symbol)
9362 {
9363  unsigned int
9364  quantum;
9365 
9366  /*
9367  User specified a magnify factor or position.
9368  */
9369  quantum=1;
9370  if ((state & Mod1Mask) != 0)
9371  quantum=10;
9372  switch ((int) key_symbol)
9373  {
9374  case QuitCommand:
9375  {
9376  (void) XWithdrawWindow(display,windows->magnify.id,
9377  windows->magnify.screen);
9378  break;
9379  }
9380  case XK_Home:
9381  case XK_KP_Home:
9382  {
9383  windows->magnify.x=(int) windows->image.width/2;
9384  windows->magnify.y=(int) windows->image.height/2;
9385  break;
9386  }
9387  case XK_Left:
9388  case XK_KP_Left:
9389  {
9390  if (windows->magnify.x > 0)
9391  windows->magnify.x-=quantum;
9392  break;
9393  }
9394  case XK_Up:
9395  case XK_KP_Up:
9396  {
9397  if (windows->magnify.y > 0)
9398  windows->magnify.y-=quantum;
9399  break;
9400  }
9401  case XK_Right:
9402  case XK_KP_Right:
9403  {
9404  if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9405  windows->magnify.x+=quantum;
9406  break;
9407  }
9408  case XK_Down:
9409  case XK_KP_Down:
9410  {
9411  if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9412  windows->magnify.y+=quantum;
9413  break;
9414  }
9415  case XK_0:
9416  case XK_1:
9417  case XK_2:
9418  case XK_3:
9419  case XK_4:
9420  case XK_5:
9421  case XK_6:
9422  case XK_7:
9423  case XK_8:
9424  case XK_9:
9425  {
9426  windows->magnify.data=(key_symbol-XK_0);
9427  break;
9428  }
9429  case XK_KP_0:
9430  case XK_KP_1:
9431  case XK_KP_2:
9432  case XK_KP_3:
9433  case XK_KP_4:
9434  case XK_KP_5:
9435  case XK_KP_6:
9436  case XK_KP_7:
9437  case XK_KP_8:
9438  case XK_KP_9:
9439  {
9440  windows->magnify.data=(key_symbol-XK_KP_0);
9441  break;
9442  }
9443  default:
9444  break;
9445  }
9446  XMakeMagnifyImage(display,windows);
9447 }
9448 
9449 /*
9450 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9451 % %
9452 % %
9453 % %
9454 + X M a k e P a n I m a g e %
9455 % %
9456 % %
9457 % %
9458 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9459 %
9460 % XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9461 % icon window.
9462 %
9463 % The format of the XMakePanImage method is:
9464 %
9465 % void XMakePanImage(Display *display,XResourceInfo *resource_info,
9466 % XWindows *windows,Image *image)
9467 %
9468 % A description of each parameter follows:
9469 %
9470 % o display: Specifies a connection to an X server; returned from
9471 % XOpenDisplay.
9472 %
9473 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9474 %
9475 % o windows: Specifies a pointer to a XWindows structure.
9476 %
9477 % o image: the image.
9478 %
9479 */
9480 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9481  XWindows *windows,Image *image)
9482 {
9483  MagickStatusType
9484  status;
9485 
9486  /*
9487  Create and display image for panning icon.
9488  */
9489  XSetCursorState(display,windows,MagickTrue);
9490  XCheckRefreshWindows(display,windows);
9491  windows->pan.x=(int) windows->image.x;
9492  windows->pan.y=(int) windows->image.y;
9493  status=XMakeImage(display,resource_info,&windows->pan,image,
9494  windows->pan.width,windows->pan.height);
9495  if (status == MagickFalse)
9496  ThrowXWindowFatalException(XServerFatalError,image->exception.reason,
9497  image->exception.description);
9498  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9499  windows->pan.pixmap);
9500  (void) XClearWindow(display,windows->pan.id);
9501  XDrawPanRectangle(display,windows);
9502  XSetCursorState(display,windows,MagickFalse);
9503 }
9504 
9505 /*
9506 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9507 % %
9508 % %
9509 % %
9510 + X M a t t a E d i t I m a g e %
9511 % %
9512 % %
9513 % %
9514 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9515 %
9516 % XMatteEditImage() allows the user to interactively change the Matte channel
9517 % of an image. If the image is PseudoClass it is promoted to DirectClass
9518 % before the matte information is stored.
9519 %
9520 % The format of the XMatteEditImage method is:
9521 %
9522 % MagickBooleanType XMatteEditImage(Display *display,
9523 % XResourceInfo *resource_info,XWindows *windows,Image **image)
9524 %
9525 % A description of each parameter follows:
9526 %
9527 % o display: Specifies a connection to an X server; returned from
9528 % XOpenDisplay.
9529 %
9530 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9531 %
9532 % o windows: Specifies a pointer to a XWindows structure.
9533 %
9534 % o image: the image; returned from ReadImage.
9535 %
9536 */
9537 static MagickBooleanType XMatteEditImage(Display *display,
9538  XResourceInfo *resource_info,XWindows *windows,Image **image)
9539 {
9540  const char
9541  *const MatteEditMenu[] =
9542  {
9543  "Method",
9544  "Border Color",
9545  "Fuzz",
9546  "Matte Value",
9547  "Undo",
9548  "Help",
9549  "Dismiss",
9550  (char *) NULL
9551  };
9552 
9553  static char
9554  matte[MaxTextExtent] = "0";
9555 
9556  static const ModeType
9557  MatteEditCommands[] =
9558  {
9559  MatteEditMethod,
9560  MatteEditBorderCommand,
9561  MatteEditFuzzCommand,
9562  MatteEditValueCommand,
9563  MatteEditUndoCommand,
9564  MatteEditHelpCommand,
9565  MatteEditDismissCommand
9566  };
9567 
9568  static PaintMethod
9569  method = PointMethod;
9570 
9571  static XColor
9572  border_color = { 0, 0, 0, 0, 0, 0 };
9573 
9574  char
9575  command[MaxTextExtent],
9576  text[MaxTextExtent] = "";
9577 
9578  Cursor
9579  cursor;
9580 
9581  int
9582  entry,
9583  id,
9584  x,
9585  x_offset,
9586  y,
9587  y_offset;
9588 
9589  int
9590  i;
9591 
9592  PixelPacket
9593  *q;
9594 
9595  unsigned int
9596  height,
9597  width;
9598 
9599  size_t
9600  state;
9601 
9602  XEvent
9603  event;
9604 
9605  /*
9606  Map Command widget.
9607  */
9608  (void) CloneString(&windows->command.name,"Matte Edit");
9609  windows->command.data=4;
9610  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9611  (void) XMapRaised(display,windows->command.id);
9612  XClientMessage(display,windows->image.id,windows->im_protocols,
9613  windows->im_update_widget,CurrentTime);
9614  /*
9615  Make cursor.
9616  */
9617  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9618  resource_info->background_color,resource_info->foreground_color);
9619  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9620  /*
9621  Track pointer until button 1 is pressed.
9622  */
9623  XQueryPosition(display,windows->image.id,&x,&y);
9624  (void) XSelectInput(display,windows->image.id,
9625  windows->image.attributes.event_mask | PointerMotionMask);
9626  state=DefaultState;
9627  do
9628  {
9629  if (windows->info.mapped != MagickFalse)
9630  {
9631  /*
9632  Display pointer position.
9633  */
9634  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9635  x+windows->image.x,y+windows->image.y);
9636  XInfoWidget(display,windows,text);
9637  }
9638  /*
9639  Wait for next event.
9640  */
9641  XScreenEvent(display,windows,&event);
9642  if (event.xany.window == windows->command.id)
9643  {
9644  /*
9645  Select a command from the Command widget.
9646  */
9647  id=XCommandWidget(display,windows,MatteEditMenu,&event);
9648  if (id < 0)
9649  {
9650  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9651  continue;
9652  }
9653  switch (MatteEditCommands[id])
9654  {
9655  case MatteEditMethod:
9656  {
9657  char
9658  **methods;
9659 
9660  /*
9661  Select a method from the pop-up menu.
9662  */
9663  methods=GetCommandOptions(MagickMethodOptions);
9664  if (methods == (char **) NULL)
9665  break;
9666  entry=XMenuWidget(display,windows,MatteEditMenu[id],
9667  (const char **) methods,command);
9668  if (entry >= 0)
9669  method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9670  MagickFalse,methods[entry]);
9671  methods=DestroyStringList(methods);
9672  break;
9673  }
9674  case MatteEditBorderCommand:
9675  {
9676  const char
9677  *ColorMenu[MaxNumberPens];
9678 
9679  int
9680  pen_number;
9681 
9682  /*
9683  Initialize menu selections.
9684  */
9685  for (i=0; i < (int) (MaxNumberPens-2); i++)
9686  ColorMenu[i]=resource_info->pen_colors[i];
9687  ColorMenu[MaxNumberPens-2]="Browser...";
9688  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9689  /*
9690  Select a pen color from the pop-up menu.
9691  */
9692  pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9693  (const char **) ColorMenu,command);
9694  if (pen_number < 0)
9695  break;
9696  if (pen_number == (MaxNumberPens-2))
9697  {
9698  static char
9699  color_name[MaxTextExtent] = "gray";
9700 
9701  /*
9702  Select a pen color from a dialog.
9703  */
9704  resource_info->pen_colors[pen_number]=color_name;
9705  XColorBrowserWidget(display,windows,"Select",color_name);
9706  if (*color_name == '\0')
9707  break;
9708  }
9709  /*
9710  Set border color.
9711  */
9712  (void) XParseColor(display,windows->map_info->colormap,
9713  resource_info->pen_colors[pen_number],&border_color);
9714  break;
9715  }
9716  case MatteEditFuzzCommand:
9717  {
9718  const char
9719  *const FuzzMenu[] =
9720  {
9721  "0%",
9722  "2%",
9723  "5%",
9724  "10%",
9725  "15%",
9726  "Dialog...",
9727  (char *) NULL,
9728  };
9729 
9730  static char
9731  fuzz[MaxTextExtent];
9732 
9733  /*
9734  Select a command from the pop-up menu.
9735  */
9736  entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9737  command);
9738  if (entry < 0)
9739  break;
9740  if (entry != 5)
9741  {
9742  (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9743  QuantumRange+1.0);
9744  break;
9745  }
9746  (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9747  (void) XDialogWidget(display,windows,"Ok",
9748  "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9749  if (*fuzz == '\0')
9750  break;
9751  (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9752  (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9753  1.0);
9754  break;
9755  }
9756  case MatteEditValueCommand:
9757  {
9758  const char
9759  *const MatteMenu[] =
9760  {
9761  "Opaque",
9762  "Transparent",
9763  "Dialog...",
9764  (char *) NULL,
9765  };
9766 
9767  static char
9768  message[MaxTextExtent];
9769 
9770  /*
9771  Select a command from the pop-up menu.
9772  */
9773  entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9774  command);
9775  if (entry < 0)
9776  break;
9777  if (entry != 2)
9778  {
9779  (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9780  OpaqueOpacity);
9781  if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9782  (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9783  (Quantum) TransparentOpacity);
9784  break;
9785  }
9786  (void) FormatLocaleString(message,MaxTextExtent,
9787  "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9788  QuantumRange);
9789  (void) XDialogWidget(display,windows,"Matte",message,matte);
9790  if (*matte == '\0')
9791  break;
9792  break;
9793  }
9794  case MatteEditUndoCommand:
9795  {
9796  (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9797  image);
9798  break;
9799  }
9800  case MatteEditHelpCommand:
9801  {
9802  XTextViewHelp(display,resource_info,windows,MagickFalse,
9803  "Help Viewer - Matte Edit",ImageMatteEditHelp);
9804  break;
9805  }
9806  case MatteEditDismissCommand:
9807  {
9808  /*
9809  Prematurely exit.
9810  */
9811  state|=EscapeState;
9812  state|=ExitState;
9813  break;
9814  }
9815  default:
9816  break;
9817  }
9818  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9819  continue;
9820  }
9821  switch (event.type)
9822  {
9823  case ButtonPress:
9824  {
9825  if (event.xbutton.button != Button1)
9826  break;
9827  if ((event.xbutton.window != windows->image.id) &&
9828  (event.xbutton.window != windows->magnify.id))
9829  break;
9830  /*
9831  Update matte data.
9832  */
9833  x=event.xbutton.x;
9834  y=event.xbutton.y;
9835  (void) XMagickCommand(display,resource_info,windows,
9836  SaveToUndoBufferCommand,image);
9837  state|=UpdateConfigurationState;
9838  break;
9839  }
9840  case ButtonRelease:
9841  {
9842  if (event.xbutton.button != Button1)
9843  break;
9844  if ((event.xbutton.window != windows->image.id) &&
9845  (event.xbutton.window != windows->magnify.id))
9846  break;
9847  /*
9848  Update colormap information.
9849  */
9850  x=event.xbutton.x;
9851  y=event.xbutton.y;
9852  XConfigureImageColormap(display,resource_info,windows,*image);
9853  (void) XConfigureImage(display,resource_info,windows,*image);
9854  XInfoWidget(display,windows,text);
9855  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9856  state&=(~UpdateConfigurationState);
9857  break;
9858  }
9859  case Expose:
9860  break;
9861  case KeyPress:
9862  {
9863  char
9864  command[MaxTextExtent];
9865 
9866  KeySym
9867  key_symbol;
9868 
9869  if (event.xkey.window == windows->magnify.id)
9870  {
9871  Window
9872  window;
9873 
9874  window=windows->magnify.id;
9875  while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9876  }
9877  if (event.xkey.window != windows->image.id)
9878  break;
9879  /*
9880  Respond to a user key press.
9881  */
9882  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9883  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9884  switch ((int) key_symbol)
9885  {
9886  case XK_Escape:
9887  case XK_F20:
9888  {
9889  /*
9890  Prematurely exit.
9891  */
9892  state|=ExitState;
9893  break;
9894  }
9895  case XK_F1:
9896  case XK_Help:
9897  {
9898  XTextViewHelp(display,resource_info,windows,MagickFalse,
9899  "Help Viewer - Matte Edit",ImageMatteEditHelp);
9900  break;
9901  }
9902  default:
9903  {
9904  (void) XBell(display,0);
9905  break;
9906  }
9907  }
9908  break;
9909  }
9910  case MotionNotify:
9911  {
9912  /*
9913  Map and unmap Info widget as cursor crosses its boundaries.
9914  */
9915  x=event.xmotion.x;
9916  y=event.xmotion.y;
9917  if (windows->info.mapped != MagickFalse)
9918  {
9919  if ((x < (int) (windows->info.x+windows->info.width)) &&
9920  (y < (int) (windows->info.y+windows->info.height)))
9921  (void) XWithdrawWindow(display,windows->info.id,
9922  windows->info.screen);
9923  }
9924  else
9925  if ((x > (int) (windows->info.x+windows->info.width)) ||
9926  (y > (int) (windows->info.y+windows->info.height)))
9927  (void) XMapWindow(display,windows->info.id);
9928  break;
9929  }
9930  default:
9931  break;
9932  }
9933  if (event.xany.window == windows->magnify.id)
9934  {
9935  x=windows->magnify.x-windows->image.x;
9936  y=windows->magnify.y-windows->image.y;
9937  }
9938  x_offset=x;
9939  y_offset=y;
9940  if ((state & UpdateConfigurationState) != 0)
9941  {
9942  CacheView
9943  *image_view;
9944 
9946  *exception;
9947 
9948  int
9949  x,
9950  y;
9951 
9952  /*
9953  Matte edit is relative to image configuration.
9954  */
9955  (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
9956  MagickTrue);
9957  XPutPixel(windows->image.ximage,x_offset,y_offset,
9958  windows->pixel_info->background_color.pixel);
9959  width=(unsigned int) (*image)->columns;
9960  height=(unsigned int) (*image)->rows;
9961  x=0;
9962  y=0;
9963  if (windows->image.crop_geometry != (char *) NULL)
9964  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
9965  &width,&height);
9966  x_offset=(int)
9967  (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
9968  y_offset=(int)
9969  (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
9970  if ((x_offset < 0) || (y_offset < 0))
9971  continue;
9972  if ((x_offset >= (int) (*image)->columns) ||
9973  (y_offset >= (int) (*image)->rows))
9974  continue;
9975  if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
9976  return(MagickFalse);
9977  (*image)->matte=MagickTrue;
9978  exception=(&(*image)->exception);
9979  image_view=AcquireAuthenticCacheView(*image,exception);
9980  switch (method)
9981  {
9982  case PointMethod:
9983  default:
9984  {
9985  /*
9986  Update matte information using point algorithm.
9987  */
9988  q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
9989  (ssize_t) y_offset,1,1,exception);
9990  if (q == (PixelPacket *) NULL)
9991  break;
9992  q->opacity=(Quantum) StringToLong(matte);
9993  (void) SyncCacheViewAuthenticPixels(image_view,exception);
9994  break;
9995  }
9996  case ReplaceMethod:
9997  {
9998  PixelPacket
9999  target;
10000 
10001  /*
10002  Update matte information using replace algorithm.
10003  */
10004  (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10005  (ssize_t) y_offset,&target,exception);
10006  for (y=0; y < (int) (*image)->rows; y++)
10007  {
10008  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10009  (*image)->columns,1,&(*image)->exception);
10010  if (q == (PixelPacket *) NULL)
10011  break;
10012  for (x=0; x < (int) (*image)->columns; x++)
10013  {
10014  if (IsColorSimilar(*image,q,&target))
10015  q->opacity=(Quantum) StringToLong(matte);
10016  q++;
10017  }
10018  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10019  break;
10020  }
10021  break;
10022  }
10023  case FloodfillMethod:
10024  case FillToBorderMethod:
10025  {
10026  DrawInfo
10027  *draw_info;
10028 
10030  target;
10031 
10032  /*
10033  Update matte information using floodfill algorithm.
10034  */
10035  (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10036  (ssize_t) y_offset,&target,exception);
10037  if (method == FillToBorderMethod)
10038  {
10039  target.red=(MagickRealType)
10040  ScaleShortToQuantum(border_color.red);
10041  target.green=(MagickRealType)
10042  ScaleShortToQuantum(border_color.green);
10043  target.blue=(MagickRealType)
10044  ScaleShortToQuantum(border_color.blue);
10045  }
10046  draw_info=CloneDrawInfo(resource_info->image_info,
10047  (DrawInfo *) NULL);
10048  draw_info->fill.opacity=ClampToQuantum(StringToDouble(matte,
10049  (char **) NULL));
10050  (void) FloodfillPaintImage(*image,OpacityChannel,draw_info,&target,
10051  (ssize_t) x_offset,(ssize_t) y_offset,
10052  method == FloodfillMethod ? MagickFalse : MagickTrue);
10053  draw_info=DestroyDrawInfo(draw_info);
10054  break;
10055  }
10056  case ResetMethod:
10057  {
10058  /*
10059  Update matte information using reset algorithm.
10060  */
10061  if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
10062  return(MagickFalse);
10063  for (y=0; y < (int) (*image)->rows; y++)
10064  {
10065  q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10066  (*image)->columns,1,exception);
10067  if (q == (PixelPacket *) NULL)
10068  break;
10069  for (x=0; x < (int) (*image)->columns; x++)
10070  {
10071  q->opacity=(Quantum) StringToLong(matte);
10072  q++;
10073  }
10074  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10075  break;
10076  }
10077  if (StringToLong(matte) == OpaqueOpacity)
10078  (*image)->matte=MagickFalse;
10079  break;
10080  }
10081  }
10082  image_view=DestroyCacheView(image_view);
10083  state&=(~UpdateConfigurationState);
10084  }
10085  } while ((state & ExitState) == 0);
10086  (void) XSelectInput(display,windows->image.id,
10087  windows->image.attributes.event_mask);
10088  XSetCursorState(display,windows,MagickFalse);
10089  (void) XFreeCursor(display,cursor);
10090  return(MagickTrue);
10091 }
10092 
10093 /*
10094 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10095 % %
10096 % %
10097 % %
10098 + X O p e n I m a g e %
10099 % %
10100 % %
10101 % %
10102 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10103 %
10104 % XOpenImage() loads an image from a file.
10105 %
10106 % The format of the XOpenImage method is:
10107 %
10108 % Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10109 % XWindows *windows,const unsigned int command)
10110 %
10111 % A description of each parameter follows:
10112 %
10113 % o display: Specifies a connection to an X server; returned from
10114 % XOpenDisplay.
10115 %
10116 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10117 %
10118 % o windows: Specifies a pointer to a XWindows structure.
10119 %
10120 % o command: A value other than zero indicates that the file is selected
10121 % from the command line argument list.
10122 %
10123 */
10124 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10125  XWindows *windows,const MagickBooleanType command)
10126 {
10127  const MagickInfo
10128  *magick_info;
10129 
10131  *exception;
10132 
10133  Image
10134  *nexus;
10135 
10136  ImageInfo
10137  *image_info;
10138 
10139  static char
10140  filename[MaxTextExtent] = "\0";
10141 
10142  /*
10143  Request file name from user.
10144  */
10145  if (command == MagickFalse)
10146  XFileBrowserWidget(display,windows,"Open",filename);
10147  else
10148  {
10149  char
10150  **filelist,
10151  **files;
10152 
10153  int
10154  count,
10155  status;
10156 
10157  int
10158  i,
10159  j;
10160 
10161  /*
10162  Select next image from the command line.
10163  */
10164  status=XGetCommand(display,windows->image.id,&files,&count);
10165  if (status == 0)
10166  ThrowXWindowException(XServerError,"UnableToGetProperty","...");
10167  filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10168  if (filelist == (char **) NULL)
10169  {
10170  (void) XFreeStringList(files);
10171  ThrowXWindowException(ResourceLimitError,
10172  "MemoryAllocationFailed","...");
10173  return((Image *) NULL);
10174  }
10175  j=0;
10176  for (i=1; i < count; i++)
10177  if (*files[i] != '-')
10178  filelist[j++]=files[i];
10179  filelist[j]=(char *) NULL;
10180  XListBrowserWidget(display,windows,&windows->widget,
10181  (const char **) filelist,"Load","Select Image to Load:",filename);
10182  filelist=(char **) RelinquishMagickMemory(filelist);
10183  (void) XFreeStringList(files);
10184  }
10185  if (*filename == '\0')
10186  return((Image *) NULL);
10187  image_info=CloneImageInfo(resource_info->image_info);
10188  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10189  (void *) NULL);
10190  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10191  exception=AcquireExceptionInfo();
10192  (void) SetImageInfo(image_info,0,exception);
10193  if (LocaleCompare(image_info->magick,"X") == 0)
10194  {
10195  char
10196  seconds[MaxTextExtent];
10197 
10198  /*
10199  User may want to delay the X server screen grab.
10200  */
10201  (void) CopyMagickString(seconds,"0",MaxTextExtent);
10202  (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10203  seconds);
10204  if (*seconds == '\0')
10205  return((Image *) NULL);
10206  XDelay(display,(size_t) (1000*StringToLong(seconds)));
10207  }
10208  magick_info=GetMagickInfo(image_info->magick,exception);
10209  if ((magick_info != (const MagickInfo *) NULL) &&
10210  (magick_info->raw != MagickFalse))
10211  {
10212  char
10213  geometry[MaxTextExtent];
10214 
10215  /*
10216  Request image size from the user.
10217  */
10218  (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10219  if (image_info->size != (char *) NULL)
10220  (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10221  (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10222  geometry);
10223  (void) CloneString(&image_info->size,geometry);
10224  }
10225  /*
10226  Load the image.
10227  */
10228  XSetCursorState(display,windows,MagickTrue);
10229  XCheckRefreshWindows(display,windows);
10230  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10231  nexus=ReadImage(image_info,exception);
10232  CatchException(exception);
10233  XSetCursorState(display,windows,MagickFalse);
10234  if (nexus != (Image *) NULL)
10235  XClientMessage(display,windows->image.id,windows->im_protocols,
10236  windows->im_next_image,CurrentTime);
10237  else
10238  {
10239  char
10240  *text,
10241  **textlist;
10242 
10243  /*
10244  Unknown image format.
10245  */
10246  text=FileToString(filename,~0UL,exception);
10247  if (text == (char *) NULL)
10248  return((Image *) NULL);
10249  textlist=StringToList(text);
10250  if (textlist != (char **) NULL)
10251  {
10252  char
10253  title[MaxTextExtent];
10254 
10255  int
10256  i;
10257 
10258  (void) FormatLocaleString(title,MaxTextExtent,
10259  "Unknown format: %s",filename);
10260  XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10261  (const char **) textlist);
10262  for (i=0; textlist[i] != (char *) NULL; i++)
10263  textlist[i]=DestroyString(textlist[i]);
10264  textlist=(char **) RelinquishMagickMemory(textlist);
10265  }
10266  text=DestroyString(text);
10267  }
10268  exception=DestroyExceptionInfo(exception);
10269  image_info=DestroyImageInfo(image_info);
10270  return(nexus);
10271 }
10272 
10273 /*
10274 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10275 % %
10276 % %
10277 % %
10278 + X P a n I m a g e %
10279 % %
10280 % %
10281 % %
10282 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10283 %
10284 % XPanImage() pans the image until the mouse button is released.
10285 %
10286 % The format of the XPanImage method is:
10287 %
10288 % void XPanImage(Display *display,XWindows *windows,XEvent *event)
10289 %
10290 % A description of each parameter follows:
10291 %
10292 % o display: Specifies a connection to an X server; returned from
10293 % XOpenDisplay.
10294 %
10295 % o windows: Specifies a pointer to a XWindows structure.
10296 %
10297 % o event: Specifies a pointer to a XEvent structure. If it is NULL,
10298 % the entire image is refreshed.
10299 %
10300 */
10301 static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10302 {
10303  char
10304  text[MaxTextExtent];
10305 
10306  Cursor
10307  cursor;
10308 
10309  MagickRealType
10310  x_factor,
10311  y_factor;
10312 
10314  pan_info;
10315 
10316  size_t
10317  state;
10318 
10319  /*
10320  Define cursor.
10321  */
10322  if ((windows->image.ximage->width > (int) windows->image.width) &&
10323  (windows->image.ximage->height > (int) windows->image.height))
10324  cursor=XCreateFontCursor(display,XC_fleur);
10325  else
10326  if (windows->image.ximage->width > (int) windows->image.width)
10327  cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10328  else
10329  if (windows->image.ximage->height > (int) windows->image.height)
10330  cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10331  else
10332  cursor=XCreateFontCursor(display,XC_arrow);
10333  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10334  /*
10335  Pan image as pointer moves until the mouse button is released.
10336  */
10337  x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10338  y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10339  pan_info.width=windows->pan.width*windows->image.width/
10340  windows->image.ximage->width;
10341  pan_info.height=windows->pan.height*windows->image.height/
10342  windows->image.ximage->height;
10343  pan_info.x=0;
10344  pan_info.y=0;
10345  state=UpdateConfigurationState;
10346  do
10347  {
10348  switch (event->type)
10349  {
10350  case ButtonPress:
10351  {
10352  /*
10353  User choose an initial pan location.
10354  */
10355  pan_info.x=(ssize_t) event->xbutton.x;
10356  pan_info.y=(ssize_t) event->xbutton.y;
10357  state|=UpdateConfigurationState;
10358  break;
10359  }
10360  case ButtonRelease:
10361  {
10362  /*
10363  User has finished panning the image.
10364  */
10365  pan_info.x=(ssize_t) event->xbutton.x;
10366  pan_info.y=(ssize_t) event->xbutton.y;
10367  state|=UpdateConfigurationState | ExitState;
10368  break;
10369  }
10370  case MotionNotify:
10371  {
10372  pan_info.x=(ssize_t) event->xmotion.x;
10373  pan_info.y=(ssize_t) event->xmotion.y;
10374  state|=UpdateConfigurationState;
10375  }
10376  default:
10377  break;
10378  }
10379  if ((state & UpdateConfigurationState) != 0)
10380  {
10381  /*
10382  Check boundary conditions.
10383  */
10384  if (pan_info.x < (ssize_t) (pan_info.width/2))
10385  pan_info.x=0;
10386  else
10387  pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10388  if (pan_info.x < 0)
10389  pan_info.x=0;
10390  else
10391  if ((int) (pan_info.x+windows->image.width) >
10392  windows->image.ximage->width)
10393  pan_info.x=(ssize_t)
10394  (windows->image.ximage->width-windows->image.width);
10395  if (pan_info.y < (ssize_t) (pan_info.height/2))
10396  pan_info.y=0;
10397  else
10398  pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10399  if (pan_info.y < 0)
10400  pan_info.y=0;
10401  else
10402  if ((int) (pan_info.y+windows->image.height) >
10403  windows->image.ximage->height)
10404  pan_info.y=(ssize_t)
10405  (windows->image.ximage->height-windows->image.height);
10406  if ((windows->image.x != (int) pan_info.x) ||
10407  (windows->image.y != (int) pan_info.y))
10408  {
10409  /*
10410  Display image pan offset.
10411  */
10412  windows->image.x=(int) pan_info.x;
10413  windows->image.y=(int) pan_info.y;
10414  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10415  windows->image.width,windows->image.height,windows->image.x,
10416  windows->image.y);
10417  XInfoWidget(display,windows,text);
10418  /*
10419  Refresh Image window.
10420  */
10421  XDrawPanRectangle(display,windows);
10422  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10423  }
10424  state&=(~UpdateConfigurationState);
10425  }
10426  /*
10427  Wait for next event.
10428  */
10429  if ((state & ExitState) == 0)
10430  XScreenEvent(display,windows,event);
10431  } while ((state & ExitState) == 0);
10432  /*
10433  Restore cursor.
10434  */
10435  (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10436  (void) XFreeCursor(display,cursor);
10437  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10438 }
10439 
10440 /*
10441 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10442 % %
10443 % %
10444 % %
10445 + X P a s t e I m a g e %
10446 % %
10447 % %
10448 % %
10449 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10450 %
10451 % XPasteImage() pastes an image previously saved with XCropImage in the X
10452 % window image at a location the user chooses with the pointer.
10453 %
10454 % The format of the XPasteImage method is:
10455 %
10456 % MagickBooleanType XPasteImage(Display *display,
10457 % XResourceInfo *resource_info,XWindows *windows,Image *image)
10458 %
10459 % A description of each parameter follows:
10460 %
10461 % o display: Specifies a connection to an X server; returned from
10462 % XOpenDisplay.
10463 %
10464 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10465 %
10466 % o windows: Specifies a pointer to a XWindows structure.
10467 %
10468 % o image: the image; returned from ReadImage.
10469 %
10470 */
10471 static MagickBooleanType XPasteImage(Display *display,
10472  XResourceInfo *resource_info,XWindows *windows,Image *image)
10473 {
10474  const char
10475  *const PasteMenu[] =
10476  {
10477  "Operator",
10478  "Help",
10479  "Dismiss",
10480  (char *) NULL
10481  };
10482 
10483  static const ModeType
10484  PasteCommands[] =
10485  {
10486  PasteOperatorsCommand,
10487  PasteHelpCommand,
10488  PasteDismissCommand
10489  };
10490 
10491  static CompositeOperator
10492  compose = CopyCompositeOp;
10493 
10494  char
10495  text[MaxTextExtent];
10496 
10497  Cursor
10498  cursor;
10499 
10500  Image
10501  *paste_image;
10502 
10503  int
10504  entry,
10505  id,
10506  x,
10507  y;
10508 
10509  MagickRealType
10510  scale_factor;
10511 
10513  highlight_info,
10514  paste_info;
10515 
10516  unsigned int
10517  height,
10518  width;
10519 
10520  size_t
10521  state;
10522 
10523  XEvent
10524  event;
10525 
10526  /*
10527  Copy image.
10528  */
10529  if (resource_info->copy_image == (Image *) NULL)
10530  return(MagickFalse);
10531  paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,
10532  &image->exception);
10533  if (paste_image == (Image *) NULL)
10534  return(MagickFalse);
10535  /*
10536  Map Command widget.
10537  */
10538  (void) CloneString(&windows->command.name,"Paste");
10539  windows->command.data=1;
10540  (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10541  (void) XMapRaised(display,windows->command.id);
10542  XClientMessage(display,windows->image.id,windows->im_protocols,
10543  windows->im_update_widget,CurrentTime);
10544  /*
10545  Track pointer until button 1 is pressed.
10546  */
10547  XSetCursorState(display,windows,MagickFalse);
10548  XQueryPosition(display,windows->image.id,&x,&y);
10549  (void) XSelectInput(display,windows->image.id,
10550  windows->image.attributes.event_mask | PointerMotionMask);
10551  paste_info.x=(ssize_t) windows->image.x+x;
10552  paste_info.y=(ssize_t) windows->image.y+y;
10553  paste_info.width=0;
10554  paste_info.height=0;
10555  cursor=XCreateFontCursor(display,XC_ul_angle);
10556  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10557  state=DefaultState;
10558  do
10559  {
10560  if (windows->info.mapped != MagickFalse)
10561  {
10562  /*
10563  Display pointer position.
10564  */
10565  (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10566  (long) paste_info.x,(long) paste_info.y);
10567  XInfoWidget(display,windows,text);
10568  }
10569  highlight_info=paste_info;
10570  highlight_info.x=paste_info.x-windows->image.x;
10571  highlight_info.y=paste_info.y-windows->image.y;
10572  XHighlightRectangle(display,windows->image.id,
10573  windows->image.highlight_context,&highlight_info);
10574  /*
10575  Wait for next event.
10576  */
10577  XScreenEvent(display,windows,&event);
10578  XHighlightRectangle(display,windows->image.id,
10579  windows->image.highlight_context,&highlight_info);
10580  if (event.xany.window == windows->command.id)
10581  {
10582  /*
10583  Select a command from the Command widget.
10584  */
10585  id=XCommandWidget(display,windows,PasteMenu,&event);
10586  if (id < 0)
10587  continue;
10588  switch (PasteCommands[id])
10589  {
10590  case PasteOperatorsCommand:
10591  {
10592  char
10593  command[MaxTextExtent],
10594  **operators;
10595 
10596  /*
10597  Select a command from the pop-up menu.
10598  */
10599  operators=GetCommandOptions(MagickComposeOptions);
10600  if (operators == (char **) NULL)
10601  break;
10602  entry=XMenuWidget(display,windows,PasteMenu[id],
10603  (const char **) operators,command);
10604  if (entry >= 0)
10605  compose=(CompositeOperator) ParseCommandOption(
10606  MagickComposeOptions,MagickFalse,operators[entry]);
10607  operators=DestroyStringList(operators);
10608  break;
10609  }
10610  case PasteHelpCommand:
10611  {
10612  XTextViewHelp(display,resource_info,windows,MagickFalse,
10613  "Help Viewer - Image Composite",ImagePasteHelp);
10614  break;
10615  }
10616  case PasteDismissCommand:
10617  {
10618  /*
10619  Prematurely exit.
10620  */
10621  state|=EscapeState;
10622  state|=ExitState;
10623  break;
10624  }
10625  default:
10626  break;
10627  }
10628  continue;
10629  }
10630  switch (event.type)
10631  {
10632  case ButtonPress:
10633  {
10634  if (resource_info->debug != MagickFalse)
10635  (void) LogMagickEvent(X11Event,GetMagickModule(),
10636  "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10637  event.xbutton.button,event.xbutton.x,event.xbutton.y);
10638  if (event.xbutton.button != Button1)
10639  break;
10640  if (event.xbutton.window != windows->image.id)
10641  break;
10642  /*
10643  Paste rectangle is relative to image configuration.
10644  */
10645  width=(unsigned int) image->columns;
10646  height=(unsigned int) image->rows;
10647  x=0;
10648  y=0;
10649  if (windows->image.crop_geometry != (char *) NULL)
10650  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10651  &width,&height);
10652  scale_factor=(MagickRealType) windows->image.ximage->width/width;
10653  paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10654  scale_factor=(MagickRealType) windows->image.ximage->height/height;
10655  paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10656  (void) XCheckDefineCursor(display,windows->image.id,cursor);
10657  paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10658  paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10659  break;
10660  }
10661  case ButtonRelease:
10662  {
10663  if (resource_info->debug != MagickFalse)
10664  (void) LogMagickEvent(X11Event,GetMagickModule(),
10665  "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10666  event.xbutton.button,event.xbutton.x,event.xbutton.y);
10667  if (event.xbutton.button != Button1)
10668  break;
10669  if (event.xbutton.window != windows->image.id)
10670  break;
10671  if ((paste_info.width != 0) && (paste_info.height != 0))
10672  {
10673  /*
10674  User has selected the location of the paste image.
10675  */
10676  paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10677  paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10678  state|=ExitState;
10679  }
10680  break;
10681  }
10682  case Expose:
10683  break;
10684  case KeyPress:
10685  {
10686  char
10687  command[MaxTextExtent];
10688 
10689  KeySym
10690  key_symbol;
10691 
10692  int
10693  length;
10694 
10695  if (event.xkey.window != windows->image.id)
10696  break;
10697  /*
10698  Respond to a user key press.
10699  */
10700  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10701  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10702  *(command+length)='\0';
10703  if (resource_info->debug != MagickFalse)
10704  (void) LogMagickEvent(X11Event,GetMagickModule(),
10705  "Key press: 0x%lx (%s)",(long) key_symbol,command);
10706  switch ((int) key_symbol)
10707  {
10708  case XK_Escape:
10709  case XK_F20:
10710  {
10711  /*
10712  Prematurely exit.
10713  */
10714  paste_image=DestroyImage(paste_image);
10715  state|=EscapeState;
10716  state|=ExitState;
10717  break;
10718  }
10719  case XK_F1:
10720  case XK_Help:
10721  {
10722  (void) XSetFunction(display,windows->image.highlight_context,
10723  GXcopy);
10724  XTextViewHelp(display,resource_info,windows,MagickFalse,
10725  "Help Viewer - Image Composite",ImagePasteHelp);
10726  (void) XSetFunction(display,windows->image.highlight_context,
10727  GXinvert);
10728  break;
10729  }
10730  default:
10731  {
10732  (void) XBell(display,0);
10733  break;
10734  }
10735  }
10736  break;
10737  }
10738  case MotionNotify:
10739  {
10740  /*
10741  Map and unmap Info widget as text cursor crosses its boundaries.
10742  */
10743  x=event.xmotion.x;
10744  y=event.xmotion.y;
10745  if (windows->info.mapped != MagickFalse)
10746  {
10747  if ((x < (int) (windows->info.x+windows->info.width)) &&
10748  (y < (int) (windows->info.y+windows->info.height)))
10749  (void) XWithdrawWindow(display,windows->info.id,
10750  windows->info.screen);
10751  }
10752  else
10753  if ((x > (int) (windows->info.x+windows->info.width)) ||
10754  (y > (int) (windows->info.y+windows->info.height)))
10755  (void) XMapWindow(display,windows->info.id);
10756  paste_info.x=(ssize_t) windows->image.x+x;
10757  paste_info.y=(ssize_t) windows->image.y+y;
10758  break;
10759  }
10760  default:
10761  {
10762  if (resource_info->debug != MagickFalse)
10763  (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10764  event.type);
10765  break;
10766  }
10767  }
10768  } while ((state & ExitState) == 0);
10769  (void) XSelectInput(display,windows->image.id,
10770  windows->image.attributes.event_mask);
10771  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10772  XSetCursorState(display,windows,MagickFalse);
10773  (void) XFreeCursor(display,cursor);
10774  if ((state & EscapeState) != 0)
10775  return(MagickTrue);
10776  /*
10777  Image pasting is relative to image configuration.
10778  */
10779  XSetCursorState(display,windows,MagickTrue);
10780  XCheckRefreshWindows(display,windows);
10781  width=(unsigned int) image->columns;
10782  height=(unsigned int) image->rows;
10783  x=0;
10784  y=0;
10785  if (windows->image.crop_geometry != (char *) NULL)
10786  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10787  scale_factor=(MagickRealType) width/windows->image.ximage->width;
10788  paste_info.x+=x;
10789  paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10790  paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10791  scale_factor=(MagickRealType) height/windows->image.ximage->height;
10792  paste_info.y+=y;
10793  paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10794  paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10795  /*
10796  Paste image with X Image window.
10797  */
10798  (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10799  paste_image=DestroyImage(paste_image);
10800  XSetCursorState(display,windows,MagickFalse);
10801  /*
10802  Update image colormap.
10803  */
10804  XConfigureImageColormap(display,resource_info,windows,image);
10805  (void) XConfigureImage(display,resource_info,windows,image);
10806  return(MagickTrue);
10807 }
10808 
10809 /*
10810 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10811 % %
10812 % %
10813 % %
10814 + X P r i n t I m a g e %
10815 % %
10816 % %
10817 % %
10818 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10819 %
10820 % XPrintImage() prints an image to a Postscript printer.
10821 %
10822 % The format of the XPrintImage method is:
10823 %
10824 % MagickBooleanType XPrintImage(Display *display,
10825 % XResourceInfo *resource_info,XWindows *windows,Image *image)
10826 %
10827 % A description of each parameter follows:
10828 %
10829 % o display: Specifies a connection to an X server; returned from
10830 % XOpenDisplay.
10831 %
10832 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10833 %
10834 % o windows: Specifies a pointer to a XWindows structure.
10835 %
10836 % o image: the image.
10837 %
10838 */
10839 static MagickBooleanType XPrintImage(Display *display,
10840  XResourceInfo *resource_info,XWindows *windows,Image *image)
10841 {
10842  char
10843  filename[MaxTextExtent],
10844  geometry[MaxTextExtent];
10845 
10846  const char
10847  *const PageSizes[] =
10848  {
10849  "Letter",
10850  "Tabloid",
10851  "Ledger",
10852  "Legal",
10853  "Statement",
10854  "Executive",
10855  "A3",
10856  "A4",
10857  "A5",
10858  "B4",
10859  "B5",
10860  "Folio",
10861  "Quarto",
10862  "10x14",
10863  (char *) NULL
10864  };
10865 
10866  Image
10867  *print_image;
10868 
10869  ImageInfo
10870  *image_info;
10871 
10872  MagickStatusType
10873  status;
10874 
10875  /*
10876  Request Postscript page geometry from user.
10877  */
10878  image_info=CloneImageInfo(resource_info->image_info);
10879  (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10880  if (image_info->page != (char *) NULL)
10881  (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10882  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10883  "Select Postscript Page Geometry:",geometry);
10884  if (*geometry == '\0')
10885  return(MagickTrue);
10886  image_info->page=GetPageGeometry(geometry);
10887  /*
10888  Apply image transforms.
10889  */
10890  XSetCursorState(display,windows,MagickTrue);
10891  XCheckRefreshWindows(display,windows);
10892  print_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10893  if (print_image == (Image *) NULL)
10894  return(MagickFalse);
10895  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
10896  windows->image.ximage->width,windows->image.ximage->height);
10897  (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10898  /*
10899  Print image.
10900  */
10901  (void) AcquireUniqueFilename(filename);
10902  (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
10903  filename);
10904  status=WriteImage(image_info,print_image);
10905  (void) RelinquishUniqueFileResource(filename);
10906  print_image=DestroyImage(print_image);
10907  image_info=DestroyImageInfo(image_info);
10908  XSetCursorState(display,windows,MagickFalse);
10909  return(status != 0 ? MagickTrue : MagickFalse);
10910 }
10911 
10912 /*
10913 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10914 % %
10915 % %
10916 % %
10917 + X R O I I m a g e %
10918 % %
10919 % %
10920 % %
10921 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10922 %
10923 % XROIImage() applies an image processing technique to a region of interest.
10924 %
10925 % The format of the XROIImage method is:
10926 %
10927 % MagickBooleanType XROIImage(Display *display,
10928 % XResourceInfo *resource_info,XWindows *windows,Image **image)
10929 %
10930 % A description of each parameter follows:
10931 %
10932 % o display: Specifies a connection to an X server; returned from
10933 % XOpenDisplay.
10934 %
10935 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10936 %
10937 % o windows: Specifies a pointer to a XWindows structure.
10938 %
10939 % o image: the image; returned from ReadImage.
10940 %
10941 */
10942 static MagickBooleanType XROIImage(Display *display,
10943  XResourceInfo *resource_info,XWindows *windows,Image **image)
10944 {
10945 #define ApplyMenus 7
10946 
10947  const char
10948  *const ROIMenu[] =
10949  {
10950  "Help",
10951  "Dismiss",
10952  (char *) NULL
10953  },
10954  *const ApplyMenu[] =
10955  {
10956  "File",
10957  "Edit",
10958  "Transform",
10959  "Enhance",
10960  "Effects",
10961  "F/X",
10962  "Miscellany",
10963  "Help",
10964  "Dismiss",
10965  (char *) NULL
10966  },
10967  *const FileMenu[] =
10968  {
10969  "Save...",
10970  "Print...",
10971  (char *) NULL
10972  },
10973  *const EditMenu[] =
10974  {
10975  "Undo",
10976  "Redo",
10977  (char *) NULL
10978  },
10979  *const TransformMenu[] =
10980  {
10981  "Flop",
10982  "Flip",
10983  "Rotate Right",
10984  "Rotate Left",
10985  (char *) NULL
10986  },
10987  *const EnhanceMenu[] =
10988  {
10989  "Hue...",
10990  "Saturation...",
10991  "Brightness...",
10992  "Gamma...",
10993  "Spiff",
10994  "Dull",
10995  "Contrast Stretch...",
10996  "Sigmoidal Contrast...",
10997  "Normalize",
10998  "Equalize",
10999  "Negate",
11000  "Grayscale",
11001  "Map...",
11002  "Quantize...",
11003  (char *) NULL
11004  },
11005  *const EffectsMenu[] =
11006  {
11007  "Despeckle",
11008  "Emboss",
11009  "Reduce Noise",
11010  "Add Noise",
11011  "Sharpen...",
11012  "Blur...",
11013  "Threshold...",
11014  "Edge Detect...",
11015  "Spread...",
11016  "Shade...",
11017  "Raise...",
11018  "Segment...",
11019  (char *) NULL
11020  },
11021  *const FXMenu[] =
11022  {
11023  "Solarize...",
11024  "Sepia Tone...",
11025  "Swirl...",
11026  "Implode...",
11027  "Vignette...",
11028  "Wave...",
11029  "Oil Paint...",
11030  "Charcoal Draw...",
11031  (char *) NULL
11032  },
11033  *const MiscellanyMenu[] =
11034  {
11035  "Image Info",
11036  "Zoom Image",
11037  "Show Preview...",
11038  "Show Histogram",
11039  "Show Matte",
11040  (char *) NULL
11041  };
11042 
11043  const char
11044  *const *Menus[ApplyMenus] =
11045  {
11046  FileMenu,
11047  EditMenu,
11048  TransformMenu,
11049  EnhanceMenu,
11050  EffectsMenu,
11051  FXMenu,
11052  MiscellanyMenu
11053  };
11054 
11055  static const CommandType
11056  ApplyCommands[] =
11057  {
11058  NullCommand,
11059  NullCommand,
11060  NullCommand,
11061  NullCommand,
11062  NullCommand,
11063  NullCommand,
11064  NullCommand,
11065  HelpCommand,
11066  QuitCommand
11067  },
11068  FileCommands[] =
11069  {
11070  SaveCommand,
11071  PrintCommand
11072  },
11073  EditCommands[] =
11074  {
11075  UndoCommand,
11076  RedoCommand
11077  },
11078  TransformCommands[] =
11079  {
11080  FlopCommand,
11081  FlipCommand,
11082  RotateRightCommand,
11083  RotateLeftCommand
11084  },
11085  EnhanceCommands[] =
11086  {
11087  HueCommand,
11088  SaturationCommand,
11089  BrightnessCommand,
11090  GammaCommand,
11091  SpiffCommand,
11092  DullCommand,
11093  ContrastStretchCommand,
11094  SigmoidalContrastCommand,
11095  NormalizeCommand,
11096  EqualizeCommand,
11097  NegateCommand,
11098  GrayscaleCommand,
11099  MapCommand,
11100  QuantizeCommand
11101  },
11102  EffectsCommands[] =
11103  {
11104  DespeckleCommand,
11105  EmbossCommand,
11106  ReduceNoiseCommand,
11107  AddNoiseCommand,
11108  SharpenCommand,
11109  BlurCommand,
11110  EdgeDetectCommand,
11111  SpreadCommand,
11112  ShadeCommand,
11113  RaiseCommand,
11114  SegmentCommand
11115  },
11116  FXCommands[] =
11117  {
11118  SolarizeCommand,
11119  SepiaToneCommand,
11120  SwirlCommand,
11121  ImplodeCommand,
11122  VignetteCommand,
11123  WaveCommand,
11124  OilPaintCommand,
11125  CharcoalDrawCommand
11126  },
11127  MiscellanyCommands[] =
11128  {
11129  InfoCommand,
11130  ZoomCommand,
11131  ShowPreviewCommand,
11132  ShowHistogramCommand,
11133  ShowMatteCommand
11134  },
11135  ROICommands[] =
11136  {
11137  ROIHelpCommand,
11138  ROIDismissCommand
11139  };
11140 
11141  static const CommandType
11142  *Commands[ApplyMenus] =
11143  {
11144  FileCommands,
11145  EditCommands,
11146  TransformCommands,
11147  EnhanceCommands,
11148  EffectsCommands,
11149  FXCommands,
11150  MiscellanyCommands
11151  };
11152 
11153  char
11154  command[MaxTextExtent],
11155  text[MaxTextExtent];
11156 
11157  CommandType
11158  command_type;
11159 
11160  Cursor
11161  cursor;
11162 
11163  Image
11164  *roi_image;
11165 
11166  int
11167  entry,
11168  id,
11169  x,
11170  y;
11171 
11172  MagickRealType
11173  scale_factor;
11174 
11175  MagickProgressMonitor
11176  progress_monitor;
11177 
11179  crop_info,
11180  highlight_info,
11181  roi_info;
11182 
11183  unsigned int
11184  height,
11185  width;
11186 
11187  size_t
11188  state;
11189 
11190  XEvent
11191  event;
11192 
11193  /*
11194  Map Command widget.
11195  */
11196  (void) CloneString(&windows->command.name,"ROI");
11197  windows->command.data=0;
11198  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11199  (void) XMapRaised(display,windows->command.id);
11200  XClientMessage(display,windows->image.id,windows->im_protocols,
11201  windows->im_update_widget,CurrentTime);
11202  /*
11203  Track pointer until button 1 is pressed.
11204  */
11205  XQueryPosition(display,windows->image.id,&x,&y);
11206  (void) XSelectInput(display,windows->image.id,
11207  windows->image.attributes.event_mask | PointerMotionMask);
11208  crop_info.width=0;
11209  crop_info.height=0;
11210  crop_info.x=0;
11211  crop_info.y=0;
11212  roi_info.x=(ssize_t) windows->image.x+x;
11213  roi_info.y=(ssize_t) windows->image.y+y;
11214  roi_info.width=0;
11215  roi_info.height=0;
11216  cursor=XCreateFontCursor(display,XC_fleur);
11217  state=DefaultState;
11218  do
11219  {
11220  if (windows->info.mapped != MagickFalse)
11221  {
11222  /*
11223  Display pointer position.
11224  */
11225  (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11226  (long) roi_info.x,(long) roi_info.y);
11227  XInfoWidget(display,windows,text);
11228  }
11229  /*
11230  Wait for next event.
11231  */
11232  XScreenEvent(display,windows,&event);
11233  if (event.xany.window == windows->command.id)
11234  {
11235  /*
11236  Select a command from the Command widget.
11237  */
11238  id=XCommandWidget(display,windows,ROIMenu,&event);
11239  if (id < 0)
11240  continue;
11241  switch (ROICommands[id])
11242  {
11243  case ROIHelpCommand:
11244  {
11245  XTextViewHelp(display,resource_info,windows,MagickFalse,
11246  "Help Viewer - Region of Interest",ImageROIHelp);
11247  break;
11248  }
11249  case ROIDismissCommand:
11250  {
11251  /*
11252  Prematurely exit.
11253  */
11254  state|=EscapeState;
11255  state|=ExitState;
11256  break;
11257  }
11258  default:
11259  break;
11260  }
11261  continue;
11262  }
11263  switch (event.type)
11264  {
11265  case ButtonPress:
11266  {
11267  if (event.xbutton.button != Button1)
11268  break;
11269  if (event.xbutton.window != windows->image.id)
11270  break;
11271  /*
11272  Note first corner of region of interest rectangle-- exit loop.
11273  */
11274  (void) XCheckDefineCursor(display,windows->image.id,cursor);
11275  roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11276  roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11277  state|=ExitState;
11278  break;
11279  }
11280  case ButtonRelease:
11281  break;
11282  case Expose:
11283  break;
11284  case KeyPress:
11285  {
11286  KeySym
11287  key_symbol;
11288 
11289  if (event.xkey.window != windows->image.id)
11290  break;
11291  /*
11292  Respond to a user key press.
11293  */
11294  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11295  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11296  switch ((int) key_symbol)
11297  {
11298  case XK_Escape:
11299  case XK_F20:
11300  {
11301  /*
11302  Prematurely exit.
11303  */
11304  state|=EscapeState;
11305  state|=ExitState;
11306  break;
11307  }
11308  case XK_F1:
11309  case XK_Help:
11310  {
11311  XTextViewHelp(display,resource_info,windows,MagickFalse,
11312  "Help Viewer - Region of Interest",ImageROIHelp);
11313  break;
11314  }
11315  default:
11316  {
11317  (void) XBell(display,0);
11318  break;
11319  }
11320  }
11321  break;
11322  }
11323  case MotionNotify:
11324  {
11325  /*
11326  Map and unmap Info widget as text cursor crosses its boundaries.
11327  */
11328  x=event.xmotion.x;
11329  y=event.xmotion.y;
11330  if (windows->info.mapped != MagickFalse)
11331  {
11332  if ((x < (int) (windows->info.x+windows->info.width)) &&
11333  (y < (int) (windows->info.y+windows->info.height)))
11334  (void) XWithdrawWindow(display,windows->info.id,
11335  windows->info.screen);
11336  }
11337  else
11338  if ((x > (int) (windows->info.x+windows->info.width)) ||
11339  (y > (int) (windows->info.y+windows->info.height)))
11340  (void) XMapWindow(display,windows->info.id);
11341  roi_info.x=(ssize_t) windows->image.x+x;
11342  roi_info.y=(ssize_t) windows->image.y+y;
11343  break;
11344  }
11345  default:
11346  break;
11347  }
11348  } while ((state & ExitState) == 0);
11349  (void) XSelectInput(display,windows->image.id,
11350  windows->image.attributes.event_mask);
11351  if ((state & EscapeState) != 0)
11352  {
11353  /*
11354  User want to exit without region of interest.
11355  */
11356  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11357  (void) XFreeCursor(display,cursor);
11358  return(MagickTrue);
11359  }
11360  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11361  do
11362  {
11363  /*
11364  Size rectangle as pointer moves until the mouse button is released.
11365  */
11366  x=(int) roi_info.x;
11367  y=(int) roi_info.y;
11368  roi_info.width=0;
11369  roi_info.height=0;
11370  state=DefaultState;
11371  do
11372  {
11373  highlight_info=roi_info;
11374  highlight_info.x=roi_info.x-windows->image.x;
11375  highlight_info.y=roi_info.y-windows->image.y;
11376  if ((highlight_info.width > 3) && (highlight_info.height > 3))
11377  {
11378  /*
11379  Display info and draw region of interest rectangle.
11380  */
11381  if (windows->info.mapped == MagickFalse)
11382  (void) XMapWindow(display,windows->info.id);
11383  (void) FormatLocaleString(text,MaxTextExtent,
11384  " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11385  roi_info.height,(double) roi_info.x,(double) roi_info.y);
11386  XInfoWidget(display,windows,text);
11387  XHighlightRectangle(display,windows->image.id,
11388  windows->image.highlight_context,&highlight_info);
11389  }
11390  else
11391  if (windows->info.mapped != MagickFalse)
11392  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11393  /*
11394  Wait for next event.
11395  */
11396  XScreenEvent(display,windows,&event);
11397  if ((highlight_info.width > 3) && (highlight_info.height > 3))
11398  XHighlightRectangle(display,windows->image.id,
11399  windows->image.highlight_context,&highlight_info);
11400  switch (event.type)
11401  {
11402  case ButtonPress:
11403  {
11404  roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11405  roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11406  break;
11407  }
11408  case ButtonRelease:
11409  {
11410  /*
11411  User has committed to region of interest rectangle.
11412  */
11413  roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11414  roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11415  XSetCursorState(display,windows,MagickFalse);
11416  state|=ExitState;
11417  if (LocaleCompare(windows->command.name,"Apply") == 0)
11418  break;
11419  (void) CloneString(&windows->command.name,"Apply");
11420  windows->command.data=ApplyMenus;
11421  (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11422  break;
11423  }
11424  case Expose:
11425  break;
11426  case MotionNotify:
11427  {
11428  roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11429  roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11430  }
11431  default:
11432  break;
11433  }
11434  if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11435  ((state & ExitState) != 0))
11436  {
11437  /*
11438  Check boundary conditions.
11439  */
11440  if (roi_info.x < 0)
11441  roi_info.x=0;
11442  else
11443  if (roi_info.x > (ssize_t) windows->image.ximage->width)
11444  roi_info.x=(ssize_t) windows->image.ximage->width;
11445  if ((int) roi_info.x < x)
11446  roi_info.width=(unsigned int) (x-roi_info.x);
11447  else
11448  {
11449  roi_info.width=(unsigned int) (roi_info.x-x);
11450  roi_info.x=(ssize_t) x;
11451  }
11452  if (roi_info.y < 0)
11453  roi_info.y=0;
11454  else
11455  if (roi_info.y > (ssize_t) windows->image.ximage->height)
11456  roi_info.y=(ssize_t) windows->image.ximage->height;
11457  if ((int) roi_info.y < y)
11458  roi_info.height=(unsigned int) (y-roi_info.y);
11459  else
11460  {
11461  roi_info.height=(unsigned int) (roi_info.y-y);
11462  roi_info.y=(ssize_t) y;
11463  }
11464  }
11465  } while ((state & ExitState) == 0);
11466  /*
11467  Wait for user to grab a corner of the rectangle or press return.
11468  */
11469  state=DefaultState;
11470  command_type=NullCommand;
11471  (void) XMapWindow(display,windows->info.id);
11472  do
11473  {
11474  if (windows->info.mapped != MagickFalse)
11475  {
11476  /*
11477  Display pointer position.
11478  */
11479  (void) FormatLocaleString(text,MaxTextExtent,
11480  " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11481  roi_info.height,(double) roi_info.x,(double) roi_info.y);
11482  XInfoWidget(display,windows,text);
11483  }
11484  highlight_info=roi_info;
11485  highlight_info.x=roi_info.x-windows->image.x;
11486  highlight_info.y=roi_info.y-windows->image.y;
11487  if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11488  {
11489  state|=EscapeState;
11490  state|=ExitState;
11491  break;
11492  }
11493  if ((state & UpdateRegionState) != 0)
11494  {
11495  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11496  switch (command_type)
11497  {
11498  case UndoCommand:
11499  case RedoCommand:
11500  {
11501  (void) XMagickCommand(display,resource_info,windows,command_type,
11502  image);
11503  break;
11504  }
11505  default:
11506  {
11507  /*
11508  Region of interest is relative to image configuration.
11509  */
11510  progress_monitor=SetImageProgressMonitor(*image,
11511  (MagickProgressMonitor) NULL,(*image)->client_data);
11512  crop_info=roi_info;
11513  width=(unsigned int) (*image)->columns;
11514  height=(unsigned int) (*image)->rows;
11515  x=0;
11516  y=0;
11517  if (windows->image.crop_geometry != (char *) NULL)
11518  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11519  &width,&height);
11520  scale_factor=(MagickRealType) width/windows->image.ximage->width;
11521  crop_info.x+=x;
11522  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11523  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11524  scale_factor=(MagickRealType)
11525  height/windows->image.ximage->height;
11526  crop_info.y+=y;
11527  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11528  crop_info.height=(unsigned int)
11529  (scale_factor*crop_info.height+0.5);
11530  roi_image=CropImage(*image,&crop_info,&(*image)->exception);
11531  (void) SetImageProgressMonitor(*image,progress_monitor,
11532  (*image)->client_data);
11533  if (roi_image == (Image *) NULL)
11534  continue;
11535  /*
11536  Apply image processing technique to the region of interest.
11537  */
11538  windows->image.orphan=MagickTrue;
11539  (void) XMagickCommand(display,resource_info,windows,command_type,
11540  &roi_image);
11541  progress_monitor=SetImageProgressMonitor(*image,
11542  (MagickProgressMonitor) NULL,(*image)->client_data);
11543  (void) XMagickCommand(display,resource_info,windows,
11544  SaveToUndoBufferCommand,image);
11545  windows->image.orphan=MagickFalse;
11546  (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11547  crop_info.x,crop_info.y);
11548  roi_image=DestroyImage(roi_image);
11549  (void) SetImageProgressMonitor(*image,progress_monitor,
11550  (*image)->client_data);
11551  break;
11552  }
11553  }
11554  if (command_type != InfoCommand)
11555  {
11556  XConfigureImageColormap(display,resource_info,windows,*image);
11557  (void) XConfigureImage(display,resource_info,windows,*image);
11558  }
11559  XCheckRefreshWindows(display,windows);
11560  XInfoWidget(display,windows,text);
11561  (void) XSetFunction(display,windows->image.highlight_context,
11562  GXinvert);
11563  state&=(~UpdateRegionState);
11564  }
11565  XHighlightRectangle(display,windows->image.id,
11566  windows->image.highlight_context,&highlight_info);
11567  XScreenEvent(display,windows,&event);
11568  if (event.xany.window == windows->command.id)
11569  {
11570  /*
11571  Select a command from the Command widget.
11572  */
11573  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11574  command_type=NullCommand;
11575  id=XCommandWidget(display,windows,ApplyMenu,&event);
11576  if (id >= 0)
11577  {
11578  (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11579  command_type=ApplyCommands[id];
11580  if (id < ApplyMenus)
11581  {
11582  /*
11583  Select a command from a pop-up menu.
11584  */
11585  entry=XMenuWidget(display,windows,ApplyMenu[id],
11586  (const char **) Menus[id],command);
11587  if (entry >= 0)
11588  {
11589  (void) CopyMagickString(command,Menus[id][entry],
11590  MaxTextExtent);
11591  command_type=Commands[id][entry];
11592  }
11593  }
11594  }
11595  (void) XSetFunction(display,windows->image.highlight_context,
11596  GXinvert);
11597  XHighlightRectangle(display,windows->image.id,
11598  windows->image.highlight_context,&highlight_info);
11599  if (command_type == HelpCommand)
11600  {
11601  (void) XSetFunction(display,windows->image.highlight_context,
11602  GXcopy);
11603  XTextViewHelp(display,resource_info,windows,MagickFalse,
11604  "Help Viewer - Region of Interest",ImageROIHelp);
11605  (void) XSetFunction(display,windows->image.highlight_context,
11606  GXinvert);
11607  continue;
11608  }
11609  if (command_type == QuitCommand)
11610  {
11611  /*
11612  exit.
11613  */
11614  state|=EscapeState;
11615  state|=ExitState;
11616  continue;
11617  }
11618  if (command_type != NullCommand)
11619  state|=UpdateRegionState;
11620  continue;
11621  }
11622  XHighlightRectangle(display,windows->image.id,
11623  windows->image.highlight_context,&highlight_info);
11624  switch (event.type)
11625  {
11626  case ButtonPress:
11627  {
11628  x=windows->image.x;
11629  y=windows->image.y;
11630  if (event.xbutton.button != Button1)
11631  break;
11632  if (event.xbutton.window != windows->image.id)
11633  break;
11634  x=windows->image.x+event.xbutton.x;
11635  y=windows->image.y+event.xbutton.y;
11636  if ((x < (int) (roi_info.x+RoiDelta)) &&
11637  (x > (int) (roi_info.x-RoiDelta)) &&
11638  (y < (int) (roi_info.y+RoiDelta)) &&
11639  (y > (int) (roi_info.y-RoiDelta)))
11640  {
11641  roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11642  roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11643  state|=UpdateConfigurationState;
11644  break;
11645  }
11646  if ((x < (int) (roi_info.x+RoiDelta)) &&
11647  (x > (int) (roi_info.x-RoiDelta)) &&
11648  (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11649  (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11650  {
11651  roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11652  state|=UpdateConfigurationState;
11653  break;
11654  }
11655  if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11656  (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11657  (y < (int) (roi_info.y+RoiDelta)) &&
11658  (y > (int) (roi_info.y-RoiDelta)))
11659  {
11660  roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11661  state|=UpdateConfigurationState;
11662  break;
11663  }
11664  if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11665  (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11666  (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11667  (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11668  {
11669  state|=UpdateConfigurationState;
11670  break;
11671  }
11672  magick_fallthrough;
11673  }
11674  case ButtonRelease:
11675  {
11676  if (event.xbutton.window == windows->pan.id)
11677  if ((highlight_info.x != crop_info.x-windows->image.x) ||
11678  (highlight_info.y != crop_info.y-windows->image.y))
11679  XHighlightRectangle(display,windows->image.id,
11680  windows->image.highlight_context,&highlight_info);
11681  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11682  event.xbutton.time);
11683  break;
11684  }
11685  case Expose:
11686  {
11687  if (event.xexpose.window == windows->image.id)
11688  if (event.xexpose.count == 0)
11689  {
11690  event.xexpose.x=(int) highlight_info.x;
11691  event.xexpose.y=(int) highlight_info.y;
11692  event.xexpose.width=(int) highlight_info.width;
11693  event.xexpose.height=(int) highlight_info.height;
11694  XRefreshWindow(display,&windows->image,&event);
11695  }
11696  if (event.xexpose.window == windows->info.id)
11697  if (event.xexpose.count == 0)
11698  XInfoWidget(display,windows,text);
11699  break;
11700  }
11701  case KeyPress:
11702  {
11703  KeySym
11704  key_symbol;
11705 
11706  if (event.xkey.window != windows->image.id)
11707  break;
11708  /*
11709  Respond to a user key press.
11710  */
11711  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11712  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11713  switch ((int) key_symbol)
11714  {
11715  case XK_Shift_L:
11716  case XK_Shift_R:
11717  break;
11718  case XK_Escape:
11719  case XK_F20:
11720  {
11721  state|=EscapeState;
11722  magick_fallthrough;
11723  }
11724  case XK_Return:
11725  {
11726  state|=ExitState;
11727  break;
11728  }
11729  case XK_Home:
11730  case XK_KP_Home:
11731  {
11732  roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11733  roi_info.y=(ssize_t) (windows->image.height/2L-
11734  roi_info.height/2L);
11735  break;
11736  }
11737  case XK_Left:
11738  case XK_KP_Left:
11739  {
11740  roi_info.x--;
11741  break;
11742  }
11743  case XK_Up:
11744  case XK_KP_Up:
11745  case XK_Next:
11746  {
11747  roi_info.y--;
11748  break;
11749  }
11750  case XK_Right:
11751  case XK_KP_Right:
11752  {
11753  roi_info.x++;
11754  break;
11755  }
11756  case XK_Prior:
11757  case XK_Down:
11758  case XK_KP_Down:
11759  {
11760  roi_info.y++;
11761  break;
11762  }
11763  case XK_F1:
11764  case XK_Help:
11765  {
11766  (void) XSetFunction(display,windows->image.highlight_context,
11767  GXcopy);
11768  XTextViewHelp(display,resource_info,windows,MagickFalse,
11769  "Help Viewer - Region of Interest",ImageROIHelp);
11770  (void) XSetFunction(display,windows->image.highlight_context,
11771  GXinvert);
11772  break;
11773  }
11774  default:
11775  {
11776  command_type=XImageWindowCommand(display,resource_info,windows,
11777  event.xkey.state,key_symbol,image);
11778  if (command_type != NullCommand)
11779  state|=UpdateRegionState;
11780  break;
11781  }
11782  }
11783  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11784  event.xkey.time);
11785  break;
11786  }
11787  case KeyRelease:
11788  break;
11789  case MotionNotify:
11790  {
11791  if (event.xbutton.window != windows->image.id)
11792  break;
11793  /*
11794  Map and unmap Info widget as text cursor crosses its boundaries.
11795  */
11796  x=event.xmotion.x;
11797  y=event.xmotion.y;
11798  if (windows->info.mapped != MagickFalse)
11799  {
11800  if ((x < (int) (windows->info.x+windows->info.width)) &&
11801  (y < (int) (windows->info.y+windows->info.height)))
11802  (void) XWithdrawWindow(display,windows->info.id,
11803  windows->info.screen);
11804  }
11805  else
11806  if ((x > (int) (windows->info.x+windows->info.width)) ||
11807  (y > (int) (windows->info.y+windows->info.height)))
11808  (void) XMapWindow(display,windows->info.id);
11809  roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11810  roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11811  break;
11812  }
11813  case SelectionRequest:
11814  {
11815  XSelectionEvent
11816  notify;
11817 
11818  XSelectionRequestEvent
11819  *request;
11820 
11821  /*
11822  Set primary selection.
11823  */
11824  (void) FormatLocaleString(text,MaxTextExtent,
11825  "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11826  roi_info.height,(double) roi_info.x,(double) roi_info.y);
11827  request=(&(event.xselectionrequest));
11828  (void) XChangeProperty(request->display,request->requestor,
11829  request->property,request->target,8,PropModeReplace,
11830  (unsigned char *) text,(int) strlen(text));
11831  notify.type=SelectionNotify;
11832  notify.display=request->display;
11833  notify.requestor=request->requestor;
11834  notify.selection=request->selection;
11835  notify.target=request->target;
11836  notify.time=request->time;
11837  if (request->property == None)
11838  notify.property=request->target;
11839  else
11840  notify.property=request->property;
11841  (void) XSendEvent(request->display,request->requestor,False,0,
11842  (XEvent *) &notify);
11843  }
11844  default:
11845  break;
11846  }
11847  if ((state & UpdateConfigurationState) != 0)
11848  {
11849  (void) XPutBackEvent(display,&event);
11850  (void) XCheckDefineCursor(display,windows->image.id,cursor);
11851  break;
11852  }
11853  } while ((state & ExitState) == 0);
11854  } while ((state & ExitState) == 0);
11855  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11856  XSetCursorState(display,windows,MagickFalse);
11857  if ((state & EscapeState) != 0)
11858  return(MagickTrue);
11859  return(MagickTrue);
11860 }
11861 
11862 /*
11863 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11864 % %
11865 % %
11866 % %
11867 + X R o t a t e I m a g e %
11868 % %
11869 % %
11870 % %
11871 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11872 %
11873 % XRotateImage() rotates the X image. If the degrees parameter if zero, the
11874 % rotation angle is computed from the slope of a line drawn by the user.
11875 %
11876 % The format of the XRotateImage method is:
11877 %
11878 % MagickBooleanType XRotateImage(Display *display,
11879 % XResourceInfo *resource_info,XWindows *windows,double degrees,
11880 % Image **image)
11881 %
11882 % A description of each parameter follows:
11883 %
11884 % o display: Specifies a connection to an X server; returned from
11885 % XOpenDisplay.
11886 %
11887 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11888 %
11889 % o windows: Specifies a pointer to a XWindows structure.
11890 %
11891 % o degrees: Specifies the number of degrees to rotate the image.
11892 %
11893 % o image: the image.
11894 %
11895 */
11896 static MagickBooleanType XRotateImage(Display *display,
11897  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image)
11898 {
11899  const char
11900  *const RotateMenu[] =
11901  {
11902  "Pixel Color",
11903  "Direction",
11904  "Help",
11905  "Dismiss",
11906  (char *) NULL
11907  };
11908 
11909  static ModeType
11910  direction = HorizontalRotateCommand;
11911 
11912  static const ModeType
11913  DirectionCommands[] =
11914  {
11915  HorizontalRotateCommand,
11916  VerticalRotateCommand
11917  },
11918  RotateCommands[] =
11919  {
11920  RotateColorCommand,
11921  RotateDirectionCommand,
11922  RotateHelpCommand,
11923  RotateDismissCommand
11924  };
11925 
11926  static unsigned int
11927  pen_id = 0;
11928 
11929  char
11930  command[MaxTextExtent],
11931  text[MaxTextExtent];
11932 
11933  Image
11934  *rotate_image;
11935 
11936  int
11937  id,
11938  x,
11939  y;
11940 
11941  MagickRealType
11942  normalized_degrees;
11943 
11944  int
11945  i;
11946 
11947  unsigned int
11948  height,
11949  rotations,
11950  width;
11951 
11952  if (degrees == 0.0)
11953  {
11954  unsigned int
11955  distance;
11956 
11957  size_t
11958  state;
11959 
11960  XEvent
11961  event;
11962 
11963  XSegment
11964  rotate_info;
11965 
11966  /*
11967  Map Command widget.
11968  */
11969  (void) CloneString(&windows->command.name,"Rotate");
11970  windows->command.data=2;
11971  (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
11972  (void) XMapRaised(display,windows->command.id);
11973  XClientMessage(display,windows->image.id,windows->im_protocols,
11974  windows->im_update_widget,CurrentTime);
11975  /*
11976  Wait for first button press.
11977  */
11978  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11979  XQueryPosition(display,windows->image.id,&x,&y);
11980  rotate_info.x1=x;
11981  rotate_info.y1=y;
11982  rotate_info.x2=x;
11983  rotate_info.y2=y;
11984  state=DefaultState;
11985  do
11986  {
11987  XHighlightLine(display,windows->image.id,
11988  windows->image.highlight_context,&rotate_info);
11989  /*
11990  Wait for next event.
11991  */
11992  XScreenEvent(display,windows,&event);
11993  XHighlightLine(display,windows->image.id,
11994  windows->image.highlight_context,&rotate_info);
11995  if (event.xany.window == windows->command.id)
11996  {
11997  /*
11998  Select a command from the Command widget.
11999  */
12000  id=XCommandWidget(display,windows,RotateMenu,&event);
12001  if (id < 0)
12002  continue;
12003  (void) XSetFunction(display,windows->image.highlight_context,
12004  GXcopy);
12005  switch (RotateCommands[id])
12006  {
12007  case RotateColorCommand:
12008  {
12009  const char
12010  *ColorMenu[MaxNumberPens];
12011 
12012  int
12013  pen_number;
12014 
12015  XColor
12016  color;
12017 
12018  /*
12019  Initialize menu selections.
12020  */
12021  for (i=0; i < (int) (MaxNumberPens-2); i++)
12022  ColorMenu[i]=resource_info->pen_colors[i];
12023  ColorMenu[MaxNumberPens-2]="Browser...";
12024  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12025  /*
12026  Select a pen color from the pop-up menu.
12027  */
12028  pen_number=XMenuWidget(display,windows,RotateMenu[id],
12029  (const char **) ColorMenu,command);
12030  if (pen_number < 0)
12031  break;
12032  if (pen_number == (MaxNumberPens-2))
12033  {
12034  static char
12035  color_name[MaxTextExtent] = "gray";
12036 
12037  /*
12038  Select a pen color from a dialog.
12039  */
12040  resource_info->pen_colors[pen_number]=color_name;
12041  XColorBrowserWidget(display,windows,"Select",color_name);
12042  if (*color_name == '\0')
12043  break;
12044  }
12045  /*
12046  Set pen color.
12047  */
12048  (void) XParseColor(display,windows->map_info->colormap,
12049  resource_info->pen_colors[pen_number],&color);
12050  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12051  (unsigned int) MaxColors,&color);
12052  windows->pixel_info->pen_colors[pen_number]=color;
12053  pen_id=(unsigned int) pen_number;
12054  break;
12055  }
12056  case RotateDirectionCommand:
12057  {
12058  const char
12059  *const Directions[] =
12060  {
12061  "horizontal",
12062  "vertical",
12063  (char *) NULL,
12064  };
12065 
12066  /*
12067  Select a command from the pop-up menu.
12068  */
12069  id=XMenuWidget(display,windows,RotateMenu[id],
12070  Directions,command);
12071  if (id >= 0)
12072  direction=DirectionCommands[id];
12073  break;
12074  }
12075  case RotateHelpCommand:
12076  {
12077  XTextViewHelp(display,resource_info,windows,MagickFalse,
12078  "Help Viewer - Image Rotation",ImageRotateHelp);
12079  break;
12080  }
12081  case RotateDismissCommand:
12082  {
12083  /*
12084  Prematurely exit.
12085  */
12086  state|=EscapeState;
12087  state|=ExitState;
12088  break;
12089  }
12090  default:
12091  break;
12092  }
12093  (void) XSetFunction(display,windows->image.highlight_context,
12094  GXinvert);
12095  continue;
12096  }
12097  switch (event.type)
12098  {
12099  case ButtonPress:
12100  {
12101  if (event.xbutton.button != Button1)
12102  break;
12103  if (event.xbutton.window != windows->image.id)
12104  break;
12105  /*
12106  exit loop.
12107  */
12108  (void) XSetFunction(display,windows->image.highlight_context,
12109  GXcopy);
12110  rotate_info.x1=event.xbutton.x;
12111  rotate_info.y1=event.xbutton.y;
12112  state|=ExitState;
12113  break;
12114  }
12115  case ButtonRelease:
12116  break;
12117  case Expose:
12118  break;
12119  case KeyPress:
12120  {
12121  char
12122  command[MaxTextExtent];
12123 
12124  KeySym
12125  key_symbol;
12126 
12127  if (event.xkey.window != windows->image.id)
12128  break;
12129  /*
12130  Respond to a user key press.
12131  */
12132  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12133  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12134  switch ((int) key_symbol)
12135  {
12136  case XK_Escape:
12137  case XK_F20:
12138  {
12139  /*
12140  Prematurely exit.
12141  */
12142  state|=EscapeState;
12143  state|=ExitState;
12144  break;
12145  }
12146  case XK_F1:
12147  case XK_Help:
12148  {
12149  (void) XSetFunction(display,windows->image.highlight_context,
12150  GXcopy);
12151  XTextViewHelp(display,resource_info,windows,MagickFalse,
12152  "Help Viewer - Image Rotation",ImageRotateHelp);
12153  (void) XSetFunction(display,windows->image.highlight_context,
12154  GXinvert);
12155  break;
12156  }
12157  default:
12158  {
12159  (void) XBell(display,0);
12160  break;
12161  }
12162  }
12163  break;
12164  }
12165  case MotionNotify:
12166  {
12167  rotate_info.x1=event.xmotion.x;
12168  rotate_info.y1=event.xmotion.y;
12169  }
12170  }
12171  rotate_info.x2=rotate_info.x1;
12172  rotate_info.y2=rotate_info.y1;
12173  if (direction == HorizontalRotateCommand)
12174  rotate_info.x2+=32;
12175  else
12176  rotate_info.y2-=32;
12177  } while ((state & ExitState) == 0);
12178  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12179  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12180  if ((state & EscapeState) != 0)
12181  return(MagickTrue);
12182  /*
12183  Draw line as pointer moves until the mouse button is released.
12184  */
12185  distance=0;
12186  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12187  state=DefaultState;
12188  do
12189  {
12190  if (distance > 9)
12191  {
12192  /*
12193  Display info and draw rotation line.
12194  */
12195  if (windows->info.mapped == MagickFalse)
12196  (void) XMapWindow(display,windows->info.id);
12197  (void) FormatLocaleString(text,MaxTextExtent," %g",
12198  direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12199  XInfoWidget(display,windows,text);
12200  XHighlightLine(display,windows->image.id,
12201  windows->image.highlight_context,&rotate_info);
12202  }
12203  else
12204  if (windows->info.mapped != MagickFalse)
12205  (void) XWithdrawWindow(display,windows->info.id,
12206  windows->info.screen);
12207  /*
12208  Wait for next event.
12209  */
12210  XScreenEvent(display,windows,&event);
12211  if (distance > 9)
12212  XHighlightLine(display,windows->image.id,
12213  windows->image.highlight_context,&rotate_info);
12214  switch (event.type)
12215  {
12216  case ButtonPress:
12217  break;
12218  case ButtonRelease:
12219  {
12220  /*
12221  User has committed to rotation line.
12222  */
12223  rotate_info.x2=event.xbutton.x;
12224  rotate_info.y2=event.xbutton.y;
12225  state|=ExitState;
12226  break;
12227  }
12228  case Expose:
12229  break;
12230  case MotionNotify:
12231  {
12232  rotate_info.x2=event.xmotion.x;
12233  rotate_info.y2=event.xmotion.y;
12234  }
12235  default:
12236  break;
12237  }
12238  /*
12239  Check boundary conditions.
12240  */
12241  if (rotate_info.x2 < 0)
12242  rotate_info.x2=0;
12243  else
12244  if (rotate_info.x2 > (int) windows->image.width)
12245  rotate_info.x2=(short) windows->image.width;
12246  if (rotate_info.y2 < 0)
12247  rotate_info.y2=0;
12248  else
12249  if (rotate_info.y2 > (int) windows->image.height)
12250  rotate_info.y2=(short) windows->image.height;
12251  /*
12252  Compute rotation angle from the slope of the line.
12253  */
12254  degrees=0.0;
12255  distance=(unsigned int)
12256  ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12257  ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12258  if (distance > 9)
12259  degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12260  rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12261  } while ((state & ExitState) == 0);
12262  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12263  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12264  if (distance <= 9)
12265  return(MagickTrue);
12266  }
12267  if (direction == VerticalRotateCommand)
12268  degrees-=90.0;
12269  if (degrees == 0.0)
12270  return(MagickTrue);
12271  /*
12272  Rotate image.
12273  */
12274  normalized_degrees=degrees;
12275  while (normalized_degrees < -45.0)
12276  normalized_degrees+=360.0;
12277  for (rotations=0; normalized_degrees > 45.0; rotations++)
12278  normalized_degrees-=90.0;
12279  if (normalized_degrees != 0.0)
12280  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
12281  XSetCursorState(display,windows,MagickTrue);
12282  XCheckRefreshWindows(display,windows);
12283  (*image)->background_color.red=ScaleShortToQuantum(
12284  windows->pixel_info->pen_colors[pen_id].red);
12285  (*image)->background_color.green=ScaleShortToQuantum(
12286  windows->pixel_info->pen_colors[pen_id].green);
12287  (*image)->background_color.blue=ScaleShortToQuantum(
12288  windows->pixel_info->pen_colors[pen_id].blue);
12289  rotate_image=RotateImage(*image,degrees,&(*image)->exception);
12290  XSetCursorState(display,windows,MagickFalse);
12291  if (rotate_image == (Image *) NULL)
12292  return(MagickFalse);
12293  *image=DestroyImage(*image);
12294  *image=rotate_image;
12295  if (windows->image.crop_geometry != (char *) NULL)
12296  {
12297  /*
12298  Rotate crop geometry.
12299  */
12300  width=(unsigned int) (*image)->columns;
12301  height=(unsigned int) (*image)->rows;
12302  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12303  switch (rotations % 4)
12304  {
12305  default:
12306  case 0:
12307  break;
12308  case 1:
12309  {
12310  /*
12311  Rotate 90 degrees.
12312  */
12313  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12314  "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12315  (int) height-y,x);
12316  break;
12317  }
12318  case 2:
12319  {
12320  /*
12321  Rotate 180 degrees.
12322  */
12323  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12324  "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12325  break;
12326  }
12327  case 3:
12328  {
12329  /*
12330  Rotate 270 degrees.
12331  */
12332  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12333  "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12334  break;
12335  }
12336  }
12337  }
12338  if (windows->image.orphan != MagickFalse)
12339  return(MagickTrue);
12340  if (normalized_degrees != 0.0)
12341  {
12342  /*
12343  Update image colormap.
12344  */
12345  windows->image.window_changes.width=(int) (*image)->columns;
12346  windows->image.window_changes.height=(int) (*image)->rows;
12347  if (windows->image.crop_geometry != (char *) NULL)
12348  {
12349  /*
12350  Obtain dimensions of image from crop geometry.
12351  */
12352  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12353  &width,&height);
12354  windows->image.window_changes.width=(int) width;
12355  windows->image.window_changes.height=(int) height;
12356  }
12357  XConfigureImageColormap(display,resource_info,windows,*image);
12358  }
12359  else
12360  if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12361  {
12362  windows->image.window_changes.width=windows->image.ximage->height;
12363  windows->image.window_changes.height=windows->image.ximage->width;
12364  }
12365  /*
12366  Update image configuration.
12367  */
12368  (void) XConfigureImage(display,resource_info,windows,*image);
12369  return(MagickTrue);
12370 }
12371 
12372 /*
12373 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12374 % %
12375 % %
12376 % %
12377 + X S a v e I m a g e %
12378 % %
12379 % %
12380 % %
12381 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12382 %
12383 % XSaveImage() saves an image to a file.
12384 %
12385 % The format of the XSaveImage method is:
12386 %
12387 % MagickBooleanType XSaveImage(Display *display,
12388 % XResourceInfo *resource_info,XWindows *windows,Image *image)
12389 %
12390 % A description of each parameter follows:
12391 %
12392 % o display: Specifies a connection to an X server; returned from
12393 % XOpenDisplay.
12394 %
12395 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12396 %
12397 % o windows: Specifies a pointer to a XWindows structure.
12398 %
12399 % o image: the image.
12400 %
12401 */
12402 static MagickBooleanType XSaveImage(Display *display,
12403  XResourceInfo *resource_info,XWindows *windows,Image *image)
12404 {
12405  char
12406  filename[MaxTextExtent],
12407  geometry[MaxTextExtent];
12408 
12409  Image
12410  *save_image;
12411 
12412  ImageInfo
12413  *image_info;
12414 
12415  MagickStatusType
12416  status;
12417 
12418  /*
12419  Request file name from user.
12420  */
12421  if (resource_info->write_filename != (char *) NULL)
12422  (void) CopyMagickString(filename,resource_info->write_filename,
12423  MaxTextExtent);
12424  else
12425  {
12426  char
12427  path[MaxTextExtent];
12428 
12429  int
12430  status;
12431 
12432  GetPathComponent(image->filename,HeadPath,path);
12433  GetPathComponent(image->filename,TailPath,filename);
12434  if (*path != '\0')
12435  {
12436  status=chdir(path);
12437  if (status == -1)
12438  (void) ThrowMagickException(&image->exception,GetMagickModule(),
12439  FileOpenError,"UnableToOpenFile","%s",path);
12440  }
12441  }
12442  XFileBrowserWidget(display,windows,"Save",filename);
12443  if (*filename == '\0')
12444  return(MagickTrue);
12445  if (IsPathAccessible(filename) != MagickFalse)
12446  {
12447  int
12448  status;
12449 
12450  /*
12451  File exists-- seek user's permission before overwriting.
12452  */
12453  status=XConfirmWidget(display,windows,"Overwrite",filename);
12454  if (status <= 0)
12455  return(MagickTrue);
12456  }
12457  image_info=CloneImageInfo(resource_info->image_info);
12458  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12459  (void) SetImageInfo(image_info,1,&image->exception);
12460  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12461  (LocaleCompare(image_info->magick,"JPG") == 0))
12462  {
12463  char
12464  quality[MaxTextExtent];
12465 
12466  int
12467  status;
12468 
12469  /*
12470  Request JPEG quality from user.
12471  */
12472  (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12473  image->quality);
12474  status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12475  quality);
12476  if (*quality == '\0')
12477  return(MagickTrue);
12478  image->quality=StringToUnsignedLong(quality);
12479  image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12480  }
12481  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12482  (LocaleCompare(image_info->magick,"PDF") == 0) ||
12483  (LocaleCompare(image_info->magick,"PS") == 0) ||
12484  (LocaleCompare(image_info->magick,"PS2") == 0))
12485  {
12486  char
12487  geometry[MaxTextExtent];
12488 
12489  const char
12490  *const PageSizes[] =
12491  {
12492  "Letter",
12493  "Tabloid",
12494  "Ledger",
12495  "Legal",
12496  "Statement",
12497  "Executive",
12498  "A3",
12499  "A4",
12500  "A5",
12501  "B4",
12502  "B5",
12503  "Folio",
12504  "Quarto",
12505  "10x14",
12506  (char *) NULL
12507  };
12508 
12509  /*
12510  Request page geometry from user.
12511  */
12512  (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12513  if (LocaleCompare(image_info->magick,"PDF") == 0)
12514  (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12515  if (image_info->page != (char *) NULL)
12516  (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12517  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12518  "Select page geometry:",geometry);
12519  if (*geometry != '\0')
12520  image_info->page=GetPageGeometry(geometry);
12521  }
12522  /*
12523  Apply image transforms.
12524  */
12525  XSetCursorState(display,windows,MagickTrue);
12526  XCheckRefreshWindows(display,windows);
12527  save_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12528  if (save_image == (Image *) NULL)
12529  return(MagickFalse);
12530  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12531  windows->image.ximage->width,windows->image.ximage->height);
12532  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12533  /*
12534  Write image.
12535  */
12536  (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12537  status=WriteImage(image_info,save_image);
12538  if (status != MagickFalse)
12539  image->taint=MagickFalse;
12540  save_image=DestroyImage(save_image);
12541  image_info=DestroyImageInfo(image_info);
12542  XSetCursorState(display,windows,MagickFalse);
12543  return(status != 0 ? MagickTrue : MagickFalse);
12544 }
12545 
12546 /*
12547 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12548 % %
12549 % %
12550 % %
12551 + X S c r e e n E v e n t %
12552 % %
12553 % %
12554 % %
12555 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12556 %
12557 % XScreenEvent() handles global events associated with the Pan and Magnify
12558 % windows.
12559 %
12560 % The format of the XScreenEvent function is:
12561 %
12562 % void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12563 %
12564 % A description of each parameter follows:
12565 %
12566 % o display: Specifies a pointer to the Display structure; returned from
12567 % XOpenDisplay.
12568 %
12569 % o windows: Specifies a pointer to a XWindows structure.
12570 %
12571 % o event: Specifies a pointer to a X11 XEvent structure.
12572 %
12573 %
12574 */
12575 
12576 #if defined(__cplusplus) || defined(c_plusplus)
12577 extern "C" {
12578 #endif
12579 
12580 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12581 {
12582  XWindows
12583  *windows;
12584 
12585  magick_unreferenced(display);
12586 
12587  windows=(XWindows *) data;
12588  if ((event->type == ClientMessage) &&
12589  (event->xclient.window == windows->image.id))
12590  return(MagickFalse);
12591  return(MagickTrue);
12592 }
12593 
12594 #if defined(__cplusplus) || defined(c_plusplus)
12595 }
12596 #endif
12597 
12598 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12599 {
12600  int
12601  x,
12602  y;
12603 
12604  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12605  if (event->xany.window == windows->command.id)
12606  return;
12607  switch (event->type)
12608  {
12609  case ButtonPress:
12610  case ButtonRelease:
12611  {
12612  if ((event->xbutton.button == Button3) &&
12613  (event->xbutton.state & Mod1Mask))
12614  {
12615  /*
12616  Convert Alt-Button3 to Button2.
12617  */
12618  event->xbutton.button=Button2;
12619  event->xbutton.state&=(~Mod1Mask);
12620  }
12621  if (event->xbutton.window == windows->backdrop.id)
12622  {
12623  (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12624  event->xbutton.time);
12625  break;
12626  }
12627  if (event->xbutton.window == windows->pan.id)
12628  {
12629  XPanImage(display,windows,event);
12630  break;
12631  }
12632  if (event->xbutton.window == windows->image.id)
12633  if (event->xbutton.button == Button2)
12634  {
12635  /*
12636  Update magnified image.
12637  */
12638  x=event->xbutton.x;
12639  y=event->xbutton.y;
12640  if (x < 0)
12641  x=0;
12642  else
12643  if (x >= (int) windows->image.width)
12644  x=(int) (windows->image.width-1);
12645  windows->magnify.x=(int) windows->image.x+x;
12646  if (y < 0)
12647  y=0;
12648  else
12649  if (y >= (int) windows->image.height)
12650  y=(int) (windows->image.height-1);
12651  windows->magnify.y=windows->image.y+y;
12652  if (windows->magnify.mapped == MagickFalse)
12653  (void) XMapRaised(display,windows->magnify.id);
12654  XMakeMagnifyImage(display,windows);
12655  if (event->type == ButtonRelease)
12656  (void) XWithdrawWindow(display,windows->info.id,
12657  windows->info.screen);
12658  break;
12659  }
12660  break;
12661  }
12662  case ClientMessage:
12663  {
12664  /*
12665  If client window delete message, exit.
12666  */
12667  if (event->xclient.message_type != windows->wm_protocols)
12668  break;
12669  if (*event->xclient.data.l != (long) windows->wm_delete_window)
12670  break;
12671  if (event->xclient.window == windows->magnify.id)
12672  {
12673  (void) XWithdrawWindow(display,windows->magnify.id,
12674  windows->magnify.screen);
12675  break;
12676  }
12677  break;
12678  }
12679  case ConfigureNotify:
12680  {
12681  if (event->xconfigure.window == windows->magnify.id)
12682  {
12683  unsigned int
12684  magnify;
12685 
12686  /*
12687  Magnify window has a new configuration.
12688  */
12689  windows->magnify.width=(unsigned int) event->xconfigure.width;
12690  windows->magnify.height=(unsigned int) event->xconfigure.height;
12691  if (windows->magnify.mapped == MagickFalse)
12692  break;
12693  magnify=1;
12694  while ((int) magnify <= event->xconfigure.width)
12695  magnify<<=1;
12696  while ((int) magnify <= event->xconfigure.height)
12697  magnify<<=1;
12698  magnify>>=1;
12699  if (((int) magnify != event->xconfigure.width) ||
12700  ((int) magnify != event->xconfigure.height))
12701  {
12702  XWindowChanges
12703  window_changes;
12704 
12705  window_changes.width=(int) magnify;
12706  window_changes.height=(int) magnify;
12707  (void) XReconfigureWMWindow(display,windows->magnify.id,
12708  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12709  &window_changes);
12710  break;
12711  }
12712  XMakeMagnifyImage(display,windows);
12713  break;
12714  }
12715  break;
12716  }
12717  case Expose:
12718  {
12719  if (event->xexpose.window == windows->image.id)
12720  {
12721  XRefreshWindow(display,&windows->image,event);
12722  break;
12723  }
12724  if (event->xexpose.window == windows->pan.id)
12725  if (event->xexpose.count == 0)
12726  {
12727  XDrawPanRectangle(display,windows);
12728  break;
12729  }
12730  if (event->xexpose.window == windows->magnify.id)
12731  if (event->xexpose.count == 0)
12732  {
12733  XMakeMagnifyImage(display,windows);
12734  break;
12735  }
12736  break;
12737  }
12738  case KeyPress:
12739  {
12740  char
12741  command[MaxTextExtent];
12742 
12743  KeySym
12744  key_symbol;
12745 
12746  if (event->xkey.window != windows->magnify.id)
12747  break;
12748  /*
12749  Respond to a user key press.
12750  */
12751  (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12752  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12753  XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12754  break;
12755  }
12756  case MapNotify:
12757  {
12758  if (event->xmap.window == windows->magnify.id)
12759  {
12760  windows->magnify.mapped=MagickTrue;
12761  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12762  break;
12763  }
12764  if (event->xmap.window == windows->info.id)
12765  {
12766  windows->info.mapped=MagickTrue;
12767  break;
12768  }
12769  break;
12770  }
12771  case MotionNotify:
12772  {
12773  while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12774  if (event->xmotion.window == windows->image.id)
12775  if (windows->magnify.mapped != MagickFalse)
12776  {
12777  /*
12778  Update magnified image.
12779  */
12780  x=event->xmotion.x;
12781  y=event->xmotion.y;
12782  if (x < 0)
12783  x=0;
12784  else
12785  if (x >= (int) windows->image.width)
12786  x=(int) (windows->image.width-1);
12787  windows->magnify.x=(int) windows->image.x+x;
12788  if (y < 0)
12789  y=0;
12790  else
12791  if (y >= (int) windows->image.height)
12792  y=(int) (windows->image.height-1);
12793  windows->magnify.y=windows->image.y+y;
12794  XMakeMagnifyImage(display,windows);
12795  }
12796  break;
12797  }
12798  case UnmapNotify:
12799  {
12800  if (event->xunmap.window == windows->magnify.id)
12801  {
12802  windows->magnify.mapped=MagickFalse;
12803  break;
12804  }
12805  if (event->xunmap.window == windows->info.id)
12806  {
12807  windows->info.mapped=MagickFalse;
12808  break;
12809  }
12810  break;
12811  }
12812  default:
12813  break;
12814  }
12815 }
12816 
12817 /*
12818 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12819 % %
12820 % %
12821 % %
12822 + X S e t C r o p G e o m e t r y %
12823 % %
12824 % %
12825 % %
12826 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12827 %
12828 % XSetCropGeometry() accepts a cropping geometry relative to the Image window
12829 % and translates it to a cropping geometry relative to the image.
12830 %
12831 % The format of the XSetCropGeometry method is:
12832 %
12833 % void XSetCropGeometry(Display *display,XWindows *windows,
12834 % RectangleInfo *crop_info,Image *image)
12835 %
12836 % A description of each parameter follows:
12837 %
12838 % o display: Specifies a connection to an X server; returned from
12839 % XOpenDisplay.
12840 %
12841 % o windows: Specifies a pointer to a XWindows structure.
12842 %
12843 % o crop_info: A pointer to a RectangleInfo that defines a region of the
12844 % Image window to crop.
12845 %
12846 % o image: the image.
12847 %
12848 */
12849 static void XSetCropGeometry(Display *display,XWindows *windows,
12850  RectangleInfo *crop_info,Image *image)
12851 {
12852  char
12853  text[MaxTextExtent];
12854 
12855  int
12856  x,
12857  y;
12858 
12859  MagickRealType
12860  scale_factor;
12861 
12862  unsigned int
12863  height,
12864  width;
12865 
12866  if (windows->info.mapped != MagickFalse)
12867  {
12868  /*
12869  Display info on cropping rectangle.
12870  */
12871  (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12872  (double) crop_info->width,(double) crop_info->height,(double)
12873  crop_info->x,(double) crop_info->y);
12874  XInfoWidget(display,windows,text);
12875  }
12876  /*
12877  Cropping geometry is relative to any previous crop geometry.
12878  */
12879  x=0;
12880  y=0;
12881  width=(unsigned int) image->columns;
12882  height=(unsigned int) image->rows;
12883  if (windows->image.crop_geometry != (char *) NULL)
12884  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12885  else
12886  windows->image.crop_geometry=AcquireString((char *) NULL);
12887  /*
12888  Define the crop geometry string from the cropping rectangle.
12889  */
12890  scale_factor=(MagickRealType) width/windows->image.ximage->width;
12891  if (crop_info->x > 0)
12892  x+=(int) (scale_factor*crop_info->x+0.5);
12893  width=(unsigned int) (scale_factor*crop_info->width+0.5);
12894  if (width == 0)
12895  width=1;
12896  scale_factor=(MagickRealType) height/windows->image.ximage->height;
12897  if (crop_info->y > 0)
12898  y+=(int) (scale_factor*crop_info->y+0.5);
12899  height=(unsigned int) (scale_factor*crop_info->height+0.5);
12900  if (height == 0)
12901  height=1;
12902  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12903  "%ux%u%+d%+d",width,height,x,y);
12904 }
12905 
12906 /*
12907 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12908 % %
12909 % %
12910 % %
12911 + X T i l e I m a g e %
12912 % %
12913 % %
12914 % %
12915 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12916 %
12917 % XTileImage() loads or deletes a selected tile from a visual image directory.
12918 % The load or delete command is chosen from a menu.
12919 %
12920 % The format of the XTileImage method is:
12921 %
12922 % Image *XTileImage(Display *display,XResourceInfo *resource_info,
12923 % XWindows *windows,Image *image,XEvent *event)
12924 %
12925 % A description of each parameter follows:
12926 %
12927 % o tile_image: XTileImage reads or deletes the tile image
12928 % and returns it. A null image is returned if an error occurs.
12929 %
12930 % o display: Specifies a connection to an X server; returned from
12931 % XOpenDisplay.
12932 %
12933 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12934 %
12935 % o windows: Specifies a pointer to a XWindows structure.
12936 %
12937 % o image: the image; returned from ReadImage.
12938 %
12939 % o event: Specifies a pointer to a XEvent structure. If it is NULL,
12940 % the entire image is refreshed.
12941 %
12942 */
12943 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
12944  XWindows *windows,Image *image,XEvent *event)
12945 {
12946  const char
12947  *const VerbMenu[] =
12948  {
12949  "Load",
12950  "Next",
12951  "Former",
12952  "Delete",
12953  "Update",
12954  (char *) NULL,
12955  };
12956 
12957  static const ModeType
12958  TileCommands[] =
12959  {
12960  TileLoadCommand,
12961  TileNextCommand,
12962  TileFormerCommand,
12963  TileDeleteCommand,
12964  TileUpdateCommand
12965  };
12966 
12967  char
12968  command[MaxTextExtent],
12969  filename[MaxTextExtent];
12970 
12971  Image
12972  *tile_image;
12973 
12974  int
12975  id,
12976  status,
12977  tile,
12978  x,
12979  y;
12980 
12981  MagickRealType
12982  scale_factor;
12983 
12984  char
12985  *p,
12986  *q;
12987 
12988  int
12989  i;
12990 
12991  unsigned int
12992  height,
12993  width;
12994 
12995  /*
12996  Tile image is relative to montage image configuration.
12997  */
12998  x=0;
12999  y=0;
13000  width=(unsigned int) image->columns;
13001  height=(unsigned int) image->rows;
13002  if (windows->image.crop_geometry != (char *) NULL)
13003  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13004  scale_factor=(MagickRealType) width/windows->image.ximage->width;
13005  event->xbutton.x+=windows->image.x;
13006  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13007  scale_factor=(MagickRealType) height/windows->image.ximage->height;
13008  event->xbutton.y+=windows->image.y;
13009  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13010  /*
13011  Determine size and location of each tile in the visual image directory.
13012  */
13013  width=(unsigned int) image->columns;
13014  height=(unsigned int) image->rows;
13015  x=0;
13016  y=0;
13017  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13018  tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13019  (event->xbutton.x-x)/width;
13020  if (tile < 0)
13021  {
13022  /*
13023  Button press is outside any tile.
13024  */
13025  (void) XBell(display,0);
13026  return((Image *) NULL);
13027  }
13028  /*
13029  Determine file name from the tile directory.
13030  */
13031  p=image->directory;
13032  for (i=tile; (i != 0) && (*p != '\0'); )
13033  {
13034  if (*p == '\xff')
13035  i--;
13036  p++;
13037  }
13038  if (*p == '\0')
13039  {
13040  /*
13041  Button press is outside any tile.
13042  */
13043  (void) XBell(display,0);
13044  return((Image *) NULL);
13045  }
13046  /*
13047  Select a command from the pop-up menu.
13048  */
13049  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13050  if (id < 0)
13051  return((Image *) NULL);
13052  q=p;
13053  while ((*q != '\xff') && (*q != '\0'))
13054  q++;
13055  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13056  /*
13057  Perform command for the selected tile.
13058  */
13059  XSetCursorState(display,windows,MagickTrue);
13060  XCheckRefreshWindows(display,windows);
13061  tile_image=NewImageList();
13062  switch (TileCommands[id])
13063  {
13064  case TileLoadCommand:
13065  {
13066  /*
13067  Load tile image.
13068  */
13069  XCheckRefreshWindows(display,windows);
13070  (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13071  MaxTextExtent);
13072  (void) CopyMagickString(resource_info->image_info->filename,filename,
13073  MaxTextExtent);
13074  tile_image=ReadImage(resource_info->image_info,&image->exception);
13075  CatchException(&image->exception);
13076  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13077  break;
13078  }
13079  case TileNextCommand:
13080  {
13081  /*
13082  Display next image.
13083  */
13084  XClientMessage(display,windows->image.id,windows->im_protocols,
13085  windows->im_next_image,CurrentTime);
13086  break;
13087  }
13088  case TileFormerCommand:
13089  {
13090  /*
13091  Display former image.
13092  */
13093  XClientMessage(display,windows->image.id,windows->im_protocols,
13094  windows->im_former_image,CurrentTime);
13095  break;
13096  }
13097  case TileDeleteCommand:
13098  {
13099  /*
13100  Delete tile image.
13101  */
13102  if (IsPathAccessible(filename) == MagickFalse)
13103  {
13104  XNoticeWidget(display,windows,"Image file does not exist:",filename);
13105  break;
13106  }
13107  status=XConfirmWidget(display,windows,"Really delete tile",filename);
13108  if (status <= 0)
13109  break;
13110  status=ShredFile(filename);
13111  status|=remove_utf8(filename);
13112  if (status != MagickFalse)
13113  {
13114  XNoticeWidget(display,windows,"Unable to delete image file:",
13115  filename);
13116  break;
13117  }
13118  magick_fallthrough;
13119  }
13120  case TileUpdateCommand:
13121  {
13123  *exception;
13124 
13125  int
13126  x_offset,
13127  y_offset;
13128 
13129  PixelPacket
13130  pixel;
13131 
13132  int
13133  j;
13134 
13135  PixelPacket
13136  *s;
13137 
13138  /*
13139  Ensure all the images exist.
13140  */
13141  tile=0;
13142  for (p=image->directory; *p != '\0'; p++)
13143  {
13144  CacheView
13145  *image_view;
13146 
13147  q=p;
13148  while ((*q != '\xff') && (*q != '\0'))
13149  q++;
13150  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13151  p=q;
13152  if (IsPathAccessible(filename) != MagickFalse)
13153  {
13154  tile++;
13155  continue;
13156  }
13157  /*
13158  Overwrite tile with background color.
13159  */
13160  x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13161  y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13162  exception=(&image->exception);
13163  image_view=AcquireAuthenticCacheView(image,exception);
13164  (void) GetOneCacheViewVirtualPixel(image_view,0,0,&pixel,exception);
13165  for (i=0; i < (int) height; i++)
13166  {
13167  s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13168  y_offset+i,width,1,exception);
13169  if (s == (PixelPacket *) NULL)
13170  break;
13171  for (j=0; j < (int) width; j++)
13172  *s++=pixel;
13173  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13174  break;
13175  }
13176  image_view=DestroyCacheView(image_view);
13177  tile++;
13178  }
13179  windows->image.window_changes.width=(int) image->columns;
13180  windows->image.window_changes.height=(int) image->rows;
13181  XConfigureImageColormap(display,resource_info,windows,image);
13182  (void) XConfigureImage(display,resource_info,windows,image);
13183  break;
13184  }
13185  default:
13186  break;
13187  }
13188  XSetCursorState(display,windows,MagickFalse);
13189  return(tile_image);
13190 }
13191 
13192 /*
13193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13194 % %
13195 % %
13196 % %
13197 + X T r a n s l a t e I m a g e %
13198 % %
13199 % %
13200 % %
13201 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13202 %
13203 % XTranslateImage() translates the image within an Image window by one pixel
13204 % as specified by the key symbol. If the image has a `montage string the
13205 % translation is respect to the width and height contained within the string.
13206 %
13207 % The format of the XTranslateImage method is:
13208 %
13209 % void XTranslateImage(Display *display,XWindows *windows,
13210 % Image *image,const KeySym key_symbol)
13211 %
13212 % A description of each parameter follows:
13213 %
13214 % o display: Specifies a connection to an X server; returned from
13215 % XOpenDisplay.
13216 %
13217 % o windows: Specifies a pointer to a XWindows structure.
13218 %
13219 % o image: the image.
13220 %
13221 % o key_symbol: Specifies a KeySym which indicates which side of the image
13222 % to trim.
13223 %
13224 */
13225 static void XTranslateImage(Display *display,XWindows *windows,
13226  Image *image,const KeySym key_symbol)
13227 {
13228  char
13229  text[MaxTextExtent];
13230 
13231  int
13232  x,
13233  y;
13234 
13235  unsigned int
13236  x_offset,
13237  y_offset;
13238 
13239  /*
13240  User specified a pan position offset.
13241  */
13242  x_offset=windows->image.width;
13243  y_offset=windows->image.height;
13244  if (image->montage != (char *) NULL)
13245  (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13246  switch ((int) key_symbol)
13247  {
13248  case XK_Home:
13249  case XK_KP_Home:
13250  {
13251  windows->image.x=(int) windows->image.width/2;
13252  windows->image.y=(int) windows->image.height/2;
13253  break;
13254  }
13255  case XK_Left:
13256  case XK_KP_Left:
13257  {
13258  windows->image.x-=x_offset;
13259  break;
13260  }
13261  case XK_Next:
13262  case XK_Up:
13263  case XK_KP_Up:
13264  {
13265  windows->image.y-=y_offset;
13266  break;
13267  }
13268  case XK_Right:
13269  case XK_KP_Right:
13270  {
13271  windows->image.x+=x_offset;
13272  break;
13273  }
13274  case XK_Prior:
13275  case XK_Down:
13276  case XK_KP_Down:
13277  {
13278  windows->image.y+=y_offset;
13279  break;
13280  }
13281  default:
13282  return;
13283  }
13284  /*
13285  Check boundary conditions.
13286  */
13287  if (windows->image.x < 0)
13288  windows->image.x=0;
13289  else
13290  if ((int) (windows->image.x+windows->image.width) >
13291  windows->image.ximage->width)
13292  windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13293  if (windows->image.y < 0)
13294  windows->image.y=0;
13295  else
13296  if ((int) (windows->image.y+windows->image.height) >
13297  windows->image.ximage->height)
13298  windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13299  /*
13300  Refresh Image window.
13301  */
13302  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13303  windows->image.width,windows->image.height,windows->image.x,
13304  windows->image.y);
13305  XInfoWidget(display,windows,text);
13306  XCheckRefreshWindows(display,windows);
13307  XDrawPanRectangle(display,windows);
13308  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13309  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13310 }
13311 
13312 /*
13313 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13314 % %
13315 % %
13316 % %
13317 + X T r i m I m a g e %
13318 % %
13319 % %
13320 % %
13321 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13322 %
13323 % XTrimImage() trims the edges from the Image window.
13324 %
13325 % The format of the XTrimImage method is:
13326 %
13327 % MagickBooleanType XTrimImage(Display *display,
13328 % XResourceInfo *resource_info,XWindows *windows,Image *image)
13329 %
13330 % A description of each parameter follows:
13331 %
13332 % o display: Specifies a connection to an X server; returned from
13333 % XOpenDisplay.
13334 %
13335 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13336 %
13337 % o windows: Specifies a pointer to a XWindows structure.
13338 %
13339 % o image: the image.
13340 %
13341 */
13342 static MagickBooleanType XTrimImage(Display *display,
13343  XResourceInfo *resource_info,XWindows *windows,Image *image)
13344 {
13346  trim_info;
13347 
13348  int
13349  x,
13350  y;
13351 
13352  size_t
13353  background,
13354  pixel;
13355 
13356  /*
13357  Trim edges from image.
13358  */
13359  XSetCursorState(display,windows,MagickTrue);
13360  XCheckRefreshWindows(display,windows);
13361  /*
13362  Crop the left edge.
13363  */
13364  background=XGetPixel(windows->image.ximage,0,0);
13365  trim_info.width=(size_t) windows->image.ximage->width;
13366  for (x=0; x < windows->image.ximage->width; x++)
13367  {
13368  for (y=0; y < windows->image.ximage->height; y++)
13369  {
13370  pixel=XGetPixel(windows->image.ximage,x,y);
13371  if (pixel != background)
13372  break;
13373  }
13374  if (y < windows->image.ximage->height)
13375  break;
13376  }
13377  trim_info.x=(ssize_t) x;
13378  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13379  {
13380  XSetCursorState(display,windows,MagickFalse);
13381  return(MagickFalse);
13382  }
13383  /*
13384  Crop the right edge.
13385  */
13386  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13387  for (x=windows->image.ximage->width-1; x != 0; x--)
13388  {
13389  for (y=0; y < windows->image.ximage->height; y++)
13390  {
13391  pixel=XGetPixel(windows->image.ximage,x,y);
13392  if (pixel != background)
13393  break;
13394  }
13395  if (y < windows->image.ximage->height)
13396  break;
13397  }
13398  trim_info.width=(size_t) (x-trim_info.x+1);
13399  /*
13400  Crop the top edge.
13401  */
13402  background=XGetPixel(windows->image.ximage,0,0);
13403  trim_info.height=(size_t) windows->image.ximage->height;
13404  for (y=0; y < windows->image.ximage->height; y++)
13405  {
13406  for (x=0; x < windows->image.ximage->width; x++)
13407  {
13408  pixel=XGetPixel(windows->image.ximage,x,y);
13409  if (pixel != background)
13410  break;
13411  }
13412  if (x < windows->image.ximage->width)
13413  break;
13414  }
13415  trim_info.y=(ssize_t) y;
13416  /*
13417  Crop the bottom edge.
13418  */
13419  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13420  for (y=windows->image.ximage->height-1; y != 0; y--)
13421  {
13422  for (x=0; x < windows->image.ximage->width; x++)
13423  {
13424  pixel=XGetPixel(windows->image.ximage,x,y);
13425  if (pixel != background)
13426  break;
13427  }
13428  if (x < windows->image.ximage->width)
13429  break;
13430  }
13431  trim_info.height=(size_t) y-trim_info.y+1;
13432  if (((unsigned int) trim_info.width != windows->image.width) ||
13433  ((unsigned int) trim_info.height != windows->image.height))
13434  {
13435  /*
13436  Reconfigure Image window as defined by the trimming rectangle.
13437  */
13438  XSetCropGeometry(display,windows,&trim_info,image);
13439  windows->image.window_changes.width=(int) trim_info.width;
13440  windows->image.window_changes.height=(int) trim_info.height;
13441  (void) XConfigureImage(display,resource_info,windows,image);
13442  }
13443  XSetCursorState(display,windows,MagickFalse);
13444  return(MagickTrue);
13445 }
13446 
13447 /*
13448 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13449 % %
13450 % %
13451 % %
13452 + X V i s u a l D i r e c t o r y I m a g e %
13453 % %
13454 % %
13455 % %
13456 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13457 %
13458 % XVisualDirectoryImage() creates a Visual Image Directory.
13459 %
13460 % The format of the XVisualDirectoryImage method is:
13461 %
13462 % Image *XVisualDirectoryImage(Display *display,
13463 % XResourceInfo *resource_info,XWindows *windows)
13464 %
13465 % A description of each parameter follows:
13466 %
13467 % o nexus: Method XVisualDirectoryImage returns a visual image
13468 % directory if it can be created successfully. Otherwise a null image
13469 % is returned.
13470 %
13471 % o display: Specifies a connection to an X server; returned from
13472 % XOpenDisplay.
13473 %
13474 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13475 %
13476 % o windows: Specifies a pointer to a XWindows structure.
13477 %
13478 */
13479 static Image *XVisualDirectoryImage(Display *display,
13480  XResourceInfo *resource_info,XWindows *windows)
13481 {
13482 #define TileImageTag "Scale/Image"
13483 #define XClientName "montage"
13484 
13485  char
13486  **filelist;
13487 
13489  *exception;
13490 
13491  Image
13492  *images,
13493  *montage_image,
13494  *next_image,
13495  *thumbnail_image;
13496 
13497  ImageInfo
13498  *read_info;
13499 
13500  int
13501  number_files;
13502 
13503  MagickBooleanType
13504  backdrop;
13505 
13506  MagickStatusType
13507  status;
13508 
13509  MontageInfo
13510  *montage_info;
13511 
13513  geometry;
13514 
13515  int
13516  i;
13517 
13518  static char
13519  filename[MaxTextExtent] = "\0",
13520  filenames[MaxTextExtent] = "*";
13521 
13522  XResourceInfo
13523  background_resources;
13524 
13525  /*
13526  Request file name from user.
13527  */
13528  XFileBrowserWidget(display,windows,"Directory",filenames);
13529  if (*filenames == '\0')
13530  return((Image *) NULL);
13531  /*
13532  Expand the filenames.
13533  */
13534  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13535  if (filelist == (char **) NULL)
13536  {
13537  ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13538  filenames);
13539  return((Image *) NULL);
13540  }
13541  number_files=1;
13542  filelist[0]=filenames;
13543  status=ExpandFilenames(&number_files,&filelist);
13544  if ((status == MagickFalse) || (number_files == 0))
13545  {
13546  if (number_files == 0)
13547  ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13548  else
13549  ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13550  filenames);
13551  return((Image *) NULL);
13552  }
13553  /*
13554  Set image background resources.
13555  */
13556  background_resources=(*resource_info);
13557  background_resources.window_id=AcquireString("");
13558  (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13559  "0x%lx",windows->image.id);
13560  background_resources.backdrop=MagickTrue;
13561  /*
13562  Read each image and convert them to a tile.
13563  */
13564  backdrop=(windows->visual_info->klass == TrueColor) ||
13565  (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13566  read_info=CloneImageInfo(resource_info->image_info);
13567  (void) SetImageOption(read_info,"jpeg:size","120x120");
13568  (void) CloneString(&read_info->size,DefaultTileGeometry);
13569  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13570  (void *) NULL);
13571  images=NewImageList();
13572  exception=AcquireExceptionInfo();
13573  XSetCursorState(display,windows,MagickTrue);
13574  XCheckRefreshWindows(display,windows);
13575  for (i=0; i < (int) number_files; i++)
13576  {
13577  (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13578  filelist[i]=DestroyString(filelist[i]);
13579  *read_info->magick='\0';
13580  next_image=ReadImage(read_info,exception);
13581  CatchException(exception);
13582  if (next_image != (Image *) NULL)
13583  {
13584  (void) DeleteImageProperty(next_image,"label");
13585  (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13586  read_info,next_image,DefaultTileLabel));
13587  (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13588  exception);
13589  thumbnail_image=ThumbnailImage(next_image,geometry.width,
13590  geometry.height,exception);
13591  if (thumbnail_image != (Image *) NULL)
13592  {
13593  next_image=DestroyImage(next_image);
13594  next_image=thumbnail_image;
13595  }
13596  if (backdrop)
13597  {
13598  (void) XDisplayBackgroundImage(display,&background_resources,
13599  next_image);
13600  XSetCursorState(display,windows,MagickTrue);
13601  }
13602  AppendImageToList(&images,next_image);
13603  if (images->progress_monitor != (MagickProgressMonitor) NULL)
13604  {
13605  MagickBooleanType
13606  proceed;
13607 
13608  proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13609  (MagickSizeType) number_files);
13610  if (proceed == MagickFalse)
13611  break;
13612  }
13613  }
13614  }
13615  exception=DestroyExceptionInfo(exception);
13616  filelist=(char **) RelinquishMagickMemory(filelist);
13617  if (images == (Image *) NULL)
13618  {
13619  read_info=DestroyImageInfo(read_info);
13620  XSetCursorState(display,windows,MagickFalse);
13621  ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13622  return((Image *) NULL);
13623  }
13624  /*
13625  Create the Visual Image Directory.
13626  */
13627  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13628  montage_info->pointsize=10;
13629  if (resource_info->font != (char *) NULL)
13630  (void) CloneString(&montage_info->font,resource_info->font);
13631  (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13632  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13633  images),&images->exception);
13634  images=DestroyImageList(images);
13635  montage_info=DestroyMontageInfo(montage_info);
13636  read_info=DestroyImageInfo(read_info);
13637  XSetCursorState(display,windows,MagickFalse);
13638  if (montage_image == (Image *) NULL)
13639  return(montage_image);
13640  XClientMessage(display,windows->image.id,windows->im_protocols,
13641  windows->im_next_image,CurrentTime);
13642  return(montage_image);
13643 }
13644 
13645 /*
13646 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13647 % %
13648 % %
13649 % %
13650 % X D i s p l a y B a c k g r o u n d I m a g e %
13651 % %
13652 % %
13653 % %
13654 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13655 %
13656 % XDisplayBackgroundImage() displays an image in the background of a window.
13657 %
13658 % The format of the XDisplayBackgroundImage method is:
13659 %
13660 % MagickBooleanType XDisplayBackgroundImage(Display *display,
13661 % XResourceInfo *resource_info,Image *image)
13662 %
13663 % A description of each parameter follows:
13664 %
13665 % o display: Specifies a connection to an X server; returned from
13666 % XOpenDisplay.
13667 %
13668 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13669 %
13670 % o image: the image.
13671 %
13672 */
13673 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13674  XResourceInfo *resource_info,Image *image)
13675 {
13676  char
13677  geometry[MaxTextExtent],
13678  visual_type[MaxTextExtent];
13679 
13680  int
13681  height,
13682  status,
13683  width;
13684 
13686  geometry_info;
13687 
13688  static XPixelInfo
13689  pixel;
13690 
13691  static XStandardColormap
13692  *map_info;
13693 
13694  static XVisualInfo
13695  *visual_info = (XVisualInfo *) NULL;
13696 
13697  static XWindowInfo
13698  window_info;
13699 
13700  size_t
13701  delay;
13702 
13703  Window
13704  root_window;
13705 
13706  XGCValues
13707  context_values;
13708 
13709  XResourceInfo
13710  resources;
13711 
13712  XWindowAttributes
13713  window_attributes;
13714 
13715  /*
13716  Determine target window.
13717  */
13718  assert(image != (Image *) NULL);
13719  assert(image->signature == MagickCoreSignature);
13720  if (IsEventLogging() != MagickFalse)
13721  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13722  resources=(*resource_info);
13723  window_info.id=(Window) NULL;
13724  root_window=XRootWindow(display,XDefaultScreen(display));
13725  if (LocaleCompare(resources.window_id,"root") == 0)
13726  window_info.id=root_window;
13727  else
13728  {
13729  if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13730  window_info.id=XWindowByID(display,root_window,
13731  (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13732  if (window_info.id == (Window) NULL)
13733  window_info.id=XWindowByName(display,root_window,resources.window_id);
13734  }
13735  if (window_info.id == (Window) NULL)
13736  {
13737  ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13738  resources.window_id);
13739  }
13740  /*
13741  Determine window visual id.
13742  */
13743  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13744  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13745  (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13746  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13747  if (status != 0)
13748  (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13749  XVisualIDFromVisual(window_attributes.visual));
13750  if (visual_info == (XVisualInfo *) NULL)
13751  {
13752  /*
13753  Allocate standard colormap.
13754  */
13755  map_info=XAllocStandardColormap();
13756  if (map_info == (XStandardColormap *) NULL)
13757  ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13758  image->filename);
13759  map_info->colormap=(Colormap) NULL;
13760  pixel.pixels=(unsigned long *) NULL;
13761  /*
13762  Initialize visual info.
13763  */
13764  resources.map_type=(char *) NULL;
13765  resources.visual_type=visual_type;
13766  visual_info=XBestVisualInfo(display,map_info,&resources);
13767  if (visual_info == (XVisualInfo *) NULL)
13768  ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13769  resources.visual_type);
13770  /*
13771  Initialize window info.
13772  */
13773  window_info.ximage=(XImage *) NULL;
13774  window_info.matte_image=(XImage *) NULL;
13775  window_info.pixmap=(Pixmap) NULL;
13776  window_info.matte_pixmap=(Pixmap) NULL;
13777  }
13778  /*
13779  Free previous root colors.
13780  */
13781  if (window_info.id == root_window)
13782  (void) XDestroyWindowColors(display,root_window);
13783  /*
13784  Initialize Standard Colormap.
13785  */
13786  resources.colormap=SharedColormap;
13787  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13788  /*
13789  Graphic context superclass.
13790  */
13791  context_values.background=pixel.foreground_color.pixel;
13792  context_values.foreground=pixel.background_color.pixel;
13793  pixel.annotate_context=XCreateGC(display,window_info.id,
13794  (size_t) (GCBackground | GCForeground),&context_values);
13795  if (pixel.annotate_context == (GC) NULL)
13796  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13797  image->filename);
13798  /*
13799  Initialize Image window attributes.
13800  */
13801  window_info.name=AcquireString("\0");
13802  window_info.icon_name=AcquireString("\0");
13803  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13804  &resources,&window_info);
13805  /*
13806  Create the X image.
13807  */
13808  window_info.width=(unsigned int) image->columns;
13809  window_info.height=(unsigned int) image->rows;
13810  if ((image->columns != window_info.width) ||
13811  (image->rows != window_info.height))
13812  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13813  image->filename);
13814  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13815  window_attributes.width,window_attributes.height);
13816  geometry_info.width=window_info.width;
13817  geometry_info.height=window_info.height;
13818  geometry_info.x=(ssize_t) window_info.x;
13819  geometry_info.y=(ssize_t) window_info.y;
13820  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13821  &geometry_info.width,&geometry_info.height);
13822  window_info.width=(unsigned int) geometry_info.width;
13823  window_info.height=(unsigned int) geometry_info.height;
13824  window_info.x=(int) geometry_info.x;
13825  window_info.y=(int) geometry_info.y;
13826  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13827  window_info.height);
13828  if (status == MagickFalse)
13829  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13830  image->filename);
13831  window_info.x=0;
13832  window_info.y=0;
13833  if (resource_info->debug != MagickFalse)
13834  {
13835  (void) LogMagickEvent(X11Event,GetMagickModule(),
13836  "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13837  (double) image->columns,(double) image->rows);
13838  if (image->colors != 0)
13839  (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13840  image->colors);
13841  (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13842  }
13843  /*
13844  Adjust image dimensions as specified by backdrop or geometry options.
13845  */
13846  width=(int) window_info.width;
13847  height=(int) window_info.height;
13848  if (resources.backdrop != MagickFalse)
13849  {
13850  /*
13851  Center image on window.
13852  */
13853  window_info.x=(window_attributes.width/2)-(window_info.ximage->width/2);
13854  window_info.y=(window_attributes.height/2)-(window_info.ximage->height/2);
13855  width=window_attributes.width;
13856  height=window_attributes.height;
13857  }
13858  if ((resources.image_geometry != (char *) NULL) &&
13859  (*resources.image_geometry != '\0'))
13860  {
13861  char
13862  default_geometry[MaxTextExtent];
13863 
13864  int
13865  flags,
13866  gravity;
13867 
13868  XSizeHints
13869  *size_hints;
13870 
13871  /*
13872  User specified geometry.
13873  */
13874  size_hints=XAllocSizeHints();
13875  if (size_hints == (XSizeHints *) NULL)
13876  ThrowXWindowFatalException(ResourceLimitFatalError,
13877  "MemoryAllocationFailed",image->filename);
13878  size_hints->flags=0L;
13879  (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13880  width,height);
13881  flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13882  default_geometry,window_info.border_width,size_hints,&window_info.x,
13883  &window_info.y,&width,&height,&gravity);
13884  if (flags & (XValue | YValue))
13885  {
13886  width=window_attributes.width;
13887  height=window_attributes.height;
13888  }
13889  (void) XFree((void *) size_hints);
13890  }
13891  /*
13892  Create the X pixmap.
13893  */
13894  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13895  (unsigned int) height,window_info.depth);
13896  if (window_info.pixmap == (Pixmap) NULL)
13897  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13898  image->filename);
13899  /*
13900  Display pixmap on the window.
13901  */
13902  if (((unsigned int) width > window_info.width) ||
13903  ((unsigned int) height > window_info.height))
13904  (void) XFillRectangle(display,window_info.pixmap,
13905  window_info.annotate_context,0,0,(unsigned int) width,
13906  (unsigned int) height);
13907  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13908  window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13909  window_info.width,(unsigned int) window_info.height);
13910  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13911  (void) XClearWindow(display,window_info.id);
13912  delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13913  XDelay(display,delay == 0UL ? 10UL : delay);
13914  (void) XSync(display,MagickFalse);
13915  return(window_info.id == root_window ? MagickTrue : MagickFalse);
13916 }
13917 
13918 /*
13919 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13920 % %
13921 % %
13922 % %
13923 + X D i s p l a y I m a g e %
13924 % %
13925 % %
13926 % %
13927 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13928 %
13929 % XDisplayImage() displays an image via X11. A new image is created and
13930 % returned if the user interactively transforms the displayed image.
13931 %
13932 % The format of the XDisplayImage method is:
13933 %
13934 % Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13935 % char **argv,int argc,Image **image,size_t *state)
13936 %
13937 % A description of each parameter follows:
13938 %
13939 % o nexus: Method XDisplayImage returns an image when the
13940 % user chooses 'Open Image' from the command menu or picks a tile
13941 % from the image directory. Otherwise a null image is returned.
13942 %
13943 % o display: Specifies a connection to an X server; returned from
13944 % XOpenDisplay.
13945 %
13946 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13947 %
13948 % o argv: Specifies the application's argument list.
13949 %
13950 % o argc: Specifies the number of arguments.
13951 %
13952 % o image: Specifies an address to an address of an Image structure;
13953 %
13954 */
13955 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13956  char **argv,int argc,Image **image,size_t *state)
13957 {
13958 #define MagnifySize 256 /* must be a power of 2 */
13959 #define MagickMenus 10
13960 #define MagickTitle "Commands"
13961 
13962  const char
13963  *const CommandMenu[] =
13964  {
13965  "File",
13966  "Edit",
13967  "View",
13968  "Transform",
13969  "Enhance",
13970  "Effects",
13971  "F/X",
13972  "Image Edit",
13973  "Miscellany",
13974  "Help",
13975  (char *) NULL
13976  },
13977  *const FileMenu[] =
13978  {
13979  "Open...",
13980  "Next",
13981  "Former",
13982  "Select...",
13983  "Save...",
13984  "Print...",
13985  "Delete...",
13986  "New...",
13987  "Visual Directory...",
13988  "Quit",
13989  (char *) NULL
13990  },
13991  *const EditMenu[] =
13992  {
13993  "Undo",
13994  "Redo",
13995  "Cut",
13996  "Copy",
13997  "Paste",
13998  (char *) NULL
13999  },
14000  *const ViewMenu[] =
14001  {
14002  "Half Size",
14003  "Original Size",
14004  "Double Size",
14005  "Resize...",
14006  "Apply",
14007  "Refresh",
14008  "Restore",
14009  (char *) NULL
14010  },
14011  *const TransformMenu[] =
14012  {
14013  "Crop",
14014  "Chop",
14015  "Flop",
14016  "Flip",
14017  "Rotate Right",
14018  "Rotate Left",
14019  "Rotate...",
14020  "Shear...",
14021  "Roll...",
14022  "Trim Edges",
14023  (char *) NULL
14024  },
14025  *const EnhanceMenu[] =
14026  {
14027  "Hue...",
14028  "Saturation...",
14029  "Brightness...",
14030  "Gamma...",
14031  "Spiff",
14032  "Dull",
14033  "Contrast Stretch...",
14034  "Sigmoidal Contrast...",
14035  "Normalize",
14036  "Equalize",
14037  "Negate",
14038  "Grayscale",
14039  "Map...",
14040  "Quantize...",
14041  (char *) NULL
14042  },
14043  *const EffectsMenu[] =
14044  {
14045  "Despeckle",
14046  "Emboss",
14047  "Reduce Noise",
14048  "Add Noise...",
14049  "Sharpen...",
14050  "Blur...",
14051  "Threshold...",
14052  "Edge Detect...",
14053  "Spread...",
14054  "Shade...",
14055  "Raise...",
14056  "Segment...",
14057  (char *) NULL
14058  },
14059  *const FXMenu[] =
14060  {
14061  "Solarize...",
14062  "Sepia Tone...",
14063  "Swirl...",
14064  "Implode...",
14065  "Vignette...",
14066  "Wave...",
14067  "Oil Paint...",
14068  "Charcoal Draw...",
14069  (char *) NULL
14070  },
14071  *const ImageEditMenu[] =
14072  {
14073  "Annotate...",
14074  "Draw...",
14075  "Color...",
14076  "Matte...",
14077  "Composite...",
14078  "Add Border...",
14079  "Add Frame...",
14080  "Comment...",
14081  "Launch...",
14082  "Region of Interest...",
14083  (char *) NULL
14084  },
14085  *const MiscellanyMenu[] =
14086  {
14087  "Image Info",
14088  "Zoom Image",
14089  "Show Preview...",
14090  "Show Histogram",
14091  "Show Matte",
14092  "Background...",
14093  "Slide Show...",
14094  "Preferences...",
14095  (char *) NULL
14096  },
14097  *const HelpMenu[] =
14098  {
14099  "Overview",
14100  "Browse Documentation",
14101  "About Display",
14102  (char *) NULL
14103  },
14104  *const ShortCutsMenu[] =
14105  {
14106  "Next",
14107  "Former",
14108  "Open...",
14109  "Save...",
14110  "Print...",
14111  "Undo",
14112  "Restore",
14113  "Image Info",
14114  "Quit",
14115  (char *) NULL
14116  },
14117  *const VirtualMenu[] =
14118  {
14119  "Image Info",
14120  "Print",
14121  "Next",
14122  "Quit",
14123  (char *) NULL
14124  };
14125 
14126  const char
14127  *const *Menus[MagickMenus] =
14128  {
14129  FileMenu,
14130  EditMenu,
14131  ViewMenu,
14132  TransformMenu,
14133  EnhanceMenu,
14134  EffectsMenu,
14135  FXMenu,
14136  ImageEditMenu,
14137  MiscellanyMenu,
14138  HelpMenu
14139  };
14140 
14141  static CommandType
14142  CommandMenus[] =
14143  {
14144  NullCommand,
14145  NullCommand,
14146  NullCommand,
14147  NullCommand,
14148  NullCommand,
14149  NullCommand,
14150  NullCommand,
14151  NullCommand,
14152  NullCommand,
14153  NullCommand,
14154  },
14155  FileCommands[] =
14156  {
14157  OpenCommand,
14158  NextCommand,
14159  FormerCommand,
14160  SelectCommand,
14161  SaveCommand,
14162  PrintCommand,
14163  DeleteCommand,
14164  NewCommand,
14165  VisualDirectoryCommand,
14166  QuitCommand
14167  },
14168  EditCommands[] =
14169  {
14170  UndoCommand,
14171  RedoCommand,
14172  CutCommand,
14173  CopyCommand,
14174  PasteCommand
14175  },
14176  ViewCommands[] =
14177  {
14178  HalfSizeCommand,
14179  OriginalSizeCommand,
14180  DoubleSizeCommand,
14181  ResizeCommand,
14182  ApplyCommand,
14183  RefreshCommand,
14184  RestoreCommand
14185  },
14186  TransformCommands[] =
14187  {
14188  CropCommand,
14189  ChopCommand,
14190  FlopCommand,
14191  FlipCommand,
14192  RotateRightCommand,
14193  RotateLeftCommand,
14194  RotateCommand,
14195  ShearCommand,
14196  RollCommand,
14197  TrimCommand
14198  },
14199  EnhanceCommands[] =
14200  {
14201  HueCommand,
14202  SaturationCommand,
14203  BrightnessCommand,
14204  GammaCommand,
14205  SpiffCommand,
14206  DullCommand,
14207  ContrastStretchCommand,
14208  SigmoidalContrastCommand,
14209  NormalizeCommand,
14210  EqualizeCommand,
14211  NegateCommand,
14212  GrayscaleCommand,
14213  MapCommand,
14214  QuantizeCommand
14215  },
14216  EffectsCommands[] =
14217  {
14218  DespeckleCommand,
14219  EmbossCommand,
14220  ReduceNoiseCommand,
14221  AddNoiseCommand,
14222  SharpenCommand,
14223  BlurCommand,
14224  ThresholdCommand,
14225  EdgeDetectCommand,
14226  SpreadCommand,
14227  ShadeCommand,
14228  RaiseCommand,
14229  SegmentCommand
14230  },
14231  FXCommands[] =
14232  {
14233  SolarizeCommand,
14234  SepiaToneCommand,
14235  SwirlCommand,
14236  ImplodeCommand,
14237  VignetteCommand,
14238  WaveCommand,
14239  OilPaintCommand,
14240  CharcoalDrawCommand
14241  },
14242  ImageEditCommands[] =
14243  {
14244  AnnotateCommand,
14245  DrawCommand,
14246  ColorCommand,
14247  MatteCommand,
14248  CompositeCommand,
14249  AddBorderCommand,
14250  AddFrameCommand,
14251  CommentCommand,
14252  LaunchCommand,
14253  RegionOfInterestCommand
14254  },
14255  MiscellanyCommands[] =
14256  {
14257  InfoCommand,
14258  ZoomCommand,
14259  ShowPreviewCommand,
14260  ShowHistogramCommand,
14261  ShowMatteCommand,
14262  BackgroundCommand,
14263  SlideShowCommand,
14264  PreferencesCommand
14265  },
14266  HelpCommands[] =
14267  {
14268  HelpCommand,
14269  BrowseDocumentationCommand,
14270  VersionCommand
14271  },
14272  ShortCutsCommands[] =
14273  {
14274  NextCommand,
14275  FormerCommand,
14276  OpenCommand,
14277  SaveCommand,
14278  PrintCommand,
14279  UndoCommand,
14280  RestoreCommand,
14281  InfoCommand,
14282  QuitCommand
14283  },
14284  VirtualCommands[] =
14285  {
14286  InfoCommand,
14287  PrintCommand,
14288  NextCommand,
14289  QuitCommand
14290  };
14291 
14292  static CommandType
14293  *Commands[MagickMenus] =
14294  {
14295  FileCommands,
14296  EditCommands,
14297  ViewCommands,
14298  TransformCommands,
14299  EnhanceCommands,
14300  EffectsCommands,
14301  FXCommands,
14302  ImageEditCommands,
14303  MiscellanyCommands,
14304  HelpCommands
14305  };
14306 
14307  char
14308  command[MaxTextExtent],
14309  *directory,
14310  geometry[MaxTextExtent],
14311  resource_name[MaxTextExtent];
14312 
14313  CommandType
14314  command_type;
14315 
14316  Image
14317  *display_image,
14318  *nexus;
14319 
14320  int
14321  entry,
14322  id;
14323 
14324  KeySym
14325  key_symbol;
14326 
14327  MagickStatusType
14328  context_mask,
14329  status;
14330 
14332  geometry_info;
14333 
14334  int
14335  i;
14336 
14337  static char
14338  working_directory[MaxTextExtent];
14339 
14340  static XPoint
14341  vid_info;
14342 
14343  static XWindowInfo
14344  *magick_windows[MaxXWindows];
14345 
14346  static unsigned int
14347  number_windows;
14348 
14349  struct stat
14350  attributes;
14351 
14352  time_t
14353  timer,
14354  timestamp,
14355  update_time;
14356 
14357  unsigned int
14358  height,
14359  width;
14360 
14361  size_t
14362  delay;
14363 
14364  WarningHandler
14365  warning_handler;
14366 
14367  Window
14368  root_window;
14369 
14370  XClassHint
14371  *class_hints;
14372 
14373  XEvent
14374  event;
14375 
14376  XFontStruct
14377  *font_info;
14378 
14379  XGCValues
14380  context_values;
14381 
14382  XPixelInfo
14383  *icon_pixel,
14384  *pixel;
14385 
14386  XResourceInfo
14387  *icon_resources;
14388 
14389  XStandardColormap
14390  *icon_map,
14391  *map_info;
14392 
14393  XVisualInfo
14394  *icon_visual,
14395  *visual_info;
14396 
14397  XWindowChanges
14398  window_changes;
14399 
14400  XWindows
14401  *windows;
14402 
14403  XWMHints
14404  *manager_hints;
14405 
14406  assert(image != (Image **) NULL);
14407  assert((*image)->signature == MagickCoreSignature);
14408  if (IsEventLogging() != MagickFalse)
14409  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14410  display_image=(*image);
14411  warning_handler=(WarningHandler) NULL;
14412  windows=XSetWindows((XWindows *) ~0);
14413  if (windows != (XWindows *) NULL)
14414  {
14415  int
14416  status;
14417 
14418  if (*working_directory == '\0')
14419  (void) CopyMagickString(working_directory,".",MaxTextExtent);
14420  status=chdir(working_directory);
14421  if (status == -1)
14422  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
14423  FileOpenError,"UnableToOpenFile","%s",working_directory);
14424  warning_handler=resource_info->display_warnings ?
14425  SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14426  warning_handler=resource_info->display_warnings ?
14427  SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14428  }
14429  else
14430  {
14431  /*
14432  Allocate windows structure.
14433  */
14434  resource_info->colors=display_image->colors;
14435  windows=XSetWindows(XInitializeWindows(display,resource_info));
14436  if (windows == (XWindows *) NULL)
14437  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14438  (*image)->filename);
14439  /*
14440  Initialize window id's.
14441  */
14442  number_windows=0;
14443  magick_windows[number_windows++]=(&windows->icon);
14444  magick_windows[number_windows++]=(&windows->backdrop);
14445  magick_windows[number_windows++]=(&windows->image);
14446  magick_windows[number_windows++]=(&windows->info);
14447  magick_windows[number_windows++]=(&windows->command);
14448  magick_windows[number_windows++]=(&windows->widget);
14449  magick_windows[number_windows++]=(&windows->popup);
14450  magick_windows[number_windows++]=(&windows->magnify);
14451  magick_windows[number_windows++]=(&windows->pan);
14452  for (i=0; i < (int) number_windows; i++)
14453  magick_windows[i]->id=(Window) NULL;
14454  vid_info.x=0;
14455  vid_info.y=0;
14456  }
14457  /*
14458  Initialize font info.
14459  */
14460  if (windows->font_info != (XFontStruct *) NULL)
14461  (void) XFreeFont(display,windows->font_info);
14462  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14463  if (windows->font_info == (XFontStruct *) NULL)
14464  ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14465  resource_info->font);
14466  /*
14467  Initialize Standard Colormap.
14468  */
14469  map_info=windows->map_info;
14470  icon_map=windows->icon_map;
14471  visual_info=windows->visual_info;
14472  icon_visual=windows->icon_visual;
14473  pixel=windows->pixel_info;
14474  icon_pixel=windows->icon_pixel;
14475  font_info=windows->font_info;
14476  icon_resources=windows->icon_resources;
14477  class_hints=windows->class_hints;
14478  manager_hints=windows->manager_hints;
14479  root_window=XRootWindow(display,visual_info->screen);
14480  nexus=NewImageList();
14481  if (resource_info->debug != MagickFalse)
14482  {
14483  (void) LogMagickEvent(X11Event,GetMagickModule(),
14484  "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14485  (double) display_image->scene,(double) display_image->columns,
14486  (double) display_image->rows);
14487  if (display_image->colors != 0)
14488  (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14489  display_image->colors);
14490  (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14491  display_image->magick);
14492  }
14493  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14494  map_info,pixel);
14495  display_image->taint=MagickFalse;
14496  /*
14497  Initialize graphic context.
14498  */
14499  windows->context.id=(Window) NULL;
14500  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14501  resource_info,&windows->context);
14502  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14503  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14504  class_hints->res_class[0]=(char) LocaleToUppercase((int)
14505  class_hints->res_class[0]);
14506  manager_hints->flags=InputHint | StateHint;
14507  manager_hints->input=MagickFalse;
14508  manager_hints->initial_state=WithdrawnState;
14509  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14510  &windows->context);
14511  if (resource_info->debug != MagickFalse)
14512  (void) LogMagickEvent(X11Event,GetMagickModule(),
14513  "Window id: 0x%lx (context)",windows->context.id);
14514  context_values.background=pixel->background_color.pixel;
14515  context_values.font=font_info->fid;
14516  context_values.foreground=pixel->foreground_color.pixel;
14517  context_values.graphics_exposures=MagickFalse;
14518  context_mask=(MagickStatusType)
14519  (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14520  if (pixel->annotate_context != (GC) NULL)
14521  (void) XFreeGC(display,pixel->annotate_context);
14522  pixel->annotate_context=XCreateGC(display,windows->context.id,
14523  context_mask,&context_values);
14524  if (pixel->annotate_context == (GC) NULL)
14525  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14526  display_image->filename);
14527  context_values.background=pixel->depth_color.pixel;
14528  if (pixel->widget_context != (GC) NULL)
14529  (void) XFreeGC(display,pixel->widget_context);
14530  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14531  &context_values);
14532  if (pixel->widget_context == (GC) NULL)
14533  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14534  display_image->filename);
14535  context_values.background=pixel->foreground_color.pixel;
14536  context_values.foreground=pixel->background_color.pixel;
14537  context_values.plane_mask=context_values.background ^
14538  context_values.foreground;
14539  if (pixel->highlight_context != (GC) NULL)
14540  (void) XFreeGC(display,pixel->highlight_context);
14541  pixel->highlight_context=XCreateGC(display,windows->context.id,
14542  (size_t) (context_mask | GCPlaneMask),&context_values);
14543  if (pixel->highlight_context == (GC) NULL)
14544  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14545  display_image->filename);
14546  (void) XDestroyWindow(display,windows->context.id);
14547  /*
14548  Initialize icon window.
14549  */
14550  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14551  icon_resources,&windows->icon);
14552  windows->icon.geometry=resource_info->icon_geometry;
14553  XBestIconSize(display,&windows->icon,display_image);
14554  windows->icon.attributes.colormap=XDefaultColormap(display,
14555  icon_visual->screen);
14556  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14557  manager_hints->flags=InputHint | StateHint;
14558  manager_hints->input=MagickFalse;
14559  manager_hints->initial_state=IconicState;
14560  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14561  &windows->icon);
14562  if (resource_info->debug != MagickFalse)
14563  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14564  windows->icon.id);
14565  /*
14566  Initialize graphic context for icon window.
14567  */
14568  if (icon_pixel->annotate_context != (GC) NULL)
14569  (void) XFreeGC(display,icon_pixel->annotate_context);
14570  context_values.background=icon_pixel->background_color.pixel;
14571  context_values.foreground=icon_pixel->foreground_color.pixel;
14572  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14573  (size_t) (GCBackground | GCForeground),&context_values);
14574  if (icon_pixel->annotate_context == (GC) NULL)
14575  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14576  display_image->filename);
14577  windows->icon.annotate_context=icon_pixel->annotate_context;
14578  /*
14579  Initialize Image window.
14580  */
14581  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14582  &windows->image);
14583  windows->image.shape=MagickTrue; /* non-rectangular shape hint */
14584  if (resource_info->use_shared_memory == MagickFalse)
14585  windows->image.shared_memory=MagickFalse;
14586  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14587  {
14588  char
14589  *title;
14590 
14591  title=InterpretImageProperties(resource_info->image_info,display_image,
14592  resource_info->title);
14593  (void) CloneString(&windows->image.name,title);
14594  (void) CloneString(&windows->image.icon_name,title);
14595  title=DestroyString(title);
14596  }
14597  else
14598  {
14599  char
14600  filename[MaxTextExtent],
14601  window_name[MaxTextExtent];
14602 
14603  /*
14604  Window name is the base of the filename.
14605  */
14606  GetPathComponent(display_image->magick_filename,TailPath,filename);
14607  if (display_image->scene == 0)
14608  (void) FormatLocaleString(window_name,MaxTextExtent,"%s: %s",
14609  MagickPackageName,filename);
14610  else
14611  (void) FormatLocaleString(window_name,MaxTextExtent,
14612  "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14613  (double) display_image->scene,(double) GetImageListLength(
14614  display_image));
14615  (void) CloneString(&windows->image.name,window_name);
14616  (void) CloneString(&windows->image.icon_name,filename);
14617  }
14618  if (resource_info->immutable)
14619  windows->image.immutable=MagickTrue;
14620  windows->image.use_pixmap=resource_info->use_pixmap;
14621  windows->image.geometry=resource_info->image_geometry;
14622  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14623  XDisplayWidth(display,visual_info->screen),
14624  XDisplayHeight(display,visual_info->screen));
14625  geometry_info.width=display_image->columns;
14626  geometry_info.height=display_image->rows;
14627  geometry_info.x=0;
14628  geometry_info.y=0;
14629  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14630  &geometry_info.width,&geometry_info.height);
14631  windows->image.width=(unsigned int) geometry_info.width;
14632  windows->image.height=(unsigned int) geometry_info.height;
14633  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14634  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14635  KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14636  PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14637  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14638  resource_info,&windows->backdrop);
14639  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14640  {
14641  /*
14642  Initialize backdrop window.
14643  */
14644  windows->backdrop.x=0;
14645  windows->backdrop.y=0;
14646  (void) CloneString(&windows->backdrop.name,"Backdrop");
14647  windows->backdrop.flags=(size_t) (USSize | USPosition);
14648  windows->backdrop.width=(unsigned int)
14649  XDisplayWidth(display,visual_info->screen);
14650  windows->backdrop.height=(unsigned int)
14651  XDisplayHeight(display,visual_info->screen);
14652  windows->backdrop.border_width=0;
14653  windows->backdrop.immutable=MagickTrue;
14654  windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14655  ButtonReleaseMask;
14656  windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14657  StructureNotifyMask;
14658  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14659  manager_hints->icon_window=windows->icon.id;
14660  manager_hints->input=MagickTrue;
14661  manager_hints->initial_state=resource_info->iconic ? IconicState :
14662  NormalState;
14663  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14664  &windows->backdrop);
14665  if (resource_info->debug != MagickFalse)
14666  (void) LogMagickEvent(X11Event,GetMagickModule(),
14667  "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14668  (void) XMapWindow(display,windows->backdrop.id);
14669  (void) XClearWindow(display,windows->backdrop.id);
14670  if (windows->image.id != (Window) NULL)
14671  {
14672  (void) XDestroyWindow(display,windows->image.id);
14673  windows->image.id=(Window) NULL;
14674  }
14675  /*
14676  Position image in the center the backdrop.
14677  */
14678  windows->image.flags|=USPosition;
14679  windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14680  (windows->image.width/2);
14681  windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14682  (windows->image.height/2);
14683  }
14684  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14685  manager_hints->icon_window=windows->icon.id;
14686  manager_hints->input=MagickTrue;
14687  manager_hints->initial_state=resource_info->iconic ? IconicState :
14688  NormalState;
14689  if (windows->group_leader.id != (Window) NULL)
14690  {
14691  /*
14692  Follow the leader.
14693  */
14694  manager_hints->flags|=WindowGroupHint;
14695  manager_hints->window_group=windows->group_leader.id;
14696  (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14697  if (resource_info->debug != MagickFalse)
14698  (void) LogMagickEvent(X11Event,GetMagickModule(),
14699  "Window id: 0x%lx (group leader)",windows->group_leader.id);
14700  }
14701  XMakeWindow(display,
14702  (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14703  argv,argc,class_hints,manager_hints,&windows->image);
14704  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14705  XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14706  if (windows->group_leader.id != (Window) NULL)
14707  (void) XSetTransientForHint(display,windows->image.id,
14708  windows->group_leader.id);
14709  if (resource_info->debug != MagickFalse)
14710  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14711  windows->image.id);
14712  /*
14713  Initialize Info widget.
14714  */
14715  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14716  &windows->info);
14717  (void) CloneString(&windows->info.name,"Info");
14718  (void) CloneString(&windows->info.icon_name,"Info");
14719  windows->info.border_width=1;
14720  windows->info.x=2;
14721  windows->info.y=2;
14722  windows->info.flags|=PPosition;
14723  windows->info.attributes.win_gravity=UnmapGravity;
14724  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14725  StructureNotifyMask;
14726  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14727  manager_hints->input=MagickFalse;
14728  manager_hints->initial_state=NormalState;
14729  manager_hints->window_group=windows->image.id;
14730  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14731  &windows->info);
14732  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14733  windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14734  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14735  windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14736  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14737  if (windows->image.mapped != MagickFalse)
14738  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14739  if (resource_info->debug != MagickFalse)
14740  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14741  windows->info.id);
14742  /*
14743  Initialize Command widget.
14744  */
14745  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14746  resource_info,&windows->command);
14747  windows->command.data=MagickMenus;
14748  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14749  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14750  resource_info->client_name);
14751  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14752  resource_name,"geometry",(char *) NULL);
14753  (void) CloneString(&windows->command.name,MagickTitle);
14754  windows->command.border_width=0;
14755  windows->command.flags|=PPosition;
14756  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14757  ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14758  OwnerGrabButtonMask | StructureNotifyMask;
14759  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14760  manager_hints->input=MagickTrue;
14761  manager_hints->initial_state=NormalState;
14762  manager_hints->window_group=windows->image.id;
14763  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14764  &windows->command);
14765  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14766  windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14767  HighlightHeight);
14768  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14769  windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14770  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14771  if (windows->command.mapped != MagickFalse)
14772  (void) XMapRaised(display,windows->command.id);
14773  if (resource_info->debug != MagickFalse)
14774  (void) LogMagickEvent(X11Event,GetMagickModule(),
14775  "Window id: 0x%lx (command)",windows->command.id);
14776  /*
14777  Initialize Widget window.
14778  */
14779  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14780  resource_info,&windows->widget);
14781  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14782  resource_info->client_name);
14783  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14784  resource_name,"geometry",(char *) NULL);
14785  windows->widget.border_width=0;
14786  windows->widget.flags|=PPosition;
14787  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14788  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14789  KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14790  StructureNotifyMask;
14791  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14792  manager_hints->input=MagickTrue;
14793  manager_hints->initial_state=NormalState;
14794  manager_hints->window_group=windows->image.id;
14795  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14796  &windows->widget);
14797  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14798  windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14799  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14800  windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14801  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14802  if (resource_info->debug != MagickFalse)
14803  (void) LogMagickEvent(X11Event,GetMagickModule(),
14804  "Window id: 0x%lx (widget)",windows->widget.id);
14805  /*
14806  Initialize popup window.
14807  */
14808  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14809  resource_info,&windows->popup);
14810  windows->popup.border_width=0;
14811  windows->popup.flags|=PPosition;
14812  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14813  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14814  KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14815  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14816  manager_hints->input=MagickTrue;
14817  manager_hints->initial_state=NormalState;
14818  manager_hints->window_group=windows->image.id;
14819  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14820  &windows->popup);
14821  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14822  windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14823  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14824  windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14825  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14826  if (resource_info->debug != MagickFalse)
14827  (void) LogMagickEvent(X11Event,GetMagickModule(),
14828  "Window id: 0x%lx (pop up)",windows->popup.id);
14829  /*
14830  Initialize Magnify window and cursor.
14831  */
14832  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14833  resource_info,&windows->magnify);
14834  if (resource_info->use_shared_memory == MagickFalse)
14835  windows->magnify.shared_memory=MagickFalse;
14836  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14837  resource_info->client_name);
14838  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14839  resource_name,"geometry",(char *) NULL);
14840  (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14841  resource_info->magnify);
14842  if (windows->magnify.cursor != (Cursor) NULL)
14843  (void) XFreeCursor(display,windows->magnify.cursor);
14844  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14845  map_info->colormap,resource_info->background_color,
14846  resource_info->foreground_color);
14847  if (windows->magnify.cursor == (Cursor) NULL)
14848  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14849  display_image->filename);
14850  windows->magnify.width=MagnifySize;
14851  windows->magnify.height=MagnifySize;
14852  windows->magnify.flags|=PPosition;
14853  windows->magnify.min_width=MagnifySize;
14854  windows->magnify.min_height=MagnifySize;
14855  windows->magnify.width_inc=MagnifySize;
14856  windows->magnify.height_inc=MagnifySize;
14857  windows->magnify.data=resource_info->magnify;
14858  windows->magnify.attributes.cursor=windows->magnify.cursor;
14859  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14860  ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14861  StructureNotifyMask;
14862  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14863  manager_hints->input=MagickTrue;
14864  manager_hints->initial_state=NormalState;
14865  manager_hints->window_group=windows->image.id;
14866  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14867  &windows->magnify);
14868  if (resource_info->debug != MagickFalse)
14869  (void) LogMagickEvent(X11Event,GetMagickModule(),
14870  "Window id: 0x%lx (magnify)",windows->magnify.id);
14871  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14872  /*
14873  Initialize panning window.
14874  */
14875  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14876  resource_info,&windows->pan);
14877  (void) CloneString(&windows->pan.name,"Pan Icon");
14878  windows->pan.width=windows->icon.width;
14879  windows->pan.height=windows->icon.height;
14880  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14881  resource_info->client_name);
14882  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14883  resource_name,"geometry",(char *) NULL);
14884  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14885  &windows->pan.width,&windows->pan.height);
14886  windows->pan.flags|=PPosition;
14887  windows->pan.immutable=MagickTrue;
14888  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14889  ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14890  StructureNotifyMask;
14891  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14892  manager_hints->input=MagickFalse;
14893  manager_hints->initial_state=NormalState;
14894  manager_hints->window_group=windows->image.id;
14895  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14896  &windows->pan);
14897  if (resource_info->debug != MagickFalse)
14898  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14899  windows->pan.id);
14900  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14901  if (windows->info.mapped != MagickFalse)
14902  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14903  if ((windows->image.mapped == MagickFalse) ||
14904  (windows->backdrop.id != (Window) NULL))
14905  (void) XMapWindow(display,windows->image.id);
14906  /*
14907  Set our progress monitor and warning handlers.
14908  */
14909  if (warning_handler == (WarningHandler) NULL)
14910  {
14911  warning_handler=resource_info->display_warnings ?
14912  SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14913  warning_handler=resource_info->display_warnings ?
14914  SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14915  }
14916  /*
14917  Initialize Image and Magnify X images.
14918  */
14919  windows->image.x=0;
14920  windows->image.y=0;
14921  windows->magnify.shape=MagickFalse;
14922  width=(unsigned int) display_image->columns;
14923  height=(unsigned int) display_image->rows;
14924  if ((display_image->columns != width) || (display_image->rows != height))
14925  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14926  display_image->filename);
14927  status=XMakeImage(display,resource_info,&windows->image,display_image,
14928  width,height);
14929  if (status == MagickFalse)
14930  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14931  display_image->filename);
14932  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14933  windows->magnify.width,windows->magnify.height);
14934  if (status == MagickFalse)
14935  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14936  display_image->filename);
14937  if (windows->magnify.mapped != MagickFalse)
14938  (void) XMapRaised(display,windows->magnify.id);
14939  if (windows->pan.mapped != MagickFalse)
14940  (void) XMapRaised(display,windows->pan.id);
14941  windows->image.window_changes.width=(int) display_image->columns;
14942  windows->image.window_changes.height=(int) display_image->rows;
14943  (void) XConfigureImage(display,resource_info,windows,display_image);
14944  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14945  (void) XSync(display,MagickFalse);
14946  /*
14947  Respond to events.
14948  */
14949  delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
14950  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
14951  update_time=0;
14952  if (resource_info->update != MagickFalse)
14953  {
14954  MagickBooleanType
14955  status;
14956 
14957  /*
14958  Determine when file data was last modified.
14959  */
14960  status=GetPathAttributes(display_image->filename,&attributes);
14961  if (status != MagickFalse)
14962  update_time=attributes.st_mtime;
14963  }
14964  *state&=(~FormerImageState);
14965  *state&=(~MontageImageState);
14966  *state&=(~NextImageState);
14967  do
14968  {
14969  /*
14970  Handle a window event.
14971  */
14972  if (windows->image.mapped != MagickFalse)
14973  if ((display_image->delay != 0) || (resource_info->update != 0))
14974  {
14975  if (timer < GetMagickTime())
14976  {
14977  if (resource_info->update == MagickFalse)
14978  *state|=NextImageState | ExitState;
14979  else
14980  {
14981  MagickBooleanType
14982  status;
14983 
14984  /*
14985  Determine if image file was modified.
14986  */
14987  status=GetPathAttributes(display_image->filename,&attributes);
14988  if (status != MagickFalse)
14989  if (update_time != attributes.st_mtime)
14990  {
14991  /*
14992  Redisplay image.
14993  */
14994  (void) FormatLocaleString(
14995  resource_info->image_info->filename,MaxTextExtent,
14996  "%s:%s",display_image->magick,
14997  display_image->filename);
14998  nexus=ReadImage(resource_info->image_info,
14999  &display_image->exception);
15000  if (nexus != (Image *) NULL)
15001  *state|=NextImageState | ExitState;
15002  }
15003  delay=display_image->delay/MagickMax(
15004  display_image->ticks_per_second,1L);
15005  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15006  }
15007  }
15008  if (XEventsQueued(display,QueuedAfterFlush) == 0)
15009  {
15010  /*
15011  Do not block if delay > 0.
15012  */
15013  XDelay(display,SuspendTime << 2);
15014  continue;
15015  }
15016  }
15017  timestamp=GetMagickTime();
15018  (void) XNextEvent(display,&event);
15019  if (windows->image.stasis == MagickFalse)
15020  windows->image.stasis=(GetMagickTime()-timestamp) > 0 ?
15021  MagickTrue : MagickFalse;
15022  if (windows->magnify.stasis == MagickFalse)
15023  windows->magnify.stasis=(GetMagickTime()-timestamp) > 0 ?
15024  MagickTrue : MagickFalse;
15025  if (event.xany.window == windows->command.id)
15026  {
15027  /*
15028  Select a command from the Command widget.
15029  */
15030  id=XCommandWidget(display,windows,CommandMenu,&event);
15031  if (id < 0)
15032  continue;
15033  (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15034  command_type=CommandMenus[id];
15035  if (id < MagickMenus)
15036  {
15037  /*
15038  Select a command from a pop-up menu.
15039  */
15040  entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15041  command);
15042  if (entry < 0)
15043  continue;
15044  (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15045  command_type=Commands[id][entry];
15046  }
15047  if (command_type != NullCommand)
15048  nexus=XMagickCommand(display,resource_info,windows,command_type,
15049  &display_image);
15050  continue;
15051  }
15052  switch (event.type)
15053  {
15054  case ButtonPress:
15055  {
15056  if (resource_info->debug != MagickFalse)
15057  (void) LogMagickEvent(X11Event,GetMagickModule(),
15058  "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15059  event.xbutton.button,event.xbutton.x,event.xbutton.y);
15060  if ((event.xbutton.button == Button3) &&
15061  (event.xbutton.state & Mod1Mask))
15062  {
15063  /*
15064  Convert Alt-Button3 to Button2.
15065  */
15066  event.xbutton.button=Button2;
15067  event.xbutton.state&=(~Mod1Mask);
15068  }
15069  if (event.xbutton.window == windows->backdrop.id)
15070  {
15071  (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15072  event.xbutton.time);
15073  break;
15074  }
15075  if (event.xbutton.window == windows->image.id)
15076  {
15077  switch (event.xbutton.button)
15078  {
15079  case Button1:
15080  {
15081  if (resource_info->immutable)
15082  {
15083  /*
15084  Select a command from the Virtual menu.
15085  */
15086  entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15087  command);
15088  if (entry >= 0)
15089  nexus=XMagickCommand(display,resource_info,windows,
15090  VirtualCommands[entry],&display_image);
15091  break;
15092  }
15093  /*
15094  Map/unmap Command widget.
15095  */
15096  if (windows->command.mapped != MagickFalse)
15097  (void) XWithdrawWindow(display,windows->command.id,
15098  windows->command.screen);
15099  else
15100  {
15101  (void) XCommandWidget(display,windows,CommandMenu,
15102  (XEvent *) NULL);
15103  (void) XMapRaised(display,windows->command.id);
15104  }
15105  break;
15106  }
15107  case Button2:
15108  {
15109  /*
15110  User pressed the image magnify button.
15111  */
15112  (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15113  &display_image);
15114  XMagnifyImage(display,windows,&event);
15115  break;
15116  }
15117  case Button3:
15118  {
15119  if (resource_info->immutable)
15120  {
15121  /*
15122  Select a command from the Virtual menu.
15123  */
15124  entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15125  command);
15126  if (entry >= 0)
15127  nexus=XMagickCommand(display,resource_info,windows,
15128  VirtualCommands[entry],&display_image);
15129  break;
15130  }
15131  if (display_image->montage != (char *) NULL)
15132  {
15133  /*
15134  Open or delete a tile from a visual image directory.
15135  */
15136  nexus=XTileImage(display,resource_info,windows,
15137  display_image,&event);
15138  if (nexus != (Image *) NULL)
15139  *state|=MontageImageState | NextImageState | ExitState;
15140  vid_info.x=(short int) windows->image.x;
15141  vid_info.y=(short int) windows->image.y;
15142  break;
15143  }
15144  /*
15145  Select a command from the Short Cuts menu.
15146  */
15147  entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15148  command);
15149  if (entry >= 0)
15150  nexus=XMagickCommand(display,resource_info,windows,
15151  ShortCutsCommands[entry],&display_image);
15152  break;
15153  }
15154  case Button4:
15155  {
15156  /*
15157  Wheel up.
15158  */
15159  XTranslateImage(display,windows,*image,XK_Up);
15160  break;
15161  }
15162  case Button5:
15163  {
15164  /*
15165  Wheel down.
15166  */
15167  XTranslateImage(display,windows,*image,XK_Down);
15168  break;
15169  }
15170  default:
15171  break;
15172  }
15173  break;
15174  }
15175  if (event.xbutton.window == windows->magnify.id)
15176  {
15177  const char
15178  *const MagnifyMenu[] =
15179  {
15180  "2",
15181  "4",
15182  "5",
15183  "6",
15184  "7",
15185  "8",
15186  "9",
15187  "3",
15188  (char *) NULL,
15189  };
15190 
15191  int
15192  factor;
15193 
15194  static KeySym
15195  MagnifyCommands[] =
15196  {
15197  XK_2,
15198  XK_4,
15199  XK_5,
15200  XK_6,
15201  XK_7,
15202  XK_8,
15203  XK_9,
15204  XK_3
15205  };
15206 
15207  /*
15208  Select a magnify factor from the pop-up menu.
15209  */
15210  factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15211  if (factor >= 0)
15212  XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15213  break;
15214  }
15215  if (event.xbutton.window == windows->pan.id)
15216  {
15217  switch (event.xbutton.button)
15218  {
15219  case Button4:
15220  {
15221  /*
15222  Wheel up.
15223  */
15224  XTranslateImage(display,windows,*image,XK_Up);
15225  break;
15226  }
15227  case Button5:
15228  {
15229  /*
15230  Wheel down.
15231  */
15232  XTranslateImage(display,windows,*image,XK_Down);
15233  break;
15234  }
15235  default:
15236  {
15237  XPanImage(display,windows,&event);
15238  break;
15239  }
15240  }
15241  break;
15242  }
15243  delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15244  1L);
15245  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15246  break;
15247  }
15248  case ButtonRelease:
15249  {
15250  if (resource_info->debug != MagickFalse)
15251  (void) LogMagickEvent(X11Event,GetMagickModule(),
15252  "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15253  event.xbutton.button,event.xbutton.x,event.xbutton.y);
15254  break;
15255  }
15256  case ClientMessage:
15257  {
15258  if (resource_info->debug != MagickFalse)
15259  (void) LogMagickEvent(X11Event,GetMagickModule(),
15260  "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15261  event.xclient.message_type,event.xclient.format,(unsigned long)
15262  event.xclient.data.l[0]);
15263  if (event.xclient.message_type == windows->im_protocols)
15264  {
15265  if (*event.xclient.data.l == (long) windows->im_update_widget)
15266  {
15267  (void) CloneString(&windows->command.name,MagickTitle);
15268  windows->command.data=MagickMenus;
15269  (void) XCommandWidget(display,windows,CommandMenu,
15270  (XEvent *) NULL);
15271  break;
15272  }
15273  if (*event.xclient.data.l == (long) windows->im_update_colormap)
15274  {
15275  /*
15276  Update graphic context and window colormap.
15277  */
15278  for (i=0; i < (int) number_windows; i++)
15279  {
15280  if (magick_windows[i]->id == windows->icon.id)
15281  continue;
15282  context_values.background=pixel->background_color.pixel;
15283  context_values.foreground=pixel->foreground_color.pixel;
15284  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15285  context_mask,&context_values);
15286  (void) XChangeGC(display,magick_windows[i]->widget_context,
15287  context_mask,&context_values);
15288  context_values.background=pixel->foreground_color.pixel;
15289  context_values.foreground=pixel->background_color.pixel;
15290  context_values.plane_mask=context_values.background ^
15291  context_values.foreground;
15292  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15293  (size_t) (context_mask | GCPlaneMask),
15294  &context_values);
15295  magick_windows[i]->attributes.background_pixel=
15296  pixel->background_color.pixel;
15297  magick_windows[i]->attributes.border_pixel=
15298  pixel->border_color.pixel;
15299  magick_windows[i]->attributes.colormap=map_info->colormap;
15300  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15301  (unsigned long) magick_windows[i]->mask,
15302  &magick_windows[i]->attributes);
15303  }
15304  if (windows->pan.mapped != MagickFalse)
15305  {
15306  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15307  windows->pan.pixmap);
15308  (void) XClearWindow(display,windows->pan.id);
15309  XDrawPanRectangle(display,windows);
15310  }
15311  if (windows->backdrop.id != (Window) NULL)
15312  (void) XInstallColormap(display,map_info->colormap);
15313  break;
15314  }
15315  if (*event.xclient.data.l == (long) windows->im_former_image)
15316  {
15317  *state|=FormerImageState | ExitState;
15318  break;
15319  }
15320  if (*event.xclient.data.l == (long) windows->im_next_image)
15321  {
15322  *state|=NextImageState | ExitState;
15323  break;
15324  }
15325  if (*event.xclient.data.l == (long) windows->im_retain_colors)
15326  {
15327  *state|=RetainColorsState;
15328  break;
15329  }
15330  if (*event.xclient.data.l == (long) windows->im_exit)
15331  {
15332  *state|=ExitState;
15333  break;
15334  }
15335  break;
15336  }
15337  if (event.xclient.message_type == windows->dnd_protocols)
15338  {
15339  Atom
15340  selection,
15341  type;
15342 
15343  int
15344  format,
15345  status;
15346 
15347  unsigned char
15348  *data;
15349 
15350  unsigned long
15351  after,
15352  length;
15353 
15354  /*
15355  Display image named by the Drag-and-Drop selection.
15356  */
15357  if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15358  break;
15359  selection=XInternAtom(display,"DndSelection",MagickFalse);
15360  status=XGetWindowProperty(display,root_window,selection,0L,(long)
15361  MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15362  &length,&after,&data);
15363  if ((status != Success) || (length == 0))
15364  break;
15365  if (*event.xclient.data.l == 2)
15366  {
15367  /*
15368  Offix DND.
15369  */
15370  (void) CopyMagickString(resource_info->image_info->filename,
15371  (char *) data,MaxTextExtent);
15372  }
15373  else
15374  {
15375  /*
15376  XDND.
15377  */
15378  if (strncmp((char *) data, "file:", 5) != 0)
15379  {
15380  (void) XFree((void *) data);
15381  break;
15382  }
15383  (void) CopyMagickString(resource_info->image_info->filename,
15384  ((char *) data)+5,MaxTextExtent);
15385  }
15386  nexus=ReadImage(resource_info->image_info,
15387  &display_image->exception);
15388  CatchException(&display_image->exception);
15389  if (nexus != (Image *) NULL)
15390  *state|=NextImageState | ExitState;
15391  (void) XFree((void *) data);
15392  break;
15393  }
15394  /*
15395  If client window delete message, exit.
15396  */
15397  if (event.xclient.message_type != windows->wm_protocols)
15398  break;
15399  if (*event.xclient.data.l != (long) windows->wm_delete_window)
15400  break;
15401  (void) XWithdrawWindow(display,event.xclient.window,
15402  visual_info->screen);
15403  if (event.xclient.window == windows->image.id)
15404  {
15405  *state|=ExitState;
15406  break;
15407  }
15408  if (event.xclient.window == windows->pan.id)
15409  {
15410  /*
15411  Restore original image size when pan window is deleted.
15412  */
15413  windows->image.window_changes.width=windows->image.ximage->width;
15414  windows->image.window_changes.height=windows->image.ximage->height;
15415  (void) XConfigureImage(display,resource_info,windows,
15416  display_image);
15417  }
15418  break;
15419  }
15420  case ConfigureNotify:
15421  {
15422  if (resource_info->debug != MagickFalse)
15423  (void) LogMagickEvent(X11Event,GetMagickModule(),
15424  "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15425  event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15426  event.xconfigure.y,event.xconfigure.send_event);
15427  if (event.xconfigure.window == windows->image.id)
15428  {
15429  /*
15430  Image window has a new configuration.
15431  */
15432  if (event.xconfigure.send_event != 0)
15433  {
15434  XWindowChanges
15435  window_changes;
15436 
15437  /*
15438  Position the transient windows relative of the Image window.
15439  */
15440  if (windows->command.geometry == (char *) NULL)
15441  if (windows->command.mapped == MagickFalse)
15442  {
15443  windows->command.x=event.xconfigure.x-
15444  windows->command.width-25;
15445  windows->command.y=event.xconfigure.y;
15446  XConstrainWindowPosition(display,&windows->command);
15447  window_changes.x=windows->command.x;
15448  window_changes.y=windows->command.y;
15449  (void) XReconfigureWMWindow(display,windows->command.id,
15450  windows->command.screen,(unsigned int) (CWX | CWY),
15451  &window_changes);
15452  }
15453  if (windows->widget.geometry == (char *) NULL)
15454  if (windows->widget.mapped == MagickFalse)
15455  {
15456  windows->widget.x=event.xconfigure.x+
15457  event.xconfigure.width/10;
15458  windows->widget.y=event.xconfigure.y+
15459  event.xconfigure.height/10;
15460  XConstrainWindowPosition(display,&windows->widget);
15461  window_changes.x=windows->widget.x;
15462  window_changes.y=windows->widget.y;
15463  (void) XReconfigureWMWindow(display,windows->widget.id,
15464  windows->widget.screen,(unsigned int) (CWX | CWY),
15465  &window_changes);
15466  }
15467  if (windows->magnify.geometry == (char *) NULL)
15468  if (windows->magnify.mapped == MagickFalse)
15469  {
15470  windows->magnify.x=event.xconfigure.x+
15471  event.xconfigure.width+25;
15472  windows->magnify.y=event.xconfigure.y;
15473  XConstrainWindowPosition(display,&windows->magnify);
15474  window_changes.x=windows->magnify.x;
15475  window_changes.y=windows->magnify.y;
15476  (void) XReconfigureWMWindow(display,windows->magnify.id,
15477  windows->magnify.screen,(unsigned int) (CWX | CWY),
15478  &window_changes);
15479  }
15480  if (windows->pan.geometry == (char *) NULL)
15481  if (windows->pan.mapped == MagickFalse)
15482  {
15483  windows->pan.x=event.xconfigure.x+
15484  event.xconfigure.width+25;
15485  windows->pan.y=event.xconfigure.y+
15486  windows->magnify.height+50;
15487  XConstrainWindowPosition(display,&windows->pan);
15488  window_changes.x=windows->pan.x;
15489  window_changes.y=windows->pan.y;
15490  (void) XReconfigureWMWindow(display,windows->pan.id,
15491  windows->pan.screen,(unsigned int) (CWX | CWY),
15492  &window_changes);
15493  }
15494  }
15495  if ((event.xconfigure.width == (int) windows->image.width) &&
15496  (event.xconfigure.height == (int) windows->image.height))
15497  break;
15498  windows->image.width=(unsigned int) event.xconfigure.width;
15499  windows->image.height=(unsigned int) event.xconfigure.height;
15500  windows->image.x=0;
15501  windows->image.y=0;
15502  if (display_image->montage != (char *) NULL)
15503  {
15504  windows->image.x=vid_info.x;
15505  windows->image.y=vid_info.y;
15506  }
15507  if ((windows->image.mapped != MagickFalse) &&
15508  (windows->image.stasis != MagickFalse))
15509  {
15510  /*
15511  Update image window configuration.
15512  */
15513  windows->image.window_changes.width=event.xconfigure.width;
15514  windows->image.window_changes.height=event.xconfigure.height;
15515  (void) XConfigureImage(display,resource_info,windows,
15516  display_image);
15517  }
15518  /*
15519  Update pan window configuration.
15520  */
15521  if ((event.xconfigure.width < windows->image.ximage->width) ||
15522  (event.xconfigure.height < windows->image.ximage->height))
15523  {
15524  (void) XMapRaised(display,windows->pan.id);
15525  XDrawPanRectangle(display,windows);
15526  }
15527  else
15528  if (windows->pan.mapped != MagickFalse)
15529  (void) XWithdrawWindow(display,windows->pan.id,
15530  windows->pan.screen);
15531  break;
15532  }
15533  if (event.xconfigure.window == windows->magnify.id)
15534  {
15535  unsigned int
15536  magnify;
15537 
15538  /*
15539  Magnify window has a new configuration.
15540  */
15541  windows->magnify.width=(unsigned int) event.xconfigure.width;
15542  windows->magnify.height=(unsigned int) event.xconfigure.height;
15543  if (windows->magnify.mapped == MagickFalse)
15544  break;
15545  magnify=1;
15546  while ((int) magnify <= event.xconfigure.width)
15547  magnify<<=1;
15548  while ((int) magnify <= event.xconfigure.height)
15549  magnify<<=1;
15550  magnify>>=1;
15551  if (((int) magnify != event.xconfigure.width) ||
15552  ((int) magnify != event.xconfigure.height))
15553  {
15554  window_changes.width=(int) magnify;
15555  window_changes.height=(int) magnify;
15556  (void) XReconfigureWMWindow(display,windows->magnify.id,
15557  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15558  &window_changes);
15559  break;
15560  }
15561  if ((windows->magnify.mapped != MagickFalse) &&
15562  (windows->magnify.stasis != MagickFalse))
15563  {
15564  status=XMakeImage(display,resource_info,&windows->magnify,
15565  display_image,windows->magnify.width,windows->magnify.height);
15566  XMakeMagnifyImage(display,windows);
15567  }
15568  break;
15569  }
15570  if ((windows->magnify.mapped != MagickFalse) &&
15571  (event.xconfigure.window == windows->pan.id))
15572  {
15573  /*
15574  Pan icon window has a new configuration.
15575  */
15576  if (event.xconfigure.send_event != 0)
15577  {
15578  windows->pan.x=event.xconfigure.x;
15579  windows->pan.y=event.xconfigure.y;
15580  }
15581  windows->pan.width=(unsigned int) event.xconfigure.width;
15582  windows->pan.height=(unsigned int) event.xconfigure.height;
15583  break;
15584  }
15585  if (event.xconfigure.window == windows->icon.id)
15586  {
15587  /*
15588  Icon window has a new configuration.
15589  */
15590  windows->icon.width=(unsigned int) event.xconfigure.width;
15591  windows->icon.height=(unsigned int) event.xconfigure.height;
15592  break;
15593  }
15594  break;
15595  }
15596  case DestroyNotify:
15597  {
15598  /*
15599  Group leader has exited.
15600  */
15601  if (resource_info->debug != MagickFalse)
15602  (void) LogMagickEvent(X11Event,GetMagickModule(),
15603  "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15604  if (event.xdestroywindow.window == windows->group_leader.id)
15605  {
15606  *state|=ExitState;
15607  break;
15608  }
15609  break;
15610  }
15611  case EnterNotify:
15612  {
15613  /*
15614  Selectively install colormap.
15615  */
15616  if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15617  if (event.xcrossing.mode != NotifyUngrab)
15618  XInstallColormap(display,map_info->colormap);
15619  break;
15620  }
15621  case Expose:
15622  {
15623  if (resource_info->debug != MagickFalse)
15624  (void) LogMagickEvent(X11Event,GetMagickModule(),
15625  "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15626  event.xexpose.width,event.xexpose.height,event.xexpose.x,
15627  event.xexpose.y);
15628  /*
15629  Refresh windows that are now exposed.
15630  */
15631  if ((event.xexpose.window == windows->image.id) &&
15632  (windows->image.mapped != MagickFalse))
15633  {
15634  XRefreshWindow(display,&windows->image,&event);
15635  delay=display_image->delay/MagickMax(
15636  display_image->ticks_per_second,1L);
15637  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15638  break;
15639  }
15640  if ((event.xexpose.window == windows->magnify.id) &&
15641  (windows->magnify.mapped != MagickFalse))
15642  {
15643  XMakeMagnifyImage(display,windows);
15644  break;
15645  }
15646  if (event.xexpose.window == windows->pan.id)
15647  {
15648  XDrawPanRectangle(display,windows);
15649  break;
15650  }
15651  if (event.xexpose.window == windows->icon.id)
15652  {
15653  XRefreshWindow(display,&windows->icon,&event);
15654  break;
15655  }
15656  break;
15657  }
15658  case KeyPress:
15659  {
15660  int
15661  length;
15662 
15663  /*
15664  Respond to a user key press.
15665  */
15666  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15667  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15668  *(command+length)='\0';
15669  if (resource_info->debug != MagickFalse)
15670  (void) LogMagickEvent(X11Event,GetMagickModule(),
15671  "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15672  key_symbol,command);
15673  if (event.xkey.window == windows->image.id)
15674  {
15675  command_type=XImageWindowCommand(display,resource_info,windows,
15676  event.xkey.state,key_symbol,&display_image);
15677  if (command_type != NullCommand)
15678  nexus=XMagickCommand(display,resource_info,windows,command_type,
15679  &display_image);
15680  }
15681  if (event.xkey.window == windows->magnify.id)
15682  XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15683  if (event.xkey.window == windows->pan.id)
15684  {
15685  if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15686  (void) XWithdrawWindow(display,windows->pan.id,
15687  windows->pan.screen);
15688  else
15689  if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15690  XTextViewHelp(display,resource_info,windows,MagickFalse,
15691  "Help Viewer - Image Pan",ImagePanHelp);
15692  else
15693  XTranslateImage(display,windows,*image,key_symbol);
15694  }
15695  delay=display_image->delay/MagickMax(
15696  display_image->ticks_per_second,1L);
15697  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15698  break;
15699  }
15700  case KeyRelease:
15701  {
15702  /*
15703  Respond to a user key release.
15704  */
15705  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15706  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15707  if (resource_info->debug != MagickFalse)
15708  (void) LogMagickEvent(X11Event,GetMagickModule(),
15709  "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15710  break;
15711  }
15712  case LeaveNotify:
15713  {
15714  /*
15715  Selectively uninstall colormap.
15716  */
15717  if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15718  if (event.xcrossing.mode != NotifyUngrab)
15719  XUninstallColormap(display,map_info->colormap);
15720  break;
15721  }
15722  case MapNotify:
15723  {
15724  if (resource_info->debug != MagickFalse)
15725  (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15726  event.xmap.window);
15727  if (event.xmap.window == windows->backdrop.id)
15728  {
15729  (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15730  CurrentTime);
15731  windows->backdrop.mapped=MagickTrue;
15732  break;
15733  }
15734  if (event.xmap.window == windows->image.id)
15735  {
15736  if (windows->backdrop.id != (Window) NULL)
15737  (void) XInstallColormap(display,map_info->colormap);
15738  if (LocaleCompare(display_image->magick,"LOGO") == 0)
15739  {
15740  if (LocaleCompare(display_image->filename,"LOGO") == 0)
15741  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15742  }
15743  if (((int) windows->image.width < windows->image.ximage->width) ||
15744  ((int) windows->image.height < windows->image.ximage->height))
15745  (void) XMapRaised(display,windows->pan.id);
15746  windows->image.mapped=MagickTrue;
15747  break;
15748  }
15749  if (event.xmap.window == windows->magnify.id)
15750  {
15751  XMakeMagnifyImage(display,windows);
15752  windows->magnify.mapped=MagickTrue;
15753  (void) XWithdrawWindow(display,windows->info.id,
15754  windows->info.screen);
15755  break;
15756  }
15757  if (event.xmap.window == windows->pan.id)
15758  {
15759  XMakePanImage(display,resource_info,windows,display_image);
15760  windows->pan.mapped=MagickTrue;
15761  break;
15762  }
15763  if (event.xmap.window == windows->info.id)
15764  {
15765  windows->info.mapped=MagickTrue;
15766  break;
15767  }
15768  if (event.xmap.window == windows->icon.id)
15769  {
15770  MagickBooleanType
15771  taint;
15772 
15773  /*
15774  Create an icon image.
15775  */
15776  taint=display_image->taint;
15777  XMakeStandardColormap(display,icon_visual,icon_resources,
15778  display_image,icon_map,icon_pixel);
15779  (void) XMakeImage(display,icon_resources,&windows->icon,
15780  display_image,windows->icon.width,windows->icon.height);
15781  display_image->taint=taint;
15782  (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15783  windows->icon.pixmap);
15784  (void) XClearWindow(display,windows->icon.id);
15785  (void) XWithdrawWindow(display,windows->info.id,
15786  windows->info.screen);
15787  windows->icon.mapped=MagickTrue;
15788  break;
15789  }
15790  if (event.xmap.window == windows->command.id)
15791  {
15792  windows->command.mapped=MagickTrue;
15793  break;
15794  }
15795  if (event.xmap.window == windows->popup.id)
15796  {
15797  windows->popup.mapped=MagickTrue;
15798  break;
15799  }
15800  if (event.xmap.window == windows->widget.id)
15801  {
15802  windows->widget.mapped=MagickTrue;
15803  break;
15804  }
15805  break;
15806  }
15807  case MappingNotify:
15808  {
15809  (void) XRefreshKeyboardMapping(&event.xmapping);
15810  break;
15811  }
15812  case NoExpose:
15813  break;
15814  case PropertyNotify:
15815  {
15816  Atom
15817  type;
15818 
15819  int
15820  format,
15821  status;
15822 
15823  unsigned char
15824  *data;
15825 
15826  unsigned long
15827  after,
15828  length;
15829 
15830  if (resource_info->debug != MagickFalse)
15831  (void) LogMagickEvent(X11Event,GetMagickModule(),
15832  "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15833  event.xproperty.atom,event.xproperty.state);
15834  if (event.xproperty.atom != windows->im_remote_command)
15835  break;
15836  /*
15837  Display image named by the remote command protocol.
15838  */
15839  status=XGetWindowProperty(display,event.xproperty.window,
15840  event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15841  AnyPropertyType,&type,&format,&length,&after,&data);
15842  if ((status != Success) || (length == 0))
15843  break;
15844  if (LocaleCompare((char *) data,"-quit") == 0)
15845  {
15846  XClientMessage(display,windows->image.id,windows->im_protocols,
15847  windows->im_exit,CurrentTime);
15848  (void) XFree((void *) data);
15849  break;
15850  }
15851  (void) CopyMagickString(resource_info->image_info->filename,
15852  (char *) data,MaxTextExtent);
15853  (void) XFree((void *) data);
15854  nexus=ReadImage(resource_info->image_info,&display_image->exception);
15855  CatchException(&display_image->exception);
15856  if (nexus != (Image *) NULL)
15857  *state|=NextImageState | ExitState;
15858  break;
15859  }
15860  case ReparentNotify:
15861  {
15862  if (resource_info->debug != MagickFalse)
15863  (void) LogMagickEvent(X11Event,GetMagickModule(),
15864  "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15865  event.xreparent.window);
15866  break;
15867  }
15868  case UnmapNotify:
15869  {
15870  if (resource_info->debug != MagickFalse)
15871  (void) LogMagickEvent(X11Event,GetMagickModule(),
15872  "Unmap Notify: 0x%lx",event.xunmap.window);
15873  if (event.xunmap.window == windows->backdrop.id)
15874  {
15875  windows->backdrop.mapped=MagickFalse;
15876  break;
15877  }
15878  if (event.xunmap.window == windows->image.id)
15879  {
15880  windows->image.mapped=MagickFalse;
15881  break;
15882  }
15883  if (event.xunmap.window == windows->magnify.id)
15884  {
15885  windows->magnify.mapped=MagickFalse;
15886  break;
15887  }
15888  if (event.xunmap.window == windows->pan.id)
15889  {
15890  windows->pan.mapped=MagickFalse;
15891  break;
15892  }
15893  if (event.xunmap.window == windows->info.id)
15894  {
15895  windows->info.mapped=MagickFalse;
15896  break;
15897  }
15898  if (event.xunmap.window == windows->icon.id)
15899  {
15900  if (map_info->colormap == icon_map->colormap)
15901  XConfigureImageColormap(display,resource_info,windows,
15902  display_image);
15903  (void) XFreeStandardColormap(display,icon_visual,icon_map,
15904  icon_pixel);
15905  windows->icon.mapped=MagickFalse;
15906  break;
15907  }
15908  if (event.xunmap.window == windows->command.id)
15909  {
15910  windows->command.mapped=MagickFalse;
15911  break;
15912  }
15913  if (event.xunmap.window == windows->popup.id)
15914  {
15915  if (windows->backdrop.id != (Window) NULL)
15916  (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15917  CurrentTime);
15918  windows->popup.mapped=MagickFalse;
15919  break;
15920  }
15921  if (event.xunmap.window == windows->widget.id)
15922  {
15923  if (windows->backdrop.id != (Window) NULL)
15924  (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15925  CurrentTime);
15926  windows->widget.mapped=MagickFalse;
15927  break;
15928  }
15929  break;
15930  }
15931  default:
15932  {
15933  if (resource_info->debug != MagickFalse)
15934  (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
15935  event.type);
15936  break;
15937  }
15938  }
15939  } while (!(*state & ExitState));
15940  if ((*state & ExitState) == 0)
15941  (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
15942  &display_image);
15943  else
15944  if (resource_info->confirm_edit != MagickFalse)
15945  {
15946  /*
15947  Query user if image has changed.
15948  */
15949  if ((resource_info->immutable == MagickFalse) &&
15950  (display_image->taint != MagickFalse))
15951  {
15952  int
15953  status;
15954 
15955  status=XConfirmWidget(display,windows,"Your image changed.",
15956  "Do you want to save it");
15957  if (status == 0)
15958  *state&=(~ExitState);
15959  else
15960  if (status > 0)
15961  (void) XMagickCommand(display,resource_info,windows,SaveCommand,
15962  &display_image);
15963  }
15964  }
15965  if ((windows->visual_info->klass == GrayScale) ||
15966  (windows->visual_info->klass == PseudoColor) ||
15967  (windows->visual_info->klass == DirectColor))
15968  {
15969  /*
15970  Withdraw pan and Magnify window.
15971  */
15972  if (windows->info.mapped != MagickFalse)
15973  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15974  if (windows->magnify.mapped != MagickFalse)
15975  (void) XWithdrawWindow(display,windows->magnify.id,
15976  windows->magnify.screen);
15977  if (windows->command.mapped != MagickFalse)
15978  (void) XWithdrawWindow(display,windows->command.id,
15979  windows->command.screen);
15980  }
15981  if (windows->pan.mapped != MagickFalse)
15982  (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
15983  if (resource_info->backdrop == MagickFalse)
15984  if (windows->backdrop.mapped)
15985  {
15986  (void) XWithdrawWindow(display,windows->backdrop.id,
15987  windows->backdrop.screen);
15988  (void) XDestroyWindow(display,windows->backdrop.id);
15989  windows->backdrop.id=(Window) NULL;
15990  (void) XWithdrawWindow(display,windows->image.id,
15991  windows->image.screen);
15992  (void) XDestroyWindow(display,windows->image.id);
15993  windows->image.id=(Window) NULL;
15994  }
15995  XSetCursorState(display,windows,MagickTrue);
15996  XCheckRefreshWindows(display,windows);
15997  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
15998  *state&=(~ExitState);
15999  if (*state & ExitState)
16000  {
16001  /*
16002  Free Standard Colormap.
16003  */
16004  (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16005  if (resource_info->map_type == (char *) NULL)
16006  (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16007  /*
16008  Free X resources.
16009  */
16010  if (resource_info->copy_image != (Image *) NULL)
16011  resource_info->copy_image=DestroyImage(resource_info->copy_image);
16012  DestroyXResources();
16013  }
16014  (void) XSync(display,MagickFalse);
16015  /*
16016  Restore our progress monitor and warning handlers.
16017  */
16018  (void) SetErrorHandler(warning_handler);
16019  (void) SetWarningHandler(warning_handler);
16020  /*
16021  Change to home directory.
16022  */
16023  directory=getcwd(working_directory,MaxTextExtent);
16024  (void) directory;
16025  {
16026  int
16027  status;
16028 
16029  if (*resource_info->home_directory == '\0')
16030  (void) CopyMagickString(resource_info->home_directory,".",MaxTextExtent);
16031  status=chdir(resource_info->home_directory);
16032  if (status == -1)
16033  (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
16034  FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
16035  }
16036  *image=display_image;
16037  return(nexus);
16038 }
16039 #else
16040 
16041 /*
16042 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16043 % %
16044 % %
16045 % %
16046 + D i s p l a y I m a g e s %
16047 % %
16048 % %
16049 % %
16050 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16051 %
16052 % DisplayImages() displays an image sequence to any X window screen. It
16053 % returns a value other than 0 if successful. Check the exception member
16054 % of image to determine the reason for any failure.
16055 %
16056 % The format of the DisplayImages method is:
16057 %
16058 % MagickBooleanType DisplayImages(const ImageInfo *image_info,
16059 % Image *images)
16060 %
16061 % A description of each parameter follows:
16062 %
16063 % o image_info: the image info.
16064 %
16065 % o image: the image.
16066 %
16067 */
16068 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16069  Image *image)
16070 {
16071  assert(image_info != (const ImageInfo *) NULL);
16072  assert(image_info->signature == MagickCoreSignature);
16073  assert(image != (Image *) NULL);
16074  assert(image->signature == MagickCoreSignature);
16075  if (IsEventLogging() != MagickFalse)
16076  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16077  (void) image_info;
16078  (void) ThrowMagickException(&image->exception,GetMagickModule(),
16079  MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
16080  image->filename);
16081  return(MagickFalse);
16082 }
16083 
16084 /*
16085 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16086 % %
16087 % %
16088 % %
16089 + R e m o t e D i s p l a y C o m m a n d %
16090 % %
16091 % %
16092 % %
16093 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16094 %
16095 % RemoteDisplayCommand() encourages a remote display program to display the
16096 % specified image filename.
16097 %
16098 % The format of the RemoteDisplayCommand method is:
16099 %
16100 % MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16101 % const char *window,const char *filename,ExceptionInfo *exception)
16102 %
16103 % A description of each parameter follows:
16104 %
16105 % o image_info: the image info.
16106 %
16107 % o window: Specifies the name or id of an X window.
16108 %
16109 % o filename: the name of the image filename to display.
16110 %
16111 % o exception: return any errors or warnings in this structure.
16112 %
16113 */
16114 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16115  const char *window,const char *filename,ExceptionInfo *exception)
16116 {
16117  assert(image_info != (const ImageInfo *) NULL);
16118  assert(image_info->signature == MagickCoreSignature);
16119  assert(filename != (char *) NULL);
16120  if (IsEventLogging() != MagickFalse)
16121  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16122  (void) window;
16123  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16124  "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16125  return(MagickFalse);
16126 }
16127 #endif
Definition: image.h:152