MagickCore  6.9.13-9
Convert, Edit, Or Compose Bitmap Images
display.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % DDDD IIIII SSSSS PPPP L AAA Y Y %
7 % D D I SS P P L A A Y Y %
8 % D D I SSS PPPP L AAAAA Y %
9 % D D I SS P L A A Y %
10 % DDDD IIIII SSSSS P LLLLL A A Y %
11 % %
12 % %
13 % MagickCore Methods to Interactively Display and Edit an Image %
14 % %
15 % Software Design %
16 % Cristy %
17 % July 1992 %
18 % %
19 % %
20 % Copyright 1999 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
22 % %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
25 % %
26 % https://imagemagick.org/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 } DisplayCommand;
1407 
1408 typedef enum
1409 {
1410  AnnotateNameCommand,
1411  AnnotateFontColorCommand,
1412  AnnotateBackgroundColorCommand,
1413  AnnotateRotateCommand,
1414  AnnotateHelpCommand,
1415  AnnotateDismissCommand,
1416  TextHelpCommand,
1417  TextApplyCommand,
1418  ChopDirectionCommand,
1419  ChopHelpCommand,
1420  ChopDismissCommand,
1421  HorizontalChopCommand,
1422  VerticalChopCommand,
1423  ColorEditMethodCommand,
1424  ColorEditColorCommand,
1425  ColorEditBorderCommand,
1426  ColorEditFuzzCommand,
1427  ColorEditUndoCommand,
1428  ColorEditHelpCommand,
1429  ColorEditDismissCommand,
1430  CompositeOperatorsCommand,
1431  CompositeDissolveCommand,
1432  CompositeDisplaceCommand,
1433  CompositeHelpCommand,
1434  CompositeDismissCommand,
1435  CropHelpCommand,
1436  CropDismissCommand,
1437  RectifyCopyCommand,
1438  RectifyHelpCommand,
1439  RectifyDismissCommand,
1440  DrawElementCommand,
1441  DrawColorCommand,
1442  DrawStippleCommand,
1443  DrawWidthCommand,
1444  DrawUndoCommand,
1445  DrawHelpCommand,
1446  DrawDismissCommand,
1447  MatteEditMethod,
1448  MatteEditBorderCommand,
1449  MatteEditFuzzCommand,
1450  MatteEditValueCommand,
1451  MatteEditUndoCommand,
1452  MatteEditHelpCommand,
1453  MatteEditDismissCommand,
1454  PasteOperatorsCommand,
1455  PasteHelpCommand,
1456  PasteDismissCommand,
1457  RotateColorCommand,
1458  RotateDirectionCommand,
1459  RotateCropCommand,
1460  RotateSharpenCommand,
1461  RotateHelpCommand,
1462  RotateDismissCommand,
1463  HorizontalRotateCommand,
1464  VerticalRotateCommand,
1465  TileLoadCommand,
1466  TileNextCommand,
1467  TileFormerCommand,
1468  TileDeleteCommand,
1469  TileUpdateCommand
1470 } ModeType;
1471 ␌
1472 /*
1473  Stipples.
1474 */
1475 #define BricksWidth 20
1476 #define BricksHeight 20
1477 #define DiagonalWidth 16
1478 #define DiagonalHeight 16
1479 #define HighlightWidth 8
1480 #define HighlightHeight 8
1481 #define OpaqueWidth 8
1482 #define OpaqueHeight 8
1483 #define ScalesWidth 16
1484 #define ScalesHeight 16
1485 #define ShadowWidth 8
1486 #define ShadowHeight 8
1487 #define VerticalWidth 16
1488 #define VerticalHeight 16
1489 #define WavyWidth 16
1490 #define WavyHeight 16
1491 ␌
1492 /*
1493  Constant declaration.
1494 */
1495 static const int
1496  RoiDelta = 8;
1497 
1498 static const unsigned char
1499  BricksBitmap[] =
1500  {
1501  0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1502  0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1503  0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1504  0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1505  0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1506  },
1507  DiagonalBitmap[] =
1508  {
1509  0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1510  0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1511  0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1512  },
1513  ScalesBitmap[] =
1514  {
1515  0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1516  0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1517  0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1518  },
1519  VerticalBitmap[] =
1520  {
1521  0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1522  0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1523  0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1524  },
1525  WavyBitmap[] =
1526  {
1527  0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1528  0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1529  0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1530  };
1531 ␌
1532 /*
1533  Function prototypes.
1534 */
1535 static DisplayCommand
1536  XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1537  const MagickStatusType,KeySym,Image **);
1538 
1539 static Image
1540  *XMagickCommand(Display *,XResourceInfo *,XWindows *,const DisplayCommand,
1541  Image **),
1542  *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1543  *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *),
1544  *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *);
1545 
1546 static MagickBooleanType
1547  XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *),
1548  XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1549  XChopImage(Display *,XResourceInfo *,XWindows *,Image **),
1550  XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode),
1551  XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **),
1552  XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1553  XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *),
1554  XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *),
1555  XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1556  XPasteImage(Display *,XResourceInfo *,XWindows *,Image *),
1557  XPrintImage(Display *,XResourceInfo *,XWindows *,Image *),
1558  XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **),
1559  XROIImage(Display *,XResourceInfo *,XWindows *,Image **),
1560  XSaveImage(Display *,XResourceInfo *,XWindows *,Image *),
1561  XTrimImage(Display *,XResourceInfo *,XWindows *,Image *);
1562 
1563 static void
1564  XDrawPanRectangle(Display *,XWindows *),
1565  XImageCache(Display *,XResourceInfo *,XWindows *,const DisplayCommand,Image **),
1566  XMagnifyImage(Display *,XWindows *,XEvent *),
1567  XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *),
1568  XPanImage(Display *,XWindows *,XEvent *),
1569  XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1570  const KeySym),
1571  XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1572  XScreenEvent(Display *,XWindows *,XEvent *),
1573  XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1574 ␌
1575 /*
1576 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1577 % %
1578 % %
1579 % %
1580 % D i s p l a y I m a g e s %
1581 % %
1582 % %
1583 % %
1584 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1585 %
1586 % DisplayImages() displays an image sequence to any X window screen. It
1587 % returns a value other than 0 if successful. Check the exception member
1588 % of image to determine the reason for any failure.
1589 %
1590 % The format of the DisplayImages method is:
1591 %
1592 % MagickBooleanType DisplayImages(const ImageInfo *image_info,
1593 % Image *images)
1594 %
1595 % A description of each parameter follows:
1596 %
1597 % o image_info: the image info.
1598 %
1599 % o image: the image.
1600 %
1601 */
1602 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1603  Image *images)
1604 {
1605  char
1606  *argv[1];
1607 
1608  Display
1609  *display;
1610 
1611  Image
1612  *image;
1613 
1614  size_t
1615  state;
1616 
1617  ssize_t
1618  i;
1619 
1620  XrmDatabase
1621  resource_database;
1622 
1623  XResourceInfo
1624  resource_info;
1625 
1626  assert(image_info != (const ImageInfo *) NULL);
1627  assert(image_info->signature == MagickCoreSignature);
1628  assert(images != (Image *) NULL);
1629  assert(images->signature == MagickCoreSignature);
1630  if (IsEventLogging() != MagickFalse)
1631  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1632  display=XOpenDisplay(image_info->server_name);
1633  if (display == (Display *) NULL)
1634  {
1635  (void) ThrowMagickException(&images->exception,GetMagickModule(),
1636  XServerError,"UnableToOpenXServer","`%s'",XDisplayName(
1637  image_info->server_name));
1638  return(MagickFalse);
1639  }
1640  if (images->exception.severity != UndefinedException)
1641  CatchException(&images->exception);
1642  (void) XSetErrorHandler(XError);
1643  resource_database=XGetResourceDatabase(display,GetClientName());
1644  (void) memset(&resource_info,0,sizeof(resource_info));
1645  XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1646  if (image_info->page != (char *) NULL)
1647  resource_info.image_geometry=AcquireString(image_info->page);
1648  resource_info.immutable=MagickTrue;
1649  argv[0]=AcquireString(GetClientName());
1650  state=DefaultState;
1651  for (i=0; (state & ExitState) == 0; i++)
1652  {
1653  if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1654  break;
1655  image=GetImageFromList(images,i % GetImageListLength(images));
1656  (void) XDisplayImage(display,&resource_info,argv,1,&image,&state);
1657  }
1658  (void) SetErrorHandler((ErrorHandler) NULL);
1659  (void) SetWarningHandler((WarningHandler) NULL);
1660  argv[0]=DestroyString(argv[0]);
1661  XDestroyResourceInfo(&resource_info);
1662  if (images->exception.severity != UndefinedException)
1663  return(MagickFalse);
1664  return(MagickTrue);
1665 }
1666 ␌
1667 /*
1668 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1669 % %
1670 % %
1671 % %
1672 % R e m o t e D i s p l a y C o m m a n d %
1673 % %
1674 % %
1675 % %
1676 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1677 %
1678 % RemoteDisplayCommand() encourages a remote display program to display the
1679 % specified image filename.
1680 %
1681 % The format of the RemoteDisplayCommand method is:
1682 %
1683 % MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1684 % const char *window,const char *filename,ExceptionInfo *exception)
1685 %
1686 % A description of each parameter follows:
1687 %
1688 % o image_info: the image info.
1689 %
1690 % o window: Specifies the name or id of an X window.
1691 %
1692 % o filename: the name of the image filename to display.
1693 %
1694 % o exception: return any errors or warnings in this structure.
1695 %
1696 */
1697 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1698  const char *window,const char *filename,ExceptionInfo *exception)
1699 {
1700  Display
1701  *display;
1702 
1703  MagickStatusType
1704  status;
1705 
1706  assert(image_info != (const ImageInfo *) NULL);
1707  assert(image_info->signature == MagickCoreSignature);
1708  assert(filename != (char *) NULL);
1709  if (IsEventLogging() != MagickFalse)
1710  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1711  display=XOpenDisplay(image_info->server_name);
1712  if (display == (Display *) NULL)
1713  {
1714  (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1715  "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1716  return(MagickFalse);
1717  }
1718  (void) XSetErrorHandler(XError);
1719  status=XRemoteCommand(display,window,filename);
1720  (void) XCloseDisplay(display);
1721  return(status != 0 ? MagickTrue : MagickFalse);
1722 }
1723 ␌
1724 /*
1725 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1726 % %
1727 % %
1728 % %
1729 + X A n n o t a t e E d i t I m a g e %
1730 % %
1731 % %
1732 % %
1733 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1734 %
1735 % XAnnotateEditImage() annotates the image with text.
1736 %
1737 % The format of the XAnnotateEditImage method is:
1738 %
1739 % MagickBooleanType XAnnotateEditImage(Display *display,
1740 % XResourceInfo *resource_info,XWindows *windows,Image *image)
1741 %
1742 % A description of each parameter follows:
1743 %
1744 % o display: Specifies a connection to an X server; returned from
1745 % XOpenDisplay.
1746 %
1747 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1748 %
1749 % o windows: Specifies a pointer to a XWindows structure.
1750 %
1751 % o image: the image; returned from ReadImage.
1752 %
1753 */
1754 
1755 static MagickBooleanType XAnnotateEditImage(Display *display,
1756  XResourceInfo *resource_info,XWindows *windows,Image *image)
1757 {
1758  const char
1759  *const AnnotateMenu[] =
1760  {
1761  "Font Name",
1762  "Font Color",
1763  "Box Color",
1764  "Rotate Text",
1765  "Help",
1766  "Dismiss",
1767  (char *) NULL
1768  },
1769  *const TextMenu[] =
1770  {
1771  "Help",
1772  "Apply",
1773  (char *) NULL
1774  };
1775 
1776  static const ModeType
1777  AnnotateCommands[] =
1778  {
1779  AnnotateNameCommand,
1780  AnnotateFontColorCommand,
1781  AnnotateBackgroundColorCommand,
1782  AnnotateRotateCommand,
1783  AnnotateHelpCommand,
1784  AnnotateDismissCommand
1785  },
1786  TextCommands[] =
1787  {
1788  TextHelpCommand,
1789  TextApplyCommand
1790  };
1791 
1792  static MagickBooleanType
1793  transparent_box = MagickTrue,
1794  transparent_pen = MagickFalse;
1795 
1796  static MagickRealType
1797  degrees = 0.0;
1798 
1799  static unsigned int
1800  box_id = MaxNumberPens-2,
1801  font_id = 0,
1802  pen_id = 0;
1803 
1804  char
1805  command[MaxTextExtent],
1806  *p,
1807  text[MaxTextExtent];
1808 
1809  const char
1810  *ColorMenu[MaxNumberPens+1];
1811 
1812  Cursor
1813  cursor;
1814 
1815  GC
1816  annotate_context;
1817 
1818  int
1819  id,
1820  pen_number,
1821  status,
1822  x,
1823  y;
1824 
1825  KeySym
1826  key_symbol;
1827 
1828  size_t
1829  state;
1830 
1831  ssize_t
1832  i;
1833 
1834  unsigned int
1835  height,
1836  width;
1837 
1838  XAnnotateInfo
1839  *annotate_info,
1840  *previous_info;
1841 
1842  XColor
1843  color;
1844 
1845  XFontStruct
1846  *font_info;
1847 
1848  XEvent
1849  event,
1850  text_event;
1851 
1852  /*
1853  Map Command widget.
1854  */
1855  (void) CloneString(&windows->command.name,"Annotate");
1856  windows->command.data=4;
1857  (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1858  (void) XMapRaised(display,windows->command.id);
1859  XClientMessage(display,windows->image.id,windows->im_protocols,
1860  windows->im_update_widget,CurrentTime);
1861  /*
1862  Track pointer until button 1 is pressed.
1863  */
1864  XQueryPosition(display,windows->image.id,&x,&y);
1865  (void) XSelectInput(display,windows->image.id,
1866  windows->image.attributes.event_mask | PointerMotionMask);
1867  cursor=XCreateFontCursor(display,XC_left_side);
1868  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1869  state=DefaultState;
1870  do
1871  {
1872  if (windows->info.mapped != MagickFalse)
1873  {
1874  /*
1875  Display pointer position.
1876  */
1877  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1878  x+windows->image.x,y+windows->image.y);
1879  XInfoWidget(display,windows,text);
1880  }
1881  /*
1882  Wait for next event.
1883  */
1884  XScreenEvent(display,windows,&event);
1885  if (event.xany.window == windows->command.id)
1886  {
1887  /*
1888  Select a command from the Command widget.
1889  */
1890  id=XCommandWidget(display,windows,AnnotateMenu,&event);
1891  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1892  if (id < 0)
1893  continue;
1894  switch (AnnotateCommands[id])
1895  {
1896  case AnnotateNameCommand:
1897  {
1898  const char
1899  *FontMenu[MaxNumberFonts];
1900 
1901  int
1902  font_number;
1903 
1904  /*
1905  Initialize menu selections.
1906  */
1907  for (i=0; i < MaxNumberFonts; i++)
1908  FontMenu[i]=resource_info->font_name[i];
1909  FontMenu[MaxNumberFonts-2]="Browser...";
1910  FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1911  /*
1912  Select a font name from the pop-up menu.
1913  */
1914  font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1915  (const char **) FontMenu,command);
1916  if (font_number < 0)
1917  break;
1918  if (font_number == (MaxNumberFonts-2))
1919  {
1920  static char
1921  font_name[MaxTextExtent] = "fixed";
1922 
1923  /*
1924  Select a font name from a browser.
1925  */
1926  resource_info->font_name[font_number]=font_name;
1927  XFontBrowserWidget(display,windows,"Select",font_name);
1928  if (*font_name == '\0')
1929  break;
1930  }
1931  /*
1932  Initialize font info.
1933  */
1934  font_info=XLoadQueryFont(display,resource_info->font_name[
1935  font_number]);
1936  if (font_info == (XFontStruct *) NULL)
1937  {
1938  XNoticeWidget(display,windows,"Unable to load font:",
1939  resource_info->font_name[font_number]);
1940  break;
1941  }
1942  font_id=(unsigned int) font_number;
1943  (void) XFreeFont(display,font_info);
1944  break;
1945  }
1946  case AnnotateFontColorCommand:
1947  {
1948  /*
1949  Initialize menu selections.
1950  */
1951  for (i=0; i < (int) (MaxNumberPens-2); i++)
1952  ColorMenu[i]=resource_info->pen_colors[i];
1953  ColorMenu[MaxNumberPens-2]="transparent";
1954  ColorMenu[MaxNumberPens-1]="Browser...";
1955  ColorMenu[MaxNumberPens]=(const char *) NULL;
1956  /*
1957  Select a pen color from the pop-up menu.
1958  */
1959  pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
1960  (const char **) ColorMenu,command);
1961  if (pen_number < 0)
1962  break;
1963  transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
1964  MagickFalse;
1965  if (transparent_pen != MagickFalse)
1966  break;
1967  if (pen_number == (MaxNumberPens-1))
1968  {
1969  static char
1970  color_name[MaxTextExtent] = "gray";
1971 
1972  /*
1973  Select a pen color from a dialog.
1974  */
1975  resource_info->pen_colors[pen_number]=color_name;
1976  XColorBrowserWidget(display,windows,"Select",color_name);
1977  if (*color_name == '\0')
1978  break;
1979  }
1980  /*
1981  Set pen color.
1982  */
1983  (void) XParseColor(display,windows->map_info->colormap,
1984  resource_info->pen_colors[pen_number],&color);
1985  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
1986  (unsigned int) MaxColors,&color);
1987  windows->pixel_info->pen_colors[pen_number]=color;
1988  pen_id=(unsigned int) pen_number;
1989  break;
1990  }
1991  case AnnotateBackgroundColorCommand:
1992  {
1993  /*
1994  Initialize menu selections.
1995  */
1996  for (i=0; i < (int) (MaxNumberPens-2); i++)
1997  ColorMenu[i]=resource_info->pen_colors[i];
1998  ColorMenu[MaxNumberPens-2]="transparent";
1999  ColorMenu[MaxNumberPens-1]="Browser...";
2000  ColorMenu[MaxNumberPens]=(const char *) NULL;
2001  /*
2002  Select a pen color from the pop-up menu.
2003  */
2004  pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2005  (const char **) ColorMenu,command);
2006  if (pen_number < 0)
2007  break;
2008  transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2009  MagickFalse;
2010  if (transparent_box != MagickFalse)
2011  break;
2012  if (pen_number == (MaxNumberPens-1))
2013  {
2014  static char
2015  color_name[MaxTextExtent] = "gray";
2016 
2017  /*
2018  Select a pen color from a dialog.
2019  */
2020  resource_info->pen_colors[pen_number]=color_name;
2021  XColorBrowserWidget(display,windows,"Select",color_name);
2022  if (*color_name == '\0')
2023  break;
2024  }
2025  /*
2026  Set pen color.
2027  */
2028  (void) XParseColor(display,windows->map_info->colormap,
2029  resource_info->pen_colors[pen_number],&color);
2030  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2031  (unsigned int) MaxColors,&color);
2032  windows->pixel_info->pen_colors[pen_number]=color;
2033  box_id=(unsigned int) pen_number;
2034  break;
2035  }
2036  case AnnotateRotateCommand:
2037  {
2038  int
2039  entry;
2040 
2041  const char
2042  *const RotateMenu[] =
2043  {
2044  "-90",
2045  "-45",
2046  "-30",
2047  "0",
2048  "30",
2049  "45",
2050  "90",
2051  "180",
2052  "Dialog...",
2053  (char *) NULL,
2054  };
2055 
2056  static char
2057  angle[MaxTextExtent] = "30.0";
2058 
2059  /*
2060  Select a command from the pop-up menu.
2061  */
2062  entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2063  command);
2064  if (entry < 0)
2065  break;
2066  if (entry != 8)
2067  {
2068  degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2069  break;
2070  }
2071  (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2072  angle);
2073  if (*angle == '\0')
2074  break;
2075  degrees=StringToDouble(angle,(char **) NULL);
2076  break;
2077  }
2078  case AnnotateHelpCommand:
2079  {
2080  XTextViewHelp(display,resource_info,windows,MagickFalse,
2081  "Help Viewer - Image Annotation",ImageAnnotateHelp);
2082  break;
2083  }
2084  case AnnotateDismissCommand:
2085  {
2086  /*
2087  Prematurely exit.
2088  */
2089  state|=EscapeState;
2090  state|=ExitState;
2091  break;
2092  }
2093  default:
2094  break;
2095  }
2096  continue;
2097  }
2098  switch (event.type)
2099  {
2100  case ButtonPress:
2101  {
2102  if (event.xbutton.button != Button1)
2103  break;
2104  if (event.xbutton.window != windows->image.id)
2105  break;
2106  /*
2107  Change to text entering mode.
2108  */
2109  x=event.xbutton.x;
2110  y=event.xbutton.y;
2111  state|=ExitState;
2112  break;
2113  }
2114  case ButtonRelease:
2115  break;
2116  case Expose:
2117  break;
2118  case KeyPress:
2119  {
2120  if (event.xkey.window != windows->image.id)
2121  break;
2122  /*
2123  Respond to a user key press.
2124  */
2125  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2126  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2127  switch ((int) key_symbol)
2128  {
2129  case XK_Escape:
2130  case XK_F20:
2131  {
2132  /*
2133  Prematurely exit.
2134  */
2135  state|=EscapeState;
2136  state|=ExitState;
2137  break;
2138  }
2139  case XK_F1:
2140  case XK_Help:
2141  {
2142  XTextViewHelp(display,resource_info,windows,MagickFalse,
2143  "Help Viewer - Image Annotation",ImageAnnotateHelp);
2144  break;
2145  }
2146  default:
2147  {
2148  (void) XBell(display,0);
2149  break;
2150  }
2151  }
2152  break;
2153  }
2154  case MotionNotify:
2155  {
2156  /*
2157  Map and unmap Info widget as cursor crosses its boundaries.
2158  */
2159  x=event.xmotion.x;
2160  y=event.xmotion.y;
2161  if (windows->info.mapped != MagickFalse)
2162  {
2163  if ((x < (int) (windows->info.x+windows->info.width)) &&
2164  (y < (int) (windows->info.y+windows->info.height)))
2165  (void) XWithdrawWindow(display,windows->info.id,
2166  windows->info.screen);
2167  }
2168  else
2169  if ((x > (int) (windows->info.x+windows->info.width)) ||
2170  (y > (int) (windows->info.y+windows->info.height)))
2171  (void) XMapWindow(display,windows->info.id);
2172  break;
2173  }
2174  default:
2175  break;
2176  }
2177  } while ((state & ExitState) == 0);
2178  (void) XSelectInput(display,windows->image.id,
2179  windows->image.attributes.event_mask);
2180  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2181  if ((state & EscapeState) != 0)
2182  return(MagickTrue);
2183  /*
2184  Set font info and check boundary conditions.
2185  */
2186  font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2187  if (font_info == (XFontStruct *) NULL)
2188  {
2189  XNoticeWidget(display,windows,"Unable to load font:",
2190  resource_info->font_name[font_id]);
2191  font_info=windows->font_info;
2192  }
2193  if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2194  x=(int) windows->image.width-font_info->max_bounds.width;
2195  if (y < (int) (font_info->ascent+font_info->descent))
2196  y=(int) font_info->ascent+font_info->descent;
2197  if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2198  ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2199  return(MagickFalse);
2200  /*
2201  Initialize annotate structure.
2202  */
2203  annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2204  if (annotate_info == (XAnnotateInfo *) NULL)
2205  return(MagickFalse);
2206  XGetAnnotateInfo(annotate_info);
2207  annotate_info->x=x;
2208  annotate_info->y=y;
2209  if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2210  annotate_info->stencil=OpaqueStencil;
2211  else
2212  if (transparent_box == MagickFalse)
2213  annotate_info->stencil=BackgroundStencil;
2214  else
2215  annotate_info->stencil=ForegroundStencil;
2216  annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2217  annotate_info->degrees=degrees;
2218  annotate_info->font_info=font_info;
2219  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2220  windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2221  sizeof(*annotate_info->text));
2222  if (annotate_info->text == (char *) NULL)
2223  return(MagickFalse);
2224  /*
2225  Create cursor and set graphic context.
2226  */
2227  cursor=XCreateFontCursor(display,XC_pencil);
2228  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2229  annotate_context=windows->image.annotate_context;
2230  (void) XSetFont(display,annotate_context,font_info->fid);
2231  (void) XSetBackground(display,annotate_context,
2232  windows->pixel_info->pen_colors[box_id].pixel);
2233  (void) XSetForeground(display,annotate_context,
2234  windows->pixel_info->pen_colors[pen_id].pixel);
2235  /*
2236  Begin annotating the image with text.
2237  */
2238  (void) CloneString(&windows->command.name,"Text");
2239  windows->command.data=0;
2240  (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2241  state=DefaultState;
2242  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2243  text_event.xexpose.width=(int) font_info->max_bounds.width;
2244  text_event.xexpose.height=font_info->max_bounds.ascent+
2245  font_info->max_bounds.descent;
2246  p=annotate_info->text;
2247  do
2248  {
2249  /*
2250  Display text cursor.
2251  */
2252  *p='\0';
2253  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2254  /*
2255  Wait for next event.
2256  */
2257  XScreenEvent(display,windows,&event);
2258  if (event.xany.window == windows->command.id)
2259  {
2260  /*
2261  Select a command from the Command widget.
2262  */
2263  (void) XSetBackground(display,annotate_context,
2264  windows->pixel_info->background_color.pixel);
2265  (void) XSetForeground(display,annotate_context,
2266  windows->pixel_info->foreground_color.pixel);
2267  id=XCommandWidget(display,windows,AnnotateMenu,&event);
2268  (void) XSetBackground(display,annotate_context,
2269  windows->pixel_info->pen_colors[box_id].pixel);
2270  (void) XSetForeground(display,annotate_context,
2271  windows->pixel_info->pen_colors[pen_id].pixel);
2272  if (id < 0)
2273  continue;
2274  switch (TextCommands[id])
2275  {
2276  case TextHelpCommand:
2277  {
2278  XTextViewHelp(display,resource_info,windows,MagickFalse,
2279  "Help Viewer - Image Annotation",ImageAnnotateHelp);
2280  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2281  break;
2282  }
2283  case TextApplyCommand:
2284  {
2285  /*
2286  Finished annotating.
2287  */
2288  annotate_info->width=(unsigned int) XTextWidth(font_info,
2289  annotate_info->text,(int) strlen(annotate_info->text));
2290  XRefreshWindow(display,&windows->image,&text_event);
2291  state|=ExitState;
2292  break;
2293  }
2294  default:
2295  break;
2296  }
2297  continue;
2298  }
2299  /*
2300  Erase text cursor.
2301  */
2302  text_event.xexpose.x=x;
2303  text_event.xexpose.y=y-font_info->max_bounds.ascent;
2304  (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2305  (unsigned int) text_event.xexpose.width,(unsigned int)
2306  text_event.xexpose.height,MagickFalse);
2307  XRefreshWindow(display,&windows->image,&text_event);
2308  switch (event.type)
2309  {
2310  case ButtonPress:
2311  {
2312  if (event.xbutton.window != windows->image.id)
2313  break;
2314  if (event.xbutton.button == Button2)
2315  {
2316  /*
2317  Request primary selection.
2318  */
2319  (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2320  windows->image.id,CurrentTime);
2321  break;
2322  }
2323  break;
2324  }
2325  case Expose:
2326  {
2327  if (event.xexpose.count == 0)
2328  {
2329  XAnnotateInfo
2330  *text_info;
2331 
2332  /*
2333  Refresh Image window.
2334  */
2335  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2336  text_info=annotate_info;
2337  while (text_info != (XAnnotateInfo *) NULL)
2338  {
2339  if (annotate_info->stencil == ForegroundStencil)
2340  (void) XDrawString(display,windows->image.id,annotate_context,
2341  text_info->x,text_info->y,text_info->text,
2342  (int) strlen(text_info->text));
2343  else
2344  (void) XDrawImageString(display,windows->image.id,
2345  annotate_context,text_info->x,text_info->y,text_info->text,
2346  (int) strlen(text_info->text));
2347  text_info=text_info->previous;
2348  }
2349  (void) XDrawString(display,windows->image.id,annotate_context,
2350  x,y,"_",1);
2351  }
2352  break;
2353  }
2354  case KeyPress:
2355  {
2356  int
2357  length;
2358 
2359  if (event.xkey.window != windows->image.id)
2360  break;
2361  /*
2362  Respond to a user key press.
2363  */
2364  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2365  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2366  *(command+length)='\0';
2367  if (((event.xkey.state & ControlMask) != 0) ||
2368  ((event.xkey.state & Mod1Mask) != 0))
2369  state|=ModifierState;
2370  if ((state & ModifierState) != 0)
2371  switch ((int) key_symbol)
2372  {
2373  case XK_u:
2374  case XK_U:
2375  {
2376  key_symbol=DeleteCommand;
2377  break;
2378  }
2379  default:
2380  break;
2381  }
2382  switch ((int) key_symbol)
2383  {
2384  case XK_BackSpace:
2385  {
2386  /*
2387  Erase one character.
2388  */
2389  if (p == annotate_info->text)
2390  {
2391  if (annotate_info->previous == (XAnnotateInfo *) NULL)
2392  break;
2393  else
2394  {
2395  /*
2396  Go to end of the previous line of text.
2397  */
2398  annotate_info=annotate_info->previous;
2399  p=annotate_info->text;
2400  x=annotate_info->x+annotate_info->width;
2401  y=annotate_info->y;
2402  if (annotate_info->width != 0)
2403  p+=strlen(annotate_info->text);
2404  break;
2405  }
2406  }
2407  p--;
2408  x-=XTextWidth(font_info,p,1);
2409  text_event.xexpose.x=x;
2410  text_event.xexpose.y=y-font_info->max_bounds.ascent;
2411  XRefreshWindow(display,&windows->image,&text_event);
2412  break;
2413  }
2414  case XK_bracketleft:
2415  {
2416  key_symbol=XK_Escape;
2417  break;
2418  }
2419  case DeleteCommand:
2420  {
2421  /*
2422  Erase the entire line of text.
2423  */
2424  while (p != annotate_info->text)
2425  {
2426  p--;
2427  x-=XTextWidth(font_info,p,1);
2428  text_event.xexpose.x=x;
2429  XRefreshWindow(display,&windows->image,&text_event);
2430  }
2431  break;
2432  }
2433  case XK_Escape:
2434  case XK_F20:
2435  {
2436  /*
2437  Finished annotating.
2438  */
2439  annotate_info->width=(unsigned int) XTextWidth(font_info,
2440  annotate_info->text,(int) strlen(annotate_info->text));
2441  XRefreshWindow(display,&windows->image,&text_event);
2442  state|=ExitState;
2443  break;
2444  }
2445  default:
2446  {
2447  /*
2448  Draw a single character on the Image window.
2449  */
2450  if ((state & ModifierState) != 0)
2451  break;
2452  if (*command == '\0')
2453  break;
2454  *p=(*command);
2455  if (annotate_info->stencil == ForegroundStencil)
2456  (void) XDrawString(display,windows->image.id,annotate_context,
2457  x,y,p,1);
2458  else
2459  (void) XDrawImageString(display,windows->image.id,
2460  annotate_context,x,y,p,1);
2461  x+=XTextWidth(font_info,p,1);
2462  p++;
2463  if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2464  break;
2465  magick_fallthrough;
2466  }
2467  case XK_Return:
2468  case XK_KP_Enter:
2469  {
2470  /*
2471  Advance to the next line of text.
2472  */
2473  *p='\0';
2474  annotate_info->width=(unsigned int) XTextWidth(font_info,
2475  annotate_info->text,(int) strlen(annotate_info->text));
2476  if (annotate_info->next != (XAnnotateInfo *) NULL)
2477  {
2478  /*
2479  Line of text already exists.
2480  */
2481  annotate_info=annotate_info->next;
2482  x=annotate_info->x;
2483  y=annotate_info->y;
2484  p=annotate_info->text;
2485  break;
2486  }
2487  annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2488  sizeof(*annotate_info->next));
2489  if (annotate_info->next == (XAnnotateInfo *) NULL)
2490  return(MagickFalse);
2491  *annotate_info->next=(*annotate_info);
2492  annotate_info->next->previous=annotate_info;
2493  annotate_info=annotate_info->next;
2494  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2495  windows->image.width/MagickMax((ssize_t)
2496  font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2497  if (annotate_info->text == (char *) NULL)
2498  return(MagickFalse);
2499  annotate_info->y+=annotate_info->height;
2500  if (annotate_info->y > (int) windows->image.height)
2501  annotate_info->y=(int) annotate_info->height;
2502  annotate_info->next=(XAnnotateInfo *) NULL;
2503  x=annotate_info->x;
2504  y=annotate_info->y;
2505  p=annotate_info->text;
2506  break;
2507  }
2508  }
2509  break;
2510  }
2511  case KeyRelease:
2512  {
2513  /*
2514  Respond to a user key release.
2515  */
2516  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2517  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2518  state&=(~ModifierState);
2519  break;
2520  }
2521  case SelectionNotify:
2522  {
2523  Atom
2524  type;
2525 
2526  int
2527  format;
2528 
2529  unsigned char
2530  *data;
2531 
2532  unsigned long
2533  after,
2534  length;
2535 
2536  /*
2537  Obtain response from primary selection.
2538  */
2539  if (event.xselection.property == (Atom) None)
2540  break;
2541  status=XGetWindowProperty(display,event.xselection.requestor,
2542  event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2543  &type,&format,&length,&after,&data);
2544  if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2545  (length == 0))
2546  break;
2547  /*
2548  Annotate Image window with primary selection.
2549  */
2550  for (i=0; i < (ssize_t) length; i++)
2551  {
2552  if ((char) data[i] != '\n')
2553  {
2554  /*
2555  Draw a single character on the Image window.
2556  */
2557  *p=(char) data[i];
2558  (void) XDrawString(display,windows->image.id,annotate_context,
2559  x,y,p,1);
2560  x+=XTextWidth(font_info,p,1);
2561  p++;
2562  if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2563  continue;
2564  }
2565  /*
2566  Advance to the next line of text.
2567  */
2568  *p='\0';
2569  annotate_info->width=(unsigned int) XTextWidth(font_info,
2570  annotate_info->text,(int) strlen(annotate_info->text));
2571  if (annotate_info->next != (XAnnotateInfo *) NULL)
2572  {
2573  /*
2574  Line of text already exists.
2575  */
2576  annotate_info=annotate_info->next;
2577  x=annotate_info->x;
2578  y=annotate_info->y;
2579  p=annotate_info->text;
2580  continue;
2581  }
2582  annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2583  sizeof(*annotate_info->next));
2584  if (annotate_info->next == (XAnnotateInfo *) NULL)
2585  return(MagickFalse);
2586  *annotate_info->next=(*annotate_info);
2587  annotate_info->next->previous=annotate_info;
2588  annotate_info=annotate_info->next;
2589  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2590  windows->image.width/MagickMax((ssize_t)
2591  font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2592  if (annotate_info->text == (char *) NULL)
2593  return(MagickFalse);
2594  annotate_info->y+=annotate_info->height;
2595  if (annotate_info->y > (int) windows->image.height)
2596  annotate_info->y=(int) annotate_info->height;
2597  annotate_info->next=(XAnnotateInfo *) NULL;
2598  x=annotate_info->x;
2599  y=annotate_info->y;
2600  p=annotate_info->text;
2601  }
2602  (void) XFree((void *) data);
2603  break;
2604  }
2605  default:
2606  break;
2607  }
2608  } while ((state & ExitState) == 0);
2609  (void) XFreeCursor(display,cursor);
2610  /*
2611  Annotation is relative to image configuration.
2612  */
2613  width=(unsigned int) image->columns;
2614  height=(unsigned int) image->rows;
2615  x=0;
2616  y=0;
2617  if (windows->image.crop_geometry != (char *) NULL)
2618  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2619  /*
2620  Initialize annotated image.
2621  */
2622  XSetCursorState(display,windows,MagickTrue);
2623  XCheckRefreshWindows(display,windows);
2624  while (annotate_info != (XAnnotateInfo *) NULL)
2625  {
2626  if (annotate_info->width == 0)
2627  {
2628  /*
2629  No text on this line-- go to the next line of text.
2630  */
2631  previous_info=annotate_info->previous;
2632  annotate_info->text=(char *)
2633  RelinquishMagickMemory(annotate_info->text);
2634  annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2635  annotate_info=previous_info;
2636  continue;
2637  }
2638  /*
2639  Determine pixel index for box and pen color.
2640  */
2641  windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2642  if (windows->pixel_info->colors != 0)
2643  for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2644  if (windows->pixel_info->pixels[i] ==
2645  windows->pixel_info->pen_colors[box_id].pixel)
2646  {
2647  windows->pixel_info->box_index=(unsigned short) i;
2648  break;
2649  }
2650  windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2651  if (windows->pixel_info->colors != 0)
2652  for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2653  if (windows->pixel_info->pixels[i] ==
2654  windows->pixel_info->pen_colors[pen_id].pixel)
2655  {
2656  windows->pixel_info->pen_index=(unsigned short) i;
2657  break;
2658  }
2659  /*
2660  Define the annotate geometry string.
2661  */
2662  annotate_info->x=(int)
2663  width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2664  annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2665  windows->image.y)/windows->image.ximage->height;
2666  (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2667  "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2668  height*annotate_info->height/windows->image.ximage->height,
2669  annotate_info->x+x,annotate_info->y+y);
2670  /*
2671  Annotate image with text.
2672  */
2673  status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
2674  if (status == 0)
2675  return(MagickFalse);
2676  /*
2677  Free up memory.
2678  */
2679  previous_info=annotate_info->previous;
2680  annotate_info->text=DestroyString(annotate_info->text);
2681  annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2682  annotate_info=previous_info;
2683  }
2684  (void) XSetForeground(display,annotate_context,
2685  windows->pixel_info->foreground_color.pixel);
2686  (void) XSetBackground(display,annotate_context,
2687  windows->pixel_info->background_color.pixel);
2688  (void) XSetFont(display,annotate_context,windows->font_info->fid);
2689  XSetCursorState(display,windows,MagickFalse);
2690  (void) XFreeFont(display,font_info);
2691  /*
2692  Update image configuration.
2693  */
2694  XConfigureImageColormap(display,resource_info,windows,image);
2695  (void) XConfigureImage(display,resource_info,windows,image);
2696  return(MagickTrue);
2697 }
2698 ␌
2699 /*
2700 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2701 % %
2702 % %
2703 % %
2704 + X B a c k g r o u n d I m a g e %
2705 % %
2706 % %
2707 % %
2708 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2709 %
2710 % XBackgroundImage() displays the image in the background of a window.
2711 %
2712 % The format of the XBackgroundImage method is:
2713 %
2714 % MagickBooleanType XBackgroundImage(Display *display,
2715 % XResourceInfo *resource_info,XWindows *windows,Image **image)
2716 %
2717 % A description of each parameter follows:
2718 %
2719 % o display: Specifies a connection to an X server; returned from
2720 % XOpenDisplay.
2721 %
2722 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2723 %
2724 % o windows: Specifies a pointer to a XWindows structure.
2725 %
2726 % o image: the image.
2727 %
2728 */
2729 static MagickBooleanType XBackgroundImage(Display *display,
2730  XResourceInfo *resource_info,XWindows *windows,Image **image)
2731 {
2732 #define BackgroundImageTag "Background/Image"
2733 
2734  int
2735  status;
2736 
2737  static char
2738  window_id[MaxTextExtent] = "root";
2739 
2740  XResourceInfo
2741  background_resources;
2742 
2743  /*
2744  Put image in background.
2745  */
2746  status=XDialogWidget(display,windows,"Background",
2747  "Enter window id (id 0x00 selects window with pointer):",window_id);
2748  if (*window_id == '\0')
2749  return(MagickFalse);
2750  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
2751  XInfoWidget(display,windows,BackgroundImageTag);
2752  XSetCursorState(display,windows,MagickTrue);
2753  XCheckRefreshWindows(display,windows);
2754  background_resources=(*resource_info);
2755  background_resources.window_id=window_id;
2756  background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2757  status=XDisplayBackgroundImage(display,&background_resources,*image);
2758  if (status != MagickFalse)
2759  XClientMessage(display,windows->image.id,windows->im_protocols,
2760  windows->im_retain_colors,CurrentTime);
2761  XSetCursorState(display,windows,MagickFalse);
2762  (void) XMagickCommand(display,resource_info,windows,UndoCommand,image);
2763  return(MagickTrue);
2764 }
2765 ␌
2766 /*
2767 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2768 % %
2769 % %
2770 % %
2771 + X C h o p I m a g e %
2772 % %
2773 % %
2774 % %
2775 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2776 %
2777 % XChopImage() chops the X image.
2778 %
2779 % The format of the XChopImage method is:
2780 %
2781 % MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2782 % XWindows *windows,Image **image)
2783 %
2784 % A description of each parameter follows:
2785 %
2786 % o display: Specifies a connection to an X server; returned from
2787 % XOpenDisplay.
2788 %
2789 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2790 %
2791 % o windows: Specifies a pointer to a XWindows structure.
2792 %
2793 % o image: the image.
2794 %
2795 */
2796 static MagickBooleanType XChopImage(Display *display,
2797  XResourceInfo *resource_info,XWindows *windows,Image **image)
2798 {
2799  const char
2800  *const ChopMenu[] =
2801  {
2802  "Direction",
2803  "Help",
2804  "Dismiss",
2805  (char *) NULL
2806  };
2807 
2808  static ModeType
2809  direction = HorizontalChopCommand;
2810 
2811  static const ModeType
2812  ChopCommands[] =
2813  {
2814  ChopDirectionCommand,
2815  ChopHelpCommand,
2816  ChopDismissCommand
2817  },
2818  DirectionCommands[] =
2819  {
2820  HorizontalChopCommand,
2821  VerticalChopCommand
2822  };
2823 
2824  char
2825  text[MaxTextExtent];
2826 
2827  Image
2828  *chop_image;
2829 
2830  int
2831  id,
2832  x,
2833  y;
2834 
2835  MagickRealType
2836  scale_factor;
2837 
2839  chop_info;
2840 
2841  unsigned int
2842  distance,
2843  height,
2844  width;
2845 
2846  size_t
2847  state;
2848 
2849  XEvent
2850  event;
2851 
2852  XSegment
2853  segment_info;
2854 
2855  /*
2856  Map Command widget.
2857  */
2858  (void) CloneString(&windows->command.name,"Chop");
2859  windows->command.data=1;
2860  (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2861  (void) XMapRaised(display,windows->command.id);
2862  XClientMessage(display,windows->image.id,windows->im_protocols,
2863  windows->im_update_widget,CurrentTime);
2864  /*
2865  Track pointer until button 1 is pressed.
2866  */
2867  XQueryPosition(display,windows->image.id,&x,&y);
2868  (void) XSelectInput(display,windows->image.id,
2869  windows->image.attributes.event_mask | PointerMotionMask);
2870  state=DefaultState;
2871  (void) memset(&segment_info,0,sizeof(segment_info));
2872  do
2873  {
2874  if (windows->info.mapped != MagickFalse)
2875  {
2876  /*
2877  Display pointer position.
2878  */
2879  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2880  x+windows->image.x,y+windows->image.y);
2881  XInfoWidget(display,windows,text);
2882  }
2883  /*
2884  Wait for next event.
2885  */
2886  XScreenEvent(display,windows,&event);
2887  if (event.xany.window == windows->command.id)
2888  {
2889  /*
2890  Select a command from the Command widget.
2891  */
2892  id=XCommandWidget(display,windows,ChopMenu,&event);
2893  if (id < 0)
2894  continue;
2895  switch (ChopCommands[id])
2896  {
2897  case ChopDirectionCommand:
2898  {
2899  char
2900  command[MaxTextExtent];
2901 
2902  const char
2903  *const Directions[] =
2904  {
2905  "horizontal",
2906  "vertical",
2907  (char *) NULL,
2908  };
2909 
2910  /*
2911  Select a command from the pop-up menu.
2912  */
2913  id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2914  if (id >= 0)
2915  direction=DirectionCommands[id];
2916  break;
2917  }
2918  case ChopHelpCommand:
2919  {
2920  XTextViewHelp(display,resource_info,windows,MagickFalse,
2921  "Help Viewer - Image Chop",ImageChopHelp);
2922  break;
2923  }
2924  case ChopDismissCommand:
2925  {
2926  /*
2927  Prematurely exit.
2928  */
2929  state|=EscapeState;
2930  state|=ExitState;
2931  break;
2932  }
2933  default:
2934  break;
2935  }
2936  continue;
2937  }
2938  switch (event.type)
2939  {
2940  case ButtonPress:
2941  {
2942  if (event.xbutton.button != Button1)
2943  break;
2944  if (event.xbutton.window != windows->image.id)
2945  break;
2946  /*
2947  User has committed to start point of chopping line.
2948  */
2949  segment_info.x1=(short int) event.xbutton.x;
2950  segment_info.x2=(short int) event.xbutton.x;
2951  segment_info.y1=(short int) event.xbutton.y;
2952  segment_info.y2=(short int) event.xbutton.y;
2953  state|=ExitState;
2954  break;
2955  }
2956  case ButtonRelease:
2957  break;
2958  case Expose:
2959  break;
2960  case KeyPress:
2961  {
2962  char
2963  command[MaxTextExtent];
2964 
2965  KeySym
2966  key_symbol;
2967 
2968  if (event.xkey.window != windows->image.id)
2969  break;
2970  /*
2971  Respond to a user key press.
2972  */
2973  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2974  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2975  switch ((int) key_symbol)
2976  {
2977  case XK_Escape:
2978  case XK_F20:
2979  {
2980  /*
2981  Prematurely exit.
2982  */
2983  state|=EscapeState;
2984  state|=ExitState;
2985  break;
2986  }
2987  case XK_F1:
2988  case XK_Help:
2989  {
2990  (void) XSetFunction(display,windows->image.highlight_context,
2991  GXcopy);
2992  XTextViewHelp(display,resource_info,windows,MagickFalse,
2993  "Help Viewer - Image Chop",ImageChopHelp);
2994  (void) XSetFunction(display,windows->image.highlight_context,
2995  GXinvert);
2996  break;
2997  }
2998  default:
2999  {
3000  (void) XBell(display,0);
3001  break;
3002  }
3003  }
3004  break;
3005  }
3006  case MotionNotify:
3007  {
3008  /*
3009  Map and unmap Info widget as text cursor crosses its boundaries.
3010  */
3011  x=event.xmotion.x;
3012  y=event.xmotion.y;
3013  if (windows->info.mapped != MagickFalse)
3014  {
3015  if ((x < (int) (windows->info.x+windows->info.width)) &&
3016  (y < (int) (windows->info.y+windows->info.height)))
3017  (void) XWithdrawWindow(display,windows->info.id,
3018  windows->info.screen);
3019  }
3020  else
3021  if ((x > (int) (windows->info.x+windows->info.width)) ||
3022  (y > (int) (windows->info.y+windows->info.height)))
3023  (void) XMapWindow(display,windows->info.id);
3024  }
3025  }
3026  } while ((state & ExitState) == 0);
3027  (void) XSelectInput(display,windows->image.id,
3028  windows->image.attributes.event_mask);
3029  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3030  if ((state & EscapeState) != 0)
3031  return(MagickTrue);
3032  /*
3033  Draw line as pointer moves until the mouse button is released.
3034  */
3035  chop_info.width=0;
3036  chop_info.height=0;
3037  chop_info.x=0;
3038  chop_info.y=0;
3039  distance=0;
3040  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3041  state=DefaultState;
3042  do
3043  {
3044  if (distance > 9)
3045  {
3046  /*
3047  Display info and draw chopping line.
3048  */
3049  if (windows->info.mapped == MagickFalse)
3050  (void) XMapWindow(display,windows->info.id);
3051  (void) FormatLocaleString(text,MaxTextExtent,
3052  " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3053  chop_info.height,(double) chop_info.x,(double) chop_info.y);
3054  XInfoWidget(display,windows,text);
3055  XHighlightLine(display,windows->image.id,
3056  windows->image.highlight_context,&segment_info);
3057  }
3058  else
3059  if (windows->info.mapped != MagickFalse)
3060  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3061  /*
3062  Wait for next event.
3063  */
3064  XScreenEvent(display,windows,&event);
3065  if (distance > 9)
3066  XHighlightLine(display,windows->image.id,
3067  windows->image.highlight_context,&segment_info);
3068  switch (event.type)
3069  {
3070  case ButtonPress:
3071  {
3072  segment_info.x2=(short int) event.xmotion.x;
3073  segment_info.y2=(short int) event.xmotion.y;
3074  break;
3075  }
3076  case ButtonRelease:
3077  {
3078  /*
3079  User has committed to chopping line.
3080  */
3081  segment_info.x2=(short int) event.xbutton.x;
3082  segment_info.y2=(short int) event.xbutton.y;
3083  state|=ExitState;
3084  break;
3085  }
3086  case Expose:
3087  break;
3088  case MotionNotify:
3089  {
3090  segment_info.x2=(short int) event.xmotion.x;
3091  segment_info.y2=(short int) event.xmotion.y;
3092  }
3093  default:
3094  break;
3095  }
3096  /*
3097  Check boundary conditions.
3098  */
3099  if (segment_info.x2 < 0)
3100  segment_info.x2=0;
3101  else
3102  if (segment_info.x2 > windows->image.ximage->width)
3103  segment_info.x2=windows->image.ximage->width;
3104  if (segment_info.y2 < 0)
3105  segment_info.y2=0;
3106  else
3107  if (segment_info.y2 > windows->image.ximage->height)
3108  segment_info.y2=windows->image.ximage->height;
3109  distance=(unsigned int)
3110  (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3111  ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3112  /*
3113  Compute chopping geometry.
3114  */
3115  if (direction == HorizontalChopCommand)
3116  {
3117  chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3118  chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3119  chop_info.height=0;
3120  chop_info.y=0;
3121  if (segment_info.x1 > (int) segment_info.x2)
3122  {
3123  chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3124  chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3125  }
3126  }
3127  else
3128  {
3129  chop_info.width=0;
3130  chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3131  chop_info.x=0;
3132  chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3133  if (segment_info.y1 > segment_info.y2)
3134  {
3135  chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3136  chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3137  }
3138  }
3139  } while ((state & ExitState) == 0);
3140  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3141  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3142  if (distance <= 9)
3143  return(MagickTrue);
3144  /*
3145  Image chopping is relative to image configuration.
3146  */
3147  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
3148  XSetCursorState(display,windows,MagickTrue);
3149  XCheckRefreshWindows(display,windows);
3150  windows->image.window_changes.width=windows->image.ximage->width-
3151  (unsigned int) chop_info.width;
3152  windows->image.window_changes.height=windows->image.ximage->height-
3153  (unsigned int) chop_info.height;
3154  width=(unsigned int) (*image)->columns;
3155  height=(unsigned int) (*image)->rows;
3156  x=0;
3157  y=0;
3158  if (windows->image.crop_geometry != (char *) NULL)
3159  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3160  scale_factor=(MagickRealType) width/windows->image.ximage->width;
3161  chop_info.x+=x;
3162  chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3163  chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3164  scale_factor=(MagickRealType) height/windows->image.ximage->height;
3165  chop_info.y+=y;
3166  chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3167  chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3168  /*
3169  Chop image.
3170  */
3171  chop_image=ChopImage(*image,&chop_info,&(*image)->exception);
3172  XSetCursorState(display,windows,MagickFalse);
3173  if (chop_image == (Image *) NULL)
3174  return(MagickFalse);
3175  *image=DestroyImage(*image);
3176  *image=chop_image;
3177  /*
3178  Update image configuration.
3179  */
3180  XConfigureImageColormap(display,resource_info,windows,*image);
3181  (void) XConfigureImage(display,resource_info,windows,*image);
3182  return(MagickTrue);
3183 }
3184 ␌
3185 /*
3186 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3187 % %
3188 % %
3189 % %
3190 + X C o l o r E d i t I m a g e %
3191 % %
3192 % %
3193 % %
3194 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3195 %
3196 % XColorEditImage() allows the user to interactively change the color of one
3197 % pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3198 %
3199 % The format of the XColorEditImage method is:
3200 %
3201 % MagickBooleanType XColorEditImage(Display *display,
3202 % XResourceInfo *resource_info,XWindows *windows,Image **image)
3203 %
3204 % A description of each parameter follows:
3205 %
3206 % o display: Specifies a connection to an X server; returned from
3207 % XOpenDisplay.
3208 %
3209 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3210 %
3211 % o windows: Specifies a pointer to a XWindows structure.
3212 %
3213 % o image: the image; returned from ReadImage.
3214 %
3215 */
3216 
3217 
3218 static MagickBooleanType XColorEditImage(Display *display,
3219  XResourceInfo *resource_info,XWindows *windows,Image **image)
3220 {
3221  const char
3222  *const ColorEditMenu[] =
3223  {
3224  "Method",
3225  "Pixel Color",
3226  "Border Color",
3227  "Fuzz",
3228  "Undo",
3229  "Help",
3230  "Dismiss",
3231  (char *) NULL
3232  };
3233 
3234  static const ModeType
3235  ColorEditCommands[] =
3236  {
3237  ColorEditMethodCommand,
3238  ColorEditColorCommand,
3239  ColorEditBorderCommand,
3240  ColorEditFuzzCommand,
3241  ColorEditUndoCommand,
3242  ColorEditHelpCommand,
3243  ColorEditDismissCommand
3244  };
3245 
3246  static PaintMethod
3247  method = PointMethod;
3248 
3249  static unsigned int
3250  pen_id = 0;
3251 
3252  static XColor
3253  border_color = { 0, 0, 0, 0, 0, 0 };
3254 
3255  char
3256  command[MaxTextExtent] = "",
3257  text[MaxTextExtent] = "";
3258 
3259  Cursor
3260  cursor;
3261 
3263  *exception;
3264 
3265  int
3266  entry,
3267  id,
3268  x,
3269  x_offset,
3270  y,
3271  y_offset;
3272 
3273  PixelPacket
3274  *q;
3275 
3276  size_t
3277  state;
3278 
3279  ssize_t
3280  i;
3281 
3282  unsigned int
3283  height,
3284  width;
3285 
3286  XColor
3287  color;
3288 
3289  XEvent
3290  event;
3291 
3292  /*
3293  Map Command widget.
3294  */
3295  (void) CloneString(&windows->command.name,"Color Edit");
3296  windows->command.data=4;
3297  (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3298  (void) XMapRaised(display,windows->command.id);
3299  XClientMessage(display,windows->image.id,windows->im_protocols,
3300  windows->im_update_widget,CurrentTime);
3301  /*
3302  Make cursor.
3303  */
3304  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3305  resource_info->background_color,resource_info->foreground_color);
3306  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3307  /*
3308  Track pointer until button 1 is pressed.
3309  */
3310  XQueryPosition(display,windows->image.id,&x,&y);
3311  (void) XSelectInput(display,windows->image.id,
3312  windows->image.attributes.event_mask | PointerMotionMask);
3313  state=DefaultState;
3314  do
3315  {
3316  if (windows->info.mapped != MagickFalse)
3317  {
3318  /*
3319  Display pointer position.
3320  */
3321  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3322  x+windows->image.x,y+windows->image.y);
3323  XInfoWidget(display,windows,text);
3324  }
3325  /*
3326  Wait for next event.
3327  */
3328  XScreenEvent(display,windows,&event);
3329  if (event.xany.window == windows->command.id)
3330  {
3331  /*
3332  Select a command from the Command widget.
3333  */
3334  id=XCommandWidget(display,windows,ColorEditMenu,&event);
3335  if (id < 0)
3336  {
3337  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3338  continue;
3339  }
3340  switch (ColorEditCommands[id])
3341  {
3342  case ColorEditMethodCommand:
3343  {
3344  char
3345  **methods;
3346 
3347  /*
3348  Select a method from the pop-up menu.
3349  */
3350  methods=(char **) GetCommandOptions(MagickMethodOptions);
3351  if (methods == (char **) NULL)
3352  break;
3353  entry=XMenuWidget(display,windows,ColorEditMenu[id],
3354  (const char **) methods,command);
3355  if (entry >= 0)
3356  method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3357  MagickFalse,methods[entry]);
3358  methods=DestroyStringList(methods);
3359  break;
3360  }
3361  case ColorEditColorCommand:
3362  {
3363  const char
3364  *ColorMenu[MaxNumberPens];
3365 
3366  int
3367  pen_number;
3368 
3369  /*
3370  Initialize menu selections.
3371  */
3372  for (i=0; i < (int) (MaxNumberPens-2); i++)
3373  ColorMenu[i]=resource_info->pen_colors[i];
3374  ColorMenu[MaxNumberPens-2]="Browser...";
3375  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3376  /*
3377  Select a pen color from the pop-up menu.
3378  */
3379  pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3380  (const char **) ColorMenu,command);
3381  if (pen_number < 0)
3382  break;
3383  if (pen_number == (MaxNumberPens-2))
3384  {
3385  static char
3386  color_name[MaxTextExtent] = "gray";
3387 
3388  /*
3389  Select a pen color from a dialog.
3390  */
3391  resource_info->pen_colors[pen_number]=color_name;
3392  XColorBrowserWidget(display,windows,"Select",color_name);
3393  if (*color_name == '\0')
3394  break;
3395  }
3396  /*
3397  Set pen color.
3398  */
3399  (void) XParseColor(display,windows->map_info->colormap,
3400  resource_info->pen_colors[pen_number],&color);
3401  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3402  (unsigned int) MaxColors,&color);
3403  windows->pixel_info->pen_colors[pen_number]=color;
3404  pen_id=(unsigned int) pen_number;
3405  break;
3406  }
3407  case ColorEditBorderCommand:
3408  {
3409  const char
3410  *ColorMenu[MaxNumberPens];
3411 
3412  int
3413  pen_number;
3414 
3415  /*
3416  Initialize menu selections.
3417  */
3418  for (i=0; i < (int) (MaxNumberPens-2); i++)
3419  ColorMenu[i]=resource_info->pen_colors[i];
3420  ColorMenu[MaxNumberPens-2]="Browser...";
3421  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3422  /*
3423  Select a pen color from the pop-up menu.
3424  */
3425  pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3426  (const char **) ColorMenu,command);
3427  if (pen_number < 0)
3428  break;
3429  if (pen_number == (MaxNumberPens-2))
3430  {
3431  static char
3432  color_name[MaxTextExtent] = "gray";
3433 
3434  /*
3435  Select a pen color from a dialog.
3436  */
3437  resource_info->pen_colors[pen_number]=color_name;
3438  XColorBrowserWidget(display,windows,"Select",color_name);
3439  if (*color_name == '\0')
3440  break;
3441  }
3442  /*
3443  Set border color.
3444  */
3445  (void) XParseColor(display,windows->map_info->colormap,
3446  resource_info->pen_colors[pen_number],&border_color);
3447  break;
3448  }
3449  case ColorEditFuzzCommand:
3450  {
3451  static char
3452  fuzz[MaxTextExtent];
3453 
3454  static const char
3455  *FuzzMenu[] =
3456  {
3457  "0%",
3458  "2%",
3459  "5%",
3460  "10%",
3461  "15%",
3462  "Dialog...",
3463  (char *) NULL,
3464  };
3465 
3466  /*
3467  Select a command from the pop-up menu.
3468  */
3469  entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3470  command);
3471  if (entry < 0)
3472  break;
3473  if (entry != 5)
3474  {
3475  (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3476  QuantumRange+1.0);
3477  break;
3478  }
3479  (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3480  (void) XDialogWidget(display,windows,"Ok",
3481  "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3482  if (*fuzz == '\0')
3483  break;
3484  (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3485  (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3486  1.0);
3487  break;
3488  }
3489  case ColorEditUndoCommand:
3490  {
3491  (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3492  image);
3493  break;
3494  }
3495  case ColorEditHelpCommand:
3496  default:
3497  {
3498  XTextViewHelp(display,resource_info,windows,MagickFalse,
3499  "Help Viewer - Image Annotation",ImageColorEditHelp);
3500  break;
3501  }
3502  case ColorEditDismissCommand:
3503  {
3504  /*
3505  Prematurely exit.
3506  */
3507  state|=EscapeState;
3508  state|=ExitState;
3509  break;
3510  }
3511  }
3512  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3513  continue;
3514  }
3515  switch (event.type)
3516  {
3517  case ButtonPress:
3518  {
3519  if (event.xbutton.button != Button1)
3520  break;
3521  if ((event.xbutton.window != windows->image.id) &&
3522  (event.xbutton.window != windows->magnify.id))
3523  break;
3524  /*
3525  exit loop.
3526  */
3527  x=event.xbutton.x;
3528  y=event.xbutton.y;
3529  (void) XMagickCommand(display,resource_info,windows,
3530  SaveToUndoBufferCommand,image);
3531  state|=UpdateConfigurationState;
3532  break;
3533  }
3534  case ButtonRelease:
3535  {
3536  if (event.xbutton.button != Button1)
3537  break;
3538  if ((event.xbutton.window != windows->image.id) &&
3539  (event.xbutton.window != windows->magnify.id))
3540  break;
3541  /*
3542  Update colormap information.
3543  */
3544  x=event.xbutton.x;
3545  y=event.xbutton.y;
3546  XConfigureImageColormap(display,resource_info,windows,*image);
3547  (void) XConfigureImage(display,resource_info,windows,*image);
3548  XInfoWidget(display,windows,text);
3549  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3550  state&=(~UpdateConfigurationState);
3551  break;
3552  }
3553  case Expose:
3554  break;
3555  case KeyPress:
3556  {
3557  KeySym
3558  key_symbol;
3559 
3560  if (event.xkey.window == windows->magnify.id)
3561  {
3562  Window
3563  window;
3564 
3565  window=windows->magnify.id;
3566  while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3567  }
3568  if (event.xkey.window != windows->image.id)
3569  break;
3570  /*
3571  Respond to a user key press.
3572  */
3573  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3574  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3575  switch ((int) key_symbol)
3576  {
3577  case XK_Escape:
3578  case XK_F20:
3579  {
3580  /*
3581  Prematurely exit.
3582  */
3583  state|=ExitState;
3584  break;
3585  }
3586  case XK_F1:
3587  case XK_Help:
3588  {
3589  XTextViewHelp(display,resource_info,windows,MagickFalse,
3590  "Help Viewer - Image Annotation",ImageColorEditHelp);
3591  break;
3592  }
3593  default:
3594  {
3595  (void) XBell(display,0);
3596  break;
3597  }
3598  }
3599  break;
3600  }
3601  case MotionNotify:
3602  {
3603  /*
3604  Map and unmap Info widget as cursor crosses its boundaries.
3605  */
3606  x=event.xmotion.x;
3607  y=event.xmotion.y;
3608  if (windows->info.mapped != MagickFalse)
3609  {
3610  if ((x < (int) (windows->info.x+windows->info.width)) &&
3611  (y < (int) (windows->info.y+windows->info.height)))
3612  (void) XWithdrawWindow(display,windows->info.id,
3613  windows->info.screen);
3614  }
3615  else
3616  if ((x > (int) (windows->info.x+windows->info.width)) ||
3617  (y > (int) (windows->info.y+windows->info.height)))
3618  (void) XMapWindow(display,windows->info.id);
3619  break;
3620  }
3621  default:
3622  break;
3623  }
3624  if (event.xany.window == windows->magnify.id)
3625  {
3626  x=windows->magnify.x-windows->image.x;
3627  y=windows->magnify.y-windows->image.y;
3628  }
3629  x_offset=x;
3630  y_offset=y;
3631  if ((state & UpdateConfigurationState) != 0)
3632  {
3633  CacheView
3634  *image_view;
3635 
3636  int
3637  x,
3638  y;
3639 
3640  /*
3641  Pixel edit is relative to image configuration.
3642  */
3643  (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3644  MagickTrue);
3645  color=windows->pixel_info->pen_colors[pen_id];
3646  XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3647  width=(unsigned int) (*image)->columns;
3648  height=(unsigned int) (*image)->rows;
3649  x=0;
3650  y=0;
3651  if (windows->image.crop_geometry != (char *) NULL)
3652  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3653  &width,&height);
3654  x_offset=(int)
3655  (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3656  y_offset=(int)
3657  (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3658  if ((x_offset < 0) || (y_offset < 0))
3659  continue;
3660  if ((x_offset >= (int) (*image)->columns) ||
3661  (y_offset >= (int) (*image)->rows))
3662  continue;
3663  exception=(&(*image)->exception);
3664  image_view=AcquireAuthenticCacheView(*image,exception);
3665  switch (method)
3666  {
3667  case PointMethod:
3668  default:
3669  {
3670  /*
3671  Update color information using point algorithm.
3672  */
3673  if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3674  return(MagickFalse);
3675  q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3676  (ssize_t)y_offset,1,1,exception);
3677  if (q == (PixelPacket *) NULL)
3678  break;
3679  q->red=ScaleShortToQuantum(color.red);
3680  q->green=ScaleShortToQuantum(color.green);
3681  q->blue=ScaleShortToQuantum(color.blue);
3682  (void) SyncCacheViewAuthenticPixels(image_view,
3683  &(*image)->exception);
3684  break;
3685  }
3686  case ReplaceMethod:
3687  {
3688  PixelPacket
3689  target;
3690 
3691  /*
3692  Update color information using replace algorithm.
3693  */
3694  (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3695  (ssize_t) y_offset,&target,&(*image)->exception);
3696  if ((*image)->storage_class == DirectClass)
3697  {
3698  for (y=0; y < (int) (*image)->rows; y++)
3699  {
3700  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3701  (*image)->columns,1,exception);
3702  if (q == (PixelPacket *) NULL)
3703  break;
3704  for (x=0; x < (int) (*image)->columns; x++)
3705  {
3706  if (IsColorSimilar(*image,q,&target) != MagickFalse)
3707  {
3708  q->red=ScaleShortToQuantum(color.red);
3709  q->green=ScaleShortToQuantum(color.green);
3710  q->blue=ScaleShortToQuantum(color.blue);
3711  }
3712  q++;
3713  }
3714  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3715  break;
3716  }
3717  }
3718  else
3719  {
3720  for (i=0; i < (ssize_t) (*image)->colors; i++)
3721  if (IsColorSimilar(*image,(*image)->colormap+i,&target) != MagickFalse)
3722  {
3723  (*image)->colormap[i].red=ScaleShortToQuantum(color.red);
3724  (*image)->colormap[i].green=ScaleShortToQuantum(
3725  color.green);
3726  (*image)->colormap[i].blue=ScaleShortToQuantum(
3727  color.blue);
3728  }
3729  (void) SyncImage(*image);
3730  }
3731  break;
3732  }
3733  case FloodfillMethod:
3734  case FillToBorderMethod:
3735  {
3736  DrawInfo
3737  *draw_info;
3738 
3740  target;
3741 
3742  /*
3743  Update color information using floodfill algorithm.
3744  */
3745  (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
3746  (ssize_t) y_offset,&target,exception);
3747  if (method == FillToBorderMethod)
3748  {
3749  target.red=(MagickRealType)
3750  ScaleShortToQuantum(border_color.red);
3751  target.green=(MagickRealType)
3752  ScaleShortToQuantum(border_color.green);
3753  target.blue=(MagickRealType)
3754  ScaleShortToQuantum(border_color.blue);
3755  }
3756  draw_info=CloneDrawInfo(resource_info->image_info,
3757  (DrawInfo *) NULL);
3758  (void) QueryColorDatabase(resource_info->pen_colors[pen_id],
3759  &draw_info->fill,exception);
3760  (void) FloodfillPaintImage(*image,DefaultChannels,draw_info,&target,
3761  (ssize_t) x_offset,(ssize_t) y_offset,
3762  method == FloodfillMethod ? MagickFalse : MagickTrue);
3763  draw_info=DestroyDrawInfo(draw_info);
3764  break;
3765  }
3766  case ResetMethod:
3767  {
3768  /*
3769  Update color information using reset algorithm.
3770  */
3771  if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3772  return(MagickFalse);
3773  for (y=0; y < (int) (*image)->rows; y++)
3774  {
3775  q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3776  (*image)->columns,1,exception);
3777  if (q == (PixelPacket *) NULL)
3778  break;
3779  for (x=0; x < (int) (*image)->columns; x++)
3780  {
3781  q->red=ScaleShortToQuantum(color.red);
3782  q->green=ScaleShortToQuantum(color.green);
3783  q->blue=ScaleShortToQuantum(color.blue);
3784  q++;
3785  }
3786  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3787  break;
3788  }
3789  break;
3790  }
3791  }
3792  image_view=DestroyCacheView(image_view);
3793  state&=(~UpdateConfigurationState);
3794  }
3795  } while ((state & ExitState) == 0);
3796  (void) XSelectInput(display,windows->image.id,
3797  windows->image.attributes.event_mask);
3798  XSetCursorState(display,windows,MagickFalse);
3799  (void) XFreeCursor(display,cursor);
3800  return(MagickTrue);
3801 }
3802 ␌
3803 /*
3804 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3805 % %
3806 % %
3807 % %
3808 + X C o m p o s i t e I m a g e %
3809 % %
3810 % %
3811 % %
3812 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3813 %
3814 % XCompositeImage() requests an image name from the user, reads the image and
3815 % composites it with the X window image at a location the user chooses with
3816 % the pointer.
3817 %
3818 % The format of the XCompositeImage method is:
3819 %
3820 % MagickBooleanType XCompositeImage(Display *display,
3821 % XResourceInfo *resource_info,XWindows *windows,Image *image)
3822 %
3823 % A description of each parameter follows:
3824 %
3825 % o display: Specifies a connection to an X server; returned from
3826 % XOpenDisplay.
3827 %
3828 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3829 %
3830 % o windows: Specifies a pointer to a XWindows structure.
3831 %
3832 % o image: the image; returned from ReadImage.
3833 %
3834 */
3835 static MagickBooleanType XCompositeImage(Display *display,
3836  XResourceInfo *resource_info,XWindows *windows,Image *image)
3837 {
3838  const char
3839  *const CompositeMenu[] =
3840  {
3841  "Operators",
3842  "Dissolve",
3843  "Displace",
3844  "Help",
3845  "Dismiss",
3846  (char *) NULL
3847  };
3848 
3849  static char
3850  displacement_geometry[MaxTextExtent] = "30x30",
3851  filename[MaxTextExtent] = "\0";
3852 
3853  static CompositeOperator
3854  compose = CopyCompositeOp;
3855 
3856  static const ModeType
3857  CompositeCommands[] =
3858  {
3859  CompositeOperatorsCommand,
3860  CompositeDissolveCommand,
3861  CompositeDisplaceCommand,
3862  CompositeHelpCommand,
3863  CompositeDismissCommand
3864  };
3865 
3866  char
3867  text[MaxTextExtent];
3868 
3869  Cursor
3870  cursor;
3871 
3872  Image
3873  *composite_image;
3874 
3875  int
3876  entry,
3877  id,
3878  x,
3879  y;
3880 
3881  MagickRealType
3882  blend,
3883  scale_factor;
3884 
3886  highlight_info,
3887  composite_info;
3888 
3889  unsigned int
3890  height,
3891  width;
3892 
3893  size_t
3894  state;
3895 
3896  XEvent
3897  event;
3898 
3899  /*
3900  Request image file name from user.
3901  */
3902  XFileBrowserWidget(display,windows,"Composite",filename);
3903  if (*filename == '\0')
3904  return(MagickTrue);
3905  /*
3906  Read image.
3907  */
3908  XSetCursorState(display,windows,MagickTrue);
3909  XCheckRefreshWindows(display,windows);
3910  (void) CopyMagickString(resource_info->image_info->filename,filename,
3911  MaxTextExtent);
3912  composite_image=ReadImage(resource_info->image_info,&image->exception);
3913  CatchException(&image->exception);
3914  XSetCursorState(display,windows,MagickFalse);
3915  if (composite_image == (Image *) NULL)
3916  return(MagickFalse);
3917  /*
3918  Map Command widget.
3919  */
3920  (void) CloneString(&windows->command.name,"Composite");
3921  windows->command.data=1;
3922  (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3923  (void) XMapRaised(display,windows->command.id);
3924  XClientMessage(display,windows->image.id,windows->im_protocols,
3925  windows->im_update_widget,CurrentTime);
3926  /*
3927  Track pointer until button 1 is pressed.
3928  */
3929  XQueryPosition(display,windows->image.id,&x,&y);
3930  (void) XSelectInput(display,windows->image.id,
3931  windows->image.attributes.event_mask | PointerMotionMask);
3932  composite_info.x=(ssize_t) windows->image.x+x;
3933  composite_info.y=(ssize_t) windows->image.y+y;
3934  composite_info.width=0;
3935  composite_info.height=0;
3936  cursor=XCreateFontCursor(display,XC_ul_angle);
3937  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3938  blend=0.0;
3939  state=DefaultState;
3940  do
3941  {
3942  if (windows->info.mapped != MagickFalse)
3943  {
3944  /*
3945  Display pointer position.
3946  */
3947  (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
3948  (long) composite_info.x,(long) composite_info.y);
3949  XInfoWidget(display,windows,text);
3950  }
3951  highlight_info=composite_info;
3952  highlight_info.x=composite_info.x-windows->image.x;
3953  highlight_info.y=composite_info.y-windows->image.y;
3954  XHighlightRectangle(display,windows->image.id,
3955  windows->image.highlight_context,&highlight_info);
3956  /*
3957  Wait for next event.
3958  */
3959  XScreenEvent(display,windows,&event);
3960  XHighlightRectangle(display,windows->image.id,
3961  windows->image.highlight_context,&highlight_info);
3962  if (event.xany.window == windows->command.id)
3963  {
3964  /*
3965  Select a command from the Command widget.
3966  */
3967  id=XCommandWidget(display,windows,CompositeMenu,&event);
3968  if (id < 0)
3969  continue;
3970  switch (CompositeCommands[id])
3971  {
3972  case CompositeOperatorsCommand:
3973  {
3974  char
3975  command[MaxTextExtent],
3976  **operators;
3977 
3978  /*
3979  Select a command from the pop-up menu.
3980  */
3981  operators=GetCommandOptions(MagickComposeOptions);
3982  if (operators == (char **) NULL)
3983  break;
3984  entry=XMenuWidget(display,windows,CompositeMenu[id],
3985  (const char **) operators,command);
3986  if (entry >= 0)
3987  compose=(CompositeOperator) ParseCommandOption(
3988  MagickComposeOptions,MagickFalse,operators[entry]);
3989  operators=DestroyStringList(operators);
3990  break;
3991  }
3992  case CompositeDissolveCommand:
3993  {
3994  static char
3995  factor[MaxTextExtent] = "20.0";
3996 
3997  /*
3998  Dissolve the two images a given percent.
3999  */
4000  (void) XSetFunction(display,windows->image.highlight_context,
4001  GXcopy);
4002  (void) XDialogWidget(display,windows,"Dissolve",
4003  "Enter the blend factor (0.0 - 99.9%):",factor);
4004  (void) XSetFunction(display,windows->image.highlight_context,
4005  GXinvert);
4006  if (*factor == '\0')
4007  break;
4008  blend=StringToDouble(factor,(char **) NULL);
4009  compose=DissolveCompositeOp;
4010  break;
4011  }
4012  case CompositeDisplaceCommand:
4013  {
4014  /*
4015  Get horizontal and vertical scale displacement geometry.
4016  */
4017  (void) XSetFunction(display,windows->image.highlight_context,
4018  GXcopy);
4019  (void) XDialogWidget(display,windows,"Displace",
4020  "Enter the horizontal and vertical scale:",displacement_geometry);
4021  (void) XSetFunction(display,windows->image.highlight_context,
4022  GXinvert);
4023  if (*displacement_geometry == '\0')
4024  break;
4025  compose=DisplaceCompositeOp;
4026  break;
4027  }
4028  case CompositeHelpCommand:
4029  {
4030  (void) XSetFunction(display,windows->image.highlight_context,
4031  GXcopy);
4032  XTextViewHelp(display,resource_info,windows,MagickFalse,
4033  "Help Viewer - Image Composite",ImageCompositeHelp);
4034  (void) XSetFunction(display,windows->image.highlight_context,
4035  GXinvert);
4036  break;
4037  }
4038  case CompositeDismissCommand:
4039  {
4040  /*
4041  Prematurely exit.
4042  */
4043  state|=EscapeState;
4044  state|=ExitState;
4045  break;
4046  }
4047  default:
4048  break;
4049  }
4050  continue;
4051  }
4052  switch (event.type)
4053  {
4054  case ButtonPress:
4055  {
4056  if (resource_info->debug != MagickFalse)
4057  (void) LogMagickEvent(X11Event,GetMagickModule(),
4058  "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4059  event.xbutton.button,event.xbutton.x,event.xbutton.y);
4060  if (event.xbutton.button != Button1)
4061  break;
4062  if (event.xbutton.window != windows->image.id)
4063  break;
4064  /*
4065  Change cursor.
4066  */
4067  composite_info.width=composite_image->columns;
4068  composite_info.height=composite_image->rows;
4069  (void) XCheckDefineCursor(display,windows->image.id,cursor);
4070  composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4071  composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4072  break;
4073  }
4074  case ButtonRelease:
4075  {
4076  if (resource_info->debug != MagickFalse)
4077  (void) LogMagickEvent(X11Event,GetMagickModule(),
4078  "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4079  event.xbutton.button,event.xbutton.x,event.xbutton.y);
4080  if (event.xbutton.button != Button1)
4081  break;
4082  if (event.xbutton.window != windows->image.id)
4083  break;
4084  if ((composite_info.width != 0) && (composite_info.height != 0))
4085  {
4086  /*
4087  User has selected the location of the composite image.
4088  */
4089  composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4090  composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4091  state|=ExitState;
4092  }
4093  break;
4094  }
4095  case Expose:
4096  break;
4097  case KeyPress:
4098  {
4099  char
4100  command[MaxTextExtent];
4101 
4102  KeySym
4103  key_symbol;
4104 
4105  int
4106  length;
4107 
4108  if (event.xkey.window != windows->image.id)
4109  break;
4110  /*
4111  Respond to a user key press.
4112  */
4113  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4114  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4115  *(command+length)='\0';
4116  if (resource_info->debug != MagickFalse)
4117  (void) LogMagickEvent(X11Event,GetMagickModule(),
4118  "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4119  switch ((int) key_symbol)
4120  {
4121  case XK_Escape:
4122  case XK_F20:
4123  {
4124  /*
4125  Prematurely exit.
4126  */
4127  composite_image=DestroyImage(composite_image);
4128  state|=EscapeState;
4129  state|=ExitState;
4130  break;
4131  }
4132  case XK_F1:
4133  case XK_Help:
4134  {
4135  (void) XSetFunction(display,windows->image.highlight_context,
4136  GXcopy);
4137  XTextViewHelp(display,resource_info,windows,MagickFalse,
4138  "Help Viewer - Image Composite",ImageCompositeHelp);
4139  (void) XSetFunction(display,windows->image.highlight_context,
4140  GXinvert);
4141  break;
4142  }
4143  default:
4144  {
4145  (void) XBell(display,0);
4146  break;
4147  }
4148  }
4149  break;
4150  }
4151  case MotionNotify:
4152  {
4153  /*
4154  Map and unmap Info widget as text cursor crosses its boundaries.
4155  */
4156  x=event.xmotion.x;
4157  y=event.xmotion.y;
4158  if (windows->info.mapped != MagickFalse)
4159  {
4160  if ((x < (int) (windows->info.x+windows->info.width)) &&
4161  (y < (int) (windows->info.y+windows->info.height)))
4162  (void) XWithdrawWindow(display,windows->info.id,
4163  windows->info.screen);
4164  }
4165  else
4166  if ((x > (int) (windows->info.x+windows->info.width)) ||
4167  (y > (int) (windows->info.y+windows->info.height)))
4168  (void) XMapWindow(display,windows->info.id);
4169  composite_info.x=(ssize_t) windows->image.x+x;
4170  composite_info.y=(ssize_t) windows->image.y+y;
4171  break;
4172  }
4173  default:
4174  {
4175  if (resource_info->debug != MagickFalse)
4176  (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4177  event.type);
4178  break;
4179  }
4180  }
4181  } while ((state & ExitState) == 0);
4182  (void) XSelectInput(display,windows->image.id,
4183  windows->image.attributes.event_mask);
4184  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4185  XSetCursorState(display,windows,MagickFalse);
4186  (void) XFreeCursor(display,cursor);
4187  if ((state & EscapeState) != 0)
4188  return(MagickTrue);
4189  /*
4190  Image compositing is relative to image configuration.
4191  */
4192  XSetCursorState(display,windows,MagickTrue);
4193  XCheckRefreshWindows(display,windows);
4194  width=(unsigned int) image->columns;
4195  height=(unsigned int) image->rows;
4196  x=0;
4197  y=0;
4198  if (windows->image.crop_geometry != (char *) NULL)
4199  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4200  scale_factor=(MagickRealType) width/windows->image.ximage->width;
4201  composite_info.x+=x;
4202  composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4203  composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4204  scale_factor=(MagickRealType) height/windows->image.ximage->height;
4205  composite_info.y+=y;
4206  composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4207  composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4208  if ((composite_info.width != composite_image->columns) ||
4209  (composite_info.height != composite_image->rows))
4210  {
4211  Image
4212  *resize_image;
4213 
4214  /*
4215  Scale composite image.
4216  */
4217  resize_image=ResizeImage(composite_image,composite_info.width,
4218  composite_info.height,composite_image->filter,composite_image->blur,
4219  &image->exception);
4220  composite_image=DestroyImage(composite_image);
4221  if (resize_image == (Image *) NULL)
4222  {
4223  XSetCursorState(display,windows,MagickFalse);
4224  return(MagickFalse);
4225  }
4226  composite_image=resize_image;
4227  }
4228  if (compose == DisplaceCompositeOp)
4229  (void) SetImageArtifact(composite_image,"compose:args",
4230  displacement_geometry);
4231  if (blend != 0.0)
4232  {
4233  CacheView
4234  *image_view;
4235 
4237  *exception;
4238 
4239  int
4240  y;
4241 
4242  Quantum
4243  opacity;
4244 
4245  int
4246  x;
4247 
4248  PixelPacket
4249  *q;
4250 
4251  /*
4252  Create mattes for blending.
4253  */
4254  (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel);
4255  opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4256  ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4257  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
4258  return(MagickFalse);
4259  image->matte=MagickTrue;
4260  exception=(&image->exception);
4261  image_view=AcquireAuthenticCacheView(image,exception);
4262  for (y=0; y < (int) image->rows; y++)
4263  {
4264  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4265  exception);
4266  if (q == (PixelPacket *) NULL)
4267  break;
4268  for (x=0; x < (int) image->columns; x++)
4269  {
4270  q->opacity=opacity;
4271  q++;
4272  }
4273  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4274  break;
4275  }
4276  image_view=DestroyCacheView(image_view);
4277  }
4278  /*
4279  Composite image with X Image window.
4280  */
4281  (void) CompositeImage(image,compose,composite_image,composite_info.x,
4282  composite_info.y);
4283  composite_image=DestroyImage(composite_image);
4284  XSetCursorState(display,windows,MagickFalse);
4285  /*
4286  Update image configuration.
4287  */
4288  XConfigureImageColormap(display,resource_info,windows,image);
4289  (void) XConfigureImage(display,resource_info,windows,image);
4290  return(MagickTrue);
4291 }
4292 ␌
4293 /*
4294 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4295 % %
4296 % %
4297 % %
4298 + X C o n f i g u r e I m a g e %
4299 % %
4300 % %
4301 % %
4302 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4303 %
4304 % XConfigureImage() creates a new X image. It also notifies the window
4305 % manager of the new image size and configures the transient widows.
4306 %
4307 % The format of the XConfigureImage method is:
4308 %
4309 % MagickBooleanType XConfigureImage(Display *display,
4310 % XResourceInfo *resource_info,XWindows *windows,Image *image)
4311 %
4312 % A description of each parameter follows:
4313 %
4314 % o display: Specifies a connection to an X server; returned from
4315 % XOpenDisplay.
4316 %
4317 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4318 %
4319 % o windows: Specifies a pointer to a XWindows structure.
4320 %
4321 % o image: the image.
4322 %
4323 %
4324 */
4325 static MagickBooleanType XConfigureImage(Display *display,
4326  XResourceInfo *resource_info,XWindows *windows,Image *image)
4327 {
4328  char
4329  geometry[MaxTextExtent];
4330 
4331  MagickStatusType
4332  status;
4333 
4334  size_t
4335  mask,
4336  height,
4337  width;
4338 
4339  ssize_t
4340  x,
4341  y;
4342 
4343  XSizeHints
4344  *size_hints;
4345 
4346  XWindowChanges
4347  window_changes;
4348 
4349  /*
4350  Dismiss if window dimensions are zero.
4351  */
4352  width=(unsigned int) windows->image.window_changes.width;
4353  height=(unsigned int) windows->image.window_changes.height;
4354  if (resource_info->debug != MagickFalse)
4355  (void) LogMagickEvent(X11Event,GetMagickModule(),
4356  "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4357  windows->image.ximage->height,(double) width,(double) height);
4358  if ((width*height) == 0)
4359  return(MagickTrue);
4360  x=0;
4361  y=0;
4362  /*
4363  Resize image to fit Image window dimensions.
4364  */
4365  XSetCursorState(display,windows,MagickTrue);
4366  (void) XFlush(display);
4367  if (((int) width != windows->image.ximage->width) ||
4368  ((int) height != windows->image.ximage->height))
4369  image->taint=MagickTrue;
4370  windows->magnify.x=(int)
4371  width*windows->magnify.x/windows->image.ximage->width;
4372  windows->magnify.y=(int)
4373  height*windows->magnify.y/windows->image.ximage->height;
4374  windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4375  windows->image.y=(int)
4376  (height*windows->image.y/windows->image.ximage->height);
4377  status=XMakeImage(display,resource_info,&windows->image,image,
4378  (unsigned int) width,(unsigned int) height);
4379  if (status == MagickFalse)
4380  XNoticeWidget(display,windows,"Unable to configure X image:",
4381  windows->image.name);
4382  /*
4383  Notify window manager of the new configuration.
4384  */
4385  if (resource_info->image_geometry != (char *) NULL)
4386  (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4387  resource_info->image_geometry);
4388  else
4389  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4390  XDisplayWidth(display,windows->image.screen),
4391  XDisplayHeight(display,windows->image.screen));
4392  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4393  window_changes.width=(int) width;
4394  if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4395  window_changes.width=XDisplayWidth(display,windows->image.screen);
4396  window_changes.height=(int) height;
4397  if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4398  window_changes.height=XDisplayHeight(display,windows->image.screen);
4399  mask=(size_t) (CWWidth | CWHeight);
4400  if (resource_info->backdrop)
4401  {
4402  mask|=CWX | CWY;
4403  window_changes.x=(int)
4404  ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4405  window_changes.y=(int)
4406  ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4407  }
4408  (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4409  (unsigned int) mask,&window_changes);
4410  (void) XClearWindow(display,windows->image.id);
4411  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4412  /*
4413  Update Magnify window configuration.
4414  */
4415  if (windows->magnify.mapped != MagickFalse)
4416  XMakeMagnifyImage(display,windows);
4417  windows->pan.crop_geometry=windows->image.crop_geometry;
4418  XBestIconSize(display,&windows->pan,image);
4419  while (((windows->pan.width << 1) < MaxIconSize) &&
4420  ((windows->pan.height << 1) < MaxIconSize))
4421  {
4422  windows->pan.width<<=1;
4423  windows->pan.height<<=1;
4424  }
4425  if (windows->pan.geometry != (char *) NULL)
4426  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4427  &windows->pan.width,&windows->pan.height);
4428  window_changes.width=(int) windows->pan.width;
4429  window_changes.height=(int) windows->pan.height;
4430  size_hints=XAllocSizeHints();
4431  if (size_hints != (XSizeHints *) NULL)
4432  {
4433  /*
4434  Set new size hints.
4435  */
4436  size_hints->flags=PSize | PMinSize | PMaxSize;
4437  size_hints->width=window_changes.width;
4438  size_hints->height=window_changes.height;
4439  size_hints->min_width=size_hints->width;
4440  size_hints->min_height=size_hints->height;
4441  size_hints->max_width=size_hints->width;
4442  size_hints->max_height=size_hints->height;
4443  (void) XSetNormalHints(display,windows->pan.id,size_hints);
4444  (void) XFree((void *) size_hints);
4445  }
4446  (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4447  (unsigned int) (CWWidth | CWHeight),&window_changes);
4448  /*
4449  Update icon window configuration.
4450  */
4451  windows->icon.crop_geometry=windows->image.crop_geometry;
4452  XBestIconSize(display,&windows->icon,image);
4453  window_changes.width=(int) windows->icon.width;
4454  window_changes.height=(int) windows->icon.height;
4455  (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4456  (unsigned int) (CWWidth | CWHeight),&window_changes);
4457  XSetCursorState(display,windows,MagickFalse);
4458  return(status != 0 ? MagickTrue : MagickFalse);
4459 }
4460 ␌
4461 /*
4462 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4463 % %
4464 % %
4465 % %
4466 + X C r o p I m a g e %
4467 % %
4468 % %
4469 % %
4470 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4471 %
4472 % XCropImage() allows the user to select a region of the image and crop, copy,
4473 % or cut it. For copy or cut, the image can subsequently be composited onto
4474 % the image with XPasteImage.
4475 %
4476 % The format of the XCropImage method is:
4477 %
4478 % MagickBooleanType XCropImage(Display *display,
4479 % XResourceInfo *resource_info,XWindows *windows,Image *image,
4480 % const ClipboardMode mode)
4481 %
4482 % A description of each parameter follows:
4483 %
4484 % o display: Specifies a connection to an X server; returned from
4485 % XOpenDisplay.
4486 %
4487 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4488 %
4489 % o windows: Specifies a pointer to a XWindows structure.
4490 %
4491 % o image: the image; returned from ReadImage.
4492 %
4493 % o mode: This unsigned value specified whether the image should be
4494 % cropped, copied, or cut.
4495 %
4496 */
4497 static MagickBooleanType XCropImage(Display *display,
4498  XResourceInfo *resource_info,XWindows *windows,Image *image,
4499  const ClipboardMode mode)
4500 {
4501  static const char
4502  *CropModeMenu[] =
4503  {
4504  "Help",
4505  "Dismiss",
4506  (char *) NULL
4507  },
4508  *RectifyModeMenu[] =
4509  {
4510  "Crop",
4511  "Help",
4512  "Dismiss",
4513  (char *) NULL
4514  };
4515 
4516  static const ModeType
4517  CropCommands[] =
4518  {
4519  CropHelpCommand,
4520  CropDismissCommand
4521  },
4522  RectifyCommands[] =
4523  {
4524  RectifyCopyCommand,
4525  RectifyHelpCommand,
4526  RectifyDismissCommand
4527  };
4528 
4529  CacheView
4530  *image_view;
4531 
4532  char
4533  command[MaxTextExtent],
4534  text[MaxTextExtent];
4535 
4536  Cursor
4537  cursor;
4538 
4540  *exception;
4541 
4542  int
4543  id,
4544  x,
4545  y;
4546 
4547  KeySym
4548  key_symbol;
4549 
4550  Image
4551  *crop_image;
4552 
4553  MagickRealType
4554  scale_factor;
4555 
4557  crop_info,
4558  highlight_info;
4559 
4560  PixelPacket
4561  *q;
4562 
4563  unsigned int
4564  height,
4565  width;
4566 
4567  size_t
4568  state;
4569 
4570  XEvent
4571  event;
4572 
4573  /*
4574  Map Command widget.
4575  */
4576  switch (mode)
4577  {
4578  case CopyMode:
4579  {
4580  (void) CloneString(&windows->command.name,"Copy");
4581  break;
4582  }
4583  case CropMode:
4584  {
4585  (void) CloneString(&windows->command.name,"Crop");
4586  break;
4587  }
4588  case CutMode:
4589  {
4590  (void) CloneString(&windows->command.name,"Cut");
4591  break;
4592  }
4593  }
4594  RectifyModeMenu[0]=windows->command.name;
4595  windows->command.data=0;
4596  (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4597  (void) XMapRaised(display,windows->command.id);
4598  XClientMessage(display,windows->image.id,windows->im_protocols,
4599  windows->im_update_widget,CurrentTime);
4600  /*
4601  Track pointer until button 1 is pressed.
4602  */
4603  XQueryPosition(display,windows->image.id,&x,&y);
4604  (void) XSelectInput(display,windows->image.id,
4605  windows->image.attributes.event_mask | PointerMotionMask);
4606  crop_info.x=(ssize_t) windows->image.x+x;
4607  crop_info.y=(ssize_t) windows->image.y+y;
4608  crop_info.width=0;
4609  crop_info.height=0;
4610  cursor=XCreateFontCursor(display,XC_fleur);
4611  state=DefaultState;
4612  do
4613  {
4614  if (windows->info.mapped != MagickFalse)
4615  {
4616  /*
4617  Display pointer position.
4618  */
4619  (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4620  (long) crop_info.x,(long) crop_info.y);
4621  XInfoWidget(display,windows,text);
4622  }
4623  /*
4624  Wait for next event.
4625  */
4626  XScreenEvent(display,windows,&event);
4627  if (event.xany.window == windows->command.id)
4628  {
4629  /*
4630  Select a command from the Command widget.
4631  */
4632  id=XCommandWidget(display,windows,CropModeMenu,&event);
4633  if (id < 0)
4634  continue;
4635  switch (CropCommands[id])
4636  {
4637  case CropHelpCommand:
4638  {
4639  switch (mode)
4640  {
4641  case CopyMode:
4642  {
4643  XTextViewHelp(display,resource_info,windows,MagickFalse,
4644  "Help Viewer - Image Copy",ImageCopyHelp);
4645  break;
4646  }
4647  case CropMode:
4648  {
4649  XTextViewHelp(display,resource_info,windows,MagickFalse,
4650  "Help Viewer - Image Crop",ImageCropHelp);
4651  break;
4652  }
4653  case CutMode:
4654  {
4655  XTextViewHelp(display,resource_info,windows,MagickFalse,
4656  "Help Viewer - Image Cut",ImageCutHelp);
4657  break;
4658  }
4659  }
4660  break;
4661  }
4662  case CropDismissCommand:
4663  {
4664  /*
4665  Prematurely exit.
4666  */
4667  state|=EscapeState;
4668  state|=ExitState;
4669  break;
4670  }
4671  default:
4672  break;
4673  }
4674  continue;
4675  }
4676  switch (event.type)
4677  {
4678  case ButtonPress:
4679  {
4680  if (event.xbutton.button != Button1)
4681  break;
4682  if (event.xbutton.window != windows->image.id)
4683  break;
4684  /*
4685  Note first corner of cropping rectangle-- exit loop.
4686  */
4687  (void) XCheckDefineCursor(display,windows->image.id,cursor);
4688  crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4689  crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4690  state|=ExitState;
4691  break;
4692  }
4693  case ButtonRelease:
4694  break;
4695  case Expose:
4696  break;
4697  case KeyPress:
4698  {
4699  if (event.xkey.window != windows->image.id)
4700  break;
4701  /*
4702  Respond to a user key press.
4703  */
4704  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4705  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4706  switch ((int) key_symbol)
4707  {
4708  case XK_Escape:
4709  case XK_F20:
4710  {
4711  /*
4712  Prematurely exit.
4713  */
4714  state|=EscapeState;
4715  state|=ExitState;
4716  break;
4717  }
4718  case XK_F1:
4719  case XK_Help:
4720  {
4721  switch (mode)
4722  {
4723  case CopyMode:
4724  {
4725  XTextViewHelp(display,resource_info,windows,MagickFalse,
4726  "Help Viewer - Image Copy",ImageCopyHelp);
4727  break;
4728  }
4729  case CropMode:
4730  {
4731  XTextViewHelp(display,resource_info,windows,MagickFalse,
4732  "Help Viewer - Image Crop",ImageCropHelp);
4733  break;
4734  }
4735  case CutMode:
4736  {
4737  XTextViewHelp(display,resource_info,windows,MagickFalse,
4738  "Help Viewer - Image Cut",ImageCutHelp);
4739  break;
4740  }
4741  }
4742  break;
4743  }
4744  default:
4745  {
4746  (void) XBell(display,0);
4747  break;
4748  }
4749  }
4750  break;
4751  }
4752  case MotionNotify:
4753  {
4754  if (event.xmotion.window != windows->image.id)
4755  break;
4756  /*
4757  Map and unmap Info widget as text cursor crosses its boundaries.
4758  */
4759  x=event.xmotion.x;
4760  y=event.xmotion.y;
4761  if (windows->info.mapped != MagickFalse)
4762  {
4763  if ((x < (int) (windows->info.x+windows->info.width)) &&
4764  (y < (int) (windows->info.y+windows->info.height)))
4765  (void) XWithdrawWindow(display,windows->info.id,
4766  windows->info.screen);
4767  }
4768  else
4769  if ((x > (int) (windows->info.x+windows->info.width)) ||
4770  (y > (int) (windows->info.y+windows->info.height)))
4771  (void) XMapWindow(display,windows->info.id);
4772  crop_info.x=(ssize_t) windows->image.x+x;
4773  crop_info.y=(ssize_t) windows->image.y+y;
4774  break;
4775  }
4776  default:
4777  break;
4778  }
4779  } while ((state & ExitState) == 0);
4780  (void) XSelectInput(display,windows->image.id,
4781  windows->image.attributes.event_mask);
4782  if ((state & EscapeState) != 0)
4783  {
4784  /*
4785  User want to exit without cropping.
4786  */
4787  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4788  (void) XFreeCursor(display,cursor);
4789  return(MagickTrue);
4790  }
4791  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4792  do
4793  {
4794  /*
4795  Size rectangle as pointer moves until the mouse button is released.
4796  */
4797  x=(int) crop_info.x;
4798  y=(int) crop_info.y;
4799  crop_info.width=0;
4800  crop_info.height=0;
4801  state=DefaultState;
4802  do
4803  {
4804  highlight_info=crop_info;
4805  highlight_info.x=crop_info.x-windows->image.x;
4806  highlight_info.y=crop_info.y-windows->image.y;
4807  if ((highlight_info.width > 3) && (highlight_info.height > 3))
4808  {
4809  /*
4810  Display info and draw cropping rectangle.
4811  */
4812  if (windows->info.mapped == MagickFalse)
4813  (void) XMapWindow(display,windows->info.id);
4814  (void) FormatLocaleString(text,MaxTextExtent,
4815  " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4816  crop_info.height,(double) crop_info.x,(double) crop_info.y);
4817  XInfoWidget(display,windows,text);
4818  XHighlightRectangle(display,windows->image.id,
4819  windows->image.highlight_context,&highlight_info);
4820  }
4821  else
4822  if (windows->info.mapped != MagickFalse)
4823  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4824  /*
4825  Wait for next event.
4826  */
4827  XScreenEvent(display,windows,&event);
4828  if ((highlight_info.width > 3) && (highlight_info.height > 3))
4829  XHighlightRectangle(display,windows->image.id,
4830  windows->image.highlight_context,&highlight_info);
4831  switch (event.type)
4832  {
4833  case ButtonPress:
4834  {
4835  crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4836  crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4837  break;
4838  }
4839  case ButtonRelease:
4840  {
4841  /*
4842  User has committed to cropping rectangle.
4843  */
4844  crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4845  crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4846  XSetCursorState(display,windows,MagickFalse);
4847  state|=ExitState;
4848  windows->command.data=0;
4849  (void) XCommandWidget(display,windows,RectifyModeMenu,
4850  (XEvent *) NULL);
4851  break;
4852  }
4853  case Expose:
4854  break;
4855  case MotionNotify:
4856  {
4857  crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4858  crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4859  }
4860  default:
4861  break;
4862  }
4863  if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4864  ((state & ExitState) != 0))
4865  {
4866  /*
4867  Check boundary conditions.
4868  */
4869  if (crop_info.x < 0)
4870  crop_info.x=0;
4871  else
4872  if (crop_info.x > (ssize_t) windows->image.ximage->width)
4873  crop_info.x=(ssize_t) windows->image.ximage->width;
4874  if ((int) crop_info.x < x)
4875  crop_info.width=(unsigned int) (x-crop_info.x);
4876  else
4877  {
4878  crop_info.width=(unsigned int) (crop_info.x-x);
4879  crop_info.x=(ssize_t) x;
4880  }
4881  if (crop_info.y < 0)
4882  crop_info.y=0;
4883  else
4884  if (crop_info.y > (ssize_t) windows->image.ximage->height)
4885  crop_info.y=(ssize_t) windows->image.ximage->height;
4886  if ((int) crop_info.y < y)
4887  crop_info.height=(unsigned int) (y-crop_info.y);
4888  else
4889  {
4890  crop_info.height=(unsigned int) (crop_info.y-y);
4891  crop_info.y=(ssize_t) y;
4892  }
4893  }
4894  } while ((state & ExitState) == 0);
4895  /*
4896  Wait for user to grab a corner of the rectangle or press return.
4897  */
4898  state=DefaultState;
4899  (void) XMapWindow(display,windows->info.id);
4900  do
4901  {
4902  if (windows->info.mapped != MagickFalse)
4903  {
4904  /*
4905  Display pointer position.
4906  */
4907  (void) FormatLocaleString(text,MaxTextExtent,
4908  " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4909  crop_info.height,(double) crop_info.x,(double) crop_info.y);
4910  XInfoWidget(display,windows,text);
4911  }
4912  highlight_info=crop_info;
4913  highlight_info.x=crop_info.x-windows->image.x;
4914  highlight_info.y=crop_info.y-windows->image.y;
4915  if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4916  {
4917  state|=EscapeState;
4918  state|=ExitState;
4919  break;
4920  }
4921  XHighlightRectangle(display,windows->image.id,
4922  windows->image.highlight_context,&highlight_info);
4923  XScreenEvent(display,windows,&event);
4924  if (event.xany.window == windows->command.id)
4925  {
4926  /*
4927  Select a command from the Command widget.
4928  */
4929  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4930  id=XCommandWidget(display,windows,RectifyModeMenu,&event);
4931  (void) XSetFunction(display,windows->image.highlight_context,
4932  GXinvert);
4933  XHighlightRectangle(display,windows->image.id,
4934  windows->image.highlight_context,&highlight_info);
4935  if (id >= 0)
4936  switch (RectifyCommands[id])
4937  {
4938  case RectifyCopyCommand:
4939  {
4940  state|=ExitState;
4941  break;
4942  }
4943  case RectifyHelpCommand:
4944  {
4945  (void) XSetFunction(display,windows->image.highlight_context,
4946  GXcopy);
4947  switch (mode)
4948  {
4949  case CopyMode:
4950  {
4951  XTextViewHelp(display,resource_info,windows,MagickFalse,
4952  "Help Viewer - Image Copy",ImageCopyHelp);
4953  break;
4954  }
4955  case CropMode:
4956  {
4957  XTextViewHelp(display,resource_info,windows,MagickFalse,
4958  "Help Viewer - Image Crop",ImageCropHelp);
4959  break;
4960  }
4961  case CutMode:
4962  {
4963  XTextViewHelp(display,resource_info,windows,MagickFalse,
4964  "Help Viewer - Image Cut",ImageCutHelp);
4965  break;
4966  }
4967  }
4968  (void) XSetFunction(display,windows->image.highlight_context,
4969  GXinvert);
4970  break;
4971  }
4972  case RectifyDismissCommand:
4973  {
4974  /*
4975  Prematurely exit.
4976  */
4977  state|=EscapeState;
4978  state|=ExitState;
4979  break;
4980  }
4981  default:
4982  break;
4983  }
4984  continue;
4985  }
4986  XHighlightRectangle(display,windows->image.id,
4987  windows->image.highlight_context,&highlight_info);
4988  switch (event.type)
4989  {
4990  case ButtonPress:
4991  {
4992  if (event.xbutton.button != Button1)
4993  break;
4994  if (event.xbutton.window != windows->image.id)
4995  break;
4996  x=windows->image.x+event.xbutton.x;
4997  y=windows->image.y+event.xbutton.y;
4998  if ((x < (int) (crop_info.x+RoiDelta)) &&
4999  (x > (int) (crop_info.x-RoiDelta)) &&
5000  (y < (int) (crop_info.y+RoiDelta)) &&
5001  (y > (int) (crop_info.y-RoiDelta)))
5002  {
5003  crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5004  crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5005  state|=UpdateConfigurationState;
5006  break;
5007  }
5008  if ((x < (int) (crop_info.x+RoiDelta)) &&
5009  (x > (int) (crop_info.x-RoiDelta)) &&
5010  (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5011  (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5012  {
5013  crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5014  state|=UpdateConfigurationState;
5015  break;
5016  }
5017  if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5018  (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5019  (y < (int) (crop_info.y+RoiDelta)) &&
5020  (y > (int) (crop_info.y-RoiDelta)))
5021  {
5022  crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5023  state|=UpdateConfigurationState;
5024  break;
5025  }
5026  if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5027  (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5028  (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5029  (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5030  {
5031  state|=UpdateConfigurationState;
5032  break;
5033  }
5034  magick_fallthrough;
5035  }
5036  case ButtonRelease:
5037  {
5038  if (event.xbutton.window == windows->pan.id)
5039  if ((highlight_info.x != crop_info.x-windows->image.x) ||
5040  (highlight_info.y != crop_info.y-windows->image.y))
5041  XHighlightRectangle(display,windows->image.id,
5042  windows->image.highlight_context,&highlight_info);
5043  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5044  event.xbutton.time);
5045  break;
5046  }
5047  case Expose:
5048  {
5049  if (event.xexpose.window == windows->image.id)
5050  if (event.xexpose.count == 0)
5051  {
5052  event.xexpose.x=(int) highlight_info.x;
5053  event.xexpose.y=(int) highlight_info.y;
5054  event.xexpose.width=(int) highlight_info.width;
5055  event.xexpose.height=(int) highlight_info.height;
5056  XRefreshWindow(display,&windows->image,&event);
5057  }
5058  if (event.xexpose.window == windows->info.id)
5059  if (event.xexpose.count == 0)
5060  XInfoWidget(display,windows,text);
5061  break;
5062  }
5063  case KeyPress:
5064  {
5065  if (event.xkey.window != windows->image.id)
5066  break;
5067  /*
5068  Respond to a user key press.
5069  */
5070  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5071  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5072  switch ((int) key_symbol)
5073  {
5074  case XK_Escape:
5075  case XK_F20:
5076  {
5077  state|=EscapeState;
5078  magick_fallthrough;
5079  }
5080  case XK_Return:
5081  {
5082  state|=ExitState;
5083  break;
5084  }
5085  case XK_Home:
5086  case XK_KP_Home:
5087  {
5088  crop_info.x=(ssize_t) (windows->image.width/2L-crop_info.width/
5089  2L);
5090  crop_info.y=(ssize_t) (windows->image.height/2L-crop_info.height/
5091  2L);
5092  break;
5093  }
5094  case XK_Left:
5095  case XK_KP_Left:
5096  {
5097  crop_info.x--;
5098  break;
5099  }
5100  case XK_Up:
5101  case XK_KP_Up:
5102  case XK_Next:
5103  {
5104  crop_info.y--;
5105  break;
5106  }
5107  case XK_Right:
5108  case XK_KP_Right:
5109  {
5110  crop_info.x++;
5111  break;
5112  }
5113  case XK_Prior:
5114  case XK_Down:
5115  case XK_KP_Down:
5116  {
5117  crop_info.y++;
5118  break;
5119  }
5120  case XK_F1:
5121  case XK_Help:
5122  {
5123  (void) XSetFunction(display,windows->image.highlight_context,
5124  GXcopy);
5125  switch (mode)
5126  {
5127  case CopyMode:
5128  {
5129  XTextViewHelp(display,resource_info,windows,MagickFalse,
5130  "Help Viewer - Image Copy",ImageCopyHelp);
5131  break;
5132  }
5133  case CropMode:
5134  {
5135  XTextViewHelp(display,resource_info,windows,MagickFalse,
5136  "Help Viewer - Image Cropg",ImageCropHelp);
5137  break;
5138  }
5139  case CutMode:
5140  {
5141  XTextViewHelp(display,resource_info,windows,MagickFalse,
5142  "Help Viewer - Image Cutg",ImageCutHelp);
5143  break;
5144  }
5145  }
5146  (void) XSetFunction(display,windows->image.highlight_context,
5147  GXinvert);
5148  break;
5149  }
5150  default:
5151  {
5152  (void) XBell(display,0);
5153  break;
5154  }
5155  }
5156  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5157  event.xkey.time);
5158  break;
5159  }
5160  case KeyRelease:
5161  break;
5162  case MotionNotify:
5163  {
5164  if (event.xmotion.window != windows->image.id)
5165  break;
5166  /*
5167  Map and unmap Info widget as text cursor crosses its boundaries.
5168  */
5169  x=event.xmotion.x;
5170  y=event.xmotion.y;
5171  if (windows->info.mapped != MagickFalse)
5172  {
5173  if ((x < (int) (windows->info.x+windows->info.width)) &&
5174  (y < (int) (windows->info.y+windows->info.height)))
5175  (void) XWithdrawWindow(display,windows->info.id,
5176  windows->info.screen);
5177  }
5178  else
5179  if ((x > (int) (windows->info.x+windows->info.width)) ||
5180  (y > (int) (windows->info.y+windows->info.height)))
5181  (void) XMapWindow(display,windows->info.id);
5182  crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5183  crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5184  break;
5185  }
5186  case SelectionRequest:
5187  {
5188  XSelectionEvent
5189  notify;
5190 
5191  XSelectionRequestEvent
5192  *request;
5193 
5194  /*
5195  Set primary selection.
5196  */
5197  (void) FormatLocaleString(text,MaxTextExtent,
5198  "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5199  crop_info.height,(double) crop_info.x,(double) crop_info.y);
5200  request=(&(event.xselectionrequest));
5201  (void) XChangeProperty(request->display,request->requestor,
5202  request->property,request->target,8,PropModeReplace,
5203  (unsigned char *) text,(int) strlen(text));
5204  notify.type=SelectionNotify;
5205  notify.display=request->display;
5206  notify.requestor=request->requestor;
5207  notify.selection=request->selection;
5208  notify.target=request->target;
5209  notify.time=request->time;
5210  if (request->property == None)
5211  notify.property=request->target;
5212  else
5213  notify.property=request->property;
5214  (void) XSendEvent(request->display,request->requestor,False,0,
5215  (XEvent *) &notify);
5216  }
5217  default:
5218  break;
5219  }
5220  if ((state & UpdateConfigurationState) != 0)
5221  {
5222  (void) XPutBackEvent(display,&event);
5223  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5224  break;
5225  }
5226  } while ((state & ExitState) == 0);
5227  } while ((state & ExitState) == 0);
5228  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5229  XSetCursorState(display,windows,MagickFalse);
5230  if ((state & EscapeState) != 0)
5231  return(MagickTrue);
5232  if (mode == CropMode)
5233  if (((int) crop_info.width != windows->image.ximage->width) ||
5234  ((int) crop_info.height != windows->image.ximage->height))
5235  {
5236  /*
5237  Reconfigure Image window as defined by cropping rectangle.
5238  */
5239  XSetCropGeometry(display,windows,&crop_info,image);
5240  windows->image.window_changes.width=(int) crop_info.width;
5241  windows->image.window_changes.height=(int) crop_info.height;
5242  (void) XConfigureImage(display,resource_info,windows,image);
5243  return(MagickTrue);
5244  }
5245  /*
5246  Copy image before applying image transforms.
5247  */
5248  XSetCursorState(display,windows,MagickTrue);
5249  XCheckRefreshWindows(display,windows);
5250  width=(unsigned int) image->columns;
5251  height=(unsigned int) image->rows;
5252  x=0;
5253  y=0;
5254  if (windows->image.crop_geometry != (char *) NULL)
5255  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5256  scale_factor=(MagickRealType) width/windows->image.ximage->width;
5257  crop_info.x+=x;
5258  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5259  crop_info.x+=image->page.x;
5260  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5261  scale_factor=(MagickRealType) height/windows->image.ximage->height;
5262  crop_info.y+=y;
5263  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5264  crop_info.y+=image->page.y;
5265  crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5266  crop_image=CropImage(image,&crop_info,&image->exception);
5267  XSetCursorState(display,windows,MagickFalse);
5268  if (crop_image == (Image *) NULL)
5269  return(MagickFalse);
5270  if (resource_info->copy_image != (Image *) NULL)
5271  resource_info->copy_image=DestroyImage(resource_info->copy_image);
5272  resource_info->copy_image=crop_image;
5273  if (mode == CopyMode)
5274  {
5275  (void) XConfigureImage(display,resource_info,windows,image);
5276  return(MagickTrue);
5277  }
5278  /*
5279  Cut image.
5280  */
5281  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
5282  return(MagickFalse);
5283  image->matte=MagickTrue;
5284  exception=(&image->exception);
5285  image_view=AcquireAuthenticCacheView(image,exception);
5286  for (y=0; y < (int) crop_info.height; y++)
5287  {
5288  q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5289  crop_info.width,1,exception);
5290  if (q == (PixelPacket *) NULL)
5291  break;
5292  for (x=0; x < (int) crop_info.width; x++)
5293  {
5294  q->opacity=(Quantum) TransparentOpacity;
5295  q++;
5296  }
5297  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5298  break;
5299  }
5300  image_view=DestroyCacheView(image_view);
5301  /*
5302  Update image configuration.
5303  */
5304  XConfigureImageColormap(display,resource_info,windows,image);
5305  (void) XConfigureImage(display,resource_info,windows,image);
5306  return(MagickTrue);
5307 }
5308 ␌
5309 /*
5310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5311 % %
5312 % %
5313 % %
5314 + X D r a w I m a g e %
5315 % %
5316 % %
5317 % %
5318 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5319 %
5320 % XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5321 % the image.
5322 %
5323 % The format of the XDrawEditImage method is:
5324 %
5325 % MagickBooleanType XDrawEditImage(Display *display,
5326 % XResourceInfo *resource_info,XWindows *windows,Image **image)
5327 %
5328 % A description of each parameter follows:
5329 %
5330 % o display: Specifies a connection to an X server; returned from
5331 % XOpenDisplay.
5332 %
5333 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5334 %
5335 % o windows: Specifies a pointer to a XWindows structure.
5336 %
5337 % o image: the image.
5338 %
5339 */
5340 static MagickBooleanType XDrawEditImage(Display *display,
5341  XResourceInfo *resource_info,XWindows *windows,Image **image)
5342 {
5343  const char
5344  *const DrawMenu[] =
5345  {
5346  "Element",
5347  "Color",
5348  "Stipple",
5349  "Width",
5350  "Undo",
5351  "Help",
5352  "Dismiss",
5353  (char *) NULL
5354  };
5355 
5356  static ElementType
5357  element = PointElement;
5358 
5359  static const ModeType
5360  DrawCommands[] =
5361  {
5362  DrawElementCommand,
5363  DrawColorCommand,
5364  DrawStippleCommand,
5365  DrawWidthCommand,
5366  DrawUndoCommand,
5367  DrawHelpCommand,
5368  DrawDismissCommand
5369  };
5370 
5371  static Pixmap
5372  stipple = (Pixmap) NULL;
5373 
5374  static unsigned int
5375  pen_id = 0,
5376  line_width = 1;
5377 
5378  char
5379  command[MaxTextExtent],
5380  text[MaxTextExtent];
5381 
5382  Cursor
5383  cursor;
5384 
5385  int
5386  entry,
5387  id,
5388  number_coordinates,
5389  x,
5390  y;
5391 
5392  MagickRealType
5393  degrees;
5394 
5395  MagickStatusType
5396  status;
5397 
5399  rectangle_info;
5400 
5401  int
5402  i;
5403 
5404  unsigned int
5405  distance,
5406  height,
5407  max_coordinates,
5408  width;
5409 
5410  size_t
5411  state;
5412 
5413  Window
5414  root_window;
5415 
5416  XDrawInfo
5417  draw_info;
5418 
5419  XEvent
5420  event;
5421 
5422  XPoint
5423  *coordinate_info;
5424 
5425  XSegment
5426  line_info;
5427 
5428  /*
5429  Allocate polygon info.
5430  */
5431  max_coordinates=2048;
5432  coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5433  sizeof(*coordinate_info));
5434  if (coordinate_info == (XPoint *) NULL)
5435  {
5436  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
5437  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5438  return(MagickFalse);
5439  }
5440  /*
5441  Map Command widget.
5442  */
5443  (void) CloneString(&windows->command.name,"Draw");
5444  windows->command.data=4;
5445  (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5446  (void) XMapRaised(display,windows->command.id);
5447  XClientMessage(display,windows->image.id,windows->im_protocols,
5448  windows->im_update_widget,CurrentTime);
5449  /*
5450  Wait for first button press.
5451  */
5452  root_window=XRootWindow(display,XDefaultScreen(display));
5453  draw_info.stencil=OpaqueStencil;
5454  status=MagickTrue;
5455  cursor=XCreateFontCursor(display,XC_tcross);
5456  for ( ; ; )
5457  {
5458  XQueryPosition(display,windows->image.id,&x,&y);
5459  (void) XSelectInput(display,windows->image.id,
5460  windows->image.attributes.event_mask | PointerMotionMask);
5461  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5462  state=DefaultState;
5463  do
5464  {
5465  if (windows->info.mapped != MagickFalse)
5466  {
5467  /*
5468  Display pointer position.
5469  */
5470  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5471  x+windows->image.x,y+windows->image.y);
5472  XInfoWidget(display,windows,text);
5473  }
5474  /*
5475  Wait for next event.
5476  */
5477  XScreenEvent(display,windows,&event);
5478  if (event.xany.window == windows->command.id)
5479  {
5480  /*
5481  Select a command from the Command widget.
5482  */
5483  id=XCommandWidget(display,windows,DrawMenu,&event);
5484  if (id < 0)
5485  continue;
5486  switch (DrawCommands[id])
5487  {
5488  case DrawElementCommand:
5489  {
5490  const char
5491  *const Elements[] =
5492  {
5493  "point",
5494  "line",
5495  "rectangle",
5496  "fill rectangle",
5497  "circle",
5498  "fill circle",
5499  "ellipse",
5500  "fill ellipse",
5501  "polygon",
5502  "fill polygon",
5503  (char *) NULL,
5504  };
5505 
5506  /*
5507  Select a command from the pop-up menu.
5508  */
5509  element=(ElementType) (XMenuWidget(display,windows,
5510  DrawMenu[id],Elements,command)+1);
5511  break;
5512  }
5513  case DrawColorCommand:
5514  {
5515  const char
5516  *ColorMenu[MaxNumberPens+1];
5517 
5518  int
5519  pen_number;
5520 
5521  MagickBooleanType
5522  transparent;
5523 
5524  XColor
5525  color;
5526 
5527  /*
5528  Initialize menu selections.
5529  */
5530  for (i=0; i < (int) (MaxNumberPens-2); i++)
5531  ColorMenu[i]=resource_info->pen_colors[i];
5532  ColorMenu[MaxNumberPens-2]="transparent";
5533  ColorMenu[MaxNumberPens-1]="Browser...";
5534  ColorMenu[MaxNumberPens]=(char *) NULL;
5535  /*
5536  Select a pen color from the pop-up menu.
5537  */
5538  pen_number=XMenuWidget(display,windows,DrawMenu[id],
5539  (const char **) ColorMenu,command);
5540  if (pen_number < 0)
5541  break;
5542  transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5543  MagickFalse;
5544  if (transparent != MagickFalse)
5545  {
5546  draw_info.stencil=TransparentStencil;
5547  break;
5548  }
5549  if (pen_number == (MaxNumberPens-1))
5550  {
5551  static char
5552  color_name[MaxTextExtent] = "gray";
5553 
5554  /*
5555  Select a pen color from a dialog.
5556  */
5557  resource_info->pen_colors[pen_number]=color_name;
5558  XColorBrowserWidget(display,windows,"Select",color_name);
5559  if (*color_name == '\0')
5560  break;
5561  }
5562  /*
5563  Set pen color.
5564  */
5565  (void) XParseColor(display,windows->map_info->colormap,
5566  resource_info->pen_colors[pen_number],&color);
5567  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5568  (unsigned int) MaxColors,&color);
5569  windows->pixel_info->pen_colors[pen_number]=color;
5570  pen_id=(unsigned int) pen_number;
5571  draw_info.stencil=OpaqueStencil;
5572  break;
5573  }
5574  case DrawStippleCommand:
5575  {
5576  const char
5577  *StipplesMenu[] =
5578  {
5579  "Brick",
5580  "Diagonal",
5581  "Scales",
5582  "Vertical",
5583  "Wavy",
5584  "Translucent",
5585  "Opaque",
5586  (char *) NULL,
5587  (char *) NULL,
5588  };
5589 
5590  Image
5591  *stipple_image;
5592 
5593  ImageInfo
5594  *image_info;
5595 
5596  int
5597  status;
5598 
5599  static char
5600  filename[MaxTextExtent] = "\0";
5601 
5602  /*
5603  Select a command from the pop-up menu.
5604  */
5605  StipplesMenu[7]="Open...";
5606  entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5607  command);
5608  if (entry < 0)
5609  break;
5610  if (stipple != (Pixmap) NULL)
5611  (void) XFreePixmap(display,stipple);
5612  stipple=(Pixmap) NULL;
5613  if (entry != 7)
5614  {
5615  switch (entry)
5616  {
5617  case 0:
5618  {
5619  stipple=XCreateBitmapFromData(display,root_window,
5620  (char *) BricksBitmap,BricksWidth,BricksHeight);
5621  break;
5622  }
5623  case 1:
5624  {
5625  stipple=XCreateBitmapFromData(display,root_window,
5626  (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5627  break;
5628  }
5629  case 2:
5630  {
5631  stipple=XCreateBitmapFromData(display,root_window,
5632  (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5633  break;
5634  }
5635  case 3:
5636  {
5637  stipple=XCreateBitmapFromData(display,root_window,
5638  (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5639  break;
5640  }
5641  case 4:
5642  {
5643  stipple=XCreateBitmapFromData(display,root_window,
5644  (char *) WavyBitmap,WavyWidth,WavyHeight);
5645  break;
5646  }
5647  case 5:
5648  {
5649  stipple=XCreateBitmapFromData(display,root_window,
5650  (char *) HighlightBitmap,HighlightWidth,
5651  HighlightHeight);
5652  break;
5653  }
5654  case 6:
5655  default:
5656  {
5657  stipple=XCreateBitmapFromData(display,root_window,
5658  (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5659  break;
5660  }
5661  }
5662  break;
5663  }
5664  XFileBrowserWidget(display,windows,"Stipple",filename);
5665  if (*filename == '\0')
5666  break;
5667  /*
5668  Read image.
5669  */
5670  XSetCursorState(display,windows,MagickTrue);
5671  XCheckRefreshWindows(display,windows);
5672  image_info=AcquireImageInfo();
5673  (void) CopyMagickString(image_info->filename,filename,
5674  MaxTextExtent);
5675  stipple_image=ReadImage(image_info,&(*image)->exception);
5676  CatchException(&(*image)->exception);
5677  XSetCursorState(display,windows,MagickFalse);
5678  if (stipple_image == (Image *) NULL)
5679  break;
5680  (void) AcquireUniqueFileResource(filename);
5681  (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5682  "xbm:%s",filename);
5683  (void) WriteImage(image_info,stipple_image);
5684  stipple_image=DestroyImage(stipple_image);
5685  image_info=DestroyImageInfo(image_info);
5686  status=XReadBitmapFile(display,root_window,filename,&width,
5687  &height,&stipple,&x,&y);
5688  (void) RelinquishUniqueFileResource(filename);
5689  if ((status != BitmapSuccess) != 0)
5690  XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5691  filename);
5692  break;
5693  }
5694  case DrawWidthCommand:
5695  {
5696  const char
5697  *const WidthsMenu[] =
5698  {
5699  "1",
5700  "2",
5701  "4",
5702  "8",
5703  "16",
5704  "Dialog...",
5705  (char *) NULL,
5706  };
5707 
5708  static char
5709  width[MaxTextExtent] = "0";
5710 
5711  /*
5712  Select a command from the pop-up menu.
5713  */
5714  entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5715  command);
5716  if (entry < 0)
5717  break;
5718  if (entry != 5)
5719  {
5720  line_width=(unsigned int) StringToUnsignedLong(
5721  WidthsMenu[entry]);
5722  break;
5723  }
5724  (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5725  width);
5726  if (*width == '\0')
5727  break;
5728  line_width=(unsigned int) StringToUnsignedLong(width);
5729  break;
5730  }
5731  case DrawUndoCommand:
5732  {
5733  (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5734  image);
5735  break;
5736  }
5737  case DrawHelpCommand:
5738  {
5739  XTextViewHelp(display,resource_info,windows,MagickFalse,
5740  "Help Viewer - Image Rotation",ImageDrawHelp);
5741  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5742  break;
5743  }
5744  case DrawDismissCommand:
5745  {
5746  /*
5747  Prematurely exit.
5748  */
5749  state|=EscapeState;
5750  state|=ExitState;
5751  break;
5752  }
5753  default:
5754  break;
5755  }
5756  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5757  continue;
5758  }
5759  switch (event.type)
5760  {
5761  case ButtonPress:
5762  {
5763  if (event.xbutton.button != Button1)
5764  break;
5765  if (event.xbutton.window != windows->image.id)
5766  break;
5767  /*
5768  exit loop.
5769  */
5770  x=event.xbutton.x;
5771  y=event.xbutton.y;
5772  state|=ExitState;
5773  break;
5774  }
5775  case ButtonRelease:
5776  break;
5777  case Expose:
5778  break;
5779  case KeyPress:
5780  {
5781  KeySym
5782  key_symbol;
5783 
5784  if (event.xkey.window != windows->image.id)
5785  break;
5786  /*
5787  Respond to a user key press.
5788  */
5789  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5790  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5791  switch ((int) key_symbol)
5792  {
5793  case XK_Escape:
5794  case XK_F20:
5795  {
5796  /*
5797  Prematurely exit.
5798  */
5799  state|=EscapeState;
5800  state|=ExitState;
5801  break;
5802  }
5803  case XK_F1:
5804  case XK_Help:
5805  {
5806  XTextViewHelp(display,resource_info,windows,MagickFalse,
5807  "Help Viewer - Image Rotation",ImageDrawHelp);
5808  break;
5809  }
5810  default:
5811  {
5812  (void) XBell(display,0);
5813  break;
5814  }
5815  }
5816  break;
5817  }
5818  case MotionNotify:
5819  {
5820  /*
5821  Map and unmap Info widget as text cursor crosses its boundaries.
5822  */
5823  x=event.xmotion.x;
5824  y=event.xmotion.y;
5825  if (windows->info.mapped != MagickFalse)
5826  {
5827  if ((x < (int) (windows->info.x+windows->info.width)) &&
5828  (y < (int) (windows->info.y+windows->info.height)))
5829  (void) XWithdrawWindow(display,windows->info.id,
5830  windows->info.screen);
5831  }
5832  else
5833  if ((x > (int) (windows->info.x+windows->info.width)) ||
5834  (y > (int) (windows->info.y+windows->info.height)))
5835  (void) XMapWindow(display,windows->info.id);
5836  break;
5837  }
5838  }
5839  } while ((state & ExitState) == 0);
5840  (void) XSelectInput(display,windows->image.id,
5841  windows->image.attributes.event_mask);
5842  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5843  if ((state & EscapeState) != 0)
5844  break;
5845  /*
5846  Draw element as pointer moves until the button is released.
5847  */
5848  distance=0;
5849  degrees=0.0;
5850  line_info.x1=x;
5851  line_info.y1=y;
5852  line_info.x2=x;
5853  line_info.y2=y;
5854  rectangle_info.x=(ssize_t) x;
5855  rectangle_info.y=(ssize_t) y;
5856  rectangle_info.width=0;
5857  rectangle_info.height=0;
5858  number_coordinates=1;
5859  coordinate_info->x=x;
5860  coordinate_info->y=y;
5861  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5862  state=DefaultState;
5863  do
5864  {
5865  switch (element)
5866  {
5867  case PointElement:
5868  default:
5869  {
5870  if (number_coordinates > 1)
5871  {
5872  (void) XDrawLines(display,windows->image.id,
5873  windows->image.highlight_context,coordinate_info,
5874  number_coordinates,CoordModeOrigin);
5875  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5876  coordinate_info[number_coordinates-1].x,
5877  coordinate_info[number_coordinates-1].y);
5878  XInfoWidget(display,windows,text);
5879  }
5880  break;
5881  }
5882  case LineElement:
5883  {
5884  if (distance > 9)
5885  {
5886  /*
5887  Display angle of the line.
5888  */
5889  degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5890  line_info.y1),(double) (line_info.x2-line_info.x1)));
5891  (void) FormatLocaleString(text,MaxTextExtent," %g",
5892  (double) degrees);
5893  XInfoWidget(display,windows,text);
5894  XHighlightLine(display,windows->image.id,
5895  windows->image.highlight_context,&line_info);
5896  }
5897  else
5898  if (windows->info.mapped != MagickFalse)
5899  (void) XWithdrawWindow(display,windows->info.id,
5900  windows->info.screen);
5901  break;
5902  }
5903  case RectangleElement:
5904  case FillRectangleElement:
5905  {
5906  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5907  {
5908  /*
5909  Display info and draw drawing rectangle.
5910  */
5911  (void) FormatLocaleString(text,MaxTextExtent,
5912  " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5913  (double) rectangle_info.height,(double) rectangle_info.x,
5914  (double) rectangle_info.y);
5915  XInfoWidget(display,windows,text);
5916  XHighlightRectangle(display,windows->image.id,
5917  windows->image.highlight_context,&rectangle_info);
5918  }
5919  else
5920  if (windows->info.mapped != MagickFalse)
5921  (void) XWithdrawWindow(display,windows->info.id,
5922  windows->info.screen);
5923  break;
5924  }
5925  case CircleElement:
5926  case FillCircleElement:
5927  case EllipseElement:
5928  case FillEllipseElement:
5929  {
5930  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5931  {
5932  /*
5933  Display info and draw drawing rectangle.
5934  */
5935  (void) FormatLocaleString(text,MaxTextExtent,
5936  " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5937  (double) rectangle_info.height,(double) rectangle_info.x,
5938  (double) rectangle_info.y);
5939  XInfoWidget(display,windows,text);
5940  XHighlightEllipse(display,windows->image.id,
5941  windows->image.highlight_context,&rectangle_info);
5942  }
5943  else
5944  if (windows->info.mapped != MagickFalse)
5945  (void) XWithdrawWindow(display,windows->info.id,
5946  windows->info.screen);
5947  break;
5948  }
5949  case PolygonElement:
5950  case FillPolygonElement:
5951  {
5952  if (number_coordinates > 1)
5953  (void) XDrawLines(display,windows->image.id,
5954  windows->image.highlight_context,coordinate_info,
5955  number_coordinates,CoordModeOrigin);
5956  if (distance > 9)
5957  {
5958  /*
5959  Display angle of the line.
5960  */
5961  degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5962  line_info.y1),(double) (line_info.x2-line_info.x1)));
5963  (void) FormatLocaleString(text,MaxTextExtent," %g",
5964  (double) degrees);
5965  XInfoWidget(display,windows,text);
5966  XHighlightLine(display,windows->image.id,
5967  windows->image.highlight_context,&line_info);
5968  }
5969  else
5970  if (windows->info.mapped != MagickFalse)
5971  (void) XWithdrawWindow(display,windows->info.id,
5972  windows->info.screen);
5973  break;
5974  }
5975  }
5976  /*
5977  Wait for next event.
5978  */
5979  XScreenEvent(display,windows,&event);
5980  switch (element)
5981  {
5982  case PointElement:
5983  default:
5984  {
5985  if (number_coordinates > 1)
5986  (void) XDrawLines(display,windows->image.id,
5987  windows->image.highlight_context,coordinate_info,
5988  number_coordinates,CoordModeOrigin);
5989  break;
5990  }
5991  case LineElement:
5992  {
5993  if (distance > 9)
5994  XHighlightLine(display,windows->image.id,
5995  windows->image.highlight_context,&line_info);
5996  break;
5997  }
5998  case RectangleElement:
5999  case FillRectangleElement:
6000  {
6001  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6002  XHighlightRectangle(display,windows->image.id,
6003  windows->image.highlight_context,&rectangle_info);
6004  break;
6005  }
6006  case CircleElement:
6007  case FillCircleElement:
6008  case EllipseElement:
6009  case FillEllipseElement:
6010  {
6011  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6012  XHighlightEllipse(display,windows->image.id,
6013  windows->image.highlight_context,&rectangle_info);
6014  break;
6015  }
6016  case PolygonElement:
6017  case FillPolygonElement:
6018  {
6019  if (number_coordinates > 1)
6020  (void) XDrawLines(display,windows->image.id,
6021  windows->image.highlight_context,coordinate_info,
6022  number_coordinates,CoordModeOrigin);
6023  if (distance > 9)
6024  XHighlightLine(display,windows->image.id,
6025  windows->image.highlight_context,&line_info);
6026  break;
6027  }
6028  }
6029  switch (event.type)
6030  {
6031  case ButtonPress:
6032  break;
6033  case ButtonRelease:
6034  {
6035  /*
6036  User has committed to element.
6037  */
6038  line_info.x2=event.xbutton.x;
6039  line_info.y2=event.xbutton.y;
6040  rectangle_info.x=(ssize_t) event.xbutton.x;
6041  rectangle_info.y=(ssize_t) event.xbutton.y;
6042  coordinate_info[number_coordinates].x=event.xbutton.x;
6043  coordinate_info[number_coordinates].y=event.xbutton.y;
6044  if (((element != PolygonElement) &&
6045  (element != FillPolygonElement)) || (distance <= 9))
6046  {
6047  state|=ExitState;
6048  break;
6049  }
6050  number_coordinates++;
6051  if (number_coordinates < (int) max_coordinates)
6052  {
6053  line_info.x1=event.xbutton.x;
6054  line_info.y1=event.xbutton.y;
6055  break;
6056  }
6057  max_coordinates<<=1;
6058  coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6059  max_coordinates,sizeof(*coordinate_info));
6060  if (coordinate_info == (XPoint *) NULL)
6061  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6062  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6063  break;
6064  }
6065  case Expose:
6066  break;
6067  case MotionNotify:
6068  {
6069  if (event.xmotion.window != windows->image.id)
6070  break;
6071  if (element != PointElement)
6072  {
6073  line_info.x2=event.xmotion.x;
6074  line_info.y2=event.xmotion.y;
6075  rectangle_info.x=(ssize_t) event.xmotion.x;
6076  rectangle_info.y=(ssize_t) event.xmotion.y;
6077  break;
6078  }
6079  coordinate_info[number_coordinates].x=event.xbutton.x;
6080  coordinate_info[number_coordinates].y=event.xbutton.y;
6081  number_coordinates++;
6082  if (number_coordinates < (int) max_coordinates)
6083  break;
6084  max_coordinates<<=1;
6085  coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6086  max_coordinates,sizeof(*coordinate_info));
6087  if (coordinate_info == (XPoint *) NULL)
6088  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6089  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6090  break;
6091  }
6092  default:
6093  break;
6094  }
6095  /*
6096  Check boundary conditions.
6097  */
6098  if (line_info.x2 < 0)
6099  line_info.x2=0;
6100  else
6101  if (line_info.x2 > (int) windows->image.width)
6102  line_info.x2=(short) windows->image.width;
6103  if (line_info.y2 < 0)
6104  line_info.y2=0;
6105  else
6106  if (line_info.y2 > (int) windows->image.height)
6107  line_info.y2=(short) windows->image.height;
6108  distance=(unsigned int)
6109  (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6110  ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6111  if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6112  ((state & ExitState) != 0))
6113  {
6114  if (rectangle_info.x < 0)
6115  rectangle_info.x=0;
6116  else
6117  if (rectangle_info.x > (ssize_t) windows->image.width)
6118  rectangle_info.x=(ssize_t) windows->image.width;
6119  if ((int) rectangle_info.x < x)
6120  rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6121  else
6122  {
6123  rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6124  rectangle_info.x=(ssize_t) x;
6125  }
6126  if (rectangle_info.y < 0)
6127  rectangle_info.y=0;
6128  else
6129  if (rectangle_info.y > (ssize_t) windows->image.height)
6130  rectangle_info.y=(ssize_t) windows->image.height;
6131  if ((int) rectangle_info.y < y)
6132  rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6133  else
6134  {
6135  rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6136  rectangle_info.y=(ssize_t) y;
6137  }
6138  }
6139  } while ((state & ExitState) == 0);
6140  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6141  if ((element == PointElement) || (element == PolygonElement) ||
6142  (element == FillPolygonElement))
6143  {
6144  /*
6145  Determine polygon bounding box.
6146  */
6147  rectangle_info.x=(ssize_t) coordinate_info->x;
6148  rectangle_info.y=(ssize_t) coordinate_info->y;
6149  x=coordinate_info->x;
6150  y=coordinate_info->y;
6151  for (i=1; i < number_coordinates; i++)
6152  {
6153  if (coordinate_info[i].x > x)
6154  x=coordinate_info[i].x;
6155  if (coordinate_info[i].y > y)
6156  y=coordinate_info[i].y;
6157  if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6158  rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6159  if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6160  rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6161  }
6162  rectangle_info.width=(size_t) (x-rectangle_info.x);
6163  rectangle_info.height=(size_t) (y-rectangle_info.y);
6164  for (i=0; i < number_coordinates; i++)
6165  {
6166  coordinate_info[i].x-=rectangle_info.x;
6167  coordinate_info[i].y-=rectangle_info.y;
6168  }
6169  }
6170  else
6171  if (distance <= 9)
6172  continue;
6173  else
6174  if ((element == RectangleElement) ||
6175  (element == CircleElement) || (element == EllipseElement))
6176  {
6177  rectangle_info.width--;
6178  rectangle_info.height--;
6179  }
6180  /*
6181  Drawing is relative to image configuration.
6182  */
6183  draw_info.x=(int) rectangle_info.x;
6184  draw_info.y=(int) rectangle_info.y;
6185  (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6186  image);
6187  width=(unsigned int) (*image)->columns;
6188  height=(unsigned int) (*image)->rows;
6189  x=0;
6190  y=0;
6191  if (windows->image.crop_geometry != (char *) NULL)
6192  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6193  draw_info.x+=windows->image.x-(line_width/2);
6194  if (draw_info.x < 0)
6195  draw_info.x=0;
6196  draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6197  draw_info.y+=windows->image.y-(line_width/2);
6198  if (draw_info.y < 0)
6199  draw_info.y=0;
6200  draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6201  draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6202  if (draw_info.width > (unsigned int) (*image)->columns)
6203  draw_info.width=(unsigned int) (*image)->columns;
6204  draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6205  if (draw_info.height > (unsigned int) (*image)->rows)
6206  draw_info.height=(unsigned int) (*image)->rows;
6207  (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6208  width*draw_info.width/windows->image.ximage->width,
6209  height*draw_info.height/windows->image.ximage->height,
6210  draw_info.x+x,draw_info.y+y);
6211  /*
6212  Initialize drawing attributes.
6213  */
6214  draw_info.degrees=0.0;
6215  draw_info.element=element;
6216  draw_info.stipple=stipple;
6217  draw_info.line_width=line_width;
6218  draw_info.line_info=line_info;
6219  if (line_info.x1 > (int) (line_width/2))
6220  draw_info.line_info.x1=(short) line_width/2;
6221  if (line_info.y1 > (int) (line_width/2))
6222  draw_info.line_info.y1=(short) line_width/2;
6223  draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6224  draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6225  if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6226  {
6227  draw_info.line_info.x2=(-draw_info.line_info.x2);
6228  draw_info.line_info.y2=(-draw_info.line_info.y2);
6229  }
6230  if (draw_info.line_info.x2 < 0)
6231  {
6232  draw_info.line_info.x2=(-draw_info.line_info.x2);
6233  Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6234  }
6235  if (draw_info.line_info.y2 < 0)
6236  {
6237  draw_info.line_info.y2=(-draw_info.line_info.y2);
6238  Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6239  }
6240  draw_info.rectangle_info=rectangle_info;
6241  if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6242  draw_info.rectangle_info.x=(ssize_t) line_width/2;
6243  if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6244  draw_info.rectangle_info.y=(ssize_t) line_width/2;
6245  draw_info.number_coordinates=(unsigned int) number_coordinates;
6246  draw_info.coordinate_info=coordinate_info;
6247  windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6248  /*
6249  Draw element on image.
6250  */
6251  XSetCursorState(display,windows,MagickTrue);
6252  XCheckRefreshWindows(display,windows);
6253  status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6254  XSetCursorState(display,windows,MagickFalse);
6255  /*
6256  Update image colormap and return to image drawing.
6257  */
6258  XConfigureImageColormap(display,resource_info,windows,*image);
6259  (void) XConfigureImage(display,resource_info,windows,*image);
6260  }
6261  XSetCursorState(display,windows,MagickFalse);
6262  coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6263  return(status != 0 ? MagickTrue : MagickFalse);
6264 }
6265 ␌
6266 /*
6267 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6268 % %
6269 % %
6270 % %
6271 + X D r a w P a n R e c t a n g l e %
6272 % %
6273 % %
6274 % %
6275 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6276 %
6277 % XDrawPanRectangle() draws a rectangle in the pan window. The pan window
6278 % displays a zoom image and the rectangle shows which portion of the image is
6279 % displayed in the Image window.
6280 %
6281 % The format of the XDrawPanRectangle method is:
6282 %
6283 % XDrawPanRectangle(Display *display,XWindows *windows)
6284 %
6285 % A description of each parameter follows:
6286 %
6287 % o display: Specifies a connection to an X server; returned from
6288 % XOpenDisplay.
6289 %
6290 % o windows: Specifies a pointer to a XWindows structure.
6291 %
6292 */
6293 static void XDrawPanRectangle(Display *display,XWindows *windows)
6294 {
6295  MagickRealType
6296  scale_factor;
6297 
6299  highlight_info;
6300 
6301  /*
6302  Determine dimensions of the panning rectangle.
6303  */
6304  scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6305  highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6306  highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6307  scale_factor=(MagickRealType)
6308  windows->pan.height/windows->image.ximage->height;
6309  highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6310  highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6311  /*
6312  Display the panning rectangle.
6313  */
6314  (void) XClearWindow(display,windows->pan.id);
6315  XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6316  &highlight_info);
6317 }
6318 ␌
6319 /*
6320 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6321 % %
6322 % %
6323 % %
6324 + X I m a g e C a c h e %
6325 % %
6326 % %
6327 % %
6328 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6329 %
6330 % XImageCache() handles the creation, manipulation, and destruction of the
6331 % image cache (undo and redo buffers).
6332 %
6333 % The format of the XImageCache method is:
6334 %
6335 % void XImageCache(Display *display,XResourceInfo *resource_info,
6336 % XWindows *windows,const DisplayCommand command,Image **image)
6337 %
6338 % A description of each parameter follows:
6339 %
6340 % o display: Specifies a connection to an X server; returned from
6341 % XOpenDisplay.
6342 %
6343 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6344 %
6345 % o windows: Specifies a pointer to a XWindows structure.
6346 %
6347 % o command: Specifies a command to perform.
6348 %
6349 % o image: the image; XImageCache may transform the image and return a new
6350 % image pointer.
6351 %
6352 */
6353 static void XImageCache(Display *display,XResourceInfo *resource_info,
6354  XWindows *windows,const DisplayCommand command,Image **image)
6355 {
6356  Image
6357  *cache_image;
6358 
6359  static Image
6360  *redo_image = (Image *) NULL,
6361  *undo_image = (Image *) NULL;
6362 
6363  switch (command)
6364  {
6365  case FreeBuffersCommand:
6366  {
6367  /*
6368  Free memory from the undo and redo cache.
6369  */
6370  while (undo_image != (Image *) NULL)
6371  {
6372  cache_image=undo_image;
6373  undo_image=GetPreviousImageInList(undo_image);
6374  cache_image->list=DestroyImage(cache_image->list);
6375  cache_image=DestroyImage(cache_image);
6376  }
6377  undo_image=NewImageList();
6378  if (redo_image != (Image *) NULL)
6379  redo_image=DestroyImage(redo_image);
6380  redo_image=NewImageList();
6381  return;
6382  }
6383  case UndoCommand:
6384  {
6385  char
6386  image_geometry[MaxTextExtent];
6387 
6388  /*
6389  Undo the last image transformation.
6390  */
6391  if (undo_image == (Image *) NULL)
6392  {
6393  (void) XBell(display,0);
6394  return;
6395  }
6396  cache_image=undo_image;
6397  undo_image=GetPreviousImageInList(undo_image);
6398  windows->image.window_changes.width=(int) cache_image->columns;
6399  windows->image.window_changes.height=(int) cache_image->rows;
6400  (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6401  windows->image.ximage->width,windows->image.ximage->height);
6402  (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
6403  if (windows->image.crop_geometry != (char *) NULL)
6404  windows->image.crop_geometry=(char *)
6405  RelinquishMagickMemory(windows->image.crop_geometry);
6406  windows->image.crop_geometry=cache_image->geometry;
6407  if (redo_image != (Image *) NULL)
6408  redo_image=DestroyImage(redo_image);
6409  redo_image=(*image);
6410  *image=cache_image->list;
6411  cache_image=DestroyImage(cache_image);
6412  if (windows->image.orphan != MagickFalse)
6413  return;
6414  XConfigureImageColormap(display,resource_info,windows,*image);
6415  (void) XConfigureImage(display,resource_info,windows,*image);
6416  return;
6417  }
6418  case CutCommand:
6419  case PasteCommand:
6420  case ApplyCommand:
6421  case HalfSizeCommand:
6422  case OriginalSizeCommand:
6423  case DoubleSizeCommand:
6424  case ResizeCommand:
6425  case TrimCommand:
6426  case CropCommand:
6427  case ChopCommand:
6428  case FlipCommand:
6429  case FlopCommand:
6430  case RotateRightCommand:
6431  case RotateLeftCommand:
6432  case RotateCommand:
6433  case ShearCommand:
6434  case RollCommand:
6435  case NegateCommand:
6436  case ContrastStretchCommand:
6437  case SigmoidalContrastCommand:
6438  case NormalizeCommand:
6439  case EqualizeCommand:
6440  case HueCommand:
6441  case SaturationCommand:
6442  case BrightnessCommand:
6443  case GammaCommand:
6444  case SpiffCommand:
6445  case DullCommand:
6446  case GrayscaleCommand:
6447  case MapCommand:
6448  case QuantizeCommand:
6449  case DespeckleCommand:
6450  case EmbossCommand:
6451  case ReduceNoiseCommand:
6452  case AddNoiseCommand:
6453  case SharpenCommand:
6454  case BlurCommand:
6455  case ThresholdCommand:
6456  case EdgeDetectCommand:
6457  case SpreadCommand:
6458  case ShadeCommand:
6459  case RaiseCommand:
6460  case SegmentCommand:
6461  case SolarizeCommand:
6462  case SepiaToneCommand:
6463  case SwirlCommand:
6464  case ImplodeCommand:
6465  case VignetteCommand:
6466  case WaveCommand:
6467  case OilPaintCommand:
6468  case CharcoalDrawCommand:
6469  case AnnotateCommand:
6470  case AddBorderCommand:
6471  case AddFrameCommand:
6472  case CompositeCommand:
6473  case CommentCommand:
6474  case LaunchCommand:
6475  case RegionOfInterestCommand:
6476  case SaveToUndoBufferCommand:
6477  case RedoCommand:
6478  {
6479  Image
6480  *previous_image;
6481 
6482  ssize_t
6483  bytes;
6484 
6485  bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
6486  if (undo_image != (Image *) NULL)
6487  {
6488  /*
6489  Ensure the undo cache has enough memory available.
6490  */
6491  previous_image=undo_image;
6492  while (previous_image != (Image *) NULL)
6493  {
6494  bytes+=previous_image->list->columns*previous_image->list->rows*
6495  sizeof(PixelPacket);
6496  if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6497  {
6498  previous_image=GetPreviousImageInList(previous_image);
6499  continue;
6500  }
6501  bytes-=previous_image->list->columns*previous_image->list->rows*
6502  sizeof(PixelPacket);
6503  if (previous_image == undo_image)
6504  undo_image=NewImageList();
6505  else
6506  previous_image->next->previous=NewImageList();
6507  break;
6508  }
6509  while (previous_image != (Image *) NULL)
6510  {
6511  /*
6512  Delete any excess memory from undo cache.
6513  */
6514  cache_image=previous_image;
6515  previous_image=GetPreviousImageInList(previous_image);
6516  cache_image->list=DestroyImage(cache_image->list);
6517  cache_image=DestroyImage(cache_image);
6518  }
6519  }
6520  if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6521  break;
6522  /*
6523  Save image before transformations are applied.
6524  */
6525  cache_image=AcquireImage((ImageInfo *) NULL);
6526  if (cache_image == (Image *) NULL)
6527  break;
6528  XSetCursorState(display,windows,MagickTrue);
6529  XCheckRefreshWindows(display,windows);
6530  cache_image->list=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
6531  XSetCursorState(display,windows,MagickFalse);
6532  if (cache_image->list == (Image *) NULL)
6533  {
6534  cache_image=DestroyImage(cache_image);
6535  break;
6536  }
6537  cache_image->columns=(size_t) windows->image.ximage->width;
6538  cache_image->rows=(size_t) windows->image.ximage->height;
6539  cache_image->geometry=windows->image.crop_geometry;
6540  if (windows->image.crop_geometry != (char *) NULL)
6541  {
6542  cache_image->geometry=AcquireString((char *) NULL);
6543  (void) CopyMagickString(cache_image->geometry,
6544  windows->image.crop_geometry,MaxTextExtent);
6545  }
6546  if (undo_image == (Image *) NULL)
6547  {
6548  undo_image=cache_image;
6549  break;
6550  }
6551  undo_image->next=cache_image;
6552  undo_image->next->previous=undo_image;
6553  undo_image=undo_image->next;
6554  break;
6555  }
6556  default:
6557  break;
6558  }
6559  if (command == RedoCommand)
6560  {
6561  /*
6562  Redo the last image transformation.
6563  */
6564  if (redo_image == (Image *) NULL)
6565  {
6566  (void) XBell(display,0);
6567  return;
6568  }
6569  windows->image.window_changes.width=(int) redo_image->columns;
6570  windows->image.window_changes.height=(int) redo_image->rows;
6571  if (windows->image.crop_geometry != (char *) NULL)
6572  windows->image.crop_geometry=(char *)
6573  RelinquishMagickMemory(windows->image.crop_geometry);
6574  windows->image.crop_geometry=redo_image->geometry;
6575  *image=DestroyImage(*image);
6576  *image=redo_image;
6577  redo_image=NewImageList();
6578  if (windows->image.orphan != MagickFalse)
6579  return;
6580  XConfigureImageColormap(display,resource_info,windows,*image);
6581  (void) XConfigureImage(display,resource_info,windows,*image);
6582  return;
6583  }
6584  if (command != InfoCommand)
6585  return;
6586  /*
6587  Display image info.
6588  */
6589  XSetCursorState(display,windows,MagickTrue);
6590  XCheckRefreshWindows(display,windows);
6591  XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6592  XSetCursorState(display,windows,MagickFalse);
6593 }
6594 ␌
6595 /*
6596 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6597 % %
6598 % %
6599 % %
6600 + X I m a g e W i n d o w C o m m a n d %
6601 % %
6602 % %
6603 % %
6604 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6605 %
6606 % XImageWindowCommand() makes a transform to the image or Image window as
6607 % specified by a user menu button or keyboard command.
6608 %
6609 % The format of the XMagickCommand method is:
6610 %
6611 % DisplayCommand XImageWindowCommand(Display *display,
6612 % XResourceInfo *resource_info,XWindows *windows,
6613 % const MagickStatusType state,KeySym key_symbol,Image **image)
6614 %
6615 % A description of each parameter follows:
6616 %
6617 % o nexus: Method XImageWindowCommand returns an image when the
6618 % user chooses 'Open Image' from the command menu. Otherwise a null
6619 % image is returned.
6620 %
6621 % o display: Specifies a connection to an X server; returned from
6622 % XOpenDisplay.
6623 %
6624 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6625 %
6626 % o windows: Specifies a pointer to a XWindows structure.
6627 %
6628 % o state: key mask.
6629 %
6630 % o key_symbol: Specifies a command to perform.
6631 %
6632 % o image: the image; XImageWIndowCommand
6633 % may transform the image and return a new image pointer.
6634 %
6635 */
6636 static DisplayCommand XImageWindowCommand(Display *display,
6637  XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6638  KeySym key_symbol,Image **image)
6639 {
6640  static char
6641  delta[MaxTextExtent] = "";
6642 
6643  static const char
6644  Digits[] = "01234567890";
6645 
6646  static KeySym
6647  last_symbol = XK_0;
6648 
6649  if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6650  {
6651  if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6652  {
6653  *delta='\0';
6654  resource_info->quantum=1;
6655  }
6656  last_symbol=key_symbol;
6657  delta[strlen(delta)+1]='\0';
6658  delta[strlen(delta)]=Digits[key_symbol-XK_0];
6659  resource_info->quantum=StringToLong(delta);
6660  return(NullCommand);
6661  }
6662  last_symbol=key_symbol;
6663  if (resource_info->immutable)
6664  {
6665  /*
6666  Virtual image window has a restricted command set.
6667  */
6668  switch (key_symbol)
6669  {
6670  case XK_question:
6671  return(InfoCommand);
6672  case XK_p:
6673  case XK_Print:
6674  return(PrintCommand);
6675  case XK_space:
6676  return(NextCommand);
6677  case XK_q:
6678  case XK_Escape:
6679  return(QuitCommand);
6680  default:
6681  break;
6682  }
6683  return(NullCommand);
6684  }
6685  switch ((int) key_symbol)
6686  {
6687  case XK_o:
6688  {
6689  if ((state & ControlMask) == 0)
6690  break;
6691  return(OpenCommand);
6692  }
6693  case XK_space:
6694  return(NextCommand);
6695  case XK_BackSpace:
6696  return(FormerCommand);
6697  case XK_s:
6698  {
6699  if ((state & Mod1Mask) != 0)
6700  return(SwirlCommand);
6701  if ((state & ControlMask) == 0)
6702  return(ShearCommand);
6703  return(SaveCommand);
6704  }
6705  case XK_p:
6706  case XK_Print:
6707  {
6708  if ((state & Mod1Mask) != 0)
6709  return(OilPaintCommand);
6710  if ((state & Mod4Mask) != 0)
6711  return(ColorCommand);
6712  if ((state & ControlMask) == 0)
6713  return(NullCommand);
6714  return(PrintCommand);
6715  }
6716  case XK_d:
6717  {
6718  if ((state & Mod4Mask) != 0)
6719  return(DrawCommand);
6720  if ((state & ControlMask) == 0)
6721  return(NullCommand);
6722  return(DeleteCommand);
6723  }
6724  case XK_Select:
6725  {
6726  if ((state & ControlMask) == 0)
6727  return(NullCommand);
6728  return(SelectCommand);
6729  }
6730  case XK_n:
6731  {
6732  if ((state & ControlMask) == 0)
6733  return(NullCommand);
6734  return(NewCommand);
6735  }
6736  case XK_q:
6737  case XK_Escape:
6738  return(QuitCommand);
6739  case XK_z:
6740  case XK_Undo:
6741  {
6742  if ((state & ControlMask) == 0)
6743  return(NullCommand);
6744  return(UndoCommand);
6745  }
6746  case XK_r:
6747  case XK_Redo:
6748  {
6749  if ((state & ControlMask) == 0)
6750  return(RollCommand);
6751  return(RedoCommand);
6752  }
6753  case XK_x:
6754  {
6755  if ((state & ControlMask) == 0)
6756  return(NullCommand);
6757  return(CutCommand);
6758  }
6759  case XK_c:
6760  {
6761  if ((state & Mod1Mask) != 0)
6762  return(CharcoalDrawCommand);
6763  if ((state & ControlMask) == 0)
6764  return(CropCommand);
6765  return(CopyCommand);
6766  }
6767  case XK_v:
6768  case XK_Insert:
6769  {
6770  if ((state & Mod4Mask) != 0)
6771  return(CompositeCommand);
6772  if ((state & ControlMask) == 0)
6773  return(FlipCommand);
6774  return(PasteCommand);
6775  }
6776  case XK_less:
6777  return(HalfSizeCommand);
6778  case XK_minus:
6779  return(OriginalSizeCommand);
6780  case XK_greater:
6781  return(DoubleSizeCommand);
6782  case XK_percent:
6783  return(ResizeCommand);
6784  case XK_at:
6785  return(RefreshCommand);
6786  case XK_bracketleft:
6787  return(ChopCommand);
6788  case XK_h:
6789  return(FlopCommand);
6790  case XK_slash:
6791  return(RotateRightCommand);
6792  case XK_backslash:
6793  return(RotateLeftCommand);
6794  case XK_asterisk:
6795  return(RotateCommand);
6796  case XK_t:
6797  return(TrimCommand);
6798  case XK_H:
6799  return(HueCommand);
6800  case XK_S:
6801  return(SaturationCommand);
6802  case XK_L:
6803  return(BrightnessCommand);
6804  case XK_G:
6805  return(GammaCommand);
6806  case XK_C:
6807  return(SpiffCommand);
6808  case XK_Z:
6809  return(DullCommand);
6810  case XK_N:
6811  return(NormalizeCommand);
6812  case XK_equal:
6813  return(EqualizeCommand);
6814  case XK_asciitilde:
6815  return(NegateCommand);
6816  case XK_period:
6817  return(GrayscaleCommand);
6818  case XK_numbersign:
6819  return(QuantizeCommand);
6820  case XK_F2:
6821  return(DespeckleCommand);
6822  case XK_F3:
6823  return(EmbossCommand);
6824  case XK_F4:
6825  return(ReduceNoiseCommand);
6826  case XK_F5:
6827  return(AddNoiseCommand);
6828  case XK_F6:
6829  return(SharpenCommand);
6830  case XK_F7:
6831  return(BlurCommand);
6832  case XK_F8:
6833  return(ThresholdCommand);
6834  case XK_F9:
6835  return(EdgeDetectCommand);
6836  case XK_F10:
6837  return(SpreadCommand);
6838  case XK_F11:
6839  return(ShadeCommand);
6840  case XK_F12:
6841  return(RaiseCommand);
6842  case XK_F13:
6843  return(SegmentCommand);
6844  case XK_i:
6845  {
6846  if ((state & Mod1Mask) == 0)
6847  return(NullCommand);
6848  return(ImplodeCommand);
6849  }
6850  case XK_w:
6851  {
6852  if ((state & Mod1Mask) == 0)
6853  return(NullCommand);
6854  return(WaveCommand);
6855  }
6856  case XK_m:
6857  {
6858  if ((state & Mod4Mask) == 0)
6859  return(NullCommand);
6860  return(MatteCommand);
6861  }
6862  case XK_b:
6863  {
6864  if ((state & Mod4Mask) == 0)
6865  return(NullCommand);
6866  return(AddBorderCommand);
6867  }
6868  case XK_f:
6869  {
6870  if ((state & Mod4Mask) == 0)
6871  return(NullCommand);
6872  return(AddFrameCommand);
6873  }
6874  case XK_exclam:
6875  {
6876  if ((state & Mod4Mask) == 0)
6877  return(NullCommand);
6878  return(CommentCommand);
6879  }
6880  case XK_a:
6881  {
6882  if ((state & Mod1Mask) != 0)
6883  return(ApplyCommand);
6884  if ((state & Mod4Mask) != 0)
6885  return(AnnotateCommand);
6886  if ((state & ControlMask) == 0)
6887  return(NullCommand);
6888  return(RegionOfInterestCommand);
6889  }
6890  case XK_question:
6891  return(InfoCommand);
6892  case XK_plus:
6893  return(ZoomCommand);
6894  case XK_P:
6895  {
6896  if ((state & ShiftMask) == 0)
6897  return(NullCommand);
6898  return(ShowPreviewCommand);
6899  }
6900  case XK_Execute:
6901  return(LaunchCommand);
6902  case XK_F1:
6903  return(HelpCommand);
6904  case XK_Find:
6905  return(BrowseDocumentationCommand);
6906  case XK_Menu:
6907  {
6908  (void) XMapRaised(display,windows->command.id);
6909  return(NullCommand);
6910  }
6911  case XK_Next:
6912  case XK_Prior:
6913  case XK_Home:
6914  case XK_KP_Home:
6915  {
6916  XTranslateImage(display,windows,*image,key_symbol);
6917  return(NullCommand);
6918  }
6919  case XK_Up:
6920  case XK_KP_Up:
6921  case XK_Down:
6922  case XK_KP_Down:
6923  case XK_Left:
6924  case XK_KP_Left:
6925  case XK_Right:
6926  case XK_KP_Right:
6927  {
6928  if ((state & Mod1Mask) != 0)
6929  {
6931  crop_info;
6932 
6933  /*
6934  Trim one pixel from edge of image.
6935  */
6936  crop_info.x=0;
6937  crop_info.y=0;
6938  crop_info.width=(size_t) windows->image.ximage->width;
6939  crop_info.height=(size_t) windows->image.ximage->height;
6940  if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
6941  {
6942  if (resource_info->quantum >= (int) crop_info.height)
6943  resource_info->quantum=(int) crop_info.height-1;
6944  crop_info.height-=resource_info->quantum;
6945  }
6946  if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
6947  {
6948  if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
6949  resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
6950  crop_info.y+=resource_info->quantum;
6951  crop_info.height-=resource_info->quantum;
6952  }
6953  if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
6954  {
6955  if (resource_info->quantum >= (int) crop_info.width)
6956  resource_info->quantum=(int) crop_info.width-1;
6957  crop_info.width-=resource_info->quantum;
6958  }
6959  if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
6960  {
6961  if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
6962  resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
6963  crop_info.x+=resource_info->quantum;
6964  crop_info.width-=resource_info->quantum;
6965  }
6966  if ((int) (windows->image.x+windows->image.width) >
6967  (int) crop_info.width)
6968  windows->image.x=(int) (crop_info.width-windows->image.width);
6969  if ((int) (windows->image.y+windows->image.height) >
6970  (int) crop_info.height)
6971  windows->image.y=(int) (crop_info.height-windows->image.height);
6972  XSetCropGeometry(display,windows,&crop_info,*image);
6973  windows->image.window_changes.width=(int) crop_info.width;
6974  windows->image.window_changes.height=(int) crop_info.height;
6975  (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
6976  (void) XConfigureImage(display,resource_info,windows,*image);
6977  return(NullCommand);
6978  }
6979  XTranslateImage(display,windows,*image,key_symbol);
6980  return(NullCommand);
6981  }
6982  default:
6983  return(NullCommand);
6984  }
6985  return(NullCommand);
6986 }
6987 ␌
6988 /*
6989 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6990 % %
6991 % %
6992 % %
6993 + X M a g i c k C o m m a n d %
6994 % %
6995 % %
6996 % %
6997 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6998 %
6999 % XMagickCommand() makes a transform to the image or Image window as
7000 % specified by a user menu button or keyboard command.
7001 %
7002 % The format of the XMagickCommand method is:
7003 %
7004 % Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7005 % XWindows *windows,const DisplayCommand command,Image **image)
7006 %
7007 % A description of each parameter follows:
7008 %
7009 % o nexus: Method XMagickCommand returns an image when the
7010 % user chooses 'Load Image' from the command menu. Otherwise a null
7011 % image is returned.
7012 %
7013 % o display: Specifies a connection to an X server; returned from
7014 % XOpenDisplay.
7015 %
7016 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7017 %
7018 % o windows: Specifies a pointer to a XWindows structure.
7019 %
7020 % o command: Specifies a command to perform.
7021 %
7022 % o image: the image; XMagickCommand
7023 % may transform the image and return a new image pointer.
7024 %
7025 */
7026 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7027  XWindows *windows,const DisplayCommand command,Image **image)
7028 {
7029  char
7030  filename[MaxTextExtent],
7031  geometry[MaxTextExtent],
7032  modulate_factors[MaxTextExtent];
7033 
7034  GeometryInfo
7035  geometry_info;
7036 
7037  Image
7038  *nexus;
7039 
7040  ImageInfo
7041  *image_info;
7042 
7043  int
7044  x,
7045  y;
7046 
7047  MagickStatusType
7048  flags,
7049  status;
7050 
7051  QuantizeInfo
7052  quantize_info;
7053 
7055  page_geometry;
7056 
7057  int
7058  i;
7059 
7060  static char
7061  color[MaxTextExtent] = "gray";
7062 
7063  unsigned int
7064  height,
7065  width;
7066 
7067  /*
7068  Process user command.
7069  */
7070  XCheckRefreshWindows(display,windows);
7071  XImageCache(display,resource_info,windows,command,image);
7072  nexus=NewImageList();
7073  windows->image.window_changes.width=windows->image.ximage->width;
7074  windows->image.window_changes.height=windows->image.ximage->height;
7075  image_info=CloneImageInfo(resource_info->image_info);
7076  SetGeometryInfo(&geometry_info);
7077  GetQuantizeInfo(&quantize_info);
7078  switch (command)
7079  {
7080  case OpenCommand:
7081  {
7082  /*
7083  Load image.
7084  */
7085  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7086  break;
7087  }
7088  case NextCommand:
7089  {
7090  /*
7091  Display next image.
7092  */
7093  for (i=0; i < resource_info->quantum; i++)
7094  XClientMessage(display,windows->image.id,windows->im_protocols,
7095  windows->im_next_image,CurrentTime);
7096  break;
7097  }
7098  case FormerCommand:
7099  {
7100  /*
7101  Display former image.
7102  */
7103  for (i=0; i < resource_info->quantum; i++)
7104  XClientMessage(display,windows->image.id,windows->im_protocols,
7105  windows->im_former_image,CurrentTime);
7106  break;
7107  }
7108  case SelectCommand:
7109  {
7110  int
7111  status;
7112 
7113  /*
7114  Select image.
7115  */
7116  if (*resource_info->home_directory == '\0')
7117  (void) CopyMagickString(resource_info->home_directory,".",
7118  MaxTextExtent);
7119  status=chdir(resource_info->home_directory);
7120  if (status == -1)
7121  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
7122  FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
7123  nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7124  break;
7125  }
7126  case SaveCommand:
7127  {
7128  /*
7129  Save image.
7130  */
7131  status=XSaveImage(display,resource_info,windows,*image);
7132  if (status == MagickFalse)
7133  {
7134  char
7135  message[MaxTextExtent];
7136 
7137  (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7138  (*image)->exception.reason != (char *) NULL ?
7139  (*image)->exception.reason : "",
7140  (*image)->exception.description != (char *) NULL ?
7141  (*image)->exception.description : "");
7142  XNoticeWidget(display,windows,"Unable to save file:",message);
7143  break;
7144  }
7145  break;
7146  }
7147  case PrintCommand:
7148  {
7149  /*
7150  Print image.
7151  */
7152  status=XPrintImage(display,resource_info,windows,*image);
7153  if (status == MagickFalse)
7154  {
7155  char
7156  message[MaxTextExtent];
7157 
7158  (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7159  (*image)->exception.reason != (char *) NULL ?
7160  (*image)->exception.reason : "",
7161  (*image)->exception.description != (char *) NULL ?
7162  (*image)->exception.description : "");
7163  XNoticeWidget(display,windows,"Unable to print file:",message);
7164  break;
7165  }
7166  break;
7167  }
7168  case DeleteCommand:
7169  {
7170  static char
7171  filename[MaxTextExtent] = "\0";
7172 
7173  /*
7174  Delete image file.
7175  */
7176  XFileBrowserWidget(display,windows,"Delete",filename);
7177  if (*filename == '\0')
7178  break;
7179  status=ShredFile(filename);
7180  status|=remove_utf8(filename);
7181  if (status != MagickFalse)
7182  XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7183  break;
7184  }
7185  case NewCommand:
7186  {
7187  int
7188  status;
7189 
7190  static char
7191  color[MaxTextExtent] = "gray",
7192  geometry[MaxTextExtent] = "640x480";
7193 
7194  static const char
7195  *format = "gradient";
7196 
7197  /*
7198  Query user for canvas geometry.
7199  */
7200  status=XDialogWidget(display,windows,"New","Enter image geometry:",
7201  geometry);
7202  if (*geometry == '\0')
7203  break;
7204  if (status == 0)
7205  format="xc";
7206  XColorBrowserWidget(display,windows,"Select",color);
7207  if (*color == '\0')
7208  break;
7209  /*
7210  Create canvas.
7211  */
7212  (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7213  "%s:%s",format,color);
7214  (void) CloneString(&image_info->size,geometry);
7215  nexus=ReadImage(image_info,&(*image)->exception);
7216  CatchException(&(*image)->exception);
7217  XClientMessage(display,windows->image.id,windows->im_protocols,
7218  windows->im_next_image,CurrentTime);
7219  break;
7220  }
7221  case VisualDirectoryCommand:
7222  {
7223  /*
7224  Visual Image directory.
7225  */
7226  nexus=XVisualDirectoryImage(display,resource_info,windows);
7227  break;
7228  }
7229  case QuitCommand:
7230  {
7231  /*
7232  exit program.
7233  */
7234  if (resource_info->confirm_exit == MagickFalse)
7235  XClientMessage(display,windows->image.id,windows->im_protocols,
7236  windows->im_exit,CurrentTime);
7237  else
7238  {
7239  int
7240  status;
7241 
7242  /*
7243  Confirm program exit.
7244  */
7245  status=XConfirmWidget(display,windows,"Do you really want to exit",
7246  resource_info->client_name);
7247  if (status > 0)
7248  XClientMessage(display,windows->image.id,windows->im_protocols,
7249  windows->im_exit,CurrentTime);
7250  }
7251  break;
7252  }
7253  case CutCommand:
7254  {
7255  /*
7256  Cut image.
7257  */
7258  (void) XCropImage(display,resource_info,windows,*image,CutMode);
7259  break;
7260  }
7261  case CopyCommand:
7262  {
7263  /*
7264  Copy image.
7265  */
7266  (void) XCropImage(display,resource_info,windows,*image,CopyMode);
7267  break;
7268  }
7269  case PasteCommand:
7270  {
7271  /*
7272  Paste image.
7273  */
7274  status=XPasteImage(display,resource_info,windows,*image);
7275  if (status == MagickFalse)
7276  {
7277  XNoticeWidget(display,windows,"Unable to paste X image",
7278  (*image)->filename);
7279  break;
7280  }
7281  break;
7282  }
7283  case HalfSizeCommand:
7284  {
7285  /*
7286  Half image size.
7287  */
7288  windows->image.window_changes.width=windows->image.ximage->width/2;
7289  windows->image.window_changes.height=windows->image.ximage->height/2;
7290  (void) XConfigureImage(display,resource_info,windows,*image);
7291  break;
7292  }
7293  case OriginalSizeCommand:
7294  {
7295  /*
7296  Original image size.
7297  */
7298  windows->image.window_changes.width=(int) (*image)->columns;
7299  windows->image.window_changes.height=(int) (*image)->rows;
7300  (void) XConfigureImage(display,resource_info,windows,*image);
7301  break;
7302  }
7303  case DoubleSizeCommand:
7304  {
7305  /*
7306  Double the image size.
7307  */
7308  windows->image.window_changes.width=windows->image.ximage->width << 1;
7309  windows->image.window_changes.height=windows->image.ximage->height << 1;
7310  (void) XConfigureImage(display,resource_info,windows,*image);
7311  break;
7312  }
7313  case ResizeCommand:
7314  {
7315  int
7316  status;
7317 
7318  size_t
7319  height,
7320  width;
7321 
7322  ssize_t
7323  x,
7324  y;
7325 
7326  /*
7327  Resize image.
7328  */
7329  width=(size_t) windows->image.ximage->width;
7330  height=(size_t) windows->image.ximage->height;
7331  x=0;
7332  y=0;
7333  (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7334  (double) width,(double) height);
7335  status=XDialogWidget(display,windows,"Resize",
7336  "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7337  if (*geometry == '\0')
7338  break;
7339  if (status == 0)
7340  (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7341  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7342  windows->image.window_changes.width=(int) width;
7343  windows->image.window_changes.height=(int) height;
7344  (void) XConfigureImage(display,resource_info,windows,*image);
7345  break;
7346  }
7347  case ApplyCommand:
7348  {
7349  char
7350  image_geometry[MaxTextExtent];
7351 
7352  if ((windows->image.crop_geometry == (char *) NULL) &&
7353  ((int) (*image)->columns == windows->image.ximage->width) &&
7354  ((int) (*image)->rows == windows->image.ximage->height))
7355  break;
7356  /*
7357  Apply size transforms to image.
7358  */
7359  XSetCursorState(display,windows,MagickTrue);
7360  XCheckRefreshWindows(display,windows);
7361  /*
7362  Crop and/or scale displayed image.
7363  */
7364  (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7365  windows->image.ximage->width,windows->image.ximage->height);
7366  (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7367  if (windows->image.crop_geometry != (char *) NULL)
7368  windows->image.crop_geometry=(char *)
7369  RelinquishMagickMemory(windows->image.crop_geometry);
7370  windows->image.x=0;
7371  windows->image.y=0;
7372  XConfigureImageColormap(display,resource_info,windows,*image);
7373  (void) XConfigureImage(display,resource_info,windows,*image);
7374  break;
7375  }
7376  case RefreshCommand:
7377  {
7378  (void) XConfigureImage(display,resource_info,windows,*image);
7379  break;
7380  }
7381  case RestoreCommand:
7382  {
7383  /*
7384  Restore Image window to its original size.
7385  */
7386  if ((windows->image.width == (unsigned int) (*image)->columns) &&
7387  (windows->image.height == (unsigned int) (*image)->rows) &&
7388  (windows->image.crop_geometry == (char *) NULL))
7389  {
7390  (void) XBell(display,0);
7391  break;
7392  }
7393  windows->image.window_changes.width=(int) (*image)->columns;
7394  windows->image.window_changes.height=(int) (*image)->rows;
7395  if (windows->image.crop_geometry != (char *) NULL)
7396  {
7397  windows->image.crop_geometry=(char *)
7398  RelinquishMagickMemory(windows->image.crop_geometry);
7399  windows->image.crop_geometry=(char *) NULL;
7400  windows->image.x=0;
7401  windows->image.y=0;
7402  }
7403  XConfigureImageColormap(display,resource_info,windows,*image);
7404  (void) XConfigureImage(display,resource_info,windows,*image);
7405  break;
7406  }
7407  case CropCommand:
7408  {
7409  /*
7410  Crop image.
7411  */
7412  (void) XCropImage(display,resource_info,windows,*image,CropMode);
7413  break;
7414  }
7415  case ChopCommand:
7416  {
7417  /*
7418  Chop image.
7419  */
7420  status=XChopImage(display,resource_info,windows,image);
7421  if (status == MagickFalse)
7422  {
7423  XNoticeWidget(display,windows,"Unable to cut X image",
7424  (*image)->filename);
7425  break;
7426  }
7427  break;
7428  }
7429  case FlopCommand:
7430  {
7431  Image
7432  *flop_image;
7433 
7434  /*
7435  Flop image scanlines.
7436  */
7437  XSetCursorState(display,windows,MagickTrue);
7438  XCheckRefreshWindows(display,windows);
7439  flop_image=FlopImage(*image,&(*image)->exception);
7440  if (flop_image != (Image *) NULL)
7441  {
7442  *image=DestroyImage(*image);
7443  *image=flop_image;
7444  }
7445  CatchException(&(*image)->exception);
7446  XSetCursorState(display,windows,MagickFalse);
7447  if (windows->image.crop_geometry != (char *) NULL)
7448  {
7449  /*
7450  Flop crop geometry.
7451  */
7452  width=(unsigned int) (*image)->columns;
7453  height=(unsigned int) (*image)->rows;
7454  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7455  &width,&height);
7456  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7457  "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7458  }
7459  if (windows->image.orphan != MagickFalse)
7460  break;
7461  (void) XConfigureImage(display,resource_info,windows,*image);
7462  break;
7463  }
7464  case FlipCommand:
7465  {
7466  Image
7467  *flip_image;
7468 
7469  /*
7470  Flip image scanlines.
7471  */
7472  XSetCursorState(display,windows,MagickTrue);
7473  XCheckRefreshWindows(display,windows);
7474  flip_image=FlipImage(*image,&(*image)->exception);
7475  if (flip_image != (Image *) NULL)
7476  {
7477  *image=DestroyImage(*image);
7478  *image=flip_image;
7479  }
7480  CatchException(&(*image)->exception);
7481  XSetCursorState(display,windows,MagickFalse);
7482  if (windows->image.crop_geometry != (char *) NULL)
7483  {
7484  /*
7485  Flip crop geometry.
7486  */
7487  width=(unsigned int) (*image)->columns;
7488  height=(unsigned int) (*image)->rows;
7489  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7490  &width,&height);
7491  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7492  "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7493  }
7494  if (windows->image.orphan != MagickFalse)
7495  break;
7496  (void) XConfigureImage(display,resource_info,windows,*image);
7497  break;
7498  }
7499  case RotateRightCommand:
7500  {
7501  /*
7502  Rotate image 90 degrees clockwise.
7503  */
7504  status=XRotateImage(display,resource_info,windows,90.0,image);
7505  if (status == MagickFalse)
7506  {
7507  XNoticeWidget(display,windows,"Unable to rotate X image",
7508  (*image)->filename);
7509  break;
7510  }
7511  break;
7512  }
7513  case RotateLeftCommand:
7514  {
7515  /*
7516  Rotate image 90 degrees counter-clockwise.
7517  */
7518  status=XRotateImage(display,resource_info,windows,-90.0,image);
7519  if (status == MagickFalse)
7520  {
7521  XNoticeWidget(display,windows,"Unable to rotate X image",
7522  (*image)->filename);
7523  break;
7524  }
7525  break;
7526  }
7527  case RotateCommand:
7528  {
7529  /*
7530  Rotate image.
7531  */
7532  status=XRotateImage(display,resource_info,windows,0.0,image);
7533  if (status == MagickFalse)
7534  {
7535  XNoticeWidget(display,windows,"Unable to rotate X image",
7536  (*image)->filename);
7537  break;
7538  }
7539  break;
7540  }
7541  case ShearCommand:
7542  {
7543  Image
7544  *shear_image;
7545 
7546  static char
7547  geometry[MaxTextExtent] = "45.0x45.0";
7548 
7549  /*
7550  Query user for shear color and geometry.
7551  */
7552  XColorBrowserWidget(display,windows,"Select",color);
7553  if (*color == '\0')
7554  break;
7555  (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7556  geometry);
7557  if (*geometry == '\0')
7558  break;
7559  /*
7560  Shear image.
7561  */
7562  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7563  XSetCursorState(display,windows,MagickTrue);
7564  XCheckRefreshWindows(display,windows);
7565  (void) QueryColorDatabase(color,&(*image)->background_color,
7566  &(*image)->exception);
7567  flags=ParseGeometry(geometry,&geometry_info);
7568  if ((flags & SigmaValue) == 0)
7569  geometry_info.sigma=geometry_info.rho;
7570  shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7571  &(*image)->exception);
7572  if (shear_image != (Image *) NULL)
7573  {
7574  *image=DestroyImage(*image);
7575  *image=shear_image;
7576  }
7577  CatchException(&(*image)->exception);
7578  XSetCursorState(display,windows,MagickFalse);
7579  if (windows->image.orphan != MagickFalse)
7580  break;
7581  windows->image.window_changes.width=(int) (*image)->columns;
7582  windows->image.window_changes.height=(int) (*image)->rows;
7583  XConfigureImageColormap(display,resource_info,windows,*image);
7584  (void) XConfigureImage(display,resource_info,windows,*image);
7585  break;
7586  }
7587  case RollCommand:
7588  {
7589  Image
7590  *roll_image;
7591 
7592  static char
7593  geometry[MaxTextExtent] = "+2+2";
7594 
7595  /*
7596  Query user for the roll geometry.
7597  */
7598  (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7599  geometry);
7600  if (*geometry == '\0')
7601  break;
7602  /*
7603  Roll image.
7604  */
7605  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7606  XSetCursorState(display,windows,MagickTrue);
7607  XCheckRefreshWindows(display,windows);
7608  (void) ParsePageGeometry(*image,geometry,&page_geometry,
7609  &(*image)->exception);
7610  roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7611  &(*image)->exception);
7612  if (roll_image != (Image *) NULL)
7613  {
7614  *image=DestroyImage(*image);
7615  *image=roll_image;
7616  }
7617  CatchException(&(*image)->exception);
7618  XSetCursorState(display,windows,MagickFalse);
7619  if (windows->image.orphan != MagickFalse)
7620  break;
7621  windows->image.window_changes.width=(int) (*image)->columns;
7622  windows->image.window_changes.height=(int) (*image)->rows;
7623  XConfigureImageColormap(display,resource_info,windows,*image);
7624  (void) XConfigureImage(display,resource_info,windows,*image);
7625  break;
7626  }
7627  case TrimCommand:
7628  {
7629  static char
7630  fuzz[MaxTextExtent];
7631 
7632  /*
7633  Query user for the fuzz factor.
7634  */
7635  (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7636  (*image)->fuzz/((double) QuantumRange+1.0));
7637  (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7638  if (*fuzz == '\0')
7639  break;
7640  (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7641  /*
7642  Trim image.
7643  */
7644  status=XTrimImage(display,resource_info,windows,*image);
7645  if (status == MagickFalse)
7646  {
7647  XNoticeWidget(display,windows,"Unable to trim X image",
7648  (*image)->filename);
7649  break;
7650  }
7651  break;
7652  }
7653  case HueCommand:
7654  {
7655  static char
7656  hue_percent[MaxTextExtent] = "110";
7657 
7658  /*
7659  Query user for percent hue change.
7660  */
7661  (void) XDialogWidget(display,windows,"Apply",
7662  "Enter percent change in image hue (0-200):",hue_percent);
7663  if (*hue_percent == '\0')
7664  break;
7665  /*
7666  Vary the image hue.
7667  */
7668  XSetCursorState(display,windows,MagickTrue);
7669  XCheckRefreshWindows(display,windows);
7670  (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7671  (void) ConcatenateMagickString(modulate_factors,hue_percent,
7672  MaxTextExtent);
7673  (void) ModulateImage(*image,modulate_factors);
7674  XSetCursorState(display,windows,MagickFalse);
7675  if (windows->image.orphan != MagickFalse)
7676  break;
7677  XConfigureImageColormap(display,resource_info,windows,*image);
7678  (void) XConfigureImage(display,resource_info,windows,*image);
7679  break;
7680  }
7681  case SaturationCommand:
7682  {
7683  static char
7684  saturation_percent[MaxTextExtent] = "110";
7685 
7686  /*
7687  Query user for percent saturation change.
7688  */
7689  (void) XDialogWidget(display,windows,"Apply",
7690  "Enter percent change in color saturation (0-200):",saturation_percent);
7691  if (*saturation_percent == '\0')
7692  break;
7693  /*
7694  Vary color saturation.
7695  */
7696  XSetCursorState(display,windows,MagickTrue);
7697  XCheckRefreshWindows(display,windows);
7698  (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7699  (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7700  MaxTextExtent);
7701  (void) ModulateImage(*image,modulate_factors);
7702  XSetCursorState(display,windows,MagickFalse);
7703  if (windows->image.orphan != MagickFalse)
7704  break;
7705  XConfigureImageColormap(display,resource_info,windows,*image);
7706  (void) XConfigureImage(display,resource_info,windows,*image);
7707  break;
7708  }
7709  case BrightnessCommand:
7710  {
7711  static char
7712  brightness_percent[MaxTextExtent] = "110";
7713 
7714  /*
7715  Query user for percent brightness change.
7716  */
7717  (void) XDialogWidget(display,windows,"Apply",
7718  "Enter percent change in color brightness (0-200):",brightness_percent);
7719  if (*brightness_percent == '\0')
7720  break;
7721  /*
7722  Vary the color brightness.
7723  */
7724  XSetCursorState(display,windows,MagickTrue);
7725  XCheckRefreshWindows(display,windows);
7726  (void) CopyMagickString(modulate_factors,brightness_percent,
7727  MaxTextExtent);
7728  (void) ModulateImage(*image,modulate_factors);
7729  XSetCursorState(display,windows,MagickFalse);
7730  if (windows->image.orphan != MagickFalse)
7731  break;
7732  XConfigureImageColormap(display,resource_info,windows,*image);
7733  (void) XConfigureImage(display,resource_info,windows,*image);
7734  break;
7735  }
7736  case GammaCommand:
7737  {
7738  static char
7739  factor[MaxTextExtent] = "1.6";
7740 
7741  /*
7742  Query user for gamma value.
7743  */
7744  (void) XDialogWidget(display,windows,"Gamma",
7745  "Enter gamma value (e.g. 1.0,1.0,1.6):",factor);
7746  if (*factor == '\0')
7747  break;
7748  /*
7749  Gamma correct image.
7750  */
7751  XSetCursorState(display,windows,MagickTrue);
7752  XCheckRefreshWindows(display,windows);
7753  (void) GammaImage(*image,factor);
7754  XSetCursorState(display,windows,MagickFalse);
7755  if (windows->image.orphan != MagickFalse)
7756  break;
7757  XConfigureImageColormap(display,resource_info,windows,*image);
7758  (void) XConfigureImage(display,resource_info,windows,*image);
7759  break;
7760  }
7761  case SpiffCommand:
7762  {
7763  /*
7764  Sharpen the image contrast.
7765  */
7766  XSetCursorState(display,windows,MagickTrue);
7767  XCheckRefreshWindows(display,windows);
7768  (void) ContrastImage(*image,MagickTrue);
7769  XSetCursorState(display,windows,MagickFalse);
7770  if (windows->image.orphan != MagickFalse)
7771  break;
7772  XConfigureImageColormap(display,resource_info,windows,*image);
7773  (void) XConfigureImage(display,resource_info,windows,*image);
7774  break;
7775  }
7776  case DullCommand:
7777  {
7778  /*
7779  Dull the image contrast.
7780  */
7781  XSetCursorState(display,windows,MagickTrue);
7782  XCheckRefreshWindows(display,windows);
7783  (void) ContrastImage(*image,MagickFalse);
7784  XSetCursorState(display,windows,MagickFalse);
7785  if (windows->image.orphan != MagickFalse)
7786  break;
7787  XConfigureImageColormap(display,resource_info,windows,*image);
7788  (void) XConfigureImage(display,resource_info,windows,*image);
7789  break;
7790  }
7791  case ContrastStretchCommand:
7792  {
7793  double
7794  black_point,
7795  white_point;
7796 
7797  static char
7798  levels[MaxTextExtent] = "1%";
7799 
7800  /*
7801  Query user for gamma value.
7802  */
7803  (void) XDialogWidget(display,windows,"Contrast Stretch",
7804  "Enter black and white points:",levels);
7805  if (*levels == '\0')
7806  break;
7807  /*
7808  Contrast stretch image.
7809  */
7810  XSetCursorState(display,windows,MagickTrue);
7811  XCheckRefreshWindows(display,windows);
7812  flags=ParseGeometry(levels,&geometry_info);
7813  black_point=geometry_info.rho;
7814  white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7815  if ((flags & PercentValue) != 0)
7816  {
7817  black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7818  white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7819  }
7820  white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7821  (void) ContrastStretchImageChannel(*image,DefaultChannels,black_point,
7822  white_point);
7823  XSetCursorState(display,windows,MagickFalse);
7824  if (windows->image.orphan != MagickFalse)
7825  break;
7826  XConfigureImageColormap(display,resource_info,windows,*image);
7827  (void) XConfigureImage(display,resource_info,windows,*image);
7828  break;
7829  }
7830  case SigmoidalContrastCommand:
7831  {
7832  static char
7833  levels[MaxTextExtent] = "3x50%";
7834 
7835  /*
7836  Query user for gamma value.
7837  */
7838  (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7839  "Enter contrast and midpoint:",levels);
7840  if (*levels == '\0')
7841  break;
7842  /*
7843  Contrast stretch image.
7844  */
7845  XSetCursorState(display,windows,MagickTrue);
7846  XCheckRefreshWindows(display,windows);
7847  (void) SigmoidalContrastImage(*image,MagickTrue,levels);
7848  XSetCursorState(display,windows,MagickFalse);
7849  if (windows->image.orphan != MagickFalse)
7850  break;
7851  XConfigureImageColormap(display,resource_info,windows,*image);
7852  (void) XConfigureImage(display,resource_info,windows,*image);
7853  break;
7854  }
7855  case NormalizeCommand:
7856  {
7857  /*
7858  Perform histogram normalization on the image.
7859  */
7860  XSetCursorState(display,windows,MagickTrue);
7861  XCheckRefreshWindows(display,windows);
7862  (void) NormalizeImage(*image);
7863  XSetCursorState(display,windows,MagickFalse);
7864  if (windows->image.orphan != MagickFalse)
7865  break;
7866  XConfigureImageColormap(display,resource_info,windows,*image);
7867  (void) XConfigureImage(display,resource_info,windows,*image);
7868  break;
7869  }
7870  case EqualizeCommand:
7871  {
7872  /*
7873  Perform histogram equalization on the image.
7874  */
7875  XSetCursorState(display,windows,MagickTrue);
7876  XCheckRefreshWindows(display,windows);
7877  (void) EqualizeImage(*image);
7878  XSetCursorState(display,windows,MagickFalse);
7879  if (windows->image.orphan != MagickFalse)
7880  break;
7881  XConfigureImageColormap(display,resource_info,windows,*image);
7882  (void) XConfigureImage(display,resource_info,windows,*image);
7883  break;
7884  }
7885  case NegateCommand:
7886  {
7887  /*
7888  Negate colors in image.
7889  */
7890  XSetCursorState(display,windows,MagickTrue);
7891  XCheckRefreshWindows(display,windows);
7892  (void) NegateImage(*image,MagickFalse);
7893  XSetCursorState(display,windows,MagickFalse);
7894  if (windows->image.orphan != MagickFalse)
7895  break;
7896  XConfigureImageColormap(display,resource_info,windows,*image);
7897  (void) XConfigureImage(display,resource_info,windows,*image);
7898  break;
7899  }
7900  case GrayscaleCommand:
7901  {
7902  /*
7903  Convert image to grayscale.
7904  */
7905  XSetCursorState(display,windows,MagickTrue);
7906  XCheckRefreshWindows(display,windows);
7907  (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7908  GrayscaleType : GrayscaleMatteType);
7909  XSetCursorState(display,windows,MagickFalse);
7910  if (windows->image.orphan != MagickFalse)
7911  break;
7912  XConfigureImageColormap(display,resource_info,windows,*image);
7913  (void) XConfigureImage(display,resource_info,windows,*image);
7914  break;
7915  }
7916  case MapCommand:
7917  {
7918  Image
7919  *affinity_image;
7920 
7921  static char
7922  filename[MaxTextExtent] = "\0";
7923 
7924  /*
7925  Request image file name from user.
7926  */
7927  XFileBrowserWidget(display,windows,"Map",filename);
7928  if (*filename == '\0')
7929  break;
7930  /*
7931  Map image.
7932  */
7933  XSetCursorState(display,windows,MagickTrue);
7934  XCheckRefreshWindows(display,windows);
7935  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
7936  affinity_image=ReadImage(image_info,&(*image)->exception);
7937  if (affinity_image != (Image *) NULL)
7938  {
7939  (void) RemapImage(&quantize_info,*image,affinity_image);
7940  affinity_image=DestroyImage(affinity_image);
7941  }
7942  CatchException(&(*image)->exception);
7943  XSetCursorState(display,windows,MagickFalse);
7944  if (windows->image.orphan != MagickFalse)
7945  break;
7946  XConfigureImageColormap(display,resource_info,windows,*image);
7947  (void) XConfigureImage(display,resource_info,windows,*image);
7948  break;
7949  }
7950  case QuantizeCommand:
7951  {
7952  int
7953  status;
7954 
7955  static char
7956  colors[MaxTextExtent] = "256";
7957 
7958  /*
7959  Query user for maximum number of colors.
7960  */
7961  status=XDialogWidget(display,windows,"Quantize",
7962  "Maximum number of colors:",colors);
7963  if (*colors == '\0')
7964  break;
7965  /*
7966  Color reduce the image.
7967  */
7968  XSetCursorState(display,windows,MagickTrue);
7969  XCheckRefreshWindows(display,windows);
7970  quantize_info.number_colors=StringToUnsignedLong(colors);
7971  quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
7972  (void) QuantizeImage(&quantize_info,*image);
7973  XSetCursorState(display,windows,MagickFalse);
7974  if (windows->image.orphan != MagickFalse)
7975  break;
7976  XConfigureImageColormap(display,resource_info,windows,*image);
7977  (void) XConfigureImage(display,resource_info,windows,*image);
7978  break;
7979  }
7980  case DespeckleCommand:
7981  {
7982  Image
7983  *despeckle_image;
7984 
7985  /*
7986  Despeckle image.
7987  */
7988  XSetCursorState(display,windows,MagickTrue);
7989  XCheckRefreshWindows(display,windows);
7990  despeckle_image=DespeckleImage(*image,&(*image)->exception);
7991  if (despeckle_image != (Image *) NULL)
7992  {
7993  *image=DestroyImage(*image);
7994  *image=despeckle_image;
7995  }
7996  CatchException(&(*image)->exception);
7997  XSetCursorState(display,windows,MagickFalse);
7998  if (windows->image.orphan != MagickFalse)
7999  break;
8000  XConfigureImageColormap(display,resource_info,windows,*image);
8001  (void) XConfigureImage(display,resource_info,windows,*image);
8002  break;
8003  }
8004  case EmbossCommand:
8005  {
8006  Image
8007  *emboss_image;
8008 
8009  static char
8010  radius[MaxTextExtent] = "0.0x1.0";
8011 
8012  /*
8013  Query user for emboss radius.
8014  */
8015  (void) XDialogWidget(display,windows,"Emboss",
8016  "Enter the emboss radius and standard deviation:",radius);
8017  if (*radius == '\0')
8018  break;
8019  /*
8020  Reduce noise in the image.
8021  */
8022  XSetCursorState(display,windows,MagickTrue);
8023  XCheckRefreshWindows(display,windows);
8024  flags=ParseGeometry(radius,&geometry_info);
8025  if ((flags & SigmaValue) == 0)
8026  geometry_info.sigma=1.0;
8027  emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8028  &(*image)->exception);
8029  if (emboss_image != (Image *) NULL)
8030  {
8031  *image=DestroyImage(*image);
8032  *image=emboss_image;
8033  }
8034  CatchException(&(*image)->exception);
8035  XSetCursorState(display,windows,MagickFalse);
8036  if (windows->image.orphan != MagickFalse)
8037  break;
8038  XConfigureImageColormap(display,resource_info,windows,*image);
8039  (void) XConfigureImage(display,resource_info,windows,*image);
8040  break;
8041  }
8042  case ReduceNoiseCommand:
8043  {
8044  Image
8045  *noise_image;
8046 
8047  static char
8048  radius[MaxTextExtent] = "0";
8049 
8050  /*
8051  Query user for noise radius.
8052  */
8053  (void) XDialogWidget(display,windows,"Reduce Noise",
8054  "Enter the noise radius:",radius);
8055  if (*radius == '\0')
8056  break;
8057  /*
8058  Reduce noise in the image.
8059  */
8060  XSetCursorState(display,windows,MagickTrue);
8061  XCheckRefreshWindows(display,windows);
8062  flags=ParseGeometry(radius,&geometry_info);
8063  noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8064  geometry_info.rho,(size_t) geometry_info.rho,&(*image)->exception);
8065  if (noise_image != (Image *) NULL)
8066  {
8067  *image=DestroyImage(*image);
8068  *image=noise_image;
8069  }
8070  CatchException(&(*image)->exception);
8071  XSetCursorState(display,windows,MagickFalse);
8072  if (windows->image.orphan != MagickFalse)
8073  break;
8074  XConfigureImageColormap(display,resource_info,windows,*image);
8075  (void) XConfigureImage(display,resource_info,windows,*image);
8076  break;
8077  }
8078  case AddNoiseCommand:
8079  {
8080  char
8081  **noises;
8082 
8083  Image
8084  *noise_image;
8085 
8086  static char
8087  noise_type[MaxTextExtent] = "Gaussian";
8088 
8089  /*
8090  Add noise to the image.
8091  */
8092  noises=GetCommandOptions(MagickNoiseOptions);
8093  if (noises == (char **) NULL)
8094  break;
8095  XListBrowserWidget(display,windows,&windows->widget,
8096  (const char **) noises,"Add Noise",
8097  "Select a type of noise to add to your image:",noise_type);
8098  noises=DestroyStringList(noises);
8099  if (*noise_type == '\0')
8100  break;
8101  XSetCursorState(display,windows,MagickTrue);
8102  XCheckRefreshWindows(display,windows);
8103  noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8104  MagickNoiseOptions,MagickFalse,noise_type),&(*image)->exception);
8105  if (noise_image != (Image *) NULL)
8106  {
8107  *image=DestroyImage(*image);
8108  *image=noise_image;
8109  }
8110  CatchException(&(*image)->exception);
8111  XSetCursorState(display,windows,MagickFalse);
8112  if (windows->image.orphan != MagickFalse)
8113  break;
8114  XConfigureImageColormap(display,resource_info,windows,*image);
8115  (void) XConfigureImage(display,resource_info,windows,*image);
8116  break;
8117  }
8118  case SharpenCommand:
8119  {
8120  Image
8121  *sharp_image;
8122 
8123  static char
8124  radius[MaxTextExtent] = "0.0x1.0";
8125 
8126  /*
8127  Query user for sharpen radius.
8128  */
8129  (void) XDialogWidget(display,windows,"Sharpen",
8130  "Enter the sharpen radius and standard deviation:",radius);
8131  if (*radius == '\0')
8132  break;
8133  /*
8134  Sharpen image scanlines.
8135  */
8136  XSetCursorState(display,windows,MagickTrue);
8137  XCheckRefreshWindows(display,windows);
8138  flags=ParseGeometry(radius,&geometry_info);
8139  sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8140  &(*image)->exception);
8141  if (sharp_image != (Image *) NULL)
8142  {
8143  *image=DestroyImage(*image);
8144  *image=sharp_image;
8145  }
8146  CatchException(&(*image)->exception);
8147  XSetCursorState(display,windows,MagickFalse);
8148  if (windows->image.orphan != MagickFalse)
8149  break;
8150  XConfigureImageColormap(display,resource_info,windows,*image);
8151  (void) XConfigureImage(display,resource_info,windows,*image);
8152  break;
8153  }
8154  case BlurCommand:
8155  {
8156  Image
8157  *blur_image;
8158 
8159  static char
8160  radius[MaxTextExtent] = "0.0x1.0";
8161 
8162  /*
8163  Query user for blur radius.
8164  */
8165  (void) XDialogWidget(display,windows,"Blur",
8166  "Enter the blur radius and standard deviation:",radius);
8167  if (*radius == '\0')
8168  break;
8169  /*
8170  Blur an image.
8171  */
8172  XSetCursorState(display,windows,MagickTrue);
8173  XCheckRefreshWindows(display,windows);
8174  flags=ParseGeometry(radius,&geometry_info);
8175  blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8176  &(*image)->exception);
8177  if (blur_image != (Image *) NULL)
8178  {
8179  *image=DestroyImage(*image);
8180  *image=blur_image;
8181  }
8182  CatchException(&(*image)->exception);
8183  XSetCursorState(display,windows,MagickFalse);
8184  if (windows->image.orphan != MagickFalse)
8185  break;
8186  XConfigureImageColormap(display,resource_info,windows,*image);
8187  (void) XConfigureImage(display,resource_info,windows,*image);
8188  break;
8189  }
8190  case ThresholdCommand:
8191  {
8192  double
8193  threshold;
8194 
8195  static char
8196  factor[MaxTextExtent] = "128";
8197 
8198  /*
8199  Query user for threshold value.
8200  */
8201  (void) XDialogWidget(display,windows,"Threshold",
8202  "Enter threshold value:",factor);
8203  if (*factor == '\0')
8204  break;
8205  /*
8206  Gamma correct image.
8207  */
8208  XSetCursorState(display,windows,MagickTrue);
8209  XCheckRefreshWindows(display,windows);
8210  threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8211  (void) BilevelImage(*image,threshold);
8212  XSetCursorState(display,windows,MagickFalse);
8213  if (windows->image.orphan != MagickFalse)
8214  break;
8215  XConfigureImageColormap(display,resource_info,windows,*image);
8216  (void) XConfigureImage(display,resource_info,windows,*image);
8217  break;
8218  }
8219  case EdgeDetectCommand:
8220  {
8221  Image
8222  *edge_image;
8223 
8224  static char
8225  radius[MaxTextExtent] = "0";
8226 
8227  /*
8228  Query user for edge factor.
8229  */
8230  (void) XDialogWidget(display,windows,"Detect Edges",
8231  "Enter the edge detect radius:",radius);
8232  if (*radius == '\0')
8233  break;
8234  /*
8235  Detect edge in image.
8236  */
8237  XSetCursorState(display,windows,MagickTrue);
8238  XCheckRefreshWindows(display,windows);
8239  flags=ParseGeometry(radius,&geometry_info);
8240  edge_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8241  if (edge_image != (Image *) NULL)
8242  {
8243  *image=DestroyImage(*image);
8244  *image=edge_image;
8245  }
8246  CatchException(&(*image)->exception);
8247  XSetCursorState(display,windows,MagickFalse);
8248  if (windows->image.orphan != MagickFalse)
8249  break;
8250  XConfigureImageColormap(display,resource_info,windows,*image);
8251  (void) XConfigureImage(display,resource_info,windows,*image);
8252  break;
8253  }
8254  case SpreadCommand:
8255  {
8256  Image
8257  *spread_image;
8258 
8259  static char
8260  amount[MaxTextExtent] = "2";
8261 
8262  /*
8263  Query user for spread amount.
8264  */
8265  (void) XDialogWidget(display,windows,"Spread",
8266  "Enter the displacement amount:",amount);
8267  if (*amount == '\0')
8268  break;
8269  /*
8270  Displace image pixels by a random amount.
8271  */
8272  XSetCursorState(display,windows,MagickTrue);
8273  XCheckRefreshWindows(display,windows);
8274  flags=ParseGeometry(amount,&geometry_info);
8275  spread_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8276  if (spread_image != (Image *) NULL)
8277  {
8278  *image=DestroyImage(*image);
8279  *image=spread_image;
8280  }
8281  CatchException(&(*image)->exception);
8282  XSetCursorState(display,windows,MagickFalse);
8283  if (windows->image.orphan != MagickFalse)
8284  break;
8285  XConfigureImageColormap(display,resource_info,windows,*image);
8286  (void) XConfigureImage(display,resource_info,windows,*image);
8287  break;
8288  }
8289  case ShadeCommand:
8290  {
8291  Image
8292  *shade_image;
8293 
8294  int
8295  status;
8296 
8297  static char
8298  geometry[MaxTextExtent] = "30x30";
8299 
8300  /*
8301  Query user for the shade geometry.
8302  */
8303  status=XDialogWidget(display,windows,"Shade",
8304  "Enter the azimuth and elevation of the light source:",geometry);
8305  if (*geometry == '\0')
8306  break;
8307  /*
8308  Shade image pixels.
8309  */
8310  XSetCursorState(display,windows,MagickTrue);
8311  XCheckRefreshWindows(display,windows);
8312  flags=ParseGeometry(geometry,&geometry_info);
8313  if ((flags & SigmaValue) == 0)
8314  geometry_info.sigma=1.0;
8315  shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8316  geometry_info.rho,geometry_info.sigma,&(*image)->exception);
8317  if (shade_image != (Image *) NULL)
8318  {
8319  *image=DestroyImage(*image);
8320  *image=shade_image;
8321  }
8322  CatchException(&(*image)->exception);
8323  XSetCursorState(display,windows,MagickFalse);
8324  if (windows->image.orphan != MagickFalse)
8325  break;
8326  XConfigureImageColormap(display,resource_info,windows,*image);
8327  (void) XConfigureImage(display,resource_info,windows,*image);
8328  break;
8329  }
8330  case RaiseCommand:
8331  {
8332  static char
8333  bevel_width[MaxTextExtent] = "10";
8334 
8335  /*
8336  Query user for bevel width.
8337  */
8338  (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8339  if (*bevel_width == '\0')
8340  break;
8341  /*
8342  Raise an image.
8343  */
8344  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8345  XSetCursorState(display,windows,MagickTrue);
8346  XCheckRefreshWindows(display,windows);
8347  (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8348  &(*image)->exception);
8349  (void) RaiseImage(*image,&page_geometry,MagickTrue);
8350  XSetCursorState(display,windows,MagickFalse);
8351  if (windows->image.orphan != MagickFalse)
8352  break;
8353  XConfigureImageColormap(display,resource_info,windows,*image);
8354  (void) XConfigureImage(display,resource_info,windows,*image);
8355  break;
8356  }
8357  case SegmentCommand:
8358  {
8359  static char
8360  threshold[MaxTextExtent] = "1.0x1.5";
8361 
8362  /*
8363  Query user for smoothing threshold.
8364  */
8365  (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8366  threshold);
8367  if (*threshold == '\0')
8368  break;
8369  /*
8370  Segment an image.
8371  */
8372  XSetCursorState(display,windows,MagickTrue);
8373  XCheckRefreshWindows(display,windows);
8374  flags=ParseGeometry(threshold,&geometry_info);
8375  if ((flags & SigmaValue) == 0)
8376  geometry_info.sigma=1.0;
8377  (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8378  geometry_info.sigma);
8379  XSetCursorState(display,windows,MagickFalse);
8380  if (windows->image.orphan != MagickFalse)
8381  break;
8382  XConfigureImageColormap(display,resource_info,windows,*image);
8383  (void) XConfigureImage(display,resource_info,windows,*image);
8384  break;
8385  }
8386  case SepiaToneCommand:
8387  {
8388  double
8389  threshold;
8390 
8391  Image
8392  *sepia_image;
8393 
8394  static char
8395  factor[MaxTextExtent] = "80%";
8396 
8397  /*
8398  Query user for sepia-tone factor.
8399  */
8400  (void) XDialogWidget(display,windows,"Sepia Tone",
8401  "Enter the sepia tone factor (0 - 99.9%):",factor);
8402  if (*factor == '\0')
8403  break;
8404  /*
8405  Sepia tone image pixels.
8406  */
8407  XSetCursorState(display,windows,MagickTrue);
8408  XCheckRefreshWindows(display,windows);
8409  threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8410  sepia_image=SepiaToneImage(*image,threshold,&(*image)->exception);
8411  if (sepia_image != (Image *) NULL)
8412  {
8413  *image=DestroyImage(*image);
8414  *image=sepia_image;
8415  }
8416  CatchException(&(*image)->exception);
8417  XSetCursorState(display,windows,MagickFalse);
8418  if (windows->image.orphan != MagickFalse)
8419  break;
8420  XConfigureImageColormap(display,resource_info,windows,*image);
8421  (void) XConfigureImage(display,resource_info,windows,*image);
8422  break;
8423  }
8424  case SolarizeCommand:
8425  {
8426  double
8427  threshold;
8428 
8429  static char
8430  factor[MaxTextExtent] = "60%";
8431 
8432  /*
8433  Query user for solarize factor.
8434  */
8435  (void) XDialogWidget(display,windows,"Solarize",
8436  "Enter the solarize factor (0 - 99.9%):",factor);
8437  if (*factor == '\0')
8438  break;
8439  /*
8440  Solarize image pixels.
8441  */
8442  XSetCursorState(display,windows,MagickTrue);
8443  XCheckRefreshWindows(display,windows);
8444  threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8445  (void) SolarizeImage(*image,threshold);
8446  XSetCursorState(display,windows,MagickFalse);
8447  if (windows->image.orphan != MagickFalse)
8448  break;
8449  XConfigureImageColormap(display,resource_info,windows,*image);
8450  (void) XConfigureImage(display,resource_info,windows,*image);
8451  break;
8452  }
8453  case SwirlCommand:
8454  {
8455  Image
8456  *swirl_image;
8457 
8458  static char
8459  degrees[MaxTextExtent] = "60";
8460 
8461  /*
8462  Query user for swirl angle.
8463  */
8464  (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8465  degrees);
8466  if (*degrees == '\0')
8467  break;
8468  /*
8469  Swirl image pixels about the center.
8470  */
8471  XSetCursorState(display,windows,MagickTrue);
8472  XCheckRefreshWindows(display,windows);
8473  flags=ParseGeometry(degrees,&geometry_info);
8474  swirl_image=SwirlImage(*image,geometry_info.rho,&(*image)->exception);
8475  if (swirl_image != (Image *) NULL)
8476  {
8477  *image=DestroyImage(*image);
8478  *image=swirl_image;
8479  }
8480  CatchException(&(*image)->exception);
8481  XSetCursorState(display,windows,MagickFalse);
8482  if (windows->image.orphan != MagickFalse)
8483  break;
8484  XConfigureImageColormap(display,resource_info,windows,*image);
8485  (void) XConfigureImage(display,resource_info,windows,*image);
8486  break;
8487  }
8488  case ImplodeCommand:
8489  {
8490  Image
8491  *implode_image;
8492 
8493  static char
8494  factor[MaxTextExtent] = "0.3";
8495 
8496  /*
8497  Query user for implode factor.
8498  */
8499  (void) XDialogWidget(display,windows,"Implode",
8500  "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8501  if (*factor == '\0')
8502  break;
8503  /*
8504  Implode image pixels about the center.
8505  */
8506  XSetCursorState(display,windows,MagickTrue);
8507  XCheckRefreshWindows(display,windows);
8508  flags=ParseGeometry(factor,&geometry_info);
8509  implode_image=ImplodeImage(*image,geometry_info.rho,&(*image)->exception);
8510  if (implode_image != (Image *) NULL)
8511  {
8512  *image=DestroyImage(*image);
8513  *image=implode_image;
8514  }
8515  CatchException(&(*image)->exception);
8516  XSetCursorState(display,windows,MagickFalse);
8517  if (windows->image.orphan != MagickFalse)
8518  break;
8519  XConfigureImageColormap(display,resource_info,windows,*image);
8520  (void) XConfigureImage(display,resource_info,windows,*image);
8521  break;
8522  }
8523  case VignetteCommand:
8524  {
8525  Image
8526  *vignette_image;
8527 
8528  static char
8529  geometry[MaxTextExtent] = "0x20";
8530 
8531  /*
8532  Query user for the vignette geometry.
8533  */
8534  (void) XDialogWidget(display,windows,"Vignette",
8535  "Enter the radius, sigma, and x and y offsets:",geometry);
8536  if (*geometry == '\0')
8537  break;
8538  /*
8539  Soften the edges of the image in vignette style
8540  */
8541  XSetCursorState(display,windows,MagickTrue);
8542  XCheckRefreshWindows(display,windows);
8543  flags=ParseGeometry(geometry,&geometry_info);
8544  if ((flags & SigmaValue) == 0)
8545  geometry_info.sigma=1.0;
8546  if ((flags & XiValue) == 0)
8547  geometry_info.xi=0.1*(*image)->columns;
8548  if ((flags & PsiValue) == 0)
8549  geometry_info.psi=0.1*(*image)->rows;
8550  vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8551  (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8552  0.5),&(*image)->exception);
8553  if (vignette_image != (Image *) NULL)
8554  {
8555  *image=DestroyImage(*image);
8556  *image=vignette_image;
8557  }
8558  CatchException(&(*image)->exception);
8559  XSetCursorState(display,windows,MagickFalse);
8560  if (windows->image.orphan != MagickFalse)
8561  break;
8562  XConfigureImageColormap(display,resource_info,windows,*image);
8563  (void) XConfigureImage(display,resource_info,windows,*image);
8564  break;
8565  }
8566  case WaveCommand:
8567  {
8568  Image
8569  *wave_image;
8570 
8571  static char
8572  geometry[MaxTextExtent] = "25x150";
8573 
8574  /*
8575  Query user for the wave geometry.
8576  */
8577  (void) XDialogWidget(display,windows,"Wave",
8578  "Enter the amplitude and length of the wave:",geometry);
8579  if (*geometry == '\0')
8580  break;
8581  /*
8582  Alter an image along a sine wave.
8583  */
8584  XSetCursorState(display,windows,MagickTrue);
8585  XCheckRefreshWindows(display,windows);
8586  flags=ParseGeometry(geometry,&geometry_info);
8587  if ((flags & SigmaValue) == 0)
8588  geometry_info.sigma=1.0;
8589  wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8590  &(*image)->exception);
8591  if (wave_image != (Image *) NULL)
8592  {
8593  *image=DestroyImage(*image);
8594  *image=wave_image;
8595  }
8596  CatchException(&(*image)->exception);
8597  XSetCursorState(display,windows,MagickFalse);
8598  if (windows->image.orphan != MagickFalse)
8599  break;
8600  XConfigureImageColormap(display,resource_info,windows,*image);
8601  (void) XConfigureImage(display,resource_info,windows,*image);
8602  break;
8603  }
8604  case OilPaintCommand:
8605  {
8606  Image
8607  *paint_image;
8608 
8609  static char
8610  radius[MaxTextExtent] = "0";
8611 
8612  /*
8613  Query user for circular neighborhood radius.
8614  */
8615  (void) XDialogWidget(display,windows,"Oil Paint",
8616  "Enter the mask radius:",radius);
8617  if (*radius == '\0')
8618  break;
8619  /*
8620  OilPaint image scanlines.
8621  */
8622  XSetCursorState(display,windows,MagickTrue);
8623  XCheckRefreshWindows(display,windows);
8624  flags=ParseGeometry(radius,&geometry_info);
8625  paint_image=OilPaintImage(*image,geometry_info.rho,&(*image)->exception);
8626  if (paint_image != (Image *) NULL)
8627  {
8628  *image=DestroyImage(*image);
8629  *image=paint_image;
8630  }
8631  CatchException(&(*image)->exception);
8632  XSetCursorState(display,windows,MagickFalse);
8633  if (windows->image.orphan != MagickFalse)
8634  break;
8635  XConfigureImageColormap(display,resource_info,windows,*image);
8636  (void) XConfigureImage(display,resource_info,windows,*image);
8637  break;
8638  }
8639  case CharcoalDrawCommand:
8640  {
8641  Image
8642  *charcoal_image;
8643 
8644  static char
8645  radius[MaxTextExtent] = "0x1";
8646 
8647  /*
8648  Query user for charcoal radius.
8649  */
8650  (void) XDialogWidget(display,windows,"Charcoal Draw",
8651  "Enter the charcoal radius and sigma:",radius);
8652  if (*radius == '\0')
8653  break;
8654  /*
8655  Charcoal the image.
8656  */
8657  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8658  XSetCursorState(display,windows,MagickTrue);
8659  XCheckRefreshWindows(display,windows);
8660  flags=ParseGeometry(radius,&geometry_info);
8661  if ((flags & SigmaValue) == 0)
8662  geometry_info.sigma=geometry_info.rho;
8663  charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8664  &(*image)->exception);
8665  if (charcoal_image != (Image *) NULL)
8666  {
8667  *image=DestroyImage(*image);
8668  *image=charcoal_image;
8669  }
8670  CatchException(&(*image)->exception);
8671  XSetCursorState(display,windows,MagickFalse);
8672  if (windows->image.orphan != MagickFalse)
8673  break;
8674  XConfigureImageColormap(display,resource_info,windows,*image);
8675  (void) XConfigureImage(display,resource_info,windows,*image);
8676  break;
8677  }
8678  case AnnotateCommand:
8679  {
8680  /*
8681  Annotate the image with text.
8682  */
8683  status=XAnnotateEditImage(display,resource_info,windows,*image);
8684  if (status == MagickFalse)
8685  {
8686  XNoticeWidget(display,windows,"Unable to annotate X image",
8687  (*image)->filename);
8688  break;
8689  }
8690  break;
8691  }
8692  case DrawCommand:
8693  {
8694  /*
8695  Draw image.
8696  */
8697  status=XDrawEditImage(display,resource_info,windows,image);
8698  if (status == MagickFalse)
8699  {
8700  XNoticeWidget(display,windows,"Unable to draw on the X image",
8701  (*image)->filename);
8702  break;
8703  }
8704  break;
8705  }
8706  case ColorCommand:
8707  {
8708  /*
8709  Color edit.
8710  */
8711  status=XColorEditImage(display,resource_info,windows,image);
8712  if (status == MagickFalse)
8713  {
8714  XNoticeWidget(display,windows,"Unable to pixel edit X image",
8715  (*image)->filename);
8716  break;
8717  }
8718  break;
8719  }
8720  case MatteCommand:
8721  {
8722  /*
8723  Matte edit.
8724  */
8725  status=XMatteEditImage(display,resource_info,windows,image);
8726  if (status == MagickFalse)
8727  {
8728  XNoticeWidget(display,windows,"Unable to matte edit X image",
8729  (*image)->filename);
8730  break;
8731  }
8732  break;
8733  }
8734  case CompositeCommand:
8735  {
8736  /*
8737  Composite image.
8738  */
8739  status=XCompositeImage(display,resource_info,windows,*image);
8740  if (status == MagickFalse)
8741  {
8742  XNoticeWidget(display,windows,"Unable to composite X image",
8743  (*image)->filename);
8744  break;
8745  }
8746  break;
8747  }
8748  case AddBorderCommand:
8749  {
8750  Image
8751  *border_image;
8752 
8753  static char
8754  geometry[MaxTextExtent] = "6x6";
8755 
8756  /*
8757  Query user for border color and geometry.
8758  */
8759  XColorBrowserWidget(display,windows,"Select",color);
8760  if (*color == '\0')
8761  break;
8762  (void) XDialogWidget(display,windows,"Add Border",
8763  "Enter border geometry:",geometry);
8764  if (*geometry == '\0')
8765  break;
8766  /*
8767  Add a border to the image.
8768  */
8769  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8770  XSetCursorState(display,windows,MagickTrue);
8771  XCheckRefreshWindows(display,windows);
8772  (void) QueryColorDatabase(color,&(*image)->border_color,
8773  &(*image)->exception);
8774  (void) ParsePageGeometry(*image,geometry,&page_geometry,
8775  &(*image)->exception);
8776  border_image=BorderImage(*image,&page_geometry,&(*image)->exception);
8777  if (border_image != (Image *) NULL)
8778  {
8779  *image=DestroyImage(*image);
8780  *image=border_image;
8781  }
8782  CatchException(&(*image)->exception);
8783  XSetCursorState(display,windows,MagickFalse);
8784  if (windows->image.orphan != MagickFalse)
8785  break;
8786  windows->image.window_changes.width=(int) (*image)->columns;
8787  windows->image.window_changes.height=(int) (*image)->rows;
8788  XConfigureImageColormap(display,resource_info,windows,*image);
8789  (void) XConfigureImage(display,resource_info,windows,*image);
8790  break;
8791  }
8792  case AddFrameCommand:
8793  {
8794  FrameInfo
8795  frame_info;
8796 
8797  Image
8798  *frame_image;
8799 
8800  static char
8801  geometry[MaxTextExtent] = "6x6";
8802 
8803  /*
8804  Query user for frame color and geometry.
8805  */
8806  XColorBrowserWidget(display,windows,"Select",color);
8807  if (*color == '\0')
8808  break;
8809  (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8810  geometry);
8811  if (*geometry == '\0')
8812  break;
8813  /*
8814  Surround image with an ornamental border.
8815  */
8816  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8817  XSetCursorState(display,windows,MagickTrue);
8818  XCheckRefreshWindows(display,windows);
8819  (void) QueryColorDatabase(color,&(*image)->matte_color,
8820  &(*image)->exception);
8821  (void) ParsePageGeometry(*image,geometry,&page_geometry,
8822  &(*image)->exception);
8823  frame_info.width=page_geometry.width;
8824  frame_info.height=page_geometry.height;
8825  frame_info.outer_bevel=page_geometry.x;
8826  frame_info.inner_bevel=page_geometry.y;
8827  frame_info.x=(ssize_t) frame_info.width;
8828  frame_info.y=(ssize_t) frame_info.height;
8829  frame_info.width=(*image)->columns+2*frame_info.width;
8830  frame_info.height=(*image)->rows+2*frame_info.height;
8831  frame_image=FrameImage(*image,&frame_info,&(*image)->exception);
8832  if (frame_image != (Image *) NULL)
8833  {
8834  *image=DestroyImage(*image);
8835  *image=frame_image;
8836  }
8837  CatchException(&(*image)->exception);
8838  XSetCursorState(display,windows,MagickFalse);
8839  if (windows->image.orphan != MagickFalse)
8840  break;
8841  windows->image.window_changes.width=(int) (*image)->columns;
8842  windows->image.window_changes.height=(int) (*image)->rows;
8843  XConfigureImageColormap(display,resource_info,windows,*image);
8844  (void) XConfigureImage(display,resource_info,windows,*image);
8845  break;
8846  }
8847  case CommentCommand:
8848  {
8849  const char
8850  *value;
8851 
8852  FILE
8853  *file;
8854 
8855  int
8856  unique_file;
8857 
8858  /*
8859  Edit image comment.
8860  */
8861  unique_file=AcquireUniqueFileResource(image_info->filename);
8862  if (unique_file == -1)
8863  {
8864  XNoticeWidget(display,windows,"Unable to edit image comment",
8865  image_info->filename);
8866  break;
8867  }
8868  value=GetImageProperty(*image,"comment");
8869  if (value == (char *) NULL)
8870  unique_file=close(unique_file)-1;
8871  else
8872  {
8873  const char
8874  *p;
8875 
8876  file=fdopen(unique_file,"w");
8877  if (file == (FILE *) NULL)
8878  {
8879  XNoticeWidget(display,windows,"Unable to edit image comment",
8880  image_info->filename);
8881  break;
8882  }
8883  for (p=value; *p != '\0'; p++)
8884  (void) fputc((int) *p,file);
8885  (void) fputc('\n',file);
8886  (void) fclose(file);
8887  }
8888  XSetCursorState(display,windows,MagickTrue);
8889  XCheckRefreshWindows(display,windows);
8890  status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8891  &(*image)->exception);
8892  if (status == MagickFalse)
8893  XNoticeWidget(display,windows,"Unable to edit image comment",
8894  (char *) NULL);
8895  else
8896  {
8897  char
8898  *comment;
8899 
8900  comment=FileToString(image_info->filename,~0UL,&(*image)->exception);
8901  if (comment != (char *) NULL)
8902  {
8903  (void) SetImageProperty(*image,"comment",comment);
8904  (*image)->taint=MagickTrue;
8905  }
8906  }
8907  (void) RelinquishUniqueFileResource(image_info->filename);
8908  XSetCursorState(display,windows,MagickFalse);
8909  break;
8910  }
8911  case LaunchCommand:
8912  {
8913  /*
8914  Launch program.
8915  */
8916  XSetCursorState(display,windows,MagickTrue);
8917  XCheckRefreshWindows(display,windows);
8918  (void) AcquireUniqueFilename(filename);
8919  (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
8920  filename);
8921  status=WriteImage(image_info,*image);
8922  if (status == MagickFalse)
8923  XNoticeWidget(display,windows,"Unable to launch image editor",
8924  (char *) NULL);
8925  else
8926  {
8927  nexus=ReadImage(resource_info->image_info,&(*image)->exception);
8928  CatchException(&(*image)->exception);
8929  XClientMessage(display,windows->image.id,windows->im_protocols,
8930  windows->im_next_image,CurrentTime);
8931  }
8932  (void) RelinquishUniqueFileResource(filename);
8933  XSetCursorState(display,windows,MagickFalse);
8934  break;
8935  }
8936  case RegionOfInterestCommand:
8937  {
8938  /*
8939  Apply an image processing technique to a region of interest.
8940  */
8941  (void) XROIImage(display,resource_info,windows,image);
8942  break;
8943  }
8944  case InfoCommand:
8945  break;
8946  case ZoomCommand:
8947  {
8948  /*
8949  Zoom image.
8950  */
8951  if (windows->magnify.mapped != MagickFalse)
8952  (void) XRaiseWindow(display,windows->magnify.id);
8953  else
8954  {
8955  /*
8956  Make magnify image.
8957  */
8958  XSetCursorState(display,windows,MagickTrue);
8959  (void) XMapRaised(display,windows->magnify.id);
8960  XSetCursorState(display,windows,MagickFalse);
8961  }
8962  break;
8963  }
8964  case ShowPreviewCommand:
8965  {
8966  char
8967  **previews;
8968 
8969  Image
8970  *preview_image;
8971 
8972  static char
8973  preview_type[MaxTextExtent] = "Gamma";
8974 
8975  /*
8976  Select preview type from menu.
8977  */
8978  previews=GetCommandOptions(MagickPreviewOptions);
8979  if (previews == (char **) NULL)
8980  break;
8981  XListBrowserWidget(display,windows,&windows->widget,
8982  (const char **) previews,"Preview",
8983  "Select an enhancement, effect, or F/X:",preview_type);
8984  previews=DestroyStringList(previews);
8985  if (*preview_type == '\0')
8986  break;
8987  /*
8988  Show image preview.
8989  */
8990  XSetCursorState(display,windows,MagickTrue);
8991  XCheckRefreshWindows(display,windows);
8992  image_info->preview_type=(PreviewType)
8993  ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
8994  image_info->group=(ssize_t) windows->image.id;
8995  (void) DeleteImageProperty(*image,"label");
8996  (void) SetImageProperty(*image,"label","Preview");
8997  (void) AcquireUniqueFilename(filename);
8998  (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
8999  filename);
9000  status=WriteImage(image_info,*image);
9001  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9002  preview_image=ReadImage(image_info,&(*image)->exception);
9003  (void) RelinquishUniqueFileResource(filename);
9004  if (preview_image == (Image *) NULL)
9005  break;
9006  (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9007  filename);
9008  status=WriteImage(image_info,preview_image);
9009  preview_image=DestroyImage(preview_image);
9010  if (status == MagickFalse)
9011  XNoticeWidget(display,windows,"Unable to show image preview",
9012  (*image)->filename);
9013  XDelay(display,1500);
9014  XSetCursorState(display,windows,MagickFalse);
9015  break;
9016  }
9017  case ShowHistogramCommand:
9018  {
9019  Image
9020  *histogram_image;
9021 
9022  /*
9023  Show image histogram.
9024  */
9025  XSetCursorState(display,windows,MagickTrue);
9026  XCheckRefreshWindows(display,windows);
9027  image_info->group=(ssize_t) windows->image.id;
9028  (void) DeleteImageProperty(*image,"label");
9029  (void) SetImageProperty(*image,"label","Histogram");
9030  (void) AcquireUniqueFilename(filename);
9031  (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9032  filename);
9033  status=WriteImage(image_info,*image);
9034  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9035  histogram_image=ReadImage(image_info,&(*image)->exception);
9036  (void) RelinquishUniqueFileResource(filename);
9037  if (histogram_image == (Image *) NULL)
9038  break;
9039  (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9040  "show:%s",filename);
9041  status=WriteImage(image_info,histogram_image);
9042  histogram_image=DestroyImage(histogram_image);
9043  if (status == MagickFalse)
9044  XNoticeWidget(display,windows,"Unable to show histogram",
9045  (*image)->filename);
9046  XDelay(display,1500);
9047  XSetCursorState(display,windows,MagickFalse);
9048  break;
9049  }
9050  case ShowMatteCommand:
9051  {
9052  Image
9053  *matte_image;
9054 
9055  if ((*image)->matte == MagickFalse)
9056  {
9057  XNoticeWidget(display,windows,
9058  "Image does not have any matte information",(*image)->filename);
9059  break;
9060  }
9061  /*
9062  Show image matte.
9063  */
9064  XSetCursorState(display,windows,MagickTrue);
9065  XCheckRefreshWindows(display,windows);
9066  image_info->group=(ssize_t) windows->image.id;
9067  (void) DeleteImageProperty(*image,"label");
9068  (void) SetImageProperty(*image,"label","Matte");
9069  (void) AcquireUniqueFilename(filename);
9070  (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9071  filename);
9072  status=WriteImage(image_info,*image);
9073  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9074  matte_image=ReadImage(image_info,&(*image)->exception);
9075  (void) RelinquishUniqueFileResource(filename);
9076  if (matte_image == (Image *) NULL)
9077  break;
9078  (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9079  filename);
9080  status=WriteImage(image_info,matte_image);
9081  matte_image=DestroyImage(matte_image);
9082  if (status == MagickFalse)
9083  XNoticeWidget(display,windows,"Unable to show matte",
9084  (*image)->filename);
9085  XDelay(display,1500);
9086  XSetCursorState(display,windows,MagickFalse);
9087  break;
9088  }
9089  case BackgroundCommand:
9090  {
9091  /*
9092  Background image.
9093  */
9094  status=XBackgroundImage(display,resource_info,windows,image);
9095  if (status == MagickFalse)
9096  break;
9097  nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9098  if (nexus != (Image *) NULL)
9099  XClientMessage(display,windows->image.id,windows->im_protocols,
9100  windows->im_next_image,CurrentTime);
9101  break;
9102  }
9103  case SlideShowCommand:
9104  {
9105  static char
9106  delay[MaxTextExtent] = "5";
9107 
9108  /*
9109  Display next image after pausing.
9110  */
9111  (void) XDialogWidget(display,windows,"Slide Show",
9112  "Pause how many 1/100ths of a second between images:",delay);
9113  if (*delay == '\0')
9114  break;
9115  resource_info->delay=StringToUnsignedLong(delay);
9116  XClientMessage(display,windows->image.id,windows->im_protocols,
9117  windows->im_next_image,CurrentTime);
9118  break;
9119  }
9120  case PreferencesCommand:
9121  {
9122  /*
9123  Set user preferences.
9124  */
9125  status=XPreferencesWidget(display,resource_info,windows);
9126  if (status == MagickFalse)
9127  break;
9128  nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9129  if (nexus != (Image *) NULL)
9130  XClientMessage(display,windows->image.id,windows->im_protocols,
9131  windows->im_next_image,CurrentTime);
9132  break;
9133  }
9134  case HelpCommand:
9135  {
9136  /*
9137  User requested help.
9138  */
9139  XTextViewHelp(display,resource_info,windows,MagickFalse,
9140  "Help Viewer - Display",DisplayHelp);
9141  break;
9142  }
9143  case BrowseDocumentationCommand:
9144  {
9145  Atom
9146  mozilla_atom;
9147 
9148  Window
9149  mozilla_window,
9150  root_window;
9151 
9152  /*
9153  Browse the ImageMagick documentation.
9154  */
9155  root_window=XRootWindow(display,XDefaultScreen(display));
9156  mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9157  mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9158  if (mozilla_window != (Window) NULL)
9159  {
9160  char
9161  command[MaxTextExtent];
9162 
9163  /*
9164  Display documentation using Netscape remote control.
9165  */
9166  (void) FormatLocaleString(command,MaxTextExtent,
9167  "openurl(%s,new-tab)",MagickAuthoritativeURL);
9168  mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9169  (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9170  8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9171  XSetCursorState(display,windows,MagickFalse);
9172  break;
9173  }
9174  XSetCursorState(display,windows,MagickTrue);
9175  XCheckRefreshWindows(display,windows);
9176  status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9177  &(*image)->exception);
9178  if (status == MagickFalse)
9179  XNoticeWidget(display,windows,"Unable to browse documentation",
9180  (char *) NULL);
9181  XDelay(display,1500);
9182  XSetCursorState(display,windows,MagickFalse);
9183  break;
9184  }
9185  case VersionCommand:
9186  {
9187  XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9188  GetMagickCopyright());
9189  break;
9190  }
9191  case SaveToUndoBufferCommand:
9192  break;
9193  default:
9194  {
9195  (void) XBell(display,0);
9196  break;
9197  }
9198  }
9199  image_info=DestroyImageInfo(image_info);
9200  return(nexus);
9201 }
9202 ␌
9203 /*
9204 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9205 % %
9206 % %
9207 % %
9208 + X M a g n i f y I m a g e %
9209 % %
9210 % %
9211 % %
9212 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9213 %
9214 % XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9215 % The magnified portion is displayed in a separate window.
9216 %
9217 % The format of the XMagnifyImage method is:
9218 %
9219 % void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9220 %
9221 % A description of each parameter follows:
9222 %
9223 % o display: Specifies a connection to an X server; returned from
9224 % XOpenDisplay.
9225 %
9226 % o windows: Specifies a pointer to a XWindows structure.
9227 %
9228 % o event: Specifies a pointer to a XEvent structure. If it is NULL,
9229 % the entire image is refreshed.
9230 %
9231 */
9232 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9233 {
9234  char
9235  text[MaxTextExtent];
9236 
9237  int
9238  x,
9239  y;
9240 
9241  size_t
9242  state;
9243 
9244  /*
9245  Update magnified image until the mouse button is released.
9246  */
9247  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9248  state=DefaultState;
9249  x=event->xbutton.x;
9250  y=event->xbutton.y;
9251  windows->magnify.x=(int) windows->image.x+x;
9252  windows->magnify.y=(int) windows->image.y+y;
9253  do
9254  {
9255  /*
9256  Map and unmap Info widget as text cursor crosses its boundaries.
9257  */
9258  if (windows->info.mapped != MagickFalse)
9259  {
9260  if ((x < (int) (windows->info.x+windows->info.width)) &&
9261  (y < (int) (windows->info.y+windows->info.height)))
9262  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9263  }
9264  else
9265  if ((x > (int) (windows->info.x+windows->info.width)) ||
9266  (y > (int) (windows->info.y+windows->info.height)))
9267  (void) XMapWindow(display,windows->info.id);
9268  if (windows->info.mapped != MagickFalse)
9269  {
9270  /*
9271  Display pointer position.
9272  */
9273  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9274  windows->magnify.x,windows->magnify.y);
9275  XInfoWidget(display,windows,text);
9276  }
9277  /*
9278  Wait for next event.
9279  */
9280  XScreenEvent(display,windows,event);
9281  switch (event->type)
9282  {
9283  case ButtonPress:
9284  break;
9285  case ButtonRelease:
9286  {
9287  /*
9288  User has finished magnifying image.
9289  */
9290  x=event->xbutton.x;
9291  y=event->xbutton.y;
9292  state|=ExitState;
9293  break;
9294  }
9295  case Expose:
9296  break;
9297  case MotionNotify:
9298  {
9299  x=event->xmotion.x;
9300  y=event->xmotion.y;
9301  break;
9302  }
9303  default:
9304  break;
9305  }
9306  /*
9307  Check boundary conditions.
9308  */
9309  if (x < 0)
9310  x=0;
9311  else
9312  if (x >= (int) windows->image.width)
9313  x=(int) windows->image.width-1;
9314  if (y < 0)
9315  y=0;
9316  else
9317  if (y >= (int) windows->image.height)
9318  y=(int) windows->image.height-1;
9319  } while ((state & ExitState) == 0);
9320  /*
9321  Display magnified image.
9322  */
9323  XSetCursorState(display,windows,MagickFalse);
9324 }
9325 ␌
9326 /*
9327 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9328 % %
9329 % %
9330 % %
9331 + X M a g n i f y W i n d o w C o m m a n d %
9332 % %
9333 % %
9334 % %
9335 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9336 %
9337 % XMagnifyWindowCommand() moves the image within an Magnify window by one
9338 % pixel as specified by the key symbol.
9339 %
9340 % The format of the XMagnifyWindowCommand method is:
9341 %
9342 % void XMagnifyWindowCommand(Display *display,XWindows *windows,
9343 % const MagickStatusType state,const KeySym key_symbol)
9344 %
9345 % A description of each parameter follows:
9346 %
9347 % o display: Specifies a connection to an X server; returned from
9348 % XOpenDisplay.
9349 %
9350 % o windows: Specifies a pointer to a XWindows structure.
9351 %
9352 % o state: key mask.
9353 %
9354 % o key_symbol: Specifies a KeySym which indicates which side of the image
9355 % to trim.
9356 %
9357 */
9358 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9359  const MagickStatusType state,const KeySym key_symbol)
9360 {
9361  unsigned int
9362  quantum;
9363 
9364  /*
9365  User specified a magnify factor or position.
9366  */
9367  quantum=1;
9368  if ((state & Mod1Mask) != 0)
9369  quantum=10;
9370  switch ((int) key_symbol)
9371  {
9372  case QuitCommand:
9373  {
9374  (void) XWithdrawWindow(display,windows->magnify.id,
9375  windows->magnify.screen);
9376  break;
9377  }
9378  case XK_Home:
9379  case XK_KP_Home:
9380  {
9381  windows->magnify.x=(int) windows->image.width/2;
9382  windows->magnify.y=(int) windows->image.height/2;
9383  break;
9384  }
9385  case XK_Left:
9386  case XK_KP_Left:
9387  {
9388  if (windows->magnify.x > 0)
9389  windows->magnify.x-=quantum;
9390  break;
9391  }
9392  case XK_Up:
9393  case XK_KP_Up:
9394  {
9395  if (windows->magnify.y > 0)
9396  windows->magnify.y-=quantum;
9397  break;
9398  }
9399  case XK_Right:
9400  case XK_KP_Right:
9401  {
9402  if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9403  windows->magnify.x+=quantum;
9404  break;
9405  }
9406  case XK_Down:
9407  case XK_KP_Down:
9408  {
9409  if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9410  windows->magnify.y+=quantum;
9411  break;
9412  }
9413  case XK_0:
9414  case XK_1:
9415  case XK_2:
9416  case XK_3:
9417  case XK_4:
9418  case XK_5:
9419  case XK_6:
9420  case XK_7:
9421  case XK_8:
9422  case XK_9:
9423  {
9424  windows->magnify.data=(key_symbol-XK_0);
9425  break;
9426  }
9427  case XK_KP_0:
9428  case XK_KP_1:
9429  case XK_KP_2:
9430  case XK_KP_3:
9431  case XK_KP_4:
9432  case XK_KP_5:
9433  case XK_KP_6:
9434  case XK_KP_7:
9435  case XK_KP_8:
9436  case XK_KP_9:
9437  {
9438  windows->magnify.data=(key_symbol-XK_KP_0);
9439  break;
9440  }
9441  default:
9442  break;
9443  }
9444  XMakeMagnifyImage(display,windows);
9445 }
9446 ␌
9447 /*
9448 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9449 % %
9450 % %
9451 % %
9452 + X M a k e P a n I m a g e %
9453 % %
9454 % %
9455 % %
9456 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9457 %
9458 % XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9459 % icon window.
9460 %
9461 % The format of the XMakePanImage method is:
9462 %
9463 % void XMakePanImage(Display *display,XResourceInfo *resource_info,
9464 % XWindows *windows,Image *image)
9465 %
9466 % A description of each parameter follows:
9467 %
9468 % o display: Specifies a connection to an X server; returned from
9469 % XOpenDisplay.
9470 %
9471 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9472 %
9473 % o windows: Specifies a pointer to a XWindows structure.
9474 %
9475 % o image: the image.
9476 %
9477 */
9478 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9479  XWindows *windows,Image *image)
9480 {
9481  MagickStatusType
9482  status;
9483 
9484  /*
9485  Create and display image for panning icon.
9486  */
9487  XSetCursorState(display,windows,MagickTrue);
9488  XCheckRefreshWindows(display,windows);
9489  windows->pan.x=(int) windows->image.x;
9490  windows->pan.y=(int) windows->image.y;
9491  status=XMakeImage(display,resource_info,&windows->pan,image,
9492  windows->pan.width,windows->pan.height);
9493  if (status == MagickFalse)
9494  ThrowXWindowFatalException(XServerFatalError,image->exception.reason,
9495  image->exception.description);
9496  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9497  windows->pan.pixmap);
9498  (void) XClearWindow(display,windows->pan.id);
9499  XDrawPanRectangle(display,windows);
9500  XSetCursorState(display,windows,MagickFalse);
9501 }
9502 ␌
9503 /*
9504 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9505 % %
9506 % %
9507 % %
9508 + X M a t t a E d i t I m a g e %
9509 % %
9510 % %
9511 % %
9512 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9513 %
9514 % XMatteEditImage() allows the user to interactively change the Matte channel
9515 % of an image. If the image is PseudoClass it is promoted to DirectClass
9516 % before the matte information is stored.
9517 %
9518 % The format of the XMatteEditImage method is:
9519 %
9520 % MagickBooleanType XMatteEditImage(Display *display,
9521 % XResourceInfo *resource_info,XWindows *windows,Image **image)
9522 %
9523 % A description of each parameter follows:
9524 %
9525 % o display: Specifies a connection to an X server; returned from
9526 % XOpenDisplay.
9527 %
9528 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9529 %
9530 % o windows: Specifies a pointer to a XWindows structure.
9531 %
9532 % o image: the image; returned from ReadImage.
9533 %
9534 */
9535 static MagickBooleanType XMatteEditImage(Display *display,
9536  XResourceInfo *resource_info,XWindows *windows,Image **image)
9537 {
9538  const char
9539  *const MatteEditMenu[] =
9540  {
9541  "Method",
9542  "Border Color",
9543  "Fuzz",
9544  "Matte Value",
9545  "Undo",
9546  "Help",
9547  "Dismiss",
9548  (char *) NULL
9549  };
9550 
9551  static char
9552  matte[MaxTextExtent] = "0";
9553 
9554  static const ModeType
9555  MatteEditCommands[] =
9556  {
9557  MatteEditMethod,
9558  MatteEditBorderCommand,
9559  MatteEditFuzzCommand,
9560  MatteEditValueCommand,
9561  MatteEditUndoCommand,
9562  MatteEditHelpCommand,
9563  MatteEditDismissCommand
9564  };
9565 
9566  static PaintMethod
9567  method = PointMethod;
9568 
9569  static XColor
9570  border_color = { 0, 0, 0, 0, 0, 0 };
9571 
9572  char
9573  command[MaxTextExtent],
9574  text[MaxTextExtent] = "";
9575 
9576  Cursor
9577  cursor;
9578 
9579  int
9580  entry,
9581  id,
9582  x,
9583  x_offset,
9584  y,
9585  y_offset;
9586 
9587  int
9588  i;
9589 
9590  PixelPacket
9591  *q;
9592 
9593  unsigned int
9594  height,
9595  width;
9596 
9597  size_t
9598  state;
9599 
9600  XEvent
9601  event;
9602 
9603  /*
9604  Map Command widget.
9605  */
9606  (void) CloneString(&windows->command.name,"Matte Edit");
9607  windows->command.data=4;
9608  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9609  (void) XMapRaised(display,windows->command.id);
9610  XClientMessage(display,windows->image.id,windows->im_protocols,
9611  windows->im_update_widget,CurrentTime);
9612  /*
9613  Make cursor.
9614  */
9615  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9616  resource_info->background_color,resource_info->foreground_color);
9617  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9618  /*
9619  Track pointer until button 1 is pressed.
9620  */
9621  XQueryPosition(display,windows->image.id,&x,&y);
9622  (void) XSelectInput(display,windows->image.id,
9623  windows->image.attributes.event_mask | PointerMotionMask);
9624  state=DefaultState;
9625  do
9626  {
9627  if (windows->info.mapped != MagickFalse)
9628  {
9629  /*
9630  Display pointer position.
9631  */
9632  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9633  x+windows->image.x,y+windows->image.y);
9634  XInfoWidget(display,windows,text);
9635  }
9636  /*
9637  Wait for next event.
9638  */
9639  XScreenEvent(display,windows,&event);
9640  if (event.xany.window == windows->command.id)
9641  {
9642  /*
9643  Select a command from the Command widget.
9644  */
9645  id=XCommandWidget(display,windows,MatteEditMenu,&event);
9646  if (id < 0)
9647  {
9648  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9649  continue;
9650  }
9651  switch (MatteEditCommands[id])
9652  {
9653  case MatteEditMethod:
9654  {
9655  char
9656  **methods;
9657 
9658  /*
9659  Select a method from the pop-up menu.
9660  */
9661  methods=GetCommandOptions(MagickMethodOptions);
9662  if (methods == (char **) NULL)
9663  break;
9664  entry=XMenuWidget(display,windows,MatteEditMenu[id],
9665  (const char **) methods,command);
9666  if (entry >= 0)
9667  method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9668  MagickFalse,methods[entry]);
9669  methods=DestroyStringList(methods);
9670  break;
9671  }
9672  case MatteEditBorderCommand:
9673  {
9674  const char
9675  *ColorMenu[MaxNumberPens];
9676 
9677  int
9678  pen_number;
9679 
9680  /*
9681  Initialize menu selections.
9682  */
9683  for (i=0; i < (int) (MaxNumberPens-2); i++)
9684  ColorMenu[i]=resource_info->pen_colors[i];
9685  ColorMenu[MaxNumberPens-2]="Browser...";
9686  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9687  /*
9688  Select a pen color from the pop-up menu.
9689  */
9690  pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9691  (const char **) ColorMenu,command);
9692  if (pen_number < 0)
9693  break;
9694  if (pen_number == (MaxNumberPens-2))
9695  {
9696  static char
9697  color_name[MaxTextExtent] = "gray";
9698 
9699  /*
9700  Select a pen color from a dialog.
9701  */
9702  resource_info->pen_colors[pen_number]=color_name;
9703  XColorBrowserWidget(display,windows,"Select",color_name);
9704  if (*color_name == '\0')
9705  break;
9706  }
9707  /*
9708  Set border color.
9709  */
9710  (void) XParseColor(display,windows->map_info->colormap,
9711  resource_info->pen_colors[pen_number],&border_color);
9712  break;
9713  }
9714  case MatteEditFuzzCommand:
9715  {
9716  const char
9717  *const FuzzMenu[] =
9718  {
9719  "0%",
9720  "2%",
9721  "5%",
9722  "10%",
9723  "15%",
9724  "Dialog...",
9725  (char *) NULL,
9726  };
9727 
9728  static char
9729  fuzz[MaxTextExtent];
9730 
9731  /*
9732  Select a command from the pop-up menu.
9733  */
9734  entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9735  command);
9736  if (entry < 0)
9737  break;
9738  if (entry != 5)
9739  {
9740  (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9741  QuantumRange+1.0);
9742  break;
9743  }
9744  (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9745  (void) XDialogWidget(display,windows,"Ok",
9746  "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9747  if (*fuzz == '\0')
9748  break;
9749  (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9750  (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9751  1.0);
9752  break;
9753  }
9754  case MatteEditValueCommand:
9755  {
9756  const char
9757  *const MatteMenu[] =
9758  {
9759  "Opaque",
9760  "Transparent",
9761  "Dialog...",
9762  (char *) NULL,
9763  };
9764 
9765  static char
9766  message[MaxTextExtent];
9767 
9768  /*
9769  Select a command from the pop-up menu.
9770  */
9771  entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9772  command);
9773  if (entry < 0)
9774  break;
9775  if (entry != 2)
9776  {
9777  (void) FormatLocaleString(matte,MaxTextExtent,"%g",
9778  (double) OpaqueOpacity);
9779  if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9780  (void) FormatLocaleString(matte,MaxTextExtent,"%g",
9781  (double) TransparentOpacity);
9782  break;
9783  }
9784  (void) FormatLocaleString(message,MaxTextExtent,
9785  "Enter matte value (0 - " "%g" "):",(double) QuantumRange);
9786  (void) XDialogWidget(display,windows,"Matte",message,matte);
9787  if (*matte == '\0')
9788  break;
9789  break;
9790  }
9791  case MatteEditUndoCommand:
9792  {
9793  (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9794  image);
9795  break;
9796  }
9797  case MatteEditHelpCommand:
9798  {
9799  XTextViewHelp(display,resource_info,windows,MagickFalse,
9800  "Help Viewer - Matte Edit",ImageMatteEditHelp);
9801  break;
9802  }
9803  case MatteEditDismissCommand:
9804  {
9805  /*
9806  Prematurely exit.
9807  */
9808  state|=EscapeState;
9809  state|=ExitState;
9810  break;
9811  }
9812  default:
9813  break;
9814  }
9815  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9816  continue;
9817  }
9818  switch (event.type)
9819  {
9820  case ButtonPress:
9821  {
9822  if (event.xbutton.button != Button1)
9823  break;
9824  if ((event.xbutton.window != windows->image.id) &&
9825  (event.xbutton.window != windows->magnify.id))
9826  break;
9827  /*
9828  Update matte data.
9829  */
9830  x=event.xbutton.x;
9831  y=event.xbutton.y;
9832  (void) XMagickCommand(display,resource_info,windows,
9833  SaveToUndoBufferCommand,image);
9834  state|=UpdateConfigurationState;
9835  break;
9836  }
9837  case ButtonRelease:
9838  {
9839  if (event.xbutton.button != Button1)
9840  break;
9841  if ((event.xbutton.window != windows->image.id) &&
9842  (event.xbutton.window != windows->magnify.id))
9843  break;
9844  /*
9845  Update colormap information.
9846  */
9847  x=event.xbutton.x;
9848  y=event.xbutton.y;
9849  XConfigureImageColormap(display,resource_info,windows,*image);
9850  (void) XConfigureImage(display,resource_info,windows,*image);
9851  XInfoWidget(display,windows,text);
9852  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9853  state&=(~UpdateConfigurationState);
9854  break;
9855  }
9856  case Expose:
9857  break;
9858  case KeyPress:
9859  {
9860  char
9861  command[MaxTextExtent];
9862 
9863  KeySym
9864  key_symbol;
9865 
9866  if (event.xkey.window == windows->magnify.id)
9867  {
9868  Window
9869  window;
9870 
9871  window=windows->magnify.id;
9872  while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9873  }
9874  if (event.xkey.window != windows->image.id)
9875  break;
9876  /*
9877  Respond to a user key press.
9878  */
9879  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9880  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9881  switch ((int) key_symbol)
9882  {
9883  case XK_Escape:
9884  case XK_F20:
9885  {
9886  /*
9887  Prematurely exit.
9888  */
9889  state|=ExitState;
9890  break;
9891  }
9892  case XK_F1:
9893  case XK_Help:
9894  {
9895  XTextViewHelp(display,resource_info,windows,MagickFalse,
9896  "Help Viewer - Matte Edit",ImageMatteEditHelp);
9897  break;
9898  }
9899  default:
9900  {
9901  (void) XBell(display,0);
9902  break;
9903  }
9904  }
9905  break;
9906  }
9907  case MotionNotify:
9908  {
9909  /*
9910  Map and unmap Info widget as cursor crosses its boundaries.
9911  */
9912  x=event.xmotion.x;
9913  y=event.xmotion.y;
9914  if (windows->info.mapped != MagickFalse)
9915  {
9916  if ((x < (int) (windows->info.x+windows->info.width)) &&
9917  (y < (int) (windows->info.y+windows->info.height)))
9918  (void) XWithdrawWindow(display,windows->info.id,
9919  windows->info.screen);
9920  }
9921  else
9922  if ((x > (int) (windows->info.x+windows->info.width)) ||
9923  (y > (int) (windows->info.y+windows->info.height)))
9924  (void) XMapWindow(display,windows->info.id);
9925  break;
9926  }
9927  default:
9928  break;
9929  }
9930  if (event.xany.window == windows->magnify.id)
9931  {
9932  x=windows->magnify.x-windows->image.x;
9933  y=windows->magnify.y-windows->image.y;
9934  }
9935  x_offset=x;
9936  y_offset=y;
9937  if ((state & UpdateConfigurationState) != 0)
9938  {
9939  CacheView
9940  *image_view;
9941 
9943  *exception;
9944 
9945  int
9946  x,
9947  y;
9948 
9949  /*
9950  Matte edit is relative to image configuration.
9951  */
9952  (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
9953  MagickTrue);
9954  XPutPixel(windows->image.ximage,x_offset,y_offset,
9955  windows->pixel_info->background_color.pixel);
9956  width=(unsigned int) (*image)->columns;
9957  height=(unsigned int) (*image)->rows;
9958  x=0;
9959  y=0;
9960  if (windows->image.crop_geometry != (char *) NULL)
9961  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
9962  &width,&height);
9963  x_offset=(int)
9964  (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
9965  y_offset=(int)
9966  (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
9967  if ((x_offset < 0) || (y_offset < 0))
9968  continue;
9969  if ((x_offset >= (int) (*image)->columns) ||
9970  (y_offset >= (int) (*image)->rows))
9971  continue;
9972  if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
9973  return(MagickFalse);
9974  (*image)->matte=MagickTrue;
9975  exception=(&(*image)->exception);
9976  image_view=AcquireAuthenticCacheView(*image,exception);
9977  switch (method)
9978  {
9979  case PointMethod:
9980  default:
9981  {
9982  /*
9983  Update matte information using point algorithm.
9984  */
9985  q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
9986  (ssize_t) y_offset,1,1,exception);
9987  if (q == (PixelPacket *) NULL)
9988  break;
9989  q->opacity=(Quantum) StringToLong(matte);
9990  (void) SyncCacheViewAuthenticPixels(image_view,exception);
9991  break;
9992  }
9993  case ReplaceMethod:
9994  {
9995  PixelPacket
9996  target;
9997 
9998  /*
9999  Update matte information using replace algorithm.
10000  */
10001  (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10002  (ssize_t) y_offset,&target,exception);
10003  for (y=0; y < (int) (*image)->rows; y++)
10004  {
10005  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10006  (*image)->columns,1,&(*image)->exception);
10007  if (q == (PixelPacket *) NULL)
10008  break;
10009  for (x=0; x < (int) (*image)->columns; x++)
10010  {
10011  if (IsColorSimilar(*image,q,&target))
10012  q->opacity=(Quantum) StringToLong(matte);
10013  q++;
10014  }
10015  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10016  break;
10017  }
10018  break;
10019  }
10020  case FloodfillMethod:
10021  case FillToBorderMethod:
10022  {
10023  DrawInfo
10024  *draw_info;
10025 
10027  target;
10028 
10029  /*
10030  Update matte information using floodfill algorithm.
10031  */
10032  (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10033  (ssize_t) y_offset,&target,exception);
10034  if (method == FillToBorderMethod)
10035  {
10036  target.red=(MagickRealType)
10037  ScaleShortToQuantum(border_color.red);
10038  target.green=(MagickRealType)
10039  ScaleShortToQuantum(border_color.green);
10040  target.blue=(MagickRealType)
10041  ScaleShortToQuantum(border_color.blue);
10042  }
10043  draw_info=CloneDrawInfo(resource_info->image_info,
10044  (DrawInfo *) NULL);
10045  draw_info->fill.opacity=ClampToQuantum(StringToDouble(matte,
10046  (char **) NULL));
10047  (void) FloodfillPaintImage(*image,OpacityChannel,draw_info,&target,
10048  (ssize_t) x_offset,(ssize_t) y_offset,
10049  method == FloodfillMethod ? MagickFalse : MagickTrue);
10050  draw_info=DestroyDrawInfo(draw_info);
10051  break;
10052  }
10053  case ResetMethod:
10054  {
10055  /*
10056  Update matte information using reset algorithm.
10057  */
10058  if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
10059  return(MagickFalse);
10060  for (y=0; y < (int) (*image)->rows; y++)
10061  {
10062  q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10063  (*image)->columns,1,exception);
10064  if (q == (PixelPacket *) NULL)
10065  break;
10066  for (x=0; x < (int) (*image)->columns; x++)
10067  {
10068  q->opacity=(Quantum) StringToLong(matte);
10069  q++;
10070  }
10071  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10072  break;
10073  }
10074  if (StringToLong(matte) == OpaqueOpacity)
10075  (*image)->matte=MagickFalse;
10076  break;
10077  }
10078  }
10079  image_view=DestroyCacheView(image_view);
10080  state&=(~UpdateConfigurationState);
10081  }
10082  } while ((state & ExitState) == 0);
10083  (void) XSelectInput(display,windows->image.id,
10084  windows->image.attributes.event_mask);
10085  XSetCursorState(display,windows,MagickFalse);
10086  (void) XFreeCursor(display,cursor);
10087  return(MagickTrue);
10088 }
10089 ␌
10090 /*
10091 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10092 % %
10093 % %
10094 % %
10095 + X O p e n I m a g e %
10096 % %
10097 % %
10098 % %
10099 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10100 %
10101 % XOpenImage() loads an image from a file.
10102 %
10103 % The format of the XOpenImage method is:
10104 %
10105 % Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10106 % XWindows *windows,const unsigned int command)
10107 %
10108 % A description of each parameter follows:
10109 %
10110 % o display: Specifies a connection to an X server; returned from
10111 % XOpenDisplay.
10112 %
10113 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10114 %
10115 % o windows: Specifies a pointer to a XWindows structure.
10116 %
10117 % o command: A value other than zero indicates that the file is selected
10118 % from the command line argument list.
10119 %
10120 */
10121 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10122  XWindows *windows,const MagickBooleanType command)
10123 {
10124  const MagickInfo
10125  *magick_info;
10126 
10128  *exception;
10129 
10130  Image
10131  *nexus;
10132 
10133  ImageInfo
10134  *image_info;
10135 
10136  static char
10137  filename[MaxTextExtent] = "\0";
10138 
10139  /*
10140  Request file name from user.
10141  */
10142  if (command == MagickFalse)
10143  XFileBrowserWidget(display,windows,"Open",filename);
10144  else
10145  {
10146  char
10147  **filelist,
10148  **files;
10149 
10150  int
10151  count,
10152  status;
10153 
10154  int
10155  i,
10156  j;
10157 
10158  /*
10159  Select next image from the command line.
10160  */
10161  status=XGetCommand(display,windows->image.id,&files,&count);
10162  if (status == 0)
10163  ThrowXWindowException(XServerError,"UnableToGetProperty","...");
10164  filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10165  if (filelist == (char **) NULL)
10166  {
10167  (void) XFreeStringList(files);
10168  ThrowXWindowException(ResourceLimitError,
10169  "MemoryAllocationFailed","...");
10170  return((Image *) NULL);
10171  }
10172  j=0;
10173  for (i=1; i < count; i++)
10174  if (*files[i] != '-')
10175  filelist[j++]=files[i];
10176  filelist[j]=(char *) NULL;
10177  XListBrowserWidget(display,windows,&windows->widget,
10178  (const char **) filelist,"Load","Select Image to Load:",filename);
10179  filelist=(char **) RelinquishMagickMemory(filelist);
10180  (void) XFreeStringList(files);
10181  }
10182  if (*filename == '\0')
10183  return((Image *) NULL);
10184  image_info=CloneImageInfo(resource_info->image_info);
10185  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10186  (void *) NULL);
10187  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10188  exception=AcquireExceptionInfo();
10189  (void) SetImageInfo(image_info,0,exception);
10190  if (LocaleCompare(image_info->magick,"X") == 0)
10191  {
10192  char
10193  seconds[MaxTextExtent];
10194 
10195  /*
10196  User may want to delay the X server screen grab.
10197  */
10198  (void) CopyMagickString(seconds,"0",MaxTextExtent);
10199  (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10200  seconds);
10201  if (*seconds == '\0')
10202  return((Image *) NULL);
10203  XDelay(display,(size_t) (1000*StringToLong(seconds)));
10204  }
10205  magick_info=GetMagickInfo(image_info->magick,exception);
10206  if ((magick_info != (const MagickInfo *) NULL) &&
10207  (magick_info->raw != MagickFalse))
10208  {
10209  char
10210  geometry[MaxTextExtent];
10211 
10212  /*
10213  Request image size from the user.
10214  */
10215  (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10216  if (image_info->size != (char *) NULL)
10217  (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10218  (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10219  geometry);
10220  (void) CloneString(&image_info->size,geometry);
10221  }
10222  /*
10223  Load the image.
10224  */
10225  XSetCursorState(display,windows,MagickTrue);
10226  XCheckRefreshWindows(display,windows);
10227  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10228  nexus=ReadImage(image_info,exception);
10229  CatchException(exception);
10230  XSetCursorState(display,windows,MagickFalse);
10231  if (nexus != (Image *) NULL)
10232  XClientMessage(display,windows->image.id,windows->im_protocols,
10233  windows->im_next_image,CurrentTime);
10234  else
10235  {
10236  char
10237  *text,
10238  **textlist;
10239 
10240  /*
10241  Unknown image format.
10242  */
10243  text=FileToString(filename,~0UL,exception);
10244  if (text == (char *) NULL)
10245  return((Image *) NULL);
10246  textlist=StringToList(text);
10247  if (textlist != (char **) NULL)
10248  {
10249  char
10250  title[MaxTextExtent];
10251 
10252  int
10253  i;
10254 
10255  (void) FormatLocaleString(title,MaxTextExtent,
10256  "Unknown format: %s",filename);
10257  XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10258  (const char **) textlist);
10259  for (i=0; textlist[i] != (char *) NULL; i++)
10260  textlist[i]=DestroyString(textlist[i]);
10261  textlist=(char **) RelinquishMagickMemory(textlist);
10262  }
10263  text=DestroyString(text);
10264  }
10265  exception=DestroyExceptionInfo(exception);
10266  image_info=DestroyImageInfo(image_info);
10267  return(nexus);
10268 }
10269 ␌
10270 /*
10271 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10272 % %
10273 % %
10274 % %
10275 + X P a n I m a g e %
10276 % %
10277 % %
10278 % %
10279 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10280 %
10281 % XPanImage() pans the image until the mouse button is released.
10282 %
10283 % The format of the XPanImage method is:
10284 %
10285 % void XPanImage(Display *display,XWindows *windows,XEvent *event)
10286 %
10287 % A description of each parameter follows:
10288 %
10289 % o display: Specifies a connection to an X server; returned from
10290 % XOpenDisplay.
10291 %
10292 % o windows: Specifies a pointer to a XWindows structure.
10293 %
10294 % o event: Specifies a pointer to a XEvent structure. If it is NULL,
10295 % the entire image is refreshed.
10296 %
10297 */
10298 static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10299 {
10300  char
10301  text[MaxTextExtent];
10302 
10303  Cursor
10304  cursor;
10305 
10306  MagickRealType
10307  x_factor,
10308  y_factor;
10309 
10311  pan_info;
10312 
10313  size_t
10314  state;
10315 
10316  /*
10317  Define cursor.
10318  */
10319  if ((windows->image.ximage->width > (int) windows->image.width) &&
10320  (windows->image.ximage->height > (int) windows->image.height))
10321  cursor=XCreateFontCursor(display,XC_fleur);
10322  else
10323  if (windows->image.ximage->width > (int) windows->image.width)
10324  cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10325  else
10326  if (windows->image.ximage->height > (int) windows->image.height)
10327  cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10328  else
10329  cursor=XCreateFontCursor(display,XC_arrow);
10330  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10331  /*
10332  Pan image as pointer moves until the mouse button is released.
10333  */
10334  x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10335  y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10336  pan_info.width=windows->pan.width*windows->image.width/
10337  windows->image.ximage->width;
10338  pan_info.height=windows->pan.height*windows->image.height/
10339  windows->image.ximage->height;
10340  pan_info.x=0;
10341  pan_info.y=0;
10342  state=UpdateConfigurationState;
10343  do
10344  {
10345  switch (event->type)
10346  {
10347  case ButtonPress:
10348  {
10349  /*
10350  User choose an initial pan location.
10351  */
10352  pan_info.x=(ssize_t) event->xbutton.x;
10353  pan_info.y=(ssize_t) event->xbutton.y;
10354  state|=UpdateConfigurationState;
10355  break;
10356  }
10357  case ButtonRelease:
10358  {
10359  /*
10360  User has finished panning the image.
10361  */
10362  pan_info.x=(ssize_t) event->xbutton.x;
10363  pan_info.y=(ssize_t) event->xbutton.y;
10364  state|=UpdateConfigurationState | ExitState;
10365  break;
10366  }
10367  case MotionNotify:
10368  {
10369  pan_info.x=(ssize_t) event->xmotion.x;
10370  pan_info.y=(ssize_t) event->xmotion.y;
10371  state|=UpdateConfigurationState;
10372  }
10373  default:
10374  break;
10375  }
10376  if ((state & UpdateConfigurationState) != 0)
10377  {
10378  /*
10379  Check boundary conditions.
10380  */
10381  if (pan_info.x < (ssize_t) (pan_info.width/2))
10382  pan_info.x=0;
10383  else
10384  pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10385  if (pan_info.x < 0)
10386  pan_info.x=0;
10387  else
10388  if ((int) (pan_info.x+windows->image.width) >
10389  windows->image.ximage->width)
10390  pan_info.x=(ssize_t)
10391  (windows->image.ximage->width-windows->image.width);
10392  if (pan_info.y < (ssize_t) (pan_info.height/2))
10393  pan_info.y=0;
10394  else
10395  pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10396  if (pan_info.y < 0)
10397  pan_info.y=0;
10398  else
10399  if ((int) (pan_info.y+windows->image.height) >
10400  windows->image.ximage->height)
10401  pan_info.y=(ssize_t)
10402  (windows->image.ximage->height-windows->image.height);
10403  if ((windows->image.x != (int) pan_info.x) ||
10404  (windows->image.y != (int) pan_info.y))
10405  {
10406  /*
10407  Display image pan offset.
10408  */
10409  windows->image.x=(int) pan_info.x;
10410  windows->image.y=(int) pan_info.y;
10411  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10412  windows->image.width,windows->image.height,windows->image.x,
10413  windows->image.y);
10414  XInfoWidget(display,windows,text);
10415  /*
10416  Refresh Image window.
10417  */
10418  XDrawPanRectangle(display,windows);
10419  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10420  }
10421  state&=(~UpdateConfigurationState);
10422  }
10423  /*
10424  Wait for next event.
10425  */
10426  if ((state & ExitState) == 0)
10427  XScreenEvent(display,windows,event);
10428  } while ((state & ExitState) == 0);
10429  /*
10430  Restore cursor.
10431  */
10432  (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10433  (void) XFreeCursor(display,cursor);
10434  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10435 }
10436 ␌
10437 /*
10438 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10439 % %
10440 % %
10441 % %
10442 + X P a s t e I m a g e %
10443 % %
10444 % %
10445 % %
10446 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10447 %
10448 % XPasteImage() pastes an image previously saved with XCropImage in the X
10449 % window image at a location the user chooses with the pointer.
10450 %
10451 % The format of the XPasteImage method is:
10452 %
10453 % MagickBooleanType XPasteImage(Display *display,
10454 % XResourceInfo *resource_info,XWindows *windows,Image *image)
10455 %
10456 % A description of each parameter follows:
10457 %
10458 % o display: Specifies a connection to an X server; returned from
10459 % XOpenDisplay.
10460 %
10461 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10462 %
10463 % o windows: Specifies a pointer to a XWindows structure.
10464 %
10465 % o image: the image; returned from ReadImage.
10466 %
10467 */
10468 static MagickBooleanType XPasteImage(Display *display,
10469  XResourceInfo *resource_info,XWindows *windows,Image *image)
10470 {
10471  const char
10472  *const PasteMenu[] =
10473  {
10474  "Operator",
10475  "Help",
10476  "Dismiss",
10477  (char *) NULL
10478  };
10479 
10480  static const ModeType
10481  PasteCommands[] =
10482  {
10483  PasteOperatorsCommand,
10484  PasteHelpCommand,
10485  PasteDismissCommand
10486  };
10487 
10488  static CompositeOperator
10489  compose = CopyCompositeOp;
10490 
10491  char
10492  text[MaxTextExtent];
10493 
10494  Cursor
10495  cursor;
10496 
10497  Image
10498  *paste_image;
10499 
10500  int
10501  entry,
10502  id,
10503  x,
10504  y;
10505 
10506  MagickRealType
10507  scale_factor;
10508 
10510  highlight_info,
10511  paste_info;
10512 
10513  unsigned int
10514  height,
10515  width;
10516 
10517  size_t
10518  state;
10519 
10520  XEvent
10521  event;
10522 
10523  /*
10524  Copy image.
10525  */
10526  if (resource_info->copy_image == (Image *) NULL)
10527  return(MagickFalse);
10528  paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,
10529  &image->exception);
10530  if (paste_image == (Image *) NULL)
10531  return(MagickFalse);
10532  /*
10533  Map Command widget.
10534  */
10535  (void) CloneString(&windows->command.name,"Paste");
10536  windows->command.data=1;
10537  (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10538  (void) XMapRaised(display,windows->command.id);
10539  XClientMessage(display,windows->image.id,windows->im_protocols,
10540  windows->im_update_widget,CurrentTime);
10541  /*
10542  Track pointer until button 1 is pressed.
10543  */
10544  XSetCursorState(display,windows,MagickFalse);
10545  XQueryPosition(display,windows->image.id,&x,&y);
10546  (void) XSelectInput(display,windows->image.id,
10547  windows->image.attributes.event_mask | PointerMotionMask);
10548  paste_info.x=(ssize_t) windows->image.x+x;
10549  paste_info.y=(ssize_t) windows->image.y+y;
10550  paste_info.width=0;
10551  paste_info.height=0;
10552  cursor=XCreateFontCursor(display,XC_ul_angle);
10553  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10554  state=DefaultState;
10555  do
10556  {
10557  if (windows->info.mapped != MagickFalse)
10558  {
10559  /*
10560  Display pointer position.
10561  */
10562  (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10563  (long) paste_info.x,(long) paste_info.y);
10564  XInfoWidget(display,windows,text);
10565  }
10566  highlight_info=paste_info;
10567  highlight_info.x=paste_info.x-windows->image.x;
10568  highlight_info.y=paste_info.y-windows->image.y;
10569  XHighlightRectangle(display,windows->image.id,
10570  windows->image.highlight_context,&highlight_info);
10571  /*
10572  Wait for next event.
10573  */
10574  XScreenEvent(display,windows,&event);
10575  XHighlightRectangle(display,windows->image.id,
10576  windows->image.highlight_context,&highlight_info);
10577  if (event.xany.window == windows->command.id)
10578  {
10579  /*
10580  Select a command from the Command widget.
10581  */
10582  id=XCommandWidget(display,windows,PasteMenu,&event);
10583  if (id < 0)
10584  continue;
10585  switch (PasteCommands[id])
10586  {
10587  case PasteOperatorsCommand:
10588  {
10589  char
10590  command[MaxTextExtent],
10591  **operators;
10592 
10593  /*
10594  Select a command from the pop-up menu.
10595  */
10596  operators=GetCommandOptions(MagickComposeOptions);
10597  if (operators == (char **) NULL)
10598  break;
10599  entry=XMenuWidget(display,windows,PasteMenu[id],
10600  (const char **) operators,command);
10601  if (entry >= 0)
10602  compose=(CompositeOperator) ParseCommandOption(
10603  MagickComposeOptions,MagickFalse,operators[entry]);
10604  operators=DestroyStringList(operators);
10605  break;
10606  }
10607  case PasteHelpCommand:
10608  {
10609  XTextViewHelp(display,resource_info,windows,MagickFalse,
10610  "Help Viewer - Image Composite",ImagePasteHelp);
10611  break;
10612  }
10613  case PasteDismissCommand:
10614  {
10615  /*
10616  Prematurely exit.
10617  */
10618  state|=EscapeState;
10619  state|=ExitState;
10620  break;
10621  }
10622  default:
10623  break;
10624  }
10625  continue;
10626  }
10627  switch (event.type)
10628  {
10629  case ButtonPress:
10630  {
10631  if (resource_info->debug != MagickFalse)
10632  (void) LogMagickEvent(X11Event,GetMagickModule(),
10633  "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10634  event.xbutton.button,event.xbutton.x,event.xbutton.y);
10635  if (event.xbutton.button != Button1)
10636  break;
10637  if (event.xbutton.window != windows->image.id)
10638  break;
10639  /*
10640  Paste rectangle is relative to image configuration.
10641  */
10642  width=(unsigned int) image->columns;
10643  height=(unsigned int) image->rows;
10644  x=0;
10645  y=0;
10646  if (windows->image.crop_geometry != (char *) NULL)
10647  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10648  &width,&height);
10649  scale_factor=(MagickRealType) windows->image.ximage->width/width;
10650  paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10651  scale_factor=(MagickRealType) windows->image.ximage->height/height;
10652  paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10653  (void) XCheckDefineCursor(display,windows->image.id,cursor);
10654  paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10655  paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10656  break;
10657  }
10658  case ButtonRelease:
10659  {
10660  if (resource_info->debug != MagickFalse)
10661  (void) LogMagickEvent(X11Event,GetMagickModule(),
10662  "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10663  event.xbutton.button,event.xbutton.x,event.xbutton.y);
10664  if (event.xbutton.button != Button1)
10665  break;
10666  if (event.xbutton.window != windows->image.id)
10667  break;
10668  if ((paste_info.width != 0) && (paste_info.height != 0))
10669  {
10670  /*
10671  User has selected the location of the paste image.
10672  */
10673  paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10674  paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10675  state|=ExitState;
10676  }
10677  break;
10678  }
10679  case Expose:
10680  break;
10681  case KeyPress:
10682  {
10683  char
10684  command[MaxTextExtent];
10685 
10686  KeySym
10687  key_symbol;
10688 
10689  int
10690  length;
10691 
10692  if (event.xkey.window != windows->image.id)
10693  break;
10694  /*
10695  Respond to a user key press.
10696  */
10697  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10698  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10699  *(command+length)='\0';
10700  if (resource_info->debug != MagickFalse)
10701  (void) LogMagickEvent(X11Event,GetMagickModule(),
10702  "Key press: 0x%lx (%s)",(long) key_symbol,command);
10703  switch ((int) key_symbol)
10704  {
10705  case XK_Escape:
10706  case XK_F20:
10707  {
10708  /*
10709  Prematurely exit.
10710  */
10711  paste_image=DestroyImage(paste_image);
10712  state|=EscapeState;
10713  state|=ExitState;
10714  break;
10715  }
10716  case XK_F1:
10717  case XK_Help:
10718  {
10719  (void) XSetFunction(display,windows->image.highlight_context,
10720  GXcopy);
10721  XTextViewHelp(display,resource_info,windows,MagickFalse,
10722  "Help Viewer - Image Composite",ImagePasteHelp);
10723  (void) XSetFunction(display,windows->image.highlight_context,
10724  GXinvert);
10725  break;
10726  }
10727  default:
10728  {
10729  (void) XBell(display,0);
10730  break;
10731  }
10732  }
10733  break;
10734  }
10735  case MotionNotify:
10736  {
10737  /*
10738  Map and unmap Info widget as text cursor crosses its boundaries.
10739  */
10740  x=event.xmotion.x;
10741  y=event.xmotion.y;
10742  if (windows->info.mapped != MagickFalse)
10743  {
10744  if ((x < (int) (windows->info.x+windows->info.width)) &&
10745  (y < (int) (windows->info.y+windows->info.height)))
10746  (void) XWithdrawWindow(display,windows->info.id,
10747  windows->info.screen);
10748  }
10749  else
10750  if ((x > (int) (windows->info.x+windows->info.width)) ||
10751  (y > (int) (windows->info.y+windows->info.height)))
10752  (void) XMapWindow(display,windows->info.id);
10753  paste_info.x=(ssize_t) windows->image.x+x;
10754  paste_info.y=(ssize_t) windows->image.y+y;
10755  break;
10756  }
10757  default:
10758  {
10759  if (resource_info->debug != MagickFalse)
10760  (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10761  event.type);
10762  break;
10763  }
10764  }
10765  } while ((state & ExitState) == 0);
10766  (void) XSelectInput(display,windows->image.id,
10767  windows->image.attributes.event_mask);
10768  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10769  XSetCursorState(display,windows,MagickFalse);
10770  (void) XFreeCursor(display,cursor);
10771  if ((state & EscapeState) != 0)
10772  return(MagickTrue);
10773  /*
10774  Image pasting is relative to image configuration.
10775  */
10776  XSetCursorState(display,windows,MagickTrue);
10777  XCheckRefreshWindows(display,windows);
10778  width=(unsigned int) image->columns;
10779  height=(unsigned int) image->rows;
10780  x=0;
10781  y=0;
10782  if (windows->image.crop_geometry != (char *) NULL)
10783  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10784  scale_factor=(MagickRealType) width/windows->image.ximage->width;
10785  paste_info.x+=x;
10786  paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10787  paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10788  scale_factor=(MagickRealType) height/windows->image.ximage->height;
10789  paste_info.y+=y;
10790  paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10791  paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10792  /*
10793  Paste image with X Image window.
10794  */
10795  (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10796  paste_image=DestroyImage(paste_image);
10797  XSetCursorState(display,windows,MagickFalse);
10798  /*
10799  Update image colormap.
10800  */
10801  XConfigureImageColormap(display,resource_info,windows,image);
10802  (void) XConfigureImage(display,resource_info,windows,image);
10803  return(MagickTrue);
10804 }
10805 ␌
10806 /*
10807 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10808 % %
10809 % %
10810 % %
10811 + X P r i n t I m a g e %
10812 % %
10813 % %
10814 % %
10815 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10816 %
10817 % XPrintImage() prints an image to a Postscript printer.
10818 %
10819 % The format of the XPrintImage method is:
10820 %
10821 % MagickBooleanType XPrintImage(Display *display,
10822 % XResourceInfo *resource_info,XWindows *windows,Image *image)
10823 %
10824 % A description of each parameter follows:
10825 %
10826 % o display: Specifies a connection to an X server; returned from
10827 % XOpenDisplay.
10828 %
10829 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10830 %
10831 % o windows: Specifies a pointer to a XWindows structure.
10832 %
10833 % o image: the image.
10834 %
10835 */
10836 static MagickBooleanType XPrintImage(Display *display,
10837  XResourceInfo *resource_info,XWindows *windows,Image *image)
10838 {
10839  char
10840  filename[MaxTextExtent],
10841  geometry[MaxTextExtent];
10842 
10843  const char
10844  *const PageSizes[] =
10845  {
10846  "Letter",
10847  "Tabloid",
10848  "Ledger",
10849  "Legal",
10850  "Statement",
10851  "Executive",
10852  "A3",
10853  "A4",
10854  "A5",
10855  "B4",
10856  "B5",
10857  "Folio",
10858  "Quarto",
10859  "10x14",
10860  (char *) NULL
10861  };
10862 
10863  Image
10864  *print_image;
10865 
10866  ImageInfo
10867  *image_info;
10868 
10869  MagickStatusType
10870  status;
10871 
10872  /*
10873  Request Postscript page geometry from user.
10874  */
10875  image_info=CloneImageInfo(resource_info->image_info);
10876  (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10877  if (image_info->page != (char *) NULL)
10878  (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10879  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10880  "Select Postscript Page Geometry:",geometry);
10881  if (*geometry == '\0')
10882  return(MagickTrue);
10883  image_info->page=GetPageGeometry(geometry);
10884  /*
10885  Apply image transforms.
10886  */
10887  XSetCursorState(display,windows,MagickTrue);
10888  XCheckRefreshWindows(display,windows);
10889  print_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10890  if (print_image == (Image *) NULL)
10891  return(MagickFalse);
10892  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
10893  windows->image.ximage->width,windows->image.ximage->height);
10894  (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10895  /*
10896  Print image.
10897  */
10898  (void) AcquireUniqueFilename(filename);
10899  (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
10900  filename);
10901  status=WriteImage(image_info,print_image);
10902  (void) RelinquishUniqueFileResource(filename);
10903  print_image=DestroyImage(print_image);
10904  image_info=DestroyImageInfo(image_info);
10905  XSetCursorState(display,windows,MagickFalse);
10906  return(status != 0 ? MagickTrue : MagickFalse);
10907 }
10908 ␌
10909 /*
10910 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10911 % %
10912 % %
10913 % %
10914 + X R O I I m a g e %
10915 % %
10916 % %
10917 % %
10918 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10919 %
10920 % XROIImage() applies an image processing technique to a region of interest.
10921 %
10922 % The format of the XROIImage method is:
10923 %
10924 % MagickBooleanType XROIImage(Display *display,
10925 % XResourceInfo *resource_info,XWindows *windows,Image **image)
10926 %
10927 % A description of each parameter follows:
10928 %
10929 % o display: Specifies a connection to an X server; returned from
10930 % XOpenDisplay.
10931 %
10932 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10933 %
10934 % o windows: Specifies a pointer to a XWindows structure.
10935 %
10936 % o image: the image; returned from ReadImage.
10937 %
10938 */
10939 static MagickBooleanType XROIImage(Display *display,
10940  XResourceInfo *resource_info,XWindows *windows,Image **image)
10941 {
10942 #define ApplyMenus 7
10943 
10944  const char
10945  *const ROIMenu[] =
10946  {
10947  "Help",
10948  "Dismiss",
10949  (char *) NULL
10950  },
10951  *const ApplyMenu[] =
10952  {
10953  "File",
10954  "Edit",
10955  "Transform",
10956  "Enhance",
10957  "Effects",
10958  "F/X",
10959  "Miscellany",
10960  "Help",
10961  "Dismiss",
10962  (char *) NULL
10963  },
10964  *const FileMenu[] =
10965  {
10966  "Save...",
10967  "Print...",
10968  (char *) NULL
10969  },
10970  *const EditMenu[] =
10971  {
10972  "Undo",
10973  "Redo",
10974  (char *) NULL
10975  },
10976  *const TransformMenu[] =
10977  {
10978  "Flop",
10979  "Flip",
10980  "Rotate Right",
10981  "Rotate Left",
10982  (char *) NULL
10983  },
10984  *const EnhanceMenu[] =
10985  {
10986  "Hue...",
10987  "Saturation...",
10988  "Brightness...",
10989  "Gamma...",
10990  "Spiff",
10991  "Dull",
10992  "Contrast Stretch...",
10993  "Sigmoidal Contrast...",
10994  "Normalize",
10995  "Equalize",
10996  "Negate",
10997  "Grayscale",
10998  "Map...",
10999  "Quantize...",
11000  (char *) NULL
11001  },
11002  *const EffectsMenu[] =
11003  {
11004  "Despeckle",
11005  "Emboss",
11006  "Reduce Noise",
11007  "Add Noise",
11008  "Sharpen...",
11009  "Blur...",
11010  "Threshold...",
11011  "Edge Detect...",
11012  "Spread...",
11013  "Shade...",
11014  "Raise...",
11015  "Segment...",
11016  (char *) NULL
11017  },
11018  *const FXMenu[] =
11019  {
11020  "Solarize...",
11021  "Sepia Tone...",
11022  "Swirl...",
11023  "Implode...",
11024  "Vignette...",
11025  "Wave...",
11026  "Oil Paint...",
11027  "Charcoal Draw...",
11028  (char *) NULL
11029  },
11030  *const MiscellanyMenu[] =
11031  {
11032  "Image Info",
11033  "Zoom Image",
11034  "Show Preview...",
11035  "Show Histogram",
11036  "Show Matte",
11037  (char *) NULL
11038  };
11039 
11040  const char
11041  *const *Menus[ApplyMenus] =
11042  {
11043  FileMenu,
11044  EditMenu,
11045  TransformMenu,
11046  EnhanceMenu,
11047  EffectsMenu,
11048  FXMenu,
11049  MiscellanyMenu
11050  };
11051 
11052  static const DisplayCommand
11053  ApplyCommands[] =
11054  {
11055  NullCommand,
11056  NullCommand,
11057  NullCommand,
11058  NullCommand,
11059  NullCommand,
11060  NullCommand,
11061  NullCommand,
11062  HelpCommand,
11063  QuitCommand
11064  },
11065  FileCommands[] =
11066  {
11067  SaveCommand,
11068  PrintCommand
11069  },
11070  EditCommands[] =
11071  {
11072  UndoCommand,
11073  RedoCommand
11074  },
11075  TransformCommands[] =
11076  {
11077  FlopCommand,
11078  FlipCommand,
11079  RotateRightCommand,
11080  RotateLeftCommand
11081  },
11082  EnhanceCommands[] =
11083  {
11084  HueCommand,
11085  SaturationCommand,
11086  BrightnessCommand,
11087  GammaCommand,
11088  SpiffCommand,
11089  DullCommand,
11090  ContrastStretchCommand,
11091  SigmoidalContrastCommand,
11092  NormalizeCommand,
11093  EqualizeCommand,
11094  NegateCommand,
11095  GrayscaleCommand,
11096  MapCommand,
11097  QuantizeCommand
11098  },
11099  EffectsCommands[] =
11100  {
11101  DespeckleCommand,
11102  EmbossCommand,
11103  ReduceNoiseCommand,
11104  AddNoiseCommand,
11105  SharpenCommand,
11106  BlurCommand,
11107  EdgeDetectCommand,
11108  SpreadCommand,
11109  ShadeCommand,
11110  RaiseCommand,
11111  SegmentCommand
11112  },
11113  FXCommands[] =
11114  {
11115  SolarizeCommand,
11116  SepiaToneCommand,
11117  SwirlCommand,
11118  ImplodeCommand,
11119  VignetteCommand,
11120  WaveCommand,
11121  OilPaintCommand,
11122  CharcoalDrawCommand
11123  },
11124  MiscellanyCommands[] =
11125  {
11126  InfoCommand,
11127  ZoomCommand,
11128  ShowPreviewCommand,
11129  ShowHistogramCommand,
11130  ShowMatteCommand
11131  },
11132  ROICommands[] =
11133  {
11134  ROIHelpCommand,
11135  ROIDismissCommand
11136  };
11137 
11138  static const DisplayCommand
11139  *Commands[ApplyMenus] =
11140  {
11141  FileCommands,
11142  EditCommands,
11143  TransformCommands,
11144  EnhanceCommands,
11145  EffectsCommands,
11146  FXCommands,
11147  MiscellanyCommands
11148  };
11149 
11150  char
11151  command[MaxTextExtent],
11152  text[MaxTextExtent];
11153 
11154  DisplayCommand
11155  display_command;
11156 
11157  Cursor
11158  cursor;
11159 
11160  Image
11161  *roi_image;
11162 
11163  int
11164  entry,
11165  id,
11166  x,
11167  y;
11168 
11169  MagickRealType
11170  scale_factor;
11171 
11172  MagickProgressMonitor
11173  progress_monitor;
11174 
11176  crop_info,
11177  highlight_info,
11178  roi_info;
11179 
11180  unsigned int
11181  height,
11182  width;
11183 
11184  size_t
11185  state;
11186 
11187  XEvent
11188  event;
11189 
11190  /*
11191  Map Command widget.
11192  */
11193  (void) CloneString(&windows->command.name,"ROI");
11194  windows->command.data=0;
11195  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11196  (void) XMapRaised(display,windows->command.id);
11197  XClientMessage(display,windows->image.id,windows->im_protocols,
11198  windows->im_update_widget,CurrentTime);
11199  /*
11200  Track pointer until button 1 is pressed.
11201  */
11202  XQueryPosition(display,windows->image.id,&x,&y);
11203  (void) XSelectInput(display,windows->image.id,
11204  windows->image.attributes.event_mask | PointerMotionMask);
11205  crop_info.width=0;
11206  crop_info.height=0;
11207  crop_info.x=0;
11208  crop_info.y=0;
11209  roi_info.x=(ssize_t) windows->image.x+x;
11210  roi_info.y=(ssize_t) windows->image.y+y;
11211  roi_info.width=0;
11212  roi_info.height=0;
11213  cursor=XCreateFontCursor(display,XC_fleur);
11214  state=DefaultState;
11215  do
11216  {
11217  if (windows->info.mapped != MagickFalse)
11218  {
11219  /*
11220  Display pointer position.
11221  */
11222  (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11223  (long) roi_info.x,(long) roi_info.y);
11224  XInfoWidget(display,windows,text);
11225  }
11226  /*
11227  Wait for next event.
11228  */
11229  XScreenEvent(display,windows,&event);
11230  if (event.xany.window == windows->command.id)
11231  {
11232  /*
11233  Select a command from the Command widget.
11234  */
11235  id=XCommandWidget(display,windows,ROIMenu,&event);
11236  if (id < 0)
11237  continue;
11238  switch (ROICommands[id])
11239  {
11240  case ROIHelpCommand:
11241  {
11242  XTextViewHelp(display,resource_info,windows,MagickFalse,
11243  "Help Viewer - Region of Interest",ImageROIHelp);
11244  break;
11245  }
11246  case ROIDismissCommand:
11247  {
11248  /*
11249  Prematurely exit.
11250  */
11251  state|=EscapeState;
11252  state|=ExitState;
11253  break;
11254  }
11255  default:
11256  break;
11257  }
11258  continue;
11259  }
11260  switch (event.type)
11261  {
11262  case ButtonPress:
11263  {
11264  if (event.xbutton.button != Button1)
11265  break;
11266  if (event.xbutton.window != windows->image.id)
11267  break;
11268  /*
11269  Note first corner of region of interest rectangle-- exit loop.
11270  */
11271  (void) XCheckDefineCursor(display,windows->image.id,cursor);
11272  roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11273  roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11274  state|=ExitState;
11275  break;
11276  }
11277  case ButtonRelease:
11278  break;
11279  case Expose:
11280  break;
11281  case KeyPress:
11282  {
11283  KeySym
11284  key_symbol;
11285 
11286  if (event.xkey.window != windows->image.id)
11287  break;
11288  /*
11289  Respond to a user key press.
11290  */
11291  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11292  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11293  switch ((int) key_symbol)
11294  {
11295  case XK_Escape:
11296  case XK_F20:
11297  {
11298  /*
11299  Prematurely exit.
11300  */
11301  state|=EscapeState;
11302  state|=ExitState;
11303  break;
11304  }
11305  case XK_F1:
11306  case XK_Help:
11307  {
11308  XTextViewHelp(display,resource_info,windows,MagickFalse,
11309  "Help Viewer - Region of Interest",ImageROIHelp);
11310  break;
11311  }
11312  default:
11313  {
11314  (void) XBell(display,0);
11315  break;
11316  }
11317  }
11318  break;
11319  }
11320  case MotionNotify:
11321  {
11322  /*
11323  Map and unmap Info widget as text cursor crosses its boundaries.
11324  */
11325  x=event.xmotion.x;
11326  y=event.xmotion.y;
11327  if (windows->info.mapped != MagickFalse)
11328  {
11329  if ((x < (int) (windows->info.x+windows->info.width)) &&
11330  (y < (int) (windows->info.y+windows->info.height)))
11331  (void) XWithdrawWindow(display,windows->info.id,
11332  windows->info.screen);
11333  }
11334  else
11335  if ((x > (int) (windows->info.x+windows->info.width)) ||
11336  (y > (int) (windows->info.y+windows->info.height)))
11337  (void) XMapWindow(display,windows->info.id);
11338  roi_info.x=(ssize_t) windows->image.x+x;
11339  roi_info.y=(ssize_t) windows->image.y+y;
11340  break;
11341  }
11342  default:
11343  break;
11344  }
11345  } while ((state & ExitState) == 0);
11346  (void) XSelectInput(display,windows->image.id,
11347  windows->image.attributes.event_mask);
11348  if ((state & EscapeState) != 0)
11349  {
11350  /*
11351  User want to exit without region of interest.
11352  */
11353  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11354  (void) XFreeCursor(display,cursor);
11355  return(MagickTrue);
11356  }
11357  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11358  do
11359  {
11360  /*
11361  Size rectangle as pointer moves until the mouse button is released.
11362  */
11363  x=(int) roi_info.x;
11364  y=(int) roi_info.y;
11365  roi_info.width=0;
11366  roi_info.height=0;
11367  state=DefaultState;
11368  do
11369  {
11370  highlight_info=roi_info;
11371  highlight_info.x=roi_info.x-windows->image.x;
11372  highlight_info.y=roi_info.y-windows->image.y;
11373  if ((highlight_info.width > 3) && (highlight_info.height > 3))
11374  {
11375  /*
11376  Display info and draw region of interest rectangle.
11377  */
11378  if (windows->info.mapped == MagickFalse)
11379  (void) XMapWindow(display,windows->info.id);
11380  (void) FormatLocaleString(text,MaxTextExtent,
11381  " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11382  roi_info.height,(double) roi_info.x,(double) roi_info.y);
11383  XInfoWidget(display,windows,text);
11384  XHighlightRectangle(display,windows->image.id,
11385  windows->image.highlight_context,&highlight_info);
11386  }
11387  else
11388  if (windows->info.mapped != MagickFalse)
11389  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11390  /*
11391  Wait for next event.
11392  */
11393  XScreenEvent(display,windows,&event);
11394  if ((highlight_info.width > 3) && (highlight_info.height > 3))
11395  XHighlightRectangle(display,windows->image.id,
11396  windows->image.highlight_context,&highlight_info);
11397  switch (event.type)
11398  {
11399  case ButtonPress:
11400  {
11401  roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11402  roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11403  break;
11404  }
11405  case ButtonRelease:
11406  {
11407  /*
11408  User has committed to region of interest rectangle.
11409  */
11410  roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11411  roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11412  XSetCursorState(display,windows,MagickFalse);
11413  state|=ExitState;
11414  if (LocaleCompare(windows->command.name,"Apply") == 0)
11415  break;
11416  (void) CloneString(&windows->command.name,"Apply");
11417  windows->command.data=ApplyMenus;
11418  (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11419  break;
11420  }
11421  case Expose:
11422  break;
11423  case MotionNotify:
11424  {
11425  roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11426  roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11427  }
11428  default:
11429  break;
11430  }
11431  if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11432  ((state & ExitState) != 0))
11433  {
11434  /*
11435  Check boundary conditions.
11436  */
11437  if (roi_info.x < 0)
11438  roi_info.x=0;
11439  else
11440  if (roi_info.x > (ssize_t) windows->image.ximage->width)
11441  roi_info.x=(ssize_t) windows->image.ximage->width;
11442  if ((int) roi_info.x < x)
11443  roi_info.width=(unsigned int) (x-roi_info.x);
11444  else
11445  {
11446  roi_info.width=(unsigned int) (roi_info.x-x);
11447  roi_info.x=(ssize_t) x;
11448  }
11449  if (roi_info.y < 0)
11450  roi_info.y=0;
11451  else
11452  if (roi_info.y > (ssize_t) windows->image.ximage->height)
11453  roi_info.y=(ssize_t) windows->image.ximage->height;
11454  if ((int) roi_info.y < y)
11455  roi_info.height=(unsigned int) (y-roi_info.y);
11456  else
11457  {
11458  roi_info.height=(unsigned int) (roi_info.y-y);
11459  roi_info.y=(ssize_t) y;
11460  }
11461  }
11462  } while ((state & ExitState) == 0);
11463  /*
11464  Wait for user to grab a corner of the rectangle or press return.
11465  */
11466  state=DefaultState;
11467  display_command=NullCommand;
11468  (void) XMapWindow(display,windows->info.id);
11469  do
11470  {
11471  if (windows->info.mapped != MagickFalse)
11472  {
11473  /*
11474  Display pointer position.
11475  */
11476  (void) FormatLocaleString(text,MaxTextExtent,
11477  " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11478  roi_info.height,(double) roi_info.x,(double) roi_info.y);
11479  XInfoWidget(display,windows,text);
11480  }
11481  highlight_info=roi_info;
11482  highlight_info.x=roi_info.x-windows->image.x;
11483  highlight_info.y=roi_info.y-windows->image.y;
11484  if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11485  {
11486  state|=EscapeState;
11487  state|=ExitState;
11488  break;
11489  }
11490  if ((state & UpdateRegionState) != 0)
11491  {
11492  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11493  switch (display_command)
11494  {
11495  case UndoCommand:
11496  case RedoCommand:
11497  {
11498  (void) XMagickCommand(display,resource_info,windows,
11499  display_command,image);
11500  break;
11501  }
11502  default:
11503  {
11504  /*
11505  Region of interest is relative to image configuration.
11506  */
11507  progress_monitor=SetImageProgressMonitor(*image,
11508  (MagickProgressMonitor) NULL,(*image)->client_data);
11509  crop_info=roi_info;
11510  width=(unsigned int) (*image)->columns;
11511  height=(unsigned int) (*image)->rows;
11512  x=0;
11513  y=0;
11514  if (windows->image.crop_geometry != (char *) NULL)
11515  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11516  &width,&height);
11517  scale_factor=(MagickRealType) width/windows->image.ximage->width;
11518  crop_info.x+=x;
11519  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11520  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11521  scale_factor=(MagickRealType)
11522  height/windows->image.ximage->height;
11523  crop_info.y+=y;
11524  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11525  crop_info.height=(unsigned int)
11526  (scale_factor*crop_info.height+0.5);
11527  roi_image=CropImage(*image,&crop_info,&(*image)->exception);
11528  (void) SetImageProgressMonitor(*image,progress_monitor,
11529  (*image)->client_data);
11530  if (roi_image == (Image *) NULL)
11531  continue;
11532  /*
11533  Apply image processing technique to the region of interest.
11534  */
11535  windows->image.orphan=MagickTrue;
11536  (void) XMagickCommand(display,resource_info,windows,
11537  display_command,&roi_image);
11538  progress_monitor=SetImageProgressMonitor(*image,
11539  (MagickProgressMonitor) NULL,(*image)->client_data);
11540  (void) XMagickCommand(display,resource_info,windows,
11541  SaveToUndoBufferCommand,image);
11542  windows->image.orphan=MagickFalse;
11543  (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11544  crop_info.x,crop_info.y);
11545  roi_image=DestroyImage(roi_image);
11546  (void) SetImageProgressMonitor(*image,progress_monitor,
11547  (*image)->client_data);
11548  break;
11549  }
11550  }
11551  if (display_command != InfoCommand)
11552  {
11553  XConfigureImageColormap(display,resource_info,windows,*image);
11554  (void) XConfigureImage(display,resource_info,windows,*image);
11555  }
11556  XCheckRefreshWindows(display,windows);
11557  XInfoWidget(display,windows,text);
11558  (void) XSetFunction(display,windows->image.highlight_context,
11559  GXinvert);
11560  state&=(~UpdateRegionState);
11561  }
11562  XHighlightRectangle(display,windows->image.id,
11563  windows->image.highlight_context,&highlight_info);
11564  XScreenEvent(display,windows,&event);
11565  if (event.xany.window == windows->command.id)
11566  {
11567  /*
11568  Select a command from the Command widget.
11569  */
11570  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11571  display_command=NullCommand;
11572  id=XCommandWidget(display,windows,ApplyMenu,&event);
11573  if (id >= 0)
11574  {
11575  (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11576  display_command=ApplyCommands[id];
11577  if (id < ApplyMenus)
11578  {
11579  /*
11580  Select a command from a pop-up menu.
11581  */
11582  entry=XMenuWidget(display,windows,ApplyMenu[id],
11583  (const char **) Menus[id],command);
11584  if (entry >= 0)
11585  {
11586  (void) CopyMagickString(command,Menus[id][entry],
11587  MaxTextExtent);
11588  display_command=Commands[id][entry];
11589  }
11590  }
11591  }
11592  (void) XSetFunction(display,windows->image.highlight_context,
11593  GXinvert);
11594  XHighlightRectangle(display,windows->image.id,
11595  windows->image.highlight_context,&highlight_info);
11596  if (display_command == HelpCommand)
11597  {
11598  (void) XSetFunction(display,windows->image.highlight_context,
11599  GXcopy);
11600  XTextViewHelp(display,resource_info,windows,MagickFalse,
11601  "Help Viewer - Region of Interest",ImageROIHelp);
11602  (void) XSetFunction(display,windows->image.highlight_context,
11603  GXinvert);
11604  continue;
11605  }
11606  if (display_command == QuitCommand)
11607  {
11608  /*
11609  exit.
11610  */
11611  state|=EscapeState;
11612  state|=ExitState;
11613  continue;
11614  }
11615  if (display_command != NullCommand)
11616  state|=UpdateRegionState;
11617  continue;
11618  }
11619  XHighlightRectangle(display,windows->image.id,
11620  windows->image.highlight_context,&highlight_info);
11621  switch (event.type)
11622  {
11623  case ButtonPress:
11624  {
11625  x=windows->image.x;
11626  y=windows->image.y;
11627  if (event.xbutton.button != Button1)
11628  break;
11629  if (event.xbutton.window != windows->image.id)
11630  break;
11631  x=windows->image.x+event.xbutton.x;
11632  y=windows->image.y+event.xbutton.y;
11633  if ((x < (int) (roi_info.x+RoiDelta)) &&
11634  (x > (int) (roi_info.x-RoiDelta)) &&
11635  (y < (int) (roi_info.y+RoiDelta)) &&
11636  (y > (int) (roi_info.y-RoiDelta)))
11637  {
11638  roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11639  roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11640  state|=UpdateConfigurationState;
11641  break;
11642  }
11643  if ((x < (int) (roi_info.x+RoiDelta)) &&
11644  (x > (int) (roi_info.x-RoiDelta)) &&
11645  (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11646  (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11647  {
11648  roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11649  state|=UpdateConfigurationState;
11650  break;
11651  }
11652  if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11653  (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11654  (y < (int) (roi_info.y+RoiDelta)) &&
11655  (y > (int) (roi_info.y-RoiDelta)))
11656  {
11657  roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11658  state|=UpdateConfigurationState;
11659  break;
11660  }
11661  if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11662  (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11663  (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11664  (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11665  {
11666  state|=UpdateConfigurationState;
11667  break;
11668  }
11669  magick_fallthrough;
11670  }
11671  case ButtonRelease:
11672  {
11673  if (event.xbutton.window == windows->pan.id)
11674  if ((highlight_info.x != crop_info.x-windows->image.x) ||
11675  (highlight_info.y != crop_info.y-windows->image.y))
11676  XHighlightRectangle(display,windows->image.id,
11677  windows->image.highlight_context,&highlight_info);
11678  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11679  event.xbutton.time);
11680  break;
11681  }
11682  case Expose:
11683  {
11684  if (event.xexpose.window == windows->image.id)
11685  if (event.xexpose.count == 0)
11686  {
11687  event.xexpose.x=(int) highlight_info.x;
11688  event.xexpose.y=(int) highlight_info.y;
11689  event.xexpose.width=(int) highlight_info.width;
11690  event.xexpose.height=(int) highlight_info.height;
11691  XRefreshWindow(display,&windows->image,&event);
11692  }
11693  if (event.xexpose.window == windows->info.id)
11694  if (event.xexpose.count == 0)
11695  XInfoWidget(display,windows,text);
11696  break;
11697  }
11698  case KeyPress:
11699  {
11700  KeySym
11701  key_symbol;
11702 
11703  if (event.xkey.window != windows->image.id)
11704  break;
11705  /*
11706  Respond to a user key press.
11707  */
11708  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11709  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11710  switch ((int) key_symbol)
11711  {
11712  case XK_Shift_L:
11713  case XK_Shift_R:
11714  break;
11715  case XK_Escape:
11716  case XK_F20:
11717  {
11718  state|=EscapeState;
11719  magick_fallthrough;
11720  }
11721  case XK_Return:
11722  {
11723  state|=ExitState;
11724  break;
11725  }
11726  case XK_Home:
11727  case XK_KP_Home:
11728  {
11729  roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11730  roi_info.y=(ssize_t) (windows->image.height/2L-
11731  roi_info.height/2L);
11732  break;
11733  }
11734  case XK_Left:
11735  case XK_KP_Left:
11736  {
11737  roi_info.x--;
11738  break;
11739  }
11740  case XK_Up:
11741  case XK_KP_Up:
11742  case XK_Next:
11743  {
11744  roi_info.y--;
11745  break;
11746  }
11747  case XK_Right:
11748  case XK_KP_Right:
11749  {
11750  roi_info.x++;
11751  break;
11752  }
11753  case XK_Prior:
11754  case XK_Down:
11755  case XK_KP_Down:
11756  {
11757  roi_info.y++;
11758  break;
11759  }
11760  case XK_F1:
11761  case XK_Help:
11762  {
11763  (void) XSetFunction(display,windows->image.highlight_context,
11764  GXcopy);
11765  XTextViewHelp(display,resource_info,windows,MagickFalse,
11766  "Help Viewer - Region of Interest",ImageROIHelp);
11767  (void) XSetFunction(display,windows->image.highlight_context,
11768  GXinvert);
11769  break;
11770  }
11771  default:
11772  {
11773  display_command=XImageWindowCommand(display,resource_info,windows,
11774  event.xkey.state,key_symbol,image);
11775  if (display_command != NullCommand)
11776  state|=UpdateRegionState;
11777  break;
11778  }
11779  }
11780  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11781  event.xkey.time);
11782  break;
11783  }
11784  case KeyRelease:
11785  break;
11786  case MotionNotify:
11787  {
11788  if (event.xbutton.window != windows->image.id)
11789  break;
11790  /*
11791  Map and unmap Info widget as text cursor crosses its boundaries.
11792  */
11793  x=event.xmotion.x;
11794  y=event.xmotion.y;
11795  if (windows->info.mapped != MagickFalse)
11796  {
11797  if ((x < (int) (windows->info.x+windows->info.width)) &&
11798  (y < (int) (windows->info.y+windows->info.height)))
11799  (void) XWithdrawWindow(display,windows->info.id,
11800  windows->info.screen);
11801  }
11802  else
11803  if ((x > (int) (windows->info.x+windows->info.width)) ||
11804  (y > (int) (windows->info.y+windows->info.height)))
11805  (void) XMapWindow(display,windows->info.id);
11806  roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11807  roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11808  break;
11809  }
11810  case SelectionRequest:
11811  {
11812  XSelectionEvent
11813  notify;
11814 
11815  XSelectionRequestEvent
11816  *request;
11817 
11818  /*
11819  Set primary selection.
11820  */
11821  (void) FormatLocaleString(text,MaxTextExtent,
11822  "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11823  roi_info.height,(double) roi_info.x,(double) roi_info.y);
11824  request=(&(event.xselectionrequest));
11825  (void) XChangeProperty(request->display,request->requestor,
11826  request->property,request->target,8,PropModeReplace,
11827  (unsigned char *) text,(int) strlen(text));
11828  notify.type=SelectionNotify;
11829  notify.display=request->display;
11830  notify.requestor=request->requestor;
11831  notify.selection=request->selection;
11832  notify.target=request->target;
11833  notify.time=request->time;
11834  if (request->property == None)
11835  notify.property=request->target;
11836  else
11837  notify.property=request->property;
11838  (void) XSendEvent(request->display,request->requestor,False,0,
11839  (XEvent *) &notify);
11840  }
11841  default:
11842  break;
11843  }
11844  if ((state & UpdateConfigurationState) != 0)
11845  {
11846  (void) XPutBackEvent(display,&event);
11847  (void) XCheckDefineCursor(display,windows->image.id,cursor);
11848  break;
11849  }
11850  } while ((state & ExitState) == 0);
11851  } while ((state & ExitState) == 0);
11852  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11853  XSetCursorState(display,windows,MagickFalse);
11854  if ((state & EscapeState) != 0)
11855  return(MagickTrue);
11856  return(MagickTrue);
11857 }
11858 ␌
11859 /*
11860 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11861 % %
11862 % %
11863 % %
11864 + X R o t a t e I m a g e %
11865 % %
11866 % %
11867 % %
11868 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11869 %
11870 % XRotateImage() rotates the X image. If the degrees parameter if zero, the
11871 % rotation angle is computed from the slope of a line drawn by the user.
11872 %
11873 % The format of the XRotateImage method is:
11874 %
11875 % MagickBooleanType XRotateImage(Display *display,
11876 % XResourceInfo *resource_info,XWindows *windows,double degrees,
11877 % Image **image)
11878 %
11879 % A description of each parameter follows:
11880 %
11881 % o display: Specifies a connection to an X server; returned from
11882 % XOpenDisplay.
11883 %
11884 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11885 %
11886 % o windows: Specifies a pointer to a XWindows structure.
11887 %
11888 % o degrees: Specifies the number of degrees to rotate the image.
11889 %
11890 % o image: the image.
11891 %
11892 */
11893 static MagickBooleanType XRotateImage(Display *display,
11894  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image)
11895 {
11896  const char
11897  *const RotateMenu[] =
11898  {
11899  "Pixel Color",
11900  "Direction",
11901  "Help",
11902  "Dismiss",
11903  (char *) NULL
11904  };
11905 
11906  static ModeType
11907  direction = HorizontalRotateCommand;
11908 
11909  static const ModeType
11910  DirectionCommands[] =
11911  {
11912  HorizontalRotateCommand,
11913  VerticalRotateCommand
11914  },
11915  RotateCommands[] =
11916  {
11917  RotateColorCommand,
11918  RotateDirectionCommand,
11919  RotateHelpCommand,
11920  RotateDismissCommand
11921  };
11922 
11923  static unsigned int
11924  pen_id = 0;
11925 
11926  char
11927  command[MaxTextExtent],
11928  text[MaxTextExtent];
11929 
11930  Image
11931  *rotate_image;
11932 
11933  int
11934  id,
11935  x,
11936  y;
11937 
11938  MagickRealType
11939  normalized_degrees;
11940 
11941  int
11942  i;
11943 
11944  unsigned int
11945  height,
11946  rotations,
11947  width;
11948 
11949  if (degrees == 0.0)
11950  {
11951  unsigned int
11952  distance;
11953 
11954  size_t
11955  state;
11956 
11957  XEvent
11958  event;
11959 
11960  XSegment
11961  rotate_info;
11962 
11963  /*
11964  Map Command widget.
11965  */
11966  (void) CloneString(&windows->command.name,"Rotate");
11967  windows->command.data=2;
11968  (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
11969  (void) XMapRaised(display,windows->command.id);
11970  XClientMessage(display,windows->image.id,windows->im_protocols,
11971  windows->im_update_widget,CurrentTime);
11972  /*
11973  Wait for first button press.
11974  */
11975  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11976  XQueryPosition(display,windows->image.id,&x,&y);
11977  rotate_info.x1=x;
11978  rotate_info.y1=y;
11979  rotate_info.x2=x;
11980  rotate_info.y2=y;
11981  state=DefaultState;
11982  do
11983  {
11984  XHighlightLine(display,windows->image.id,
11985  windows->image.highlight_context,&rotate_info);
11986  /*
11987  Wait for next event.
11988  */
11989  XScreenEvent(display,windows,&event);
11990  XHighlightLine(display,windows->image.id,
11991  windows->image.highlight_context,&rotate_info);
11992  if (event.xany.window == windows->command.id)
11993  {
11994  /*
11995  Select a command from the Command widget.
11996  */
11997  id=XCommandWidget(display,windows,RotateMenu,&event);
11998  if (id < 0)
11999  continue;
12000  (void) XSetFunction(display,windows->image.highlight_context,
12001  GXcopy);
12002  switch (RotateCommands[id])
12003  {
12004  case RotateColorCommand:
12005  {
12006  const char
12007  *ColorMenu[MaxNumberPens];
12008 
12009  int
12010  pen_number;
12011 
12012  XColor
12013  color;
12014 
12015  /*
12016  Initialize menu selections.
12017  */
12018  for (i=0; i < (int) (MaxNumberPens-2); i++)
12019  ColorMenu[i]=resource_info->pen_colors[i];
12020  ColorMenu[MaxNumberPens-2]="Browser...";
12021  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12022  /*
12023  Select a pen color from the pop-up menu.
12024  */
12025  pen_number=XMenuWidget(display,windows,RotateMenu[id],
12026  (const char **) ColorMenu,command);
12027  if (pen_number < 0)
12028  break;
12029  if (pen_number == (MaxNumberPens-2))
12030  {
12031  static char
12032  color_name[MaxTextExtent] = "gray";
12033 
12034  /*
12035  Select a pen color from a dialog.
12036  */
12037  resource_info->pen_colors[pen_number]=color_name;
12038  XColorBrowserWidget(display,windows,"Select",color_name);
12039  if (*color_name == '\0')
12040  break;
12041  }
12042  /*
12043  Set pen color.
12044  */
12045  (void) XParseColor(display,windows->map_info->colormap,
12046  resource_info->pen_colors[pen_number],&color);
12047  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12048  (unsigned int) MaxColors,&color);
12049  windows->pixel_info->pen_colors[pen_number]=color;
12050  pen_id=(unsigned int) pen_number;
12051  break;
12052  }
12053  case RotateDirectionCommand:
12054  {
12055  const char
12056  *const Directions[] =
12057  {
12058  "horizontal",
12059  "vertical",
12060  (char *) NULL,
12061  };
12062 
12063  /*
12064  Select a command from the pop-up menu.
12065  */
12066  id=XMenuWidget(display,windows,RotateMenu[id],
12067  Directions,command);
12068  if (id >= 0)
12069  direction=DirectionCommands[id];
12070  break;
12071  }
12072  case RotateHelpCommand:
12073  {
12074  XTextViewHelp(display,resource_info,windows,MagickFalse,
12075  "Help Viewer - Image Rotation",ImageRotateHelp);
12076  break;
12077  }
12078  case RotateDismissCommand:
12079  {
12080  /*
12081  Prematurely exit.
12082  */
12083  state|=EscapeState;
12084  state|=ExitState;
12085  break;
12086  }
12087  default:
12088  break;
12089  }
12090  (void) XSetFunction(display,windows->image.highlight_context,
12091  GXinvert);
12092  continue;
12093  }
12094  switch (event.type)
12095  {
12096  case ButtonPress:
12097  {
12098  if (event.xbutton.button != Button1)
12099  break;
12100  if (event.xbutton.window != windows->image.id)
12101  break;
12102  /*
12103  exit loop.
12104  */
12105  (void) XSetFunction(display,windows->image.highlight_context,
12106  GXcopy);
12107  rotate_info.x1=event.xbutton.x;
12108  rotate_info.y1=event.xbutton.y;
12109  state|=ExitState;
12110  break;
12111  }
12112  case ButtonRelease:
12113  break;
12114  case Expose:
12115  break;
12116  case KeyPress:
12117  {
12118  char
12119  command[MaxTextExtent];
12120 
12121  KeySym
12122  key_symbol;
12123 
12124  if (event.xkey.window != windows->image.id)
12125  break;
12126  /*
12127  Respond to a user key press.
12128  */
12129  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12130  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12131  switch ((int) key_symbol)
12132  {
12133  case XK_Escape:
12134  case XK_F20:
12135  {
12136  /*
12137  Prematurely exit.
12138  */
12139  state|=EscapeState;
12140  state|=ExitState;
12141  break;
12142  }
12143  case XK_F1:
12144  case XK_Help:
12145  {
12146  (void) XSetFunction(display,windows->image.highlight_context,
12147  GXcopy);
12148  XTextViewHelp(display,resource_info,windows,MagickFalse,
12149  "Help Viewer - Image Rotation",ImageRotateHelp);
12150  (void) XSetFunction(display,windows->image.highlight_context,
12151  GXinvert);
12152  break;
12153  }
12154  default:
12155  {
12156  (void) XBell(display,0);
12157  break;
12158  }
12159  }
12160  break;
12161  }
12162  case MotionNotify:
12163  {
12164  rotate_info.x1=event.xmotion.x;
12165  rotate_info.y1=event.xmotion.y;
12166  }
12167  }
12168  rotate_info.x2=rotate_info.x1;
12169  rotate_info.y2=rotate_info.y1;
12170  if (direction == HorizontalRotateCommand)
12171  rotate_info.x2+=32;
12172  else
12173  rotate_info.y2-=32;
12174  } while ((state & ExitState) == 0);
12175  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12176  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12177  if ((state & EscapeState) != 0)
12178  return(MagickTrue);
12179  /*
12180  Draw line as pointer moves until the mouse button is released.
12181  */
12182  distance=0;
12183  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12184  state=DefaultState;
12185  do
12186  {
12187  if (distance > 9)
12188  {
12189  /*
12190  Display info and draw rotation line.
12191  */
12192  if (windows->info.mapped == MagickFalse)
12193  (void) XMapWindow(display,windows->info.id);
12194  (void) FormatLocaleString(text,MaxTextExtent," %g",
12195  direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12196  XInfoWidget(display,windows,text);
12197  XHighlightLine(display,windows->image.id,
12198  windows->image.highlight_context,&rotate_info);
12199  }
12200  else
12201  if (windows->info.mapped != MagickFalse)
12202  (void) XWithdrawWindow(display,windows->info.id,
12203  windows->info.screen);
12204  /*
12205  Wait for next event.
12206  */
12207  XScreenEvent(display,windows,&event);
12208  if (distance > 9)
12209  XHighlightLine(display,windows->image.id,
12210  windows->image.highlight_context,&rotate_info);
12211  switch (event.type)
12212  {
12213  case ButtonPress:
12214  break;
12215  case ButtonRelease:
12216  {
12217  /*
12218  User has committed to rotation line.
12219  */
12220  rotate_info.x2=event.xbutton.x;
12221  rotate_info.y2=event.xbutton.y;
12222  state|=ExitState;
12223  break;
12224  }
12225  case Expose:
12226  break;
12227  case MotionNotify:
12228  {
12229  rotate_info.x2=event.xmotion.x;
12230  rotate_info.y2=event.xmotion.y;
12231  }
12232  default:
12233  break;
12234  }
12235  /*
12236  Check boundary conditions.
12237  */
12238  if (rotate_info.x2 < 0)
12239  rotate_info.x2=0;
12240  else
12241  if (rotate_info.x2 > (int) windows->image.width)
12242  rotate_info.x2=(short) windows->image.width;
12243  if (rotate_info.y2 < 0)
12244  rotate_info.y2=0;
12245  else
12246  if (rotate_info.y2 > (int) windows->image.height)
12247  rotate_info.y2=(short) windows->image.height;
12248  /*
12249  Compute rotation angle from the slope of the line.
12250  */
12251  degrees=0.0;
12252  distance=(unsigned int)
12253  ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12254  ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12255  if (distance > 9)
12256  degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12257  rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12258  } while ((state & ExitState) == 0);
12259  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12260  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12261  if (distance <= 9)
12262  return(MagickTrue);
12263  }
12264  if (direction == VerticalRotateCommand)
12265  degrees-=90.0;
12266  if (degrees == 0.0)
12267  return(MagickTrue);
12268  /*
12269  Rotate image.
12270  */
12271  normalized_degrees=degrees;
12272  while (normalized_degrees < -45.0)
12273  normalized_degrees+=360.0;
12274  for (rotations=0; normalized_degrees > 45.0; rotations++)
12275  normalized_degrees-=90.0;
12276  if (normalized_degrees != 0.0)
12277  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
12278  XSetCursorState(display,windows,MagickTrue);
12279  XCheckRefreshWindows(display,windows);
12280  (*image)->background_color.red=ScaleShortToQuantum(
12281  windows->pixel_info->pen_colors[pen_id].red);
12282  (*image)->background_color.green=ScaleShortToQuantum(
12283  windows->pixel_info->pen_colors[pen_id].green);
12284  (*image)->background_color.blue=ScaleShortToQuantum(
12285  windows->pixel_info->pen_colors[pen_id].blue);
12286  rotate_image=RotateImage(*image,degrees,&(*image)->exception);
12287  XSetCursorState(display,windows,MagickFalse);
12288  if (rotate_image == (Image *) NULL)
12289  return(MagickFalse);
12290  *image=DestroyImage(*image);
12291  *image=rotate_image;
12292  if (windows->image.crop_geometry != (char *) NULL)
12293  {
12294  /*
12295  Rotate crop geometry.
12296  */
12297  width=(unsigned int) (*image)->columns;
12298  height=(unsigned int) (*image)->rows;
12299  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12300  switch (rotations % 4)
12301  {
12302  default:
12303  case 0:
12304  break;
12305  case 1:
12306  {
12307  /*
12308  Rotate 90 degrees.
12309  */
12310  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12311  "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12312  (int) height-y,x);
12313  break;
12314  }
12315  case 2:
12316  {
12317  /*
12318  Rotate 180 degrees.
12319  */
12320  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12321  "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12322  break;
12323  }
12324  case 3:
12325  {
12326  /*
12327  Rotate 270 degrees.
12328  */
12329  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12330  "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12331  break;
12332  }
12333  }
12334  }
12335  if (windows->image.orphan != MagickFalse)
12336  return(MagickTrue);
12337  if (normalized_degrees != 0.0)
12338  {
12339  /*
12340  Update image colormap.
12341  */
12342  windows->image.window_changes.width=(int) (*image)->columns;
12343  windows->image.window_changes.height=(int) (*image)->rows;
12344  if (windows->image.crop_geometry != (char *) NULL)
12345  {
12346  /*
12347  Obtain dimensions of image from crop geometry.
12348  */
12349  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12350  &width,&height);
12351  windows->image.window_changes.width=(int) width;
12352  windows->image.window_changes.height=(int) height;
12353  }
12354  XConfigureImageColormap(display,resource_info,windows,*image);
12355  }
12356  else
12357  if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12358  {
12359  windows->image.window_changes.width=windows->image.ximage->height;
12360  windows->image.window_changes.height=windows->image.ximage->width;
12361  }
12362  /*
12363  Update image configuration.
12364  */
12365  (void) XConfigureImage(display,resource_info,windows,*image);
12366  return(MagickTrue);
12367 }
12368 ␌
12369 /*
12370 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12371 % %
12372 % %
12373 % %
12374 + X S a v e I m a g e %
12375 % %
12376 % %
12377 % %
12378 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12379 %
12380 % XSaveImage() saves an image to a file.
12381 %
12382 % The format of the XSaveImage method is:
12383 %
12384 % MagickBooleanType XSaveImage(Display *display,
12385 % XResourceInfo *resource_info,XWindows *windows,Image *image)
12386 %
12387 % A description of each parameter follows:
12388 %
12389 % o display: Specifies a connection to an X server; returned from
12390 % XOpenDisplay.
12391 %
12392 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12393 %
12394 % o windows: Specifies a pointer to a XWindows structure.
12395 %
12396 % o image: the image.
12397 %
12398 */
12399 static MagickBooleanType XSaveImage(Display *display,
12400  XResourceInfo *resource_info,XWindows *windows,Image *image)
12401 {
12402  char
12403  filename[MaxTextExtent],
12404  geometry[MaxTextExtent];
12405 
12406  Image
12407  *save_image;
12408 
12409  ImageInfo
12410  *image_info;
12411 
12412  MagickStatusType
12413  status;
12414 
12415  /*
12416  Request file name from user.
12417  */
12418  if (resource_info->write_filename != (char *) NULL)
12419  (void) CopyMagickString(filename,resource_info->write_filename,
12420  MaxTextExtent);
12421  else
12422  {
12423  char
12424  path[MaxTextExtent];
12425 
12426  int
12427  status;
12428 
12429  GetPathComponent(image->filename,HeadPath,path);
12430  GetPathComponent(image->filename,TailPath,filename);
12431  if (*path != '\0')
12432  {
12433  status=chdir(path);
12434  if (status == -1)
12435  (void) ThrowMagickException(&image->exception,GetMagickModule(),
12436  FileOpenError,"UnableToOpenFile","%s",path);
12437  }
12438  }
12439  XFileBrowserWidget(display,windows,"Save",filename);
12440  if (*filename == '\0')
12441  return(MagickTrue);
12442  if (IsPathAccessible(filename) != MagickFalse)
12443  {
12444  int
12445  status;
12446 
12447  /*
12448  File exists-- seek user's permission before overwriting.
12449  */
12450  status=XConfirmWidget(display,windows,"Overwrite",filename);
12451  if (status <= 0)
12452  return(MagickTrue);
12453  }
12454  image_info=CloneImageInfo(resource_info->image_info);
12455  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12456  (void) SetImageInfo(image_info,1,&image->exception);
12457  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12458  (LocaleCompare(image_info->magick,"JPG") == 0))
12459  {
12460  char
12461  quality[MaxTextExtent];
12462 
12463  int
12464  status;
12465 
12466  /*
12467  Request JPEG quality from user.
12468  */
12469  (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12470  image->quality);
12471  status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12472  quality);
12473  if (*quality == '\0')
12474  return(MagickTrue);
12475  image->quality=StringToUnsignedLong(quality);
12476  image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12477  }
12478  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12479  (LocaleCompare(image_info->magick,"PDF") == 0) ||
12480  (LocaleCompare(image_info->magick,"PS") == 0) ||
12481  (LocaleCompare(image_info->magick,"PS2") == 0))
12482  {
12483  char
12484  geometry[MaxTextExtent];
12485 
12486  const char
12487  *const PageSizes[] =
12488  {
12489  "Letter",
12490  "Tabloid",
12491  "Ledger",
12492  "Legal",
12493  "Statement",
12494  "Executive",
12495  "A3",
12496  "A4",
12497  "A5",
12498  "B4",
12499  "B5",
12500  "Folio",
12501  "Quarto",
12502  "10x14",
12503  (char *) NULL
12504  };
12505 
12506  /*
12507  Request page geometry from user.
12508  */
12509  (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12510  if (LocaleCompare(image_info->magick,"PDF") == 0)
12511  (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12512  if (image_info->page != (char *) NULL)
12513  (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12514  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12515  "Select page geometry:",geometry);
12516  if (*geometry != '\0')
12517  image_info->page=GetPageGeometry(geometry);
12518  }
12519  /*
12520  Apply image transforms.
12521  */
12522  XSetCursorState(display,windows,MagickTrue);
12523  XCheckRefreshWindows(display,windows);
12524  save_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12525  if (save_image == (Image *) NULL)
12526  return(MagickFalse);
12527  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12528  windows->image.ximage->width,windows->image.ximage->height);
12529  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12530  /*
12531  Write image.
12532  */
12533  (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12534  status=WriteImage(image_info,save_image);
12535  if (status != MagickFalse)
12536  image->taint=MagickFalse;
12537  save_image=DestroyImage(save_image);
12538  image_info=DestroyImageInfo(image_info);
12539  XSetCursorState(display,windows,MagickFalse);
12540  return(status != 0 ? MagickTrue : MagickFalse);
12541 }
12542 ␌
12543 /*
12544 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12545 % %
12546 % %
12547 % %
12548 + X S c r e e n E v e n t %
12549 % %
12550 % %
12551 % %
12552 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12553 %
12554 % XScreenEvent() handles global events associated with the Pan and Magnify
12555 % windows.
12556 %
12557 % The format of the XScreenEvent function is:
12558 %
12559 % void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12560 %
12561 % A description of each parameter follows:
12562 %
12563 % o display: Specifies a pointer to the Display structure; returned from
12564 % XOpenDisplay.
12565 %
12566 % o windows: Specifies a pointer to a XWindows structure.
12567 %
12568 % o event: Specifies a pointer to a X11 XEvent structure.
12569 %
12570 %
12571 */
12572 
12573 #if defined(__cplusplus) || defined(c_plusplus)
12574 extern "C" {
12575 #endif
12576 
12577 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12578 {
12579  XWindows
12580  *windows;
12581 
12582  magick_unreferenced(display);
12583 
12584  windows=(XWindows *) data;
12585  if ((event->type == ClientMessage) &&
12586  (event->xclient.window == windows->image.id))
12587  return(MagickFalse);
12588  return(MagickTrue);
12589 }
12590 
12591 #if defined(__cplusplus) || defined(c_plusplus)
12592 }
12593 #endif
12594 
12595 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12596 {
12597  int
12598  x,
12599  y;
12600 
12601  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12602  if (event->xany.window == windows->command.id)
12603  return;
12604  switch (event->type)
12605  {
12606  case ButtonPress:
12607  case ButtonRelease:
12608  {
12609  if ((event->xbutton.button == Button3) &&
12610  (event->xbutton.state & Mod1Mask))
12611  {
12612  /*
12613  Convert Alt-Button3 to Button2.
12614  */
12615  event->xbutton.button=Button2;
12616  event->xbutton.state&=(~Mod1Mask);
12617  }
12618  if (event->xbutton.window == windows->backdrop.id)
12619  {
12620  (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12621  event->xbutton.time);
12622  break;
12623  }
12624  if (event->xbutton.window == windows->pan.id)
12625  {
12626  XPanImage(display,windows,event);
12627  break;
12628  }
12629  if (event->xbutton.window == windows->image.id)
12630  if (event->xbutton.button == Button2)
12631  {
12632  /*
12633  Update magnified image.
12634  */
12635  x=event->xbutton.x;
12636  y=event->xbutton.y;
12637  if (x < 0)
12638  x=0;
12639  else
12640  if (x >= (int) windows->image.width)
12641  x=(int) (windows->image.width-1);
12642  windows->magnify.x=(int) windows->image.x+x;
12643  if (y < 0)
12644  y=0;
12645  else
12646  if (y >= (int) windows->image.height)
12647  y=(int) (windows->image.height-1);
12648  windows->magnify.y=windows->image.y+y;
12649  if (windows->magnify.mapped == MagickFalse)
12650  (void) XMapRaised(display,windows->magnify.id);
12651  XMakeMagnifyImage(display,windows);
12652  if (event->type == ButtonRelease)
12653  (void) XWithdrawWindow(display,windows->info.id,
12654  windows->info.screen);
12655  break;
12656  }
12657  break;
12658  }
12659  case ClientMessage:
12660  {
12661  /*
12662  If client window delete message, exit.
12663  */
12664  if (event->xclient.message_type != windows->wm_protocols)
12665  break;
12666  if (*event->xclient.data.l != (long) windows->wm_delete_window)
12667  break;
12668  if (event->xclient.window == windows->magnify.id)
12669  {
12670  (void) XWithdrawWindow(display,windows->magnify.id,
12671  windows->magnify.screen);
12672  break;
12673  }
12674  break;
12675  }
12676  case ConfigureNotify:
12677  {
12678  if (event->xconfigure.window == windows->magnify.id)
12679  {
12680  unsigned int
12681  magnify;
12682 
12683  /*
12684  Magnify window has a new configuration.
12685  */
12686  windows->magnify.width=(unsigned int) event->xconfigure.width;
12687  windows->magnify.height=(unsigned int) event->xconfigure.height;
12688  if (windows->magnify.mapped == MagickFalse)
12689  break;
12690  magnify=1;
12691  while ((int) magnify <= event->xconfigure.width)
12692  magnify<<=1;
12693  while ((int) magnify <= event->xconfigure.height)
12694  magnify<<=1;
12695  magnify>>=1;
12696  if (((int) magnify != event->xconfigure.width) ||
12697  ((int) magnify != event->xconfigure.height))
12698  {
12699  XWindowChanges
12700  window_changes;
12701 
12702  window_changes.width=(int) magnify;
12703  window_changes.height=(int) magnify;
12704  (void) XReconfigureWMWindow(display,windows->magnify.id,
12705  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12706  &window_changes);
12707  break;
12708  }
12709  XMakeMagnifyImage(display,windows);
12710  break;
12711  }
12712  break;
12713  }
12714  case Expose:
12715  {
12716  if (event->xexpose.window == windows->image.id)
12717  {
12718  XRefreshWindow(display,&windows->image,event);
12719  break;
12720  }
12721  if (event->xexpose.window == windows->pan.id)
12722  if (event->xexpose.count == 0)
12723  {
12724  XDrawPanRectangle(display,windows);
12725  break;
12726  }
12727  if (event->xexpose.window == windows->magnify.id)
12728  if (event->xexpose.count == 0)
12729  {
12730  XMakeMagnifyImage(display,windows);
12731  break;
12732  }
12733  break;
12734  }
12735  case KeyPress:
12736  {
12737  char
12738  command[MaxTextExtent];
12739 
12740  KeySym
12741  key_symbol;
12742 
12743  if (event->xkey.window != windows->magnify.id)
12744  break;
12745  /*
12746  Respond to a user key press.
12747  */
12748  (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12749  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12750  XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12751  break;
12752  }
12753  case MapNotify:
12754  {
12755  if (event->xmap.window == windows->magnify.id)
12756  {
12757  windows->magnify.mapped=MagickTrue;
12758  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12759  break;
12760  }
12761  if (event->xmap.window == windows->info.id)
12762  {
12763  windows->info.mapped=MagickTrue;
12764  break;
12765  }
12766  break;
12767  }
12768  case MotionNotify:
12769  {
12770  while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12771  if (event->xmotion.window == windows->image.id)
12772  if (windows->magnify.mapped != MagickFalse)
12773  {
12774  /*
12775  Update magnified image.
12776  */
12777  x=event->xmotion.x;
12778  y=event->xmotion.y;
12779  if (x < 0)
12780  x=0;
12781  else
12782  if (x >= (int) windows->image.width)
12783  x=(int) (windows->image.width-1);
12784  windows->magnify.x=(int) windows->image.x+x;
12785  if (y < 0)
12786  y=0;
12787  else
12788  if (y >= (int) windows->image.height)
12789  y=(int) (windows->image.height-1);
12790  windows->magnify.y=windows->image.y+y;
12791  XMakeMagnifyImage(display,windows);
12792  }
12793  break;
12794  }
12795  case UnmapNotify:
12796  {
12797  if (event->xunmap.window == windows->magnify.id)
12798  {
12799  windows->magnify.mapped=MagickFalse;
12800  break;
12801  }
12802  if (event->xunmap.window == windows->info.id)
12803  {
12804  windows->info.mapped=MagickFalse;
12805  break;
12806  }
12807  break;
12808  }
12809  default:
12810  break;
12811  }
12812 }
12813 ␌
12814 /*
12815 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12816 % %
12817 % %
12818 % %
12819 + X S e t C r o p G e o m e t r y %
12820 % %
12821 % %
12822 % %
12823 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12824 %
12825 % XSetCropGeometry() accepts a cropping geometry relative to the Image window
12826 % and translates it to a cropping geometry relative to the image.
12827 %
12828 % The format of the XSetCropGeometry method is:
12829 %
12830 % void XSetCropGeometry(Display *display,XWindows *windows,
12831 % RectangleInfo *crop_info,Image *image)
12832 %
12833 % A description of each parameter follows:
12834 %
12835 % o display: Specifies a connection to an X server; returned from
12836 % XOpenDisplay.
12837 %
12838 % o windows: Specifies a pointer to a XWindows structure.
12839 %
12840 % o crop_info: A pointer to a RectangleInfo that defines a region of the
12841 % Image window to crop.
12842 %
12843 % o image: the image.
12844 %
12845 */
12846 static void XSetCropGeometry(Display *display,XWindows *windows,
12847  RectangleInfo *crop_info,Image *image)
12848 {
12849  char
12850  text[MaxTextExtent];
12851 
12852  int
12853  x,
12854  y;
12855 
12856  MagickRealType
12857  scale_factor;
12858 
12859  unsigned int
12860  height,
12861  width;
12862 
12863  if (windows->info.mapped != MagickFalse)
12864  {
12865  /*
12866  Display info on cropping rectangle.
12867  */
12868  (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12869  (double) crop_info->width,(double) crop_info->height,(double)
12870  crop_info->x,(double) crop_info->y);
12871  XInfoWidget(display,windows,text);
12872  }
12873  /*
12874  Cropping geometry is relative to any previous crop geometry.
12875  */
12876  x=0;
12877  y=0;
12878  width=(unsigned int) image->columns;
12879  height=(unsigned int) image->rows;
12880  if (windows->image.crop_geometry != (char *) NULL)
12881  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12882  else
12883  windows->image.crop_geometry=AcquireString((char *) NULL);
12884  /*
12885  Define the crop geometry string from the cropping rectangle.
12886  */
12887  scale_factor=(MagickRealType) width/windows->image.ximage->width;
12888  if (crop_info->x > 0)
12889  x+=(int) (scale_factor*crop_info->x+0.5);
12890  width=(unsigned int) (scale_factor*crop_info->width+0.5);
12891  if (width == 0)
12892  width=1;
12893  scale_factor=(MagickRealType) height/windows->image.ximage->height;
12894  if (crop_info->y > 0)
12895  y+=(int) (scale_factor*crop_info->y+0.5);
12896  height=(unsigned int) (scale_factor*crop_info->height+0.5);
12897  if (height == 0)
12898  height=1;
12899  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12900  "%ux%u%+d%+d",width,height,x,y);
12901 }
12902 ␌
12903 /*
12904 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12905 % %
12906 % %
12907 % %
12908 + X T i l e I m a g e %
12909 % %
12910 % %
12911 % %
12912 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12913 %
12914 % XTileImage() loads or deletes a selected tile from a visual image directory.
12915 % The load or delete command is chosen from a menu.
12916 %
12917 % The format of the XTileImage method is:
12918 %
12919 % Image *XTileImage(Display *display,XResourceInfo *resource_info,
12920 % XWindows *windows,Image *image,XEvent *event)
12921 %
12922 % A description of each parameter follows:
12923 %
12924 % o tile_image: XTileImage reads or deletes the tile image
12925 % and returns it. A null image is returned if an error occurs.
12926 %
12927 % o display: Specifies a connection to an X server; returned from
12928 % XOpenDisplay.
12929 %
12930 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12931 %
12932 % o windows: Specifies a pointer to a XWindows structure.
12933 %
12934 % o image: the image; returned from ReadImage.
12935 %
12936 % o event: Specifies a pointer to a XEvent structure. If it is NULL,
12937 % the entire image is refreshed.
12938 %
12939 */
12940 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
12941  XWindows *windows,Image *image,XEvent *event)
12942 {
12943  const char
12944  *const VerbMenu[] =
12945  {
12946  "Load",
12947  "Next",
12948  "Former",
12949  "Delete",
12950  "Update",
12951  (char *) NULL,
12952  };
12953 
12954  static const ModeType
12955  TileCommands[] =
12956  {
12957  TileLoadCommand,
12958  TileNextCommand,
12959  TileFormerCommand,
12960  TileDeleteCommand,
12961  TileUpdateCommand
12962  };
12963 
12964  char
12965  command[MaxTextExtent],
12966  filename[MaxTextExtent];
12967 
12968  Image
12969  *tile_image;
12970 
12971  int
12972  id,
12973  status,
12974  tile,
12975  x,
12976  y;
12977 
12978  MagickRealType
12979  scale_factor;
12980 
12981  char
12982  *p,
12983  *q;
12984 
12985  int
12986  i;
12987 
12988  unsigned int
12989  height,
12990  width;
12991 
12992  /*
12993  Tile image is relative to montage image configuration.
12994  */
12995  x=0;
12996  y=0;
12997  width=(unsigned int) image->columns;
12998  height=(unsigned int) image->rows;
12999  if (windows->image.crop_geometry != (char *) NULL)
13000  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13001  scale_factor=(MagickRealType) width/windows->image.ximage->width;
13002  event->xbutton.x+=windows->image.x;
13003  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13004  scale_factor=(MagickRealType) height/windows->image.ximage->height;
13005  event->xbutton.y+=windows->image.y;
13006  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13007  /*
13008  Determine size and location of each tile in the visual image directory.
13009  */
13010  width=(unsigned int) image->columns;
13011  height=(unsigned int) image->rows;
13012  x=0;
13013  y=0;
13014  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13015  tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13016  (event->xbutton.x-x)/width;
13017  if (tile < 0)
13018  {
13019  /*
13020  Button press is outside any tile.
13021  */
13022  (void) XBell(display,0);
13023  return((Image *) NULL);
13024  }
13025  /*
13026  Determine file name from the tile directory.
13027  */
13028  p=image->directory;
13029  for (i=tile; (i != 0) && (*p != '\0'); )
13030  {
13031  if (*p == '\xff')
13032  i--;
13033  p++;
13034  }
13035  if (*p == '\0')
13036  {
13037  /*
13038  Button press is outside any tile.
13039  */
13040  (void) XBell(display,0);
13041  return((Image *) NULL);
13042  }
13043  /*
13044  Select a command from the pop-up menu.
13045  */
13046  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13047  if (id < 0)
13048  return((Image *) NULL);
13049  q=p;
13050  while ((*q != '\xff') && (*q != '\0'))
13051  q++;
13052  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13053  /*
13054  Perform command for the selected tile.
13055  */
13056  XSetCursorState(display,windows,MagickTrue);
13057  XCheckRefreshWindows(display,windows);
13058  tile_image=NewImageList();
13059  switch (TileCommands[id])
13060  {
13061  case TileLoadCommand:
13062  {
13063  /*
13064  Load tile image.
13065  */
13066  XCheckRefreshWindows(display,windows);
13067  (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13068  MaxTextExtent);
13069  (void) CopyMagickString(resource_info->image_info->filename,filename,
13070  MaxTextExtent);
13071  tile_image=ReadImage(resource_info->image_info,&image->exception);
13072  CatchException(&image->exception);
13073  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13074  break;
13075  }
13076  case TileNextCommand:
13077  {
13078  /*
13079  Display next image.
13080  */
13081  XClientMessage(display,windows->image.id,windows->im_protocols,
13082  windows->im_next_image,CurrentTime);
13083  break;
13084  }
13085  case TileFormerCommand:
13086  {
13087  /*
13088  Display former image.
13089  */
13090  XClientMessage(display,windows->image.id,windows->im_protocols,
13091  windows->im_former_image,CurrentTime);
13092  break;
13093  }
13094  case TileDeleteCommand:
13095  {
13096  /*
13097  Delete tile image.
13098  */
13099  if (IsPathAccessible(filename) == MagickFalse)
13100  {
13101  XNoticeWidget(display,windows,"Image file does not exist:",filename);
13102  break;
13103  }
13104  status=XConfirmWidget(display,windows,"Really delete tile",filename);
13105  if (status <= 0)
13106  break;
13107  status=ShredFile(filename);
13108  status|=remove_utf8(filename);
13109  if (status != MagickFalse)
13110  {
13111  XNoticeWidget(display,windows,"Unable to delete image file:",
13112  filename);
13113  break;
13114  }
13115  magick_fallthrough;
13116  }
13117  case TileUpdateCommand:
13118  {
13120  *exception;
13121 
13122  int
13123  x_offset,
13124  y_offset;
13125 
13126  PixelPacket
13127  pixel;
13128 
13129  int
13130  j;
13131 
13132  PixelPacket
13133  *s;
13134 
13135  /*
13136  Ensure all the images exist.
13137  */
13138  tile=0;
13139  for (p=image->directory; *p != '\0'; p++)
13140  {
13141  CacheView
13142  *image_view;
13143 
13144  q=p;
13145  while ((*q != '\xff') && (*q != '\0'))
13146  q++;
13147  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13148  p=q;
13149  if (IsPathAccessible(filename) != MagickFalse)
13150  {
13151  tile++;
13152  continue;
13153  }
13154  /*
13155  Overwrite tile with background color.
13156  */
13157  x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13158  y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13159  exception=(&image->exception);
13160  image_view=AcquireAuthenticCacheView(image,exception);
13161  (void) GetOneCacheViewVirtualPixel(image_view,0,0,&pixel,exception);
13162  for (i=0; i < (int) height; i++)
13163  {
13164  s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13165  y_offset+i,width,1,exception);
13166  if (s == (PixelPacket *) NULL)
13167  break;
13168  for (j=0; j < (int) width; j++)
13169  *s++=pixel;
13170  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13171  break;
13172  }
13173  image_view=DestroyCacheView(image_view);
13174  tile++;
13175  }
13176  windows->image.window_changes.width=(int) image->columns;
13177  windows->image.window_changes.height=(int) image->rows;
13178  XConfigureImageColormap(display,resource_info,windows,image);
13179  (void) XConfigureImage(display,resource_info,windows,image);
13180  break;
13181  }
13182  default:
13183  break;
13184  }
13185  XSetCursorState(display,windows,MagickFalse);
13186  return(tile_image);
13187 }
13188 ␌
13189 /*
13190 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13191 % %
13192 % %
13193 % %
13194 + X T r a n s l a t e I m a g e %
13195 % %
13196 % %
13197 % %
13198 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13199 %
13200 % XTranslateImage() translates the image within an Image window by one pixel
13201 % as specified by the key symbol. If the image has a `montage string the
13202 % translation is respect to the width and height contained within the string.
13203 %
13204 % The format of the XTranslateImage method is:
13205 %
13206 % void XTranslateImage(Display *display,XWindows *windows,
13207 % Image *image,const KeySym key_symbol)
13208 %
13209 % A description of each parameter follows:
13210 %
13211 % o display: Specifies a connection to an X server; returned from
13212 % XOpenDisplay.
13213 %
13214 % o windows: Specifies a pointer to a XWindows structure.
13215 %
13216 % o image: the image.
13217 %
13218 % o key_symbol: Specifies a KeySym which indicates which side of the image
13219 % to trim.
13220 %
13221 */
13222 static void XTranslateImage(Display *display,XWindows *windows,
13223  Image *image,const KeySym key_symbol)
13224 {
13225  char
13226  text[MaxTextExtent];
13227 
13228  int
13229  x,
13230  y;
13231 
13232  unsigned int
13233  x_offset,
13234  y_offset;
13235 
13236  /*
13237  User specified a pan position offset.
13238  */
13239  x_offset=windows->image.width;
13240  y_offset=windows->image.height;
13241  if (image->montage != (char *) NULL)
13242  (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13243  switch ((int) key_symbol)
13244  {
13245  case XK_Home:
13246  case XK_KP_Home:
13247  {
13248  windows->image.x=(int) windows->image.width/2;
13249  windows->image.y=(int) windows->image.height/2;
13250  break;
13251  }
13252  case XK_Left:
13253  case XK_KP_Left:
13254  {
13255  windows->image.x-=x_offset;
13256  break;
13257  }
13258  case XK_Next:
13259  case XK_Up:
13260  case XK_KP_Up:
13261  {
13262  windows->image.y-=y_offset;
13263  break;
13264  }
13265  case XK_Right:
13266  case XK_KP_Right:
13267  {
13268  windows->image.x+=x_offset;
13269  break;
13270  }
13271  case XK_Prior:
13272  case XK_Down:
13273  case XK_KP_Down:
13274  {
13275  windows->image.y+=y_offset;
13276  break;
13277  }
13278  default:
13279  return;
13280  }
13281  /*
13282  Check boundary conditions.
13283  */
13284  if (windows->image.x < 0)
13285  windows->image.x=0;
13286  else
13287  if ((int) (windows->image.x+windows->image.width) >
13288  windows->image.ximage->width)
13289  windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13290  if (windows->image.y < 0)
13291  windows->image.y=0;
13292  else
13293  if ((int) (windows->image.y+windows->image.height) >
13294  windows->image.ximage->height)
13295  windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13296  /*
13297  Refresh Image window.
13298  */
13299  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13300  windows->image.width,windows->image.height,windows->image.x,
13301  windows->image.y);
13302  XInfoWidget(display,windows,text);
13303  XCheckRefreshWindows(display,windows);
13304  XDrawPanRectangle(display,windows);
13305  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13306  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13307 }
13308 ␌
13309 /*
13310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13311 % %
13312 % %
13313 % %
13314 + X T r i m I m a g e %
13315 % %
13316 % %
13317 % %
13318 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13319 %
13320 % XTrimImage() trims the edges from the Image window.
13321 %
13322 % The format of the XTrimImage method is:
13323 %
13324 % MagickBooleanType XTrimImage(Display *display,
13325 % XResourceInfo *resource_info,XWindows *windows,Image *image)
13326 %
13327 % A description of each parameter follows:
13328 %
13329 % o display: Specifies a connection to an X server; returned from
13330 % XOpenDisplay.
13331 %
13332 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13333 %
13334 % o windows: Specifies a pointer to a XWindows structure.
13335 %
13336 % o image: the image.
13337 %
13338 */
13339 static MagickBooleanType XTrimImage(Display *display,
13340  XResourceInfo *resource_info,XWindows *windows,Image *image)
13341 {
13343  trim_info;
13344 
13345  int
13346  x,
13347  y;
13348 
13349  size_t
13350  background,
13351  pixel;
13352 
13353  /*
13354  Trim edges from image.
13355  */
13356  XSetCursorState(display,windows,MagickTrue);
13357  XCheckRefreshWindows(display,windows);
13358  /*
13359  Crop the left edge.
13360  */
13361  background=XGetPixel(windows->image.ximage,0,0);
13362  trim_info.width=(size_t) windows->image.ximage->width;
13363  for (x=0; x < windows->image.ximage->width; x++)
13364  {
13365  for (y=0; y < windows->image.ximage->height; y++)
13366  {
13367  pixel=XGetPixel(windows->image.ximage,x,y);
13368  if (pixel != background)
13369  break;
13370  }
13371  if (y < windows->image.ximage->height)
13372  break;
13373  }
13374  trim_info.x=(ssize_t) x;
13375  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13376  {
13377  XSetCursorState(display,windows,MagickFalse);
13378  return(MagickFalse);
13379  }
13380  /*
13381  Crop the right edge.
13382  */
13383  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13384  for (x=windows->image.ximage->width-1; x != 0; x--)
13385  {
13386  for (y=0; y < windows->image.ximage->height; y++)
13387  {
13388  pixel=XGetPixel(windows->image.ximage,x,y);
13389  if (pixel != background)
13390  break;
13391  }
13392  if (y < windows->image.ximage->height)
13393  break;
13394  }
13395  trim_info.width=(size_t) (x-trim_info.x+1);
13396  /*
13397  Crop the top edge.
13398  */
13399  background=XGetPixel(windows->image.ximage,0,0);
13400  trim_info.height=(size_t) windows->image.ximage->height;
13401  for (y=0; y < windows->image.ximage->height; y++)
13402  {
13403  for (x=0; x < windows->image.ximage->width; x++)
13404  {
13405  pixel=XGetPixel(windows->image.ximage,x,y);
13406  if (pixel != background)
13407  break;
13408  }
13409  if (x < windows->image.ximage->width)
13410  break;
13411  }
13412  trim_info.y=(ssize_t) y;
13413  /*
13414  Crop the bottom edge.
13415  */
13416  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13417  for (y=windows->image.ximage->height-1; y != 0; y--)
13418  {
13419  for (x=0; x < windows->image.ximage->width; x++)
13420  {
13421  pixel=XGetPixel(windows->image.ximage,x,y);
13422  if (pixel != background)
13423  break;
13424  }
13425  if (x < windows->image.ximage->width)
13426  break;
13427  }
13428  trim_info.height=(size_t) y-trim_info.y+1;
13429  if (((unsigned int) trim_info.width != windows->image.width) ||
13430  ((unsigned int) trim_info.height != windows->image.height))
13431  {
13432  /*
13433  Reconfigure Image window as defined by the trimming rectangle.
13434  */
13435  XSetCropGeometry(display,windows,&trim_info,image);
13436  windows->image.window_changes.width=(int) trim_info.width;
13437  windows->image.window_changes.height=(int) trim_info.height;
13438  (void) XConfigureImage(display,resource_info,windows,image);
13439  }
13440  XSetCursorState(display,windows,MagickFalse);
13441  return(MagickTrue);
13442 }
13443 ␌
13444 /*
13445 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13446 % %
13447 % %
13448 % %
13449 + X V i s u a l D i r e c t o r y I m a g e %
13450 % %
13451 % %
13452 % %
13453 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13454 %
13455 % XVisualDirectoryImage() creates a Visual Image Directory.
13456 %
13457 % The format of the XVisualDirectoryImage method is:
13458 %
13459 % Image *XVisualDirectoryImage(Display *display,
13460 % XResourceInfo *resource_info,XWindows *windows)
13461 %
13462 % A description of each parameter follows:
13463 %
13464 % o nexus: Method XVisualDirectoryImage returns a visual image
13465 % directory if it can be created successfully. Otherwise a null image
13466 % is returned.
13467 %
13468 % o display: Specifies a connection to an X server; returned from
13469 % XOpenDisplay.
13470 %
13471 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13472 %
13473 % o windows: Specifies a pointer to a XWindows structure.
13474 %
13475 */
13476 static Image *XVisualDirectoryImage(Display *display,
13477  XResourceInfo *resource_info,XWindows *windows)
13478 {
13479 #define TileImageTag "Scale/Image"
13480 #define XClientName "montage"
13481 
13482  char
13483  **filelist;
13484 
13486  *exception;
13487 
13488  Image
13489  *images,
13490  *montage_image,
13491  *next_image,
13492  *thumbnail_image;
13493 
13494  ImageInfo
13495  *read_info;
13496 
13497  int
13498  number_files;
13499 
13500  MagickBooleanType
13501  backdrop;
13502 
13503  MagickStatusType
13504  status;
13505 
13506  MontageInfo
13507  *montage_info;
13508 
13510  geometry;
13511 
13512  int
13513  i;
13514 
13515  static char
13516  filename[MaxTextExtent] = "\0",
13517  filenames[MaxTextExtent] = "*";
13518 
13519  XResourceInfo
13520  background_resources;
13521 
13522  /*
13523  Request file name from user.
13524  */
13525  XFileBrowserWidget(display,windows,"Directory",filenames);
13526  if (*filenames == '\0')
13527  return((Image *) NULL);
13528  /*
13529  Expand the filenames.
13530  */
13531  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13532  if (filelist == (char **) NULL)
13533  {
13534  ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13535  filenames);
13536  return((Image *) NULL);
13537  }
13538  number_files=1;
13539  filelist[0]=filenames;
13540  status=ExpandFilenames(&number_files,&filelist);
13541  if ((status == MagickFalse) || (number_files == 0))
13542  {
13543  if (number_files == 0)
13544  ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13545  else
13546  ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13547  filenames);
13548  return((Image *) NULL);
13549  }
13550  /*
13551  Set image background resources.
13552  */
13553  background_resources=(*resource_info);
13554  background_resources.window_id=AcquireString("");
13555  (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13556  "0x%lx",windows->image.id);
13557  background_resources.backdrop=MagickTrue;
13558  /*
13559  Read each image and convert them to a tile.
13560  */
13561  backdrop=(windows->visual_info->klass == TrueColor) ||
13562  (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13563  read_info=CloneImageInfo(resource_info->image_info);
13564  (void) SetImageOption(read_info,"jpeg:size","120x120");
13565  (void) CloneString(&read_info->size,DefaultTileGeometry);
13566  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13567  (void *) NULL);
13568  images=NewImageList();
13569  exception=AcquireExceptionInfo();
13570  XSetCursorState(display,windows,MagickTrue);
13571  XCheckRefreshWindows(display,windows);
13572  for (i=0; i < (int) number_files; i++)
13573  {
13574  (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13575  filelist[i]=DestroyString(filelist[i]);
13576  *read_info->magick='\0';
13577  next_image=ReadImage(read_info,exception);
13578  CatchException(exception);
13579  if (next_image != (Image *) NULL)
13580  {
13581  (void) DeleteImageProperty(next_image,"label");
13582  (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13583  read_info,next_image,DefaultTileLabel));
13584  (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13585  exception);
13586  thumbnail_image=ThumbnailImage(next_image,geometry.width,
13587  geometry.height,exception);
13588  if (thumbnail_image != (Image *) NULL)
13589  {
13590  next_image=DestroyImage(next_image);
13591  next_image=thumbnail_image;
13592  }
13593  if (backdrop)
13594  {
13595  (void) XDisplayBackgroundImage(display,&background_resources,
13596  next_image);
13597  XSetCursorState(display,windows,MagickTrue);
13598  }
13599  AppendImageToList(&images,next_image);
13600  if (images->progress_monitor != (MagickProgressMonitor) NULL)
13601  {
13602  MagickBooleanType
13603  proceed;
13604 
13605  proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13606  (MagickSizeType) number_files);
13607  if (proceed == MagickFalse)
13608  break;
13609  }
13610  }
13611  }
13612  exception=DestroyExceptionInfo(exception);
13613  filelist=(char **) RelinquishMagickMemory(filelist);
13614  if (images == (Image *) NULL)
13615  {
13616  read_info=DestroyImageInfo(read_info);
13617  XSetCursorState(display,windows,MagickFalse);
13618  ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13619  return((Image *) NULL);
13620  }
13621  /*
13622  Create the Visual Image Directory.
13623  */
13624  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13625  montage_info->pointsize=10;
13626  if (resource_info->font != (char *) NULL)
13627  (void) CloneString(&montage_info->font,resource_info->font);
13628  (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13629  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13630  images),&images->exception);
13631  images=DestroyImageList(images);
13632  montage_info=DestroyMontageInfo(montage_info);
13633  read_info=DestroyImageInfo(read_info);
13634  XSetCursorState(display,windows,MagickFalse);
13635  if (montage_image == (Image *) NULL)
13636  return(montage_image);
13637  XClientMessage(display,windows->image.id,windows->im_protocols,
13638  windows->im_next_image,CurrentTime);
13639  return(montage_image);
13640 }
13641 ␌
13642 /*
13643 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13644 % %
13645 % %
13646 % %
13647 % X D i s p l a y B a c k g r o u n d I m a g e %
13648 % %
13649 % %
13650 % %
13651 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13652 %
13653 % XDisplayBackgroundImage() displays an image in the background of a window.
13654 %
13655 % The format of the XDisplayBackgroundImage method is:
13656 %
13657 % MagickBooleanType XDisplayBackgroundImage(Display *display,
13658 % XResourceInfo *resource_info,Image *image)
13659 %
13660 % A description of each parameter follows:
13661 %
13662 % o display: Specifies a connection to an X server; returned from
13663 % XOpenDisplay.
13664 %
13665 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13666 %
13667 % o image: the image.
13668 %
13669 */
13670 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13671  XResourceInfo *resource_info,Image *image)
13672 {
13673  char
13674  geometry[MaxTextExtent],
13675  visual_type[MaxTextExtent];
13676 
13677  int
13678  height,
13679  status,
13680  width;
13681 
13683  geometry_info;
13684 
13685  static XPixelInfo
13686  pixel;
13687 
13688  static XStandardColormap
13689  *map_info;
13690 
13691  static XVisualInfo
13692  *visual_info = (XVisualInfo *) NULL;
13693 
13694  static XWindowInfo
13695  window_info;
13696 
13697  size_t
13698  delay;
13699 
13700  Window
13701  root_window;
13702 
13703  XGCValues
13704  context_values;
13705 
13706  XResourceInfo
13707  resources;
13708 
13709  XWindowAttributes
13710  window_attributes;
13711 
13712  /*
13713  Determine target window.
13714  */
13715  assert(image != (Image *) NULL);
13716  assert(image->signature == MagickCoreSignature);
13717  if (IsEventLogging() != MagickFalse)
13718  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13719  resources=(*resource_info);
13720  window_info.id=(Window) NULL;
13721  root_window=XRootWindow(display,XDefaultScreen(display));
13722  if (LocaleCompare(resources.window_id,"root") == 0)
13723  window_info.id=root_window;
13724  else
13725  {
13726  if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13727  window_info.id=XWindowByID(display,root_window,
13728  (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13729  if (window_info.id == (Window) NULL)
13730  window_info.id=XWindowByName(display,root_window,resources.window_id);
13731  }
13732  if (window_info.id == (Window) NULL)
13733  {
13734  ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13735  resources.window_id);
13736  }
13737  /*
13738  Determine window visual id.
13739  */
13740  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13741  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13742  (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13743  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13744  if (status != 0)
13745  (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13746  XVisualIDFromVisual(window_attributes.visual));
13747  if (visual_info == (XVisualInfo *) NULL)
13748  {
13749  /*
13750  Allocate standard colormap.
13751  */
13752  map_info=XAllocStandardColormap();
13753  if (map_info == (XStandardColormap *) NULL)
13754  ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13755  image->filename);
13756  map_info->colormap=(Colormap) NULL;
13757  pixel.pixels=(unsigned long *) NULL;
13758  /*
13759  Initialize visual info.
13760  */
13761  resources.map_type=(char *) NULL;
13762  resources.visual_type=visual_type;
13763  visual_info=XBestVisualInfo(display,map_info,&resources);
13764  if (visual_info == (XVisualInfo *) NULL)
13765  ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13766  resources.visual_type);
13767  /*
13768  Initialize window info.
13769  */
13770  window_info.ximage=(XImage *) NULL;
13771  window_info.matte_image=(XImage *) NULL;
13772  window_info.pixmap=(Pixmap) NULL;
13773  window_info.matte_pixmap=(Pixmap) NULL;
13774  }
13775  /*
13776  Free previous root colors.
13777  */
13778  if (window_info.id == root_window)
13779  (void) XDestroyWindowColors(display,root_window);
13780  /*
13781  Initialize Standard Colormap.
13782  */
13783  resources.colormap=SharedColormap;
13784  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13785  /*
13786  Graphic context superclass.
13787  */
13788  context_values.background=pixel.foreground_color.pixel;
13789  context_values.foreground=pixel.background_color.pixel;
13790  pixel.annotate_context=XCreateGC(display,window_info.id,
13791  (size_t) (GCBackground | GCForeground),&context_values);
13792  if (pixel.annotate_context == (GC) NULL)
13793  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13794  image->filename);
13795  /*
13796  Initialize Image window attributes.
13797  */
13798  window_info.name=AcquireString("\0");
13799  window_info.icon_name=AcquireString("\0");
13800  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13801  &resources,&window_info);
13802  /*
13803  Create the X image.
13804  */
13805  window_info.width=(unsigned int) image->columns;
13806  window_info.height=(unsigned int) image->rows;
13807  if ((image->columns != window_info.width) ||
13808  (image->rows != window_info.height))
13809  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13810  image->filename);
13811  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13812  window_attributes.width,window_attributes.height);
13813  geometry_info.width=window_info.width;
13814  geometry_info.height=window_info.height;
13815  geometry_info.x=(ssize_t) window_info.x;
13816  geometry_info.y=(ssize_t) window_info.y;
13817  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13818  &geometry_info.width,&geometry_info.height);
13819  window_info.width=(unsigned int) geometry_info.width;
13820  window_info.height=(unsigned int) geometry_info.height;
13821  window_info.x=(int) geometry_info.x;
13822  window_info.y=(int) geometry_info.y;
13823  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13824  window_info.height);
13825  if (status == MagickFalse)
13826  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13827  image->filename);
13828  window_info.x=0;
13829  window_info.y=0;
13830  if (resource_info->debug != MagickFalse)
13831  {
13832  (void) LogMagickEvent(X11Event,GetMagickModule(),
13833  "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13834  (double) image->columns,(double) image->rows);
13835  if (image->colors != 0)
13836  (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13837  image->colors);
13838  (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13839  }
13840  /*
13841  Adjust image dimensions as specified by backdrop or geometry options.
13842  */
13843  width=(int) window_info.width;
13844  height=(int) window_info.height;
13845  if (resources.backdrop != MagickFalse)
13846  {
13847  /*
13848  Center image on window.
13849  */
13850  window_info.x=(window_attributes.width/2)-(window_info.ximage->width/2);
13851  window_info.y=(window_attributes.height/2)-(window_info.ximage->height/2);
13852  width=window_attributes.width;
13853  height=window_attributes.height;
13854  }
13855  if ((resources.image_geometry != (char *) NULL) &&
13856  (*resources.image_geometry != '\0'))
13857  {
13858  char
13859  default_geometry[MaxTextExtent];
13860 
13861  int
13862  flags,
13863  gravity;
13864 
13865  XSizeHints
13866  *size_hints;
13867 
13868  /*
13869  User specified geometry.
13870  */
13871  size_hints=XAllocSizeHints();
13872  if (size_hints == (XSizeHints *) NULL)
13873  ThrowXWindowFatalException(ResourceLimitFatalError,
13874  "MemoryAllocationFailed",image->filename);
13875  size_hints->flags=0L;
13876  (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13877  width,height);
13878  flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13879  default_geometry,window_info.border_width,size_hints,&window_info.x,
13880  &window_info.y,&width,&height,&gravity);
13881  if (flags & (XValue | YValue))
13882  {
13883  width=window_attributes.width;
13884  height=window_attributes.height;
13885  }
13886  (void) XFree((void *) size_hints);
13887  }
13888  /*
13889  Create the X pixmap.
13890  */
13891  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13892  (unsigned int) height,window_info.depth);
13893  if (window_info.pixmap == (Pixmap) NULL)
13894  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13895  image->filename);
13896  /*
13897  Display pixmap on the window.
13898  */
13899  if (((unsigned int) width > window_info.width) ||
13900  ((unsigned int) height > window_info.height))
13901  (void) XFillRectangle(display,window_info.pixmap,
13902  window_info.annotate_context,0,0,(unsigned int) width,
13903  (unsigned int) height);
13904  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13905  window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13906  window_info.width,(unsigned int) window_info.height);
13907  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13908  (void) XClearWindow(display,window_info.id);
13909  delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13910  XDelay(display,delay == 0UL ? 10UL : delay);
13911  (void) XSync(display,MagickFalse);
13912  return(window_info.id == root_window ? MagickTrue : MagickFalse);
13913 }
13914 ␌
13915 /*
13916 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13917 % %
13918 % %
13919 % %
13920 + X D i s p l a y I m a g e %
13921 % %
13922 % %
13923 % %
13924 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13925 %
13926 % XDisplayImage() displays an image via X11. A new image is created and
13927 % returned if the user interactively transforms the displayed image.
13928 %
13929 % The format of the XDisplayImage method is:
13930 %
13931 % Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13932 % char **argv,int argc,Image **image,size_t *state)
13933 %
13934 % A description of each parameter follows:
13935 %
13936 % o nexus: Method XDisplayImage returns an image when the
13937 % user chooses 'Open Image' from the command menu or picks a tile
13938 % from the image directory. Otherwise a null image is returned.
13939 %
13940 % o display: Specifies a connection to an X server; returned from
13941 % XOpenDisplay.
13942 %
13943 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13944 %
13945 % o argv: Specifies the application's argument list.
13946 %
13947 % o argc: Specifies the number of arguments.
13948 %
13949 % o image: Specifies an address to an address of an Image structure;
13950 %
13951 */
13952 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13953  char **argv,int argc,Image **image,size_t *state)
13954 {
13955 #define MagnifySize 256 /* must be a power of 2 */
13956 #define MagickMenus 10
13957 #define MagickTitle "Commands"
13958 
13959  const char
13960  *const CommandMenu[] =
13961  {
13962  "File",
13963  "Edit",
13964  "View",
13965  "Transform",
13966  "Enhance",
13967  "Effects",
13968  "F/X",
13969  "Image Edit",
13970  "Miscellany",
13971  "Help",
13972  (char *) NULL
13973  },
13974  *const FileMenu[] =
13975  {
13976  "Open...",
13977  "Next",
13978  "Former",
13979  "Select...",
13980  "Save...",
13981  "Print...",
13982  "Delete...",
13983  "New...",
13984  "Visual Directory...",
13985  "Quit",
13986  (char *) NULL
13987  },
13988  *const EditMenu[] =
13989  {
13990  "Undo",
13991  "Redo",
13992  "Cut",
13993  "Copy",
13994  "Paste",
13995  (char *) NULL
13996  },
13997  *const ViewMenu[] =
13998  {
13999  "Half Size",
14000  "Original Size",
14001  "Double Size",
14002  "Resize...",
14003  "Apply",
14004  "Refresh",
14005  "Restore",
14006  (char *) NULL
14007  },
14008  *const TransformMenu[] =
14009  {
14010  "Crop",
14011  "Chop",
14012  "Flop",
14013  "Flip",
14014  "Rotate Right",
14015  "Rotate Left",
14016  "Rotate...",
14017  "Shear...",
14018  "Roll...",
14019  "Trim Edges",
14020  (char *) NULL
14021  },
14022  *const EnhanceMenu[] =
14023  {
14024  "Hue...",
14025  "Saturation...",
14026  "Brightness...",
14027  "Gamma...",
14028  "Spiff",
14029  "Dull",
14030  "Contrast Stretch...",
14031  "Sigmoidal Contrast...",
14032  "Normalize",
14033  "Equalize",
14034  "Negate",
14035  "Grayscale",
14036  "Map...",
14037  "Quantize...",
14038  (char *) NULL
14039  },
14040  *const EffectsMenu[] =
14041  {
14042  "Despeckle",
14043  "Emboss",
14044  "Reduce Noise",
14045  "Add Noise...",
14046  "Sharpen...",
14047  "Blur...",
14048  "Threshold...",
14049  "Edge Detect...",
14050  "Spread...",
14051  "Shade...",
14052  "Raise...",
14053  "Segment...",
14054  (char *) NULL
14055  },
14056  *const FXMenu[] =
14057  {
14058  "Solarize...",
14059  "Sepia Tone...",
14060  "Swirl...",
14061  "Implode...",
14062  "Vignette...",
14063  "Wave...",
14064  "Oil Paint...",
14065  "Charcoal Draw...",
14066  (char *) NULL
14067  },
14068  *const ImageEditMenu[] =
14069  {
14070  "Annotate...",
14071  "Draw...",
14072  "Color...",
14073  "Matte...",
14074  "Composite...",
14075  "Add Border...",
14076  "Add Frame...",
14077  "Comment...",
14078  "Launch...",
14079  "Region of Interest...",
14080  (char *) NULL
14081  },
14082  *const MiscellanyMenu[] =
14083  {
14084  "Image Info",
14085  "Zoom Image",
14086  "Show Preview...",
14087  "Show Histogram",
14088  "Show Matte",
14089  "Background...",
14090  "Slide Show...",
14091  "Preferences...",
14092  (char *) NULL
14093  },
14094  *const HelpMenu[] =
14095  {
14096  "Overview",
14097  "Browse Documentation",
14098  "About Display",
14099  (char *) NULL
14100  },
14101  *const ShortCutsMenu[] =
14102  {
14103  "Next",
14104  "Former",
14105  "Open...",
14106  "Save...",
14107  "Print...",
14108  "Undo",
14109  "Restore",
14110  "Image Info",
14111  "Quit",
14112  (char *) NULL
14113  },
14114  *const VirtualMenu[] =
14115  {
14116  "Image Info",
14117  "Print",
14118  "Next",
14119  "Quit",
14120  (char *) NULL
14121  };
14122 
14123  const char
14124  *const *Menus[MagickMenus] =
14125  {
14126  FileMenu,
14127  EditMenu,
14128  ViewMenu,
14129  TransformMenu,
14130  EnhanceMenu,
14131  EffectsMenu,
14132  FXMenu,
14133  ImageEditMenu,
14134  MiscellanyMenu,
14135  HelpMenu
14136  };
14137 
14138  static DisplayCommand
14139  CommandMenus[] =
14140  {
14141  NullCommand,
14142  NullCommand,
14143  NullCommand,
14144  NullCommand,
14145  NullCommand,
14146  NullCommand,
14147  NullCommand,
14148  NullCommand,
14149  NullCommand,
14150  NullCommand,
14151  },
14152  FileCommands[] =
14153  {
14154  OpenCommand,
14155  NextCommand,
14156  FormerCommand,
14157  SelectCommand,
14158  SaveCommand,
14159  PrintCommand,
14160  DeleteCommand,
14161  NewCommand,
14162  VisualDirectoryCommand,
14163  QuitCommand
14164  },
14165  EditCommands[] =
14166  {
14167  UndoCommand,
14168  RedoCommand,
14169  CutCommand,
14170  CopyCommand,
14171  PasteCommand
14172  },
14173  ViewCommands[] =
14174  {
14175  HalfSizeCommand,
14176  OriginalSizeCommand,
14177  DoubleSizeCommand,
14178  ResizeCommand,
14179  ApplyCommand,
14180  RefreshCommand,
14181  RestoreCommand
14182  },
14183  TransformCommands[] =
14184  {
14185  CropCommand,
14186  ChopCommand,
14187  FlopCommand,
14188  FlipCommand,
14189  RotateRightCommand,
14190  RotateLeftCommand,
14191  RotateCommand,
14192  ShearCommand,
14193  RollCommand,
14194  TrimCommand
14195  },
14196  EnhanceCommands[] =
14197  {
14198  HueCommand,
14199  SaturationCommand,
14200  BrightnessCommand,
14201  GammaCommand,
14202  SpiffCommand,
14203  DullCommand,
14204  ContrastStretchCommand,
14205  SigmoidalContrastCommand,
14206  NormalizeCommand,
14207  EqualizeCommand,
14208  NegateCommand,
14209  GrayscaleCommand,
14210  MapCommand,
14211  QuantizeCommand
14212  },
14213  EffectsCommands[] =
14214  {
14215  DespeckleCommand,
14216  EmbossCommand,
14217  ReduceNoiseCommand,
14218  AddNoiseCommand,
14219  SharpenCommand,
14220  BlurCommand,
14221  ThresholdCommand,
14222  EdgeDetectCommand,
14223  SpreadCommand,
14224  ShadeCommand,
14225  RaiseCommand,
14226  SegmentCommand
14227  },
14228  FXCommands[] =
14229  {
14230  SolarizeCommand,
14231  SepiaToneCommand,
14232  SwirlCommand,
14233  ImplodeCommand,
14234  VignetteCommand,
14235  WaveCommand,
14236  OilPaintCommand,
14237  CharcoalDrawCommand
14238  },
14239  ImageEditCommands[] =
14240  {
14241  AnnotateCommand,
14242  DrawCommand,
14243  ColorCommand,
14244  MatteCommand,
14245  CompositeCommand,
14246  AddBorderCommand,
14247  AddFrameCommand,
14248  CommentCommand,
14249  LaunchCommand,
14250  RegionOfInterestCommand
14251  },
14252  MiscellanyCommands[] =
14253  {
14254  InfoCommand,
14255  ZoomCommand,
14256  ShowPreviewCommand,
14257  ShowHistogramCommand,
14258  ShowMatteCommand,
14259  BackgroundCommand,
14260  SlideShowCommand,
14261  PreferencesCommand
14262  },
14263  HelpCommands[] =
14264  {
14265  HelpCommand,
14266  BrowseDocumentationCommand,
14267  VersionCommand
14268  },
14269  ShortCutsCommands[] =
14270  {
14271  NextCommand,
14272  FormerCommand,
14273  OpenCommand,
14274  SaveCommand,
14275  PrintCommand,
14276  UndoCommand,
14277  RestoreCommand,
14278  InfoCommand,
14279  QuitCommand
14280  },
14281  VirtualCommands[] =
14282  {
14283  InfoCommand,
14284  PrintCommand,
14285  NextCommand,
14286  QuitCommand
14287  };
14288 
14289  static DisplayCommand
14290  *Commands[MagickMenus] =
14291  {
14292  FileCommands,
14293  EditCommands,
14294  ViewCommands,
14295  TransformCommands,
14296  EnhanceCommands,
14297  EffectsCommands,
14298  FXCommands,
14299  ImageEditCommands,
14300  MiscellanyCommands,
14301  HelpCommands
14302  };
14303 
14304  char
14305  command[MaxTextExtent],
14306  *directory,
14307  geometry[MaxTextExtent],
14308  resource_name[MaxTextExtent];
14309 
14310  DisplayCommand
14311  display_command;
14312 
14313  Image
14314  *display_image,
14315  *nexus;
14316 
14317  int
14318  entry,
14319  id;
14320 
14321  KeySym
14322  key_symbol;
14323 
14324  MagickStatusType
14325  context_mask,
14326  status;
14327 
14329  geometry_info;
14330 
14331  int
14332  i;
14333 
14334  static char
14335  working_directory[MaxTextExtent];
14336 
14337  static XPoint
14338  vid_info;
14339 
14340  static XWindowInfo
14341  *magick_windows[MaxXWindows];
14342 
14343  static unsigned int
14344  number_windows;
14345 
14346  struct stat
14347  attributes;
14348 
14349  time_t
14350  timer,
14351  timestamp,
14352  update_time;
14353 
14354  unsigned int
14355  height,
14356  width;
14357 
14358  size_t
14359  delay;
14360 
14361  WarningHandler
14362  warning_handler;
14363 
14364  Window
14365  root_window;
14366 
14367  XClassHint
14368  *class_hints;
14369 
14370  XEvent
14371  event;
14372 
14373  XFontStruct
14374  *font_info;
14375 
14376  XGCValues
14377  context_values;
14378 
14379  XPixelInfo
14380  *icon_pixel,
14381  *pixel;
14382 
14383  XResourceInfo
14384  *icon_resources;
14385 
14386  XStandardColormap
14387  *icon_map,
14388  *map_info;
14389 
14390  XVisualInfo
14391  *icon_visual,
14392  *visual_info;
14393 
14394  XWindowChanges
14395  window_changes;
14396 
14397  XWindows
14398  *windows;
14399 
14400  XWMHints
14401  *manager_hints;
14402 
14403  assert(image != (Image **) NULL);
14404  assert((*image)->signature == MagickCoreSignature);
14405  if (IsEventLogging() != MagickFalse)
14406  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14407  display_image=(*image);
14408  warning_handler=(WarningHandler) NULL;
14409  windows=XSetWindows((XWindows *) ~0);
14410  if (windows != (XWindows *) NULL)
14411  {
14412  int
14413  status;
14414 
14415  if (*working_directory == '\0')
14416  (void) CopyMagickString(working_directory,".",MaxTextExtent);
14417  status=chdir(working_directory);
14418  if (status == -1)
14419  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
14420  FileOpenError,"UnableToOpenFile","%s",working_directory);
14421  warning_handler=resource_info->display_warnings ?
14422  SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14423  warning_handler=resource_info->display_warnings ?
14424  SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14425  }
14426  else
14427  {
14428  /*
14429  Allocate windows structure.
14430  */
14431  resource_info->colors=display_image->colors;
14432  windows=XSetWindows(XInitializeWindows(display,resource_info));
14433  if (windows == (XWindows *) NULL)
14434  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14435  (*image)->filename);
14436  /*
14437  Initialize window id's.
14438  */
14439  number_windows=0;
14440  magick_windows[number_windows++]=(&windows->icon);
14441  magick_windows[number_windows++]=(&windows->backdrop);
14442  magick_windows[number_windows++]=(&windows->image);
14443  magick_windows[number_windows++]=(&windows->info);
14444  magick_windows[number_windows++]=(&windows->command);
14445  magick_windows[number_windows++]=(&windows->widget);
14446  magick_windows[number_windows++]=(&windows->popup);
14447  magick_windows[number_windows++]=(&windows->magnify);
14448  magick_windows[number_windows++]=(&windows->pan);
14449  for (i=0; i < (int) number_windows; i++)
14450  magick_windows[i]->id=(Window) NULL;
14451  vid_info.x=0;
14452  vid_info.y=0;
14453  }
14454  /*
14455  Initialize font info.
14456  */
14457  if (windows->font_info != (XFontStruct *) NULL)
14458  (void) XFreeFont(display,windows->font_info);
14459  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14460  if (windows->font_info == (XFontStruct *) NULL)
14461  ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14462  resource_info->font);
14463  /*
14464  Initialize Standard Colormap.
14465  */
14466  map_info=windows->map_info;
14467  icon_map=windows->icon_map;
14468  visual_info=windows->visual_info;
14469  icon_visual=windows->icon_visual;
14470  pixel=windows->pixel_info;
14471  icon_pixel=windows->icon_pixel;
14472  font_info=windows->font_info;
14473  icon_resources=windows->icon_resources;
14474  class_hints=windows->class_hints;
14475  manager_hints=windows->manager_hints;
14476  root_window=XRootWindow(display,visual_info->screen);
14477  nexus=NewImageList();
14478  if (resource_info->debug != MagickFalse)
14479  {
14480  (void) LogMagickEvent(X11Event,GetMagickModule(),
14481  "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14482  (double) display_image->scene,(double) display_image->columns,
14483  (double) display_image->rows);
14484  if (display_image->colors != 0)
14485  (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14486  display_image->colors);
14487  (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14488  display_image->magick);
14489  }
14490  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14491  map_info,pixel);
14492  display_image->taint=MagickFalse;
14493  /*
14494  Initialize graphic context.
14495  */
14496  windows->context.id=(Window) NULL;
14497  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14498  resource_info,&windows->context);
14499  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14500  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14501  class_hints->res_class[0]=(char) LocaleToUppercase((int)
14502  class_hints->res_class[0]);
14503  manager_hints->flags=InputHint | StateHint;
14504  manager_hints->input=MagickFalse;
14505  manager_hints->initial_state=WithdrawnState;
14506  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14507  &windows->context);
14508  if (resource_info->debug != MagickFalse)
14509  (void) LogMagickEvent(X11Event,GetMagickModule(),
14510  "Window id: 0x%lx (context)",windows->context.id);
14511  context_values.background=pixel->background_color.pixel;
14512  context_values.font=font_info->fid;
14513  context_values.foreground=pixel->foreground_color.pixel;
14514  context_values.graphics_exposures=MagickFalse;
14515  context_mask=(MagickStatusType)
14516  (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14517  if (pixel->annotate_context != (GC) NULL)
14518  (void) XFreeGC(display,pixel->annotate_context);
14519  pixel->annotate_context=XCreateGC(display,windows->context.id,
14520  context_mask,&context_values);
14521  if (pixel->annotate_context == (GC) NULL)
14522  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14523  display_image->filename);
14524  context_values.background=pixel->depth_color.pixel;
14525  if (pixel->widget_context != (GC) NULL)
14526  (void) XFreeGC(display,pixel->widget_context);
14527  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14528  &context_values);
14529  if (pixel->widget_context == (GC) NULL)
14530  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14531  display_image->filename);
14532  context_values.background=pixel->foreground_color.pixel;
14533  context_values.foreground=pixel->background_color.pixel;
14534  context_values.plane_mask=context_values.background ^
14535  context_values.foreground;
14536  if (pixel->highlight_context != (GC) NULL)
14537  (void) XFreeGC(display,pixel->highlight_context);
14538  pixel->highlight_context=XCreateGC(display,windows->context.id,
14539  (size_t) (context_mask | GCPlaneMask),&context_values);
14540  if (pixel->highlight_context == (GC) NULL)
14541  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14542  display_image->filename);
14543  (void) XDestroyWindow(display,windows->context.id);
14544  /*
14545  Initialize icon window.
14546  */
14547  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14548  icon_resources,&windows->icon);
14549  windows->icon.geometry=resource_info->icon_geometry;
14550  XBestIconSize(display,&windows->icon,display_image);
14551  windows->icon.attributes.colormap=XDefaultColormap(display,
14552  icon_visual->screen);
14553  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14554  manager_hints->flags=InputHint | StateHint;
14555  manager_hints->input=MagickFalse;
14556  manager_hints->initial_state=IconicState;
14557  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14558  &windows->icon);
14559  if (resource_info->debug != MagickFalse)
14560  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14561  windows->icon.id);
14562  /*
14563  Initialize graphic context for icon window.
14564  */
14565  if (icon_pixel->annotate_context != (GC) NULL)
14566  (void) XFreeGC(display,icon_pixel->annotate_context);
14567  context_values.background=icon_pixel->background_color.pixel;
14568  context_values.foreground=icon_pixel->foreground_color.pixel;
14569  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14570  (size_t) (GCBackground | GCForeground),&context_values);
14571  if (icon_pixel->annotate_context == (GC) NULL)
14572  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14573  display_image->filename);
14574  windows->icon.annotate_context=icon_pixel->annotate_context;
14575  /*
14576  Initialize Image window.
14577  */
14578  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14579  &windows->image);
14580  windows->image.shape=MagickTrue; /* non-rectangular shape hint */
14581  if (resource_info->use_shared_memory == MagickFalse)
14582  windows->image.shared_memory=MagickFalse;
14583  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14584  {
14585  char
14586  *title;
14587 
14588  title=InterpretImageProperties(resource_info->image_info,display_image,
14589  resource_info->title);
14590  (void) CloneString(&windows->image.name,title);
14591  (void) CloneString(&windows->image.icon_name,title);
14592  title=DestroyString(title);
14593  }
14594  else
14595  {
14596  char
14597  filename[MaxTextExtent],
14598  window_name[MaxTextExtent];
14599 
14600  /*
14601  Window name is the base of the filename.
14602  */
14603  GetPathComponent(display_image->magick_filename,TailPath,filename);
14604  if (display_image->scene == 0)
14605  (void) FormatLocaleString(window_name,MaxTextExtent,"%s: %s",
14606  MagickPackageName,filename);
14607  else
14608  (void) FormatLocaleString(window_name,MaxTextExtent,
14609  "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14610  (double) display_image->scene,(double) GetImageListLength(
14611  display_image));
14612  (void) CloneString(&windows->image.name,window_name);
14613  (void) CloneString(&windows->image.icon_name,filename);
14614  }
14615  if (resource_info->immutable)
14616  windows->image.immutable=MagickTrue;
14617  windows->image.use_pixmap=resource_info->use_pixmap;
14618  windows->image.geometry=resource_info->image_geometry;
14619  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14620  XDisplayWidth(display,visual_info->screen),
14621  XDisplayHeight(display,visual_info->screen));
14622  geometry_info.width=display_image->columns;
14623  geometry_info.height=display_image->rows;
14624  geometry_info.x=0;
14625  geometry_info.y=0;
14626  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14627  &geometry_info.width,&geometry_info.height);
14628  windows->image.width=(unsigned int) geometry_info.width;
14629  windows->image.height=(unsigned int) geometry_info.height;
14630  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14631  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14632  KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14633  PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14634  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14635  resource_info,&windows->backdrop);
14636  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14637  {
14638  /*
14639  Initialize backdrop window.
14640  */
14641  windows->backdrop.x=0;
14642  windows->backdrop.y=0;
14643  (void) CloneString(&windows->backdrop.name,"Backdrop");
14644  windows->backdrop.flags=(size_t) (USSize | USPosition);
14645  windows->backdrop.width=(unsigned int)
14646  XDisplayWidth(display,visual_info->screen);
14647  windows->backdrop.height=(unsigned int)
14648  XDisplayHeight(display,visual_info->screen);
14649  windows->backdrop.border_width=0;
14650  windows->backdrop.immutable=MagickTrue;
14651  windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14652  ButtonReleaseMask;
14653  windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14654  StructureNotifyMask;
14655  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14656  manager_hints->icon_window=windows->icon.id;
14657  manager_hints->input=MagickTrue;
14658  manager_hints->initial_state=resource_info->iconic ? IconicState :
14659  NormalState;
14660  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14661  &windows->backdrop);
14662  if (resource_info->debug != MagickFalse)
14663  (void) LogMagickEvent(X11Event,GetMagickModule(),
14664  "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14665  (void) XMapWindow(display,windows->backdrop.id);
14666  (void) XClearWindow(display,windows->backdrop.id);
14667  if (windows->image.id != (Window) NULL)
14668  {
14669  (void) XDestroyWindow(display,windows->image.id);
14670  windows->image.id=(Window) NULL;
14671  }
14672  /*
14673  Position image in the center the backdrop.
14674  */
14675  windows->image.flags|=USPosition;
14676  windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14677  (windows->image.width/2);
14678  windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14679  (windows->image.height/2);
14680  }
14681  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14682  manager_hints->icon_window=windows->icon.id;
14683  manager_hints->input=MagickTrue;
14684  manager_hints->initial_state=resource_info->iconic ? IconicState :
14685  NormalState;
14686  if (windows->group_leader.id != (Window) NULL)
14687  {
14688  /*
14689  Follow the leader.
14690  */
14691  manager_hints->flags|=WindowGroupHint;
14692  manager_hints->window_group=windows->group_leader.id;
14693  (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14694  if (resource_info->debug != MagickFalse)
14695  (void) LogMagickEvent(X11Event,GetMagickModule(),
14696  "Window id: 0x%lx (group leader)",windows->group_leader.id);
14697  }
14698  XMakeWindow(display,
14699  (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14700  argv,argc,class_hints,manager_hints,&windows->image);
14701  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14702  XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14703  if (windows->group_leader.id != (Window) NULL)
14704  (void) XSetTransientForHint(display,windows->image.id,
14705  windows->group_leader.id);
14706  if (resource_info->debug != MagickFalse)
14707  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14708  windows->image.id);
14709  /*
14710  Initialize Info widget.
14711  */
14712  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14713  &windows->info);
14714  (void) CloneString(&windows->info.name,"Info");
14715  (void) CloneString(&windows->info.icon_name,"Info");
14716  windows->info.border_width=1;
14717  windows->info.x=2;
14718  windows->info.y=2;
14719  windows->info.flags|=PPosition;
14720  windows->info.attributes.win_gravity=UnmapGravity;
14721  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14722  StructureNotifyMask;
14723  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14724  manager_hints->input=MagickFalse;
14725  manager_hints->initial_state=NormalState;
14726  manager_hints->window_group=windows->image.id;
14727  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14728  &windows->info);
14729  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14730  windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14731  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14732  windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14733  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14734  if (windows->image.mapped != MagickFalse)
14735  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14736  if (resource_info->debug != MagickFalse)
14737  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14738  windows->info.id);
14739  /*
14740  Initialize Command widget.
14741  */
14742  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14743  resource_info,&windows->command);
14744  windows->command.data=MagickMenus;
14745  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14746  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14747  resource_info->client_name);
14748  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14749  resource_name,"geometry",(char *) NULL);
14750  (void) CloneString(&windows->command.name,MagickTitle);
14751  windows->command.border_width=0;
14752  windows->command.flags|=PPosition;
14753  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14754  ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14755  OwnerGrabButtonMask | StructureNotifyMask;
14756  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14757  manager_hints->input=MagickTrue;
14758  manager_hints->initial_state=NormalState;
14759  manager_hints->window_group=windows->image.id;
14760  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14761  &windows->command);
14762  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14763  windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14764  HighlightHeight);
14765  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14766  windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14767  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14768  if (windows->command.mapped != MagickFalse)
14769  (void) XMapRaised(display,windows->command.id);
14770  if (resource_info->debug != MagickFalse)
14771  (void) LogMagickEvent(X11Event,GetMagickModule(),
14772  "Window id: 0x%lx (command)",windows->command.id);
14773  /*
14774  Initialize Widget window.
14775  */
14776  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14777  resource_info,&windows->widget);
14778  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14779  resource_info->client_name);
14780  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14781  resource_name,"geometry",(char *) NULL);
14782  windows->widget.border_width=0;
14783  windows->widget.flags|=PPosition;
14784  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14785  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14786  KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14787  StructureNotifyMask;
14788  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14789  manager_hints->input=MagickTrue;
14790  manager_hints->initial_state=NormalState;
14791  manager_hints->window_group=windows->image.id;
14792  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14793  &windows->widget);
14794  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14795  windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14796  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14797  windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14798  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14799  if (resource_info->debug != MagickFalse)
14800  (void) LogMagickEvent(X11Event,GetMagickModule(),
14801  "Window id: 0x%lx (widget)",windows->widget.id);
14802  /*
14803  Initialize popup window.
14804  */
14805  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14806  resource_info,&windows->popup);
14807  windows->popup.border_width=0;
14808  windows->popup.flags|=PPosition;
14809  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14810  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14811  KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14812  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14813  manager_hints->input=MagickTrue;
14814  manager_hints->initial_state=NormalState;
14815  manager_hints->window_group=windows->image.id;
14816  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14817  &windows->popup);
14818  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14819  windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14820  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14821  windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14822  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14823  if (resource_info->debug != MagickFalse)
14824  (void) LogMagickEvent(X11Event,GetMagickModule(),
14825  "Window id: 0x%lx (pop up)",windows->popup.id);
14826  /*
14827  Initialize Magnify window and cursor.
14828  */
14829  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14830  resource_info,&windows->magnify);
14831  if (resource_info->use_shared_memory == MagickFalse)
14832  windows->magnify.shared_memory=MagickFalse;
14833  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14834  resource_info->client_name);
14835  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14836  resource_name,"geometry",(char *) NULL);
14837  (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14838  resource_info->magnify);
14839  if (windows->magnify.cursor != (Cursor) NULL)
14840  (void) XFreeCursor(display,windows->magnify.cursor);
14841  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14842  map_info->colormap,resource_info->background_color,
14843  resource_info->foreground_color);
14844  if (windows->magnify.cursor == (Cursor) NULL)
14845  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14846  display_image->filename);
14847  windows->magnify.width=MagnifySize;
14848  windows->magnify.height=MagnifySize;
14849  windows->magnify.flags|=PPosition;
14850  windows->magnify.min_width=MagnifySize;
14851  windows->magnify.min_height=MagnifySize;
14852  windows->magnify.width_inc=MagnifySize;
14853  windows->magnify.height_inc=MagnifySize;
14854  windows->magnify.data=resource_info->magnify;
14855  windows->magnify.attributes.cursor=windows->magnify.cursor;
14856  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14857  ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14858  StructureNotifyMask;
14859  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14860  manager_hints->input=MagickTrue;
14861  manager_hints->initial_state=NormalState;
14862  manager_hints->window_group=windows->image.id;
14863  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14864  &windows->magnify);
14865  if (resource_info->debug != MagickFalse)
14866  (void) LogMagickEvent(X11Event,GetMagickModule(),
14867  "Window id: 0x%lx (magnify)",windows->magnify.id);
14868  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14869  /*
14870  Initialize panning window.
14871  */
14872  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14873  resource_info,&windows->pan);
14874  (void) CloneString(&windows->pan.name,"Pan Icon");
14875  windows->pan.width=windows->icon.width;
14876  windows->pan.height=windows->icon.height;
14877  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14878  resource_info->client_name);
14879  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14880  resource_name,"geometry",(char *) NULL);
14881  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14882  &windows->pan.width,&windows->pan.height);
14883  windows->pan.flags|=PPosition;
14884  windows->pan.immutable=MagickTrue;
14885  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14886  ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14887  StructureNotifyMask;
14888  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14889  manager_hints->input=MagickFalse;
14890  manager_hints->initial_state=NormalState;
14891  manager_hints->window_group=windows->image.id;
14892  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14893  &windows->pan);
14894  if (resource_info->debug != MagickFalse)
14895  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14896  windows->pan.id);
14897  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14898  if (windows->info.mapped != MagickFalse)
14899  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14900  if ((windows->image.mapped == MagickFalse) ||
14901  (windows->backdrop.id != (Window) NULL))
14902  (void) XMapWindow(display,windows->image.id);
14903  /*
14904  Set our progress monitor and warning handlers.
14905  */
14906  if (warning_handler == (WarningHandler) NULL)
14907  {
14908  warning_handler=resource_info->display_warnings ?
14909  SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14910  warning_handler=resource_info->display_warnings ?
14911  SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14912  }
14913  /*
14914  Initialize Image and Magnify X images.
14915  */
14916  windows->image.x=0;
14917  windows->image.y=0;
14918  windows->magnify.shape=MagickFalse;
14919  width=(unsigned int) display_image->columns;
14920  height=(unsigned int) display_image->rows;
14921  if ((display_image->columns != width) || (display_image->rows != height))
14922  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14923  display_image->filename);
14924  status=XMakeImage(display,resource_info,&windows->image,display_image,
14925  width,height);
14926  if (status == MagickFalse)
14927  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14928  display_image->filename);
14929  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14930  windows->magnify.width,windows->magnify.height);
14931  if (status == MagickFalse)
14932  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14933  display_image->filename);
14934  if (windows->magnify.mapped != MagickFalse)
14935  (void) XMapRaised(display,windows->magnify.id);
14936  if (windows->pan.mapped != MagickFalse)
14937  (void) XMapRaised(display,windows->pan.id);
14938  windows->image.window_changes.width=(int) display_image->columns;
14939  windows->image.window_changes.height=(int) display_image->rows;
14940  (void) XConfigureImage(display,resource_info,windows,display_image);
14941  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14942  (void) XSync(display,MagickFalse);
14943  /*
14944  Respond to events.
14945  */
14946  delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
14947  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
14948  update_time=0;
14949  if (resource_info->update != MagickFalse)
14950  {
14951  MagickBooleanType
14952  status;
14953 
14954  /*
14955  Determine when file data was last modified.
14956  */
14957  status=GetPathAttributes(display_image->filename,&attributes);
14958  if (status != MagickFalse)
14959  update_time=attributes.st_mtime;
14960  }
14961  *state&=(~FormerImageState);
14962  *state&=(~MontageImageState);
14963  *state&=(~NextImageState);
14964  do
14965  {
14966  /*
14967  Handle a window event.
14968  */
14969  if (windows->image.mapped != MagickFalse)
14970  if ((display_image->delay != 0) || (resource_info->update != 0))
14971  {
14972  if (timer < GetMagickTime())
14973  {
14974  if (resource_info->update == MagickFalse)
14975  *state|=NextImageState | ExitState;
14976  else
14977  {
14978  MagickBooleanType
14979  status;
14980 
14981  /*
14982  Determine if image file was modified.
14983  */
14984  status=GetPathAttributes(display_image->filename,&attributes);
14985  if (status != MagickFalse)
14986  if (update_time != attributes.st_mtime)
14987  {
14988  /*
14989  Redisplay image.
14990  */
14991  (void) FormatLocaleString(
14992  resource_info->image_info->filename,MaxTextExtent,
14993  "%s:%s",display_image->magick,
14994  display_image->filename);
14995  nexus=ReadImage(resource_info->image_info,
14996  &display_image->exception);
14997  if (nexus != (Image *) NULL)
14998  *state|=NextImageState | ExitState;
14999  }
15000  delay=display_image->delay/MagickMax(
15001  display_image->ticks_per_second,1L);
15002  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15003  }
15004  }
15005  if (XEventsQueued(display,QueuedAfterFlush) == 0)
15006  {
15007  /*
15008  Do not block if delay > 0.
15009  */
15010  XDelay(display,SuspendTime << 2);
15011  continue;
15012  }
15013  }
15014  timestamp=GetMagickTime();
15015  (void) XNextEvent(display,&event);
15016  if (windows->image.stasis == MagickFalse)
15017  windows->image.stasis=(GetMagickTime()-timestamp) > 0 ?
15018  MagickTrue : MagickFalse;
15019  if (windows->magnify.stasis == MagickFalse)
15020  windows->magnify.stasis=(GetMagickTime()-timestamp) > 0 ?
15021  MagickTrue : MagickFalse;
15022  if (event.xany.window == windows->command.id)
15023  {
15024  /*
15025  Select a command from the Command widget.
15026  */
15027  id=XCommandWidget(display,windows,CommandMenu,&event);
15028  if (id < 0)
15029  continue;
15030  (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15031  display_command=CommandMenus[id];
15032  if (id < MagickMenus)
15033  {
15034  /*
15035  Select a command from a pop-up menu.
15036  */
15037  entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15038  command);
15039  if (entry < 0)
15040  continue;
15041  (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15042  display_command=Commands[id][entry];
15043  }
15044  if (display_command != NullCommand)
15045  nexus=XMagickCommand(display,resource_info,windows,display_command,
15046  &display_image);
15047  continue;
15048  }
15049  switch (event.type)
15050  {
15051  case ButtonPress:
15052  {
15053  if (resource_info->debug != MagickFalse)
15054  (void) LogMagickEvent(X11Event,GetMagickModule(),
15055  "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15056  event.xbutton.button,event.xbutton.x,event.xbutton.y);
15057  if ((event.xbutton.button == Button3) &&
15058  (event.xbutton.state & Mod1Mask))
15059  {
15060  /*
15061  Convert Alt-Button3 to Button2.
15062  */
15063  event.xbutton.button=Button2;
15064  event.xbutton.state&=(~Mod1Mask);
15065  }
15066  if (event.xbutton.window == windows->backdrop.id)
15067  {
15068  (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15069  event.xbutton.time);
15070  break;
15071  }
15072  if (event.xbutton.window == windows->image.id)
15073  {
15074  switch (event.xbutton.button)
15075  {
15076  case Button1:
15077  {
15078  if (resource_info->immutable)
15079  {
15080  /*
15081  Select a command from the Virtual menu.
15082  */
15083  entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15084  command);
15085  if (entry >= 0)
15086  nexus=XMagickCommand(display,resource_info,windows,
15087  VirtualCommands[entry],&display_image);
15088  break;
15089  }
15090  /*
15091  Map/unmap Command widget.
15092  */
15093  if (windows->command.mapped != MagickFalse)
15094  (void) XWithdrawWindow(display,windows->command.id,
15095  windows->command.screen);
15096  else
15097  {
15098  (void) XCommandWidget(display,windows,CommandMenu,
15099  (XEvent *) NULL);
15100  (void) XMapRaised(display,windows->command.id);
15101  }
15102  break;
15103  }
15104  case Button2:
15105  {
15106  /*
15107  User pressed the image magnify button.
15108  */
15109  (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15110  &display_image);
15111  XMagnifyImage(display,windows,&event);
15112  break;
15113  }
15114  case Button3:
15115  {
15116  if (resource_info->immutable)
15117  {
15118  /*
15119  Select a command from the Virtual menu.
15120  */
15121  entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15122  command);
15123  if (entry >= 0)
15124  nexus=XMagickCommand(display,resource_info,windows,
15125  VirtualCommands[entry],&display_image);
15126  break;
15127  }
15128  if (display_image->montage != (char *) NULL)
15129  {
15130  /*
15131  Open or delete a tile from a visual image directory.
15132  */
15133  nexus=XTileImage(display,resource_info,windows,
15134  display_image,&event);
15135  if (nexus != (Image *) NULL)
15136  *state|=MontageImageState | NextImageState | ExitState;
15137  vid_info.x=(short int) windows->image.x;
15138  vid_info.y=(short int) windows->image.y;
15139  break;
15140  }
15141  /*
15142  Select a command from the Short Cuts menu.
15143  */
15144  entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15145  command);
15146  if (entry >= 0)
15147  nexus=XMagickCommand(display,resource_info,windows,
15148  ShortCutsCommands[entry],&display_image);
15149  break;
15150  }
15151  case Button4:
15152  {
15153  /*
15154  Wheel up.
15155  */
15156  XTranslateImage(display,windows,*image,XK_Up);
15157  break;
15158  }
15159  case Button5:
15160  {
15161  /*
15162  Wheel down.
15163  */
15164  XTranslateImage(display,windows,*image,XK_Down);
15165  break;
15166  }
15167  default:
15168  break;
15169  }
15170  break;
15171  }
15172  if (event.xbutton.window == windows->magnify.id)
15173  {
15174  const char
15175  *const MagnifyMenu[] =
15176  {
15177  "2",
15178  "4",
15179  "5",
15180  "6",
15181  "7",
15182  "8",
15183  "9",
15184  "3",
15185  (char *) NULL,
15186  };
15187 
15188  int
15189  factor;
15190 
15191  static KeySym
15192  MagnifyCommands[] =
15193  {
15194  XK_2,
15195  XK_4,
15196  XK_5,
15197  XK_6,
15198  XK_7,
15199  XK_8,
15200  XK_9,
15201  XK_3
15202  };
15203 
15204  /*
15205  Select a magnify factor from the pop-up menu.
15206  */
15207  factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15208  if (factor >= 0)
15209  XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15210  break;
15211  }
15212  if (event.xbutton.window == windows->pan.id)
15213  {
15214  switch (event.xbutton.button)
15215  {
15216  case Button4:
15217  {
15218  /*
15219  Wheel up.
15220  */
15221  XTranslateImage(display,windows,*image,XK_Up);
15222  break;
15223  }
15224  case Button5:
15225  {
15226  /*
15227  Wheel down.
15228  */
15229  XTranslateImage(display,windows,*image,XK_Down);
15230  break;
15231  }
15232  default:
15233  {
15234  XPanImage(display,windows,&event);
15235  break;
15236  }
15237  }
15238  break;
15239  }
15240  delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15241  1L);
15242  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15243  break;
15244  }
15245  case ButtonRelease:
15246  {
15247  if (resource_info->debug != MagickFalse)
15248  (void) LogMagickEvent(X11Event,GetMagickModule(),
15249  "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15250  event.xbutton.button,event.xbutton.x,event.xbutton.y);
15251  break;
15252  }
15253  case ClientMessage:
15254  {
15255  if (resource_info->debug != MagickFalse)
15256  (void) LogMagickEvent(X11Event,GetMagickModule(),
15257  "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15258  event.xclient.message_type,event.xclient.format,(unsigned long)
15259  event.xclient.data.l[0]);
15260  if (event.xclient.message_type == windows->im_protocols)
15261  {
15262  if (*event.xclient.data.l == (long) windows->im_update_widget)
15263  {
15264  (void) CloneString(&windows->command.name,MagickTitle);
15265  windows->command.data=MagickMenus;
15266  (void) XCommandWidget(display,windows,CommandMenu,
15267  (XEvent *) NULL);
15268  break;
15269  }
15270  if (*event.xclient.data.l == (long) windows->im_update_colormap)
15271  {
15272  /*
15273  Update graphic context and window colormap.
15274  */
15275  for (i=0; i < (int) number_windows; i++)
15276  {
15277  if (magick_windows[i]->id == windows->icon.id)
15278  continue;
15279  context_values.background=pixel->background_color.pixel;
15280  context_values.foreground=pixel->foreground_color.pixel;
15281  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15282  context_mask,&context_values);
15283  (void) XChangeGC(display,magick_windows[i]->widget_context,
15284  context_mask,&context_values);
15285  context_values.background=pixel->foreground_color.pixel;
15286  context_values.foreground=pixel->background_color.pixel;
15287  context_values.plane_mask=context_values.background ^
15288  context_values.foreground;
15289  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15290  (size_t) (context_mask | GCPlaneMask),
15291  &context_values);
15292  magick_windows[i]->attributes.background_pixel=
15293  pixel->background_color.pixel;
15294  magick_windows[i]->attributes.border_pixel=
15295  pixel->border_color.pixel;
15296  magick_windows[i]->attributes.colormap=map_info->colormap;
15297  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15298  (unsigned long) magick_windows[i]->mask,
15299  &magick_windows[i]->attributes);
15300  }
15301  if (windows->pan.mapped != MagickFalse)
15302  {
15303  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15304  windows->pan.pixmap);
15305  (void) XClearWindow(display,windows->pan.id);
15306  XDrawPanRectangle(display,windows);
15307  }
15308  if (windows->backdrop.id != (Window) NULL)
15309  (void) XInstallColormap(display,map_info->colormap);
15310  break;
15311  }
15312  if (*event.xclient.data.l == (long) windows->im_former_image)
15313  {
15314  *state|=FormerImageState | ExitState;
15315  break;
15316  }
15317  if (*event.xclient.data.l == (long) windows->im_next_image)
15318  {
15319  *state|=NextImageState | ExitState;
15320  break;
15321  }
15322  if (*event.xclient.data.l == (long) windows->im_retain_colors)
15323  {
15324  *state|=RetainColorsState;
15325  break;
15326  }
15327  if (*event.xclient.data.l == (long) windows->im_exit)
15328  {
15329  *state|=ExitState;
15330  break;
15331  }
15332  break;
15333  }
15334  if (event.xclient.message_type == windows->dnd_protocols)
15335  {
15336  Atom
15337  selection,
15338  type;
15339 
15340  int
15341  format,
15342  status;
15343 
15344  unsigned char
15345  *data;
15346 
15347  unsigned long
15348  after,
15349  length;
15350 
15351  /*
15352  Display image named by the Drag-and-Drop selection.
15353  */
15354  if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15355  break;
15356  selection=XInternAtom(display,"DndSelection",MagickFalse);
15357  status=XGetWindowProperty(display,root_window,selection,0L,(long)
15358  MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15359  &length,&after,&data);
15360  if ((status != Success) || (length == 0))
15361  break;
15362  if (*event.xclient.data.l == 2)
15363  {
15364  /*
15365  Offix DND.
15366  */
15367  (void) CopyMagickString(resource_info->image_info->filename,
15368  (char *) data,MaxTextExtent);
15369  }
15370  else
15371  {
15372  /*
15373  XDND.
15374  */
15375  if (strncmp((char *) data, "file:", 5) != 0)
15376  {
15377  (void) XFree((void *) data);
15378  break;
15379  }
15380  (void) CopyMagickString(resource_info->image_info->filename,
15381  ((char *) data)+5,MaxTextExtent);
15382  }
15383  nexus=ReadImage(resource_info->image_info,
15384  &display_image->exception);
15385  CatchException(&display_image->exception);
15386  if (nexus != (Image *) NULL)
15387  *state|=NextImageState | ExitState;
15388  (void) XFree((void *) data);
15389  break;
15390  }
15391  /*
15392  If client window delete message, exit.
15393  */
15394  if (event.xclient.message_type != windows->wm_protocols)
15395  break;
15396  if (*event.xclient.data.l != (long) windows->wm_delete_window)
15397  break;
15398  (void) XWithdrawWindow(display,event.xclient.window,
15399  visual_info->screen);
15400  if (event.xclient.window == windows->image.id)
15401  {
15402  *state|=ExitState;
15403  break;
15404  }
15405  if (event.xclient.window == windows->pan.id)
15406  {
15407  /*
15408  Restore original image size when pan window is deleted.
15409  */
15410  windows->image.window_changes.width=windows->image.ximage->width;
15411  windows->image.window_changes.height=windows->image.ximage->height;
15412  (void) XConfigureImage(display,resource_info,windows,
15413  display_image);
15414  }
15415  break;
15416  }
15417  case ConfigureNotify:
15418  {
15419  if (resource_info->debug != MagickFalse)
15420  (void) LogMagickEvent(X11Event,GetMagickModule(),
15421  "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15422  event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15423  event.xconfigure.y,event.xconfigure.send_event);
15424  if (event.xconfigure.window == windows->image.id)
15425  {
15426  /*
15427  Image window has a new configuration.
15428  */
15429  if (event.xconfigure.send_event != 0)
15430  {
15431  XWindowChanges
15432  window_changes;
15433 
15434  /*
15435  Position the transient windows relative of the Image window.
15436  */
15437  if (windows->command.geometry == (char *) NULL)
15438  if (windows->command.mapped == MagickFalse)
15439  {
15440  windows->command.x=event.xconfigure.x-
15441  windows->command.width-25;
15442  windows->command.y=event.xconfigure.y;
15443  XConstrainWindowPosition(display,&windows->command);
15444  window_changes.x=windows->command.x;
15445  window_changes.y=windows->command.y;
15446  (void) XReconfigureWMWindow(display,windows->command.id,
15447  windows->command.screen,(unsigned int) (CWX | CWY),
15448  &window_changes);
15449  }
15450  if (windows->widget.geometry == (char *) NULL)
15451  if (windows->widget.mapped == MagickFalse)
15452  {
15453  windows->widget.x=event.xconfigure.x+
15454  event.xconfigure.width/10;
15455  windows->widget.y=event.xconfigure.y+
15456  event.xconfigure.height/10;
15457  XConstrainWindowPosition(display,&windows->widget);
15458  window_changes.x=windows->widget.x;
15459  window_changes.y=windows->widget.y;
15460  (void) XReconfigureWMWindow(display,windows->widget.id,
15461  windows->widget.screen,(unsigned int) (CWX | CWY),
15462  &window_changes);
15463  }
15464  if (windows->magnify.geometry == (char *) NULL)
15465  if (windows->magnify.mapped == MagickFalse)
15466  {
15467  windows->magnify.x=event.xconfigure.x+
15468  event.xconfigure.width+25;
15469  windows->magnify.y=event.xconfigure.y;
15470  XConstrainWindowPosition(display,&windows->magnify);
15471  window_changes.x=windows->magnify.x;
15472  window_changes.y=windows->magnify.y;
15473  (void) XReconfigureWMWindow(display,windows->magnify.id,
15474  windows->magnify.screen,(unsigned int) (CWX | CWY),
15475  &window_changes);
15476  }
15477  if (windows->pan.geometry == (char *) NULL)
15478  if (windows->pan.mapped == MagickFalse)
15479  {
15480  windows->pan.x=event.xconfigure.x+
15481  event.xconfigure.width+25;
15482  windows->pan.y=event.xconfigure.y+
15483  windows->magnify.height+50;
15484  XConstrainWindowPosition(display,&windows->pan);
15485  window_changes.x=windows->pan.x;
15486  window_changes.y=windows->pan.y;
15487  (void) XReconfigureWMWindow(display,windows->pan.id,
15488  windows->pan.screen,(unsigned int) (CWX | CWY),
15489  &window_changes);
15490  }
15491  }
15492  if ((event.xconfigure.width == (int) windows->image.width) &&
15493  (event.xconfigure.height == (int) windows->image.height))
15494  break;
15495  windows->image.width=(unsigned int) event.xconfigure.width;
15496  windows->image.height=(unsigned int) event.xconfigure.height;
15497  windows->image.x=0;
15498  windows->image.y=0;
15499  if (display_image->montage != (char *) NULL)
15500  {
15501  windows->image.x=vid_info.x;
15502  windows->image.y=vid_info.y;
15503  }
15504  if ((windows->image.mapped != MagickFalse) &&
15505  (windows->image.stasis != MagickFalse))
15506  {
15507  /*
15508  Update image window configuration.
15509  */
15510  windows->image.window_changes.width=event.xconfigure.width;
15511  windows->image.window_changes.height=event.xconfigure.height;
15512  (void) XConfigureImage(display,resource_info,windows,
15513  display_image);
15514  }
15515  /*
15516  Update pan window configuration.
15517  */
15518  if ((event.xconfigure.width < windows->image.ximage->width) ||
15519  (event.xconfigure.height < windows->image.ximage->height))
15520  {
15521  (void) XMapRaised(display,windows->pan.id);
15522  XDrawPanRectangle(display,windows);
15523  }
15524  else
15525  if (windows->pan.mapped != MagickFalse)
15526  (void) XWithdrawWindow(display,windows->pan.id,
15527  windows->pan.screen);
15528  break;
15529  }
15530  if (event.xconfigure.window == windows->magnify.id)
15531  {
15532  unsigned int
15533  magnify;
15534 
15535  /*
15536  Magnify window has a new configuration.
15537  */
15538  windows->magnify.width=(unsigned int) event.xconfigure.width;
15539  windows->magnify.height=(unsigned int) event.xconfigure.height;
15540  if (windows->magnify.mapped == MagickFalse)
15541  break;
15542  magnify=1;
15543  while ((int) magnify <= event.xconfigure.width)
15544  magnify<<=1;
15545  while ((int) magnify <= event.xconfigure.height)
15546  magnify<<=1;
15547  magnify>>=1;
15548  if (((int) magnify != event.xconfigure.width) ||
15549  ((int) magnify != event.xconfigure.height))
15550  {
15551  window_changes.width=(int) magnify;
15552  window_changes.height=(int) magnify;
15553  (void) XReconfigureWMWindow(display,windows->magnify.id,
15554  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15555  &window_changes);
15556  break;
15557  }
15558  if ((windows->magnify.mapped != MagickFalse) &&
15559  (windows->magnify.stasis != MagickFalse))
15560  {
15561  status=XMakeImage(display,resource_info,&windows->magnify,
15562  display_image,windows->magnify.width,windows->magnify.height);
15563  XMakeMagnifyImage(display,windows);
15564  }
15565  break;
15566  }
15567  if ((windows->magnify.mapped != MagickFalse) &&
15568  (event.xconfigure.window == windows->pan.id))
15569  {
15570  /*
15571  Pan icon window has a new configuration.
15572  */
15573  if (event.xconfigure.send_event != 0)
15574  {
15575  windows->pan.x=event.xconfigure.x;
15576  windows->pan.y=event.xconfigure.y;
15577  }
15578  windows->pan.width=(unsigned int) event.xconfigure.width;
15579  windows->pan.height=(unsigned int) event.xconfigure.height;
15580  break;
15581  }
15582  if (event.xconfigure.window == windows->icon.id)
15583  {
15584  /*
15585  Icon window has a new configuration.
15586  */
15587  windows->icon.width=(unsigned int) event.xconfigure.width;
15588  windows->icon.height=(unsigned int) event.xconfigure.height;
15589  break;
15590  }
15591  break;
15592  }
15593  case DestroyNotify:
15594  {
15595  /*
15596  Group leader has exited.
15597  */
15598  if (resource_info->debug != MagickFalse)
15599  (void) LogMagickEvent(X11Event,GetMagickModule(),
15600  "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15601  if (event.xdestroywindow.window == windows->group_leader.id)
15602  {
15603  *state|=ExitState;
15604  break;
15605  }
15606  break;
15607  }
15608  case EnterNotify:
15609  {
15610  /*
15611  Selectively install colormap.
15612  */
15613  if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15614  if (event.xcrossing.mode != NotifyUngrab)
15615  XInstallColormap(display,map_info->colormap);
15616  break;
15617  }
15618  case Expose:
15619  {
15620  if (resource_info->debug != MagickFalse)
15621  (void) LogMagickEvent(X11Event,GetMagickModule(),
15622  "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15623  event.xexpose.width,event.xexpose.height,event.xexpose.x,
15624  event.xexpose.y);
15625  /*
15626  Refresh windows that are now exposed.
15627  */
15628  if ((event.xexpose.window == windows->image.id) &&
15629  (windows->image.mapped != MagickFalse))
15630  {
15631  XRefreshWindow(display,&windows->image,&event);
15632  delay=display_image->delay/MagickMax(
15633  display_image->ticks_per_second,1L);
15634  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15635  break;
15636  }
15637  if ((event.xexpose.window == windows->magnify.id) &&
15638  (windows->magnify.mapped != MagickFalse))
15639  {
15640  XMakeMagnifyImage(display,windows);
15641  break;
15642  }
15643  if (event.xexpose.window == windows->pan.id)
15644  {
15645  XDrawPanRectangle(display,windows);
15646  break;
15647  }
15648  if (event.xexpose.window == windows->icon.id)
15649  {
15650  XRefreshWindow(display,&windows->icon,&event);
15651  break;
15652  }
15653  break;
15654  }
15655  case KeyPress:
15656  {
15657  int
15658  length;
15659 
15660  /*
15661  Respond to a user key press.
15662  */
15663  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15664  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15665  *(command+length)='\0';
15666  if (resource_info->debug != MagickFalse)
15667  (void) LogMagickEvent(X11Event,GetMagickModule(),
15668  "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15669  key_symbol,command);
15670  if (event.xkey.window == windows->image.id)
15671  {
15672  display_command=XImageWindowCommand(display,resource_info,windows,
15673  event.xkey.state,key_symbol,&display_image);
15674  if (display_command != NullCommand)
15675  nexus=XMagickCommand(display,resource_info,windows,display_command,
15676  &display_image);
15677  }
15678  if (event.xkey.window == windows->magnify.id)
15679  XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15680  if (event.xkey.window == windows->pan.id)
15681  {
15682  if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15683  (void) XWithdrawWindow(display,windows->pan.id,
15684  windows->pan.screen);
15685  else
15686  if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15687  XTextViewHelp(display,resource_info,windows,MagickFalse,
15688  "Help Viewer - Image Pan",ImagePanHelp);
15689  else
15690  XTranslateImage(display,windows,*image,key_symbol);
15691  }
15692  delay=display_image->delay/MagickMax(
15693  display_image->ticks_per_second,1L);
15694  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15695  break;
15696  }
15697  case KeyRelease:
15698  {
15699  /*
15700  Respond to a user key release.
15701  */
15702  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15703  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15704  if (resource_info->debug != MagickFalse)
15705  (void) LogMagickEvent(X11Event,GetMagickModule(),
15706  "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15707  break;
15708  }
15709  case LeaveNotify:
15710  {
15711  /*
15712  Selectively uninstall colormap.
15713  */
15714  if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15715  if (event.xcrossing.mode != NotifyUngrab)
15716  XUninstallColormap(display,map_info->colormap);
15717  break;
15718  }
15719  case MapNotify:
15720  {
15721  if (resource_info->debug != MagickFalse)
15722  (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15723  event.xmap.window);
15724  if (event.xmap.window == windows->backdrop.id)
15725  {
15726  (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15727  CurrentTime);
15728  windows->backdrop.mapped=MagickTrue;
15729  break;
15730  }
15731  if (event.xmap.window == windows->image.id)
15732  {
15733  if (windows->backdrop.id != (Window) NULL)
15734  (void) XInstallColormap(display,map_info->colormap);
15735  if (LocaleCompare(display_image->magick,"LOGO") == 0)
15736  {
15737  if (LocaleCompare(display_image->filename,"LOGO") == 0)
15738  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15739  }
15740  if (((int) windows->image.width < windows->image.ximage->width) ||
15741  ((int) windows->image.height < windows->image.ximage->height))
15742  (void) XMapRaised(display,windows->pan.id);
15743  windows->image.mapped=MagickTrue;
15744  break;
15745  }
15746  if (event.xmap.window == windows->magnify.id)
15747  {
15748  XMakeMagnifyImage(display,windows);
15749  windows->magnify.mapped=MagickTrue;
15750  (void) XWithdrawWindow(display,windows->info.id,
15751  windows->info.screen);
15752  break;
15753  }
15754  if (event.xmap.window == windows->pan.id)
15755  {
15756  XMakePanImage(display,resource_info,windows,display_image);
15757  windows->pan.mapped=MagickTrue;
15758  break;
15759  }
15760  if (event.xmap.window == windows->info.id)
15761  {
15762  windows->info.mapped=MagickTrue;
15763  break;
15764  }
15765  if (event.xmap.window == windows->icon.id)
15766  {
15767  MagickBooleanType
15768  taint;
15769 
15770  /*
15771  Create an icon image.
15772  */
15773  taint=display_image->taint;
15774  XMakeStandardColormap(display,icon_visual,icon_resources,
15775  display_image,icon_map,icon_pixel);
15776  (void) XMakeImage(display,icon_resources,&windows->icon,
15777  display_image,windows->icon.width,windows->icon.height);
15778  display_image->taint=taint;
15779  (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15780  windows->icon.pixmap);
15781  (void) XClearWindow(display,windows->icon.id);
15782  (void) XWithdrawWindow(display,windows->info.id,
15783  windows->info.screen);
15784  windows->icon.mapped=MagickTrue;
15785  break;
15786  }
15787  if (event.xmap.window == windows->command.id)
15788  {
15789  windows->command.mapped=MagickTrue;
15790  break;
15791  }
15792  if (event.xmap.window == windows->popup.id)
15793  {
15794  windows->popup.mapped=MagickTrue;
15795  break;
15796  }
15797  if (event.xmap.window == windows->widget.id)
15798  {
15799  windows->widget.mapped=MagickTrue;
15800  break;
15801  }
15802  break;
15803  }
15804  case MappingNotify:
15805  {
15806  (void) XRefreshKeyboardMapping(&event.xmapping);
15807  break;
15808  }
15809  case NoExpose:
15810  break;
15811  case PropertyNotify:
15812  {
15813  Atom
15814  type;
15815 
15816  int
15817  format,
15818  status;
15819 
15820  unsigned char
15821  *data;
15822 
15823  unsigned long
15824  after,
15825  length;
15826 
15827  if (resource_info->debug != MagickFalse)
15828  (void) LogMagickEvent(X11Event,GetMagickModule(),
15829  "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15830  event.xproperty.atom,event.xproperty.state);
15831  if (event.xproperty.atom != windows->im_remote_command)
15832  break;
15833  /*
15834  Display image named by the remote command protocol.
15835  */
15836  status=XGetWindowProperty(display,event.xproperty.window,
15837  event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15838  AnyPropertyType,&type,&format,&length,&after,&data);
15839  if ((status != Success) || (length == 0))
15840  break;
15841  if (LocaleCompare((char *) data,"-quit") == 0)
15842  {
15843  XClientMessage(display,windows->image.id,windows->im_protocols,
15844  windows->im_exit,CurrentTime);
15845  (void) XFree((void *) data);
15846  break;
15847  }
15848  (void) CopyMagickString(resource_info->image_info->filename,
15849  (char *) data,MaxTextExtent);
15850  (void) XFree((void *) data);
15851  nexus=ReadImage(resource_info->image_info,&display_image->exception);
15852  CatchException(&display_image->exception);
15853  if (nexus != (Image *) NULL)
15854  *state|=NextImageState | ExitState;
15855  break;
15856  }
15857  case ReparentNotify:
15858  {
15859  if (resource_info->debug != MagickFalse)
15860  (void) LogMagickEvent(X11Event,GetMagickModule(),
15861  "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15862  event.xreparent.window);
15863  break;
15864  }
15865  case UnmapNotify:
15866  {
15867  if (resource_info->debug != MagickFalse)
15868  (void) LogMagickEvent(X11Event,GetMagickModule(),
15869  "Unmap Notify: 0x%lx",event.xunmap.window);
15870  if (event.xunmap.window == windows->backdrop.id)
15871  {
15872  windows->backdrop.mapped=MagickFalse;
15873  break;
15874  }
15875  if (event.xunmap.window == windows->image.id)
15876  {
15877  windows->image.mapped=MagickFalse;
15878  break;
15879  }
15880  if (event.xunmap.window == windows->magnify.id)
15881  {
15882  windows->magnify.mapped=MagickFalse;
15883  break;
15884  }
15885  if (event.xunmap.window == windows->pan.id)
15886  {
15887  windows->pan.mapped=MagickFalse;
15888  break;
15889  }
15890  if (event.xunmap.window == windows->info.id)
15891  {
15892  windows->info.mapped=MagickFalse;
15893  break;
15894  }
15895  if (event.xunmap.window == windows->icon.id)
15896  {
15897  if (map_info->colormap == icon_map->colormap)
15898  XConfigureImageColormap(display,resource_info,windows,
15899  display_image);
15900  (void) XFreeStandardColormap(display,icon_visual,icon_map,
15901  icon_pixel);
15902  windows->icon.mapped=MagickFalse;
15903  break;
15904  }
15905  if (event.xunmap.window == windows->command.id)
15906  {
15907  windows->command.mapped=MagickFalse;
15908  break;
15909  }
15910  if (event.xunmap.window == windows->popup.id)
15911  {
15912  if (windows->backdrop.id != (Window) NULL)
15913  (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15914  CurrentTime);
15915  windows->popup.mapped=MagickFalse;
15916  break;
15917  }
15918  if (event.xunmap.window == windows->widget.id)
15919  {
15920  if (windows->backdrop.id != (Window) NULL)
15921  (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15922  CurrentTime);
15923  windows->widget.mapped=MagickFalse;
15924  break;
15925  }
15926  break;
15927  }
15928  default:
15929  {
15930  if (resource_info->debug != MagickFalse)
15931  (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
15932  event.type);
15933  break;
15934  }
15935  }
15936  } while (!(*state & ExitState));
15937  if ((*state & ExitState) == 0)
15938  (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
15939  &display_image);
15940  else
15941  if (resource_info->confirm_edit != MagickFalse)
15942  {
15943  /*
15944  Query user if image has changed.
15945  */
15946  if ((resource_info->immutable == MagickFalse) &&
15947  (display_image->taint != MagickFalse))
15948  {
15949  int
15950  status;
15951 
15952  status=XConfirmWidget(display,windows,"Your image changed.",
15953  "Do you want to save it");
15954  if (status == 0)
15955  *state&=(~ExitState);
15956  else
15957  if (status > 0)
15958  (void) XMagickCommand(display,resource_info,windows,SaveCommand,
15959  &display_image);
15960  }
15961  }
15962  if ((windows->visual_info->klass == GrayScale) ||
15963  (windows->visual_info->klass == PseudoColor) ||
15964  (windows->visual_info->klass == DirectColor))
15965  {
15966  /*
15967  Withdraw pan and Magnify window.
15968  */
15969  if (windows->info.mapped != MagickFalse)
15970  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15971  if (windows->magnify.mapped != MagickFalse)
15972  (void) XWithdrawWindow(display,windows->magnify.id,
15973  windows->magnify.screen);
15974  if (windows->command.mapped != MagickFalse)
15975  (void) XWithdrawWindow(display,windows->command.id,
15976  windows->command.screen);
15977  }
15978  if (windows->pan.mapped != MagickFalse)
15979  (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
15980  if (resource_info->backdrop == MagickFalse)
15981  if (windows->backdrop.mapped)
15982  {
15983  (void) XWithdrawWindow(display,windows->backdrop.id,
15984  windows->backdrop.screen);
15985  (void) XDestroyWindow(display,windows->backdrop.id);
15986  windows->backdrop.id=(Window) NULL;
15987  (void) XWithdrawWindow(display,windows->image.id,
15988  windows->image.screen);
15989  (void) XDestroyWindow(display,windows->image.id);
15990  windows->image.id=(Window) NULL;
15991  }
15992  XSetCursorState(display,windows,MagickTrue);
15993  XCheckRefreshWindows(display,windows);
15994  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
15995  *state&=(~ExitState);
15996  if (*state & ExitState)
15997  {
15998  /*
15999  Free Standard Colormap.
16000  */
16001  (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16002  if (resource_info->map_type == (char *) NULL)
16003  (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16004  /*
16005  Free X resources.
16006  */
16007  if (resource_info->copy_image != (Image *) NULL)
16008  resource_info->copy_image=DestroyImage(resource_info->copy_image);
16009  DestroyXResources();
16010  }
16011  (void) XSync(display,MagickFalse);
16012  /*
16013  Restore our progress monitor and warning handlers.
16014  */
16015  (void) SetErrorHandler(warning_handler);
16016  (void) SetWarningHandler(warning_handler);
16017  /*
16018  Change to home directory.
16019  */
16020  directory=getcwd(working_directory,MaxTextExtent);
16021  (void) directory;
16022  {
16023  int
16024  status;
16025 
16026  if (*resource_info->home_directory == '\0')
16027  (void) CopyMagickString(resource_info->home_directory,".",MaxTextExtent);
16028  status=chdir(resource_info->home_directory);
16029  if (status == -1)
16030  (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
16031  FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
16032  }
16033  *image=display_image;
16034  return(nexus);
16035 }
16036 #else
16037 ␌
16038 /*
16039 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16040 % %
16041 % %
16042 % %
16043 + D i s p l a y I m a g e s %
16044 % %
16045 % %
16046 % %
16047 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16048 %
16049 % DisplayImages() displays an image sequence to any X window screen. It
16050 % returns a value other than 0 if successful. Check the exception member
16051 % of image to determine the reason for any failure.
16052 %
16053 % The format of the DisplayImages method is:
16054 %
16055 % MagickBooleanType DisplayImages(const ImageInfo *image_info,
16056 % Image *images)
16057 %
16058 % A description of each parameter follows:
16059 %
16060 % o image_info: the image info.
16061 %
16062 % o image: the image.
16063 %
16064 */
16065 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16066  Image *image)
16067 {
16068  assert(image_info != (const ImageInfo *) NULL);
16069  assert(image_info->signature == MagickCoreSignature);
16070  assert(image != (Image *) NULL);
16071  assert(image->signature == MagickCoreSignature);
16072  if (IsEventLogging() != MagickFalse)
16073  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16074  (void) image_info;
16075  (void) ThrowMagickException(&image->exception,GetMagickModule(),
16076  MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
16077  image->filename);
16078  return(MagickFalse);
16079 }
16080 ␌
16081 /*
16082 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16083 % %
16084 % %
16085 % %
16086 + R e m o t e D i s p l a y C o m m a n d %
16087 % %
16088 % %
16089 % %
16090 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16091 %
16092 % RemoteDisplayCommand() encourages a remote display program to display the
16093 % specified image filename.
16094 %
16095 % The format of the RemoteDisplayCommand method is:
16096 %
16097 % MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16098 % const char *window,const char *filename,ExceptionInfo *exception)
16099 %
16100 % A description of each parameter follows:
16101 %
16102 % o image_info: the image info.
16103 %
16104 % o window: Specifies the name or id of an X window.
16105 %
16106 % o filename: the name of the image filename to display.
16107 %
16108 % o exception: return any errors or warnings in this structure.
16109 %
16110 */
16111 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16112  const char *window,const char *filename,ExceptionInfo *exception)
16113 {
16114  assert(image_info != (const ImageInfo *) NULL);
16115  assert(image_info->signature == MagickCoreSignature);
16116  assert(filename != (char *) NULL);
16117  if (IsEventLogging() != MagickFalse)
16118  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16119  (void) window;
16120  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16121  "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16122  return(MagickFalse);
16123 }
16124 #endif
Definition: image.h:134