MagickCore  6.9.13-22
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+=(ptrdiff_t) strlen(annotate_info->text);
2404  break;
2405  }
2406  }
2407  p--;
2408  x-=XTextWidth(font_info,p,1);
2409  text_event.xexpose.x=x;
2410  text_event.xexpose.y=y-font_info->max_bounds.ascent;
2411  XRefreshWindow(display,&windows->image,&text_event);
2412  break;
2413  }
2414  case XK_bracketleft:
2415  {
2416  key_symbol=XK_Escape;
2417  break;
2418  }
2419  case DeleteCommand:
2420  {
2421  /*
2422  Erase the entire line of text.
2423  */
2424  while (p != annotate_info->text)
2425  {
2426  p--;
2427  x-=XTextWidth(font_info,p,1);
2428  text_event.xexpose.x=x;
2429  XRefreshWindow(display,&windows->image,&text_event);
2430  }
2431  break;
2432  }
2433  case XK_Escape:
2434  case XK_F20:
2435  {
2436  /*
2437  Finished annotating.
2438  */
2439  annotate_info->width=(unsigned int) XTextWidth(font_info,
2440  annotate_info->text,(int) strlen(annotate_info->text));
2441  XRefreshWindow(display,&windows->image,&text_event);
2442  state|=ExitState;
2443  break;
2444  }
2445  default:
2446  {
2447  /*
2448  Draw a single character on the Image window.
2449  */
2450  if ((state & ModifierState) != 0)
2451  break;
2452  if (*command == '\0')
2453  break;
2454  *p=(*command);
2455  if (annotate_info->stencil == ForegroundStencil)
2456  (void) XDrawString(display,windows->image.id,annotate_context,
2457  x,y,p,1);
2458  else
2459  (void) XDrawImageString(display,windows->image.id,
2460  annotate_context,x,y,p,1);
2461  x+=XTextWidth(font_info,p,1);
2462  p++;
2463  if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2464  break;
2465  magick_fallthrough;
2466  }
2467  case XK_Return:
2468  case XK_KP_Enter:
2469  {
2470  /*
2471  Advance to the next line of text.
2472  */
2473  *p='\0';
2474  annotate_info->width=(unsigned int) XTextWidth(font_info,
2475  annotate_info->text,(int) strlen(annotate_info->text));
2476  if (annotate_info->next != (XAnnotateInfo *) NULL)
2477  {
2478  /*
2479  Line of text already exists.
2480  */
2481  annotate_info=annotate_info->next;
2482  x=annotate_info->x;
2483  y=annotate_info->y;
2484  p=annotate_info->text;
2485  break;
2486  }
2487  annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2488  sizeof(*annotate_info->next));
2489  if (annotate_info->next == (XAnnotateInfo *) NULL)
2490  return(MagickFalse);
2491  *annotate_info->next=(*annotate_info);
2492  annotate_info->next->previous=annotate_info;
2493  annotate_info=annotate_info->next;
2494  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2495  windows->image.width/MagickMax((ssize_t)
2496  font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2497  if (annotate_info->text == (char *) NULL)
2498  return(MagickFalse);
2499  annotate_info->y+=annotate_info->height;
2500  if (annotate_info->y > (int) windows->image.height)
2501  annotate_info->y=(int) annotate_info->height;
2502  annotate_info->next=(XAnnotateInfo *) NULL;
2503  x=annotate_info->x;
2504  y=annotate_info->y;
2505  p=annotate_info->text;
2506  break;
2507  }
2508  }
2509  break;
2510  }
2511  case KeyRelease:
2512  {
2513  /*
2514  Respond to a user key release.
2515  */
2516  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2517  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2518  state&=(~ModifierState);
2519  break;
2520  }
2521  case SelectionNotify:
2522  {
2523  Atom
2524  type;
2525 
2526  int
2527  format;
2528 
2529  unsigned char
2530  *data;
2531 
2532  unsigned long
2533  after,
2534  length;
2535 
2536  /*
2537  Obtain response from primary selection.
2538  */
2539  if (event.xselection.property == (Atom) None)
2540  break;
2541  status=XGetWindowProperty(display,event.xselection.requestor,
2542  event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2543  &type,&format,&length,&after,&data);
2544  if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2545  (length == 0))
2546  break;
2547  /*
2548  Annotate Image window with primary selection.
2549  */
2550  for (i=0; i < (ssize_t) length; i++)
2551  {
2552  if ((char) data[i] != '\n')
2553  {
2554  /*
2555  Draw a single character on the Image window.
2556  */
2557  *p=(char) data[i];
2558  (void) XDrawString(display,windows->image.id,annotate_context,
2559  x,y,p,1);
2560  x+=XTextWidth(font_info,p,1);
2561  p++;
2562  if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2563  continue;
2564  }
2565  /*
2566  Advance to the next line of text.
2567  */
2568  *p='\0';
2569  annotate_info->width=(unsigned int) XTextWidth(font_info,
2570  annotate_info->text,(int) strlen(annotate_info->text));
2571  if (annotate_info->next != (XAnnotateInfo *) NULL)
2572  {
2573  /*
2574  Line of text already exists.
2575  */
2576  annotate_info=annotate_info->next;
2577  x=annotate_info->x;
2578  y=annotate_info->y;
2579  p=annotate_info->text;
2580  continue;
2581  }
2582  annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2583  sizeof(*annotate_info->next));
2584  if (annotate_info->next == (XAnnotateInfo *) NULL)
2585  return(MagickFalse);
2586  *annotate_info->next=(*annotate_info);
2587  annotate_info->next->previous=annotate_info;
2588  annotate_info=annotate_info->next;
2589  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2590  windows->image.width/MagickMax((ssize_t)
2591  font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2592  if (annotate_info->text == (char *) NULL)
2593  return(MagickFalse);
2594  annotate_info->y+=annotate_info->height;
2595  if (annotate_info->y > (int) windows->image.height)
2596  annotate_info->y=(int) annotate_info->height;
2597  annotate_info->next=(XAnnotateInfo *) NULL;
2598  x=annotate_info->x;
2599  y=annotate_info->y;
2600  p=annotate_info->text;
2601  }
2602  (void) XFree((void *) data);
2603  break;
2604  }
2605  default:
2606  break;
2607  }
2608  } while ((state & ExitState) == 0);
2609  (void) XFreeCursor(display,cursor);
2610  /*
2611  Annotation is relative to image configuration.
2612  */
2613  width=(unsigned int) image->columns;
2614  height=(unsigned int) image->rows;
2615  x=0;
2616  y=0;
2617  if (windows->image.crop_geometry != (char *) NULL)
2618  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2619  /*
2620  Initialize annotated image.
2621  */
2622  XSetCursorState(display,windows,MagickTrue);
2623  XCheckRefreshWindows(display,windows);
2624  while (annotate_info != (XAnnotateInfo *) NULL)
2625  {
2626  if (annotate_info->width == 0)
2627  {
2628  /*
2629  No text on this line-- go to the next line of text.
2630  */
2631  previous_info=annotate_info->previous;
2632  annotate_info->text=(char *)
2633  RelinquishMagickMemory(annotate_info->text);
2634  annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2635  annotate_info=previous_info;
2636  continue;
2637  }
2638  /*
2639  Determine pixel index for box and pen color.
2640  */
2641  windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2642  if (windows->pixel_info->colors != 0)
2643  for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2644  if (windows->pixel_info->pixels[i] ==
2645  windows->pixel_info->pen_colors[box_id].pixel)
2646  {
2647  windows->pixel_info->box_index=(unsigned short) i;
2648  break;
2649  }
2650  windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2651  if (windows->pixel_info->colors != 0)
2652  for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2653  if (windows->pixel_info->pixels[i] ==
2654  windows->pixel_info->pen_colors[pen_id].pixel)
2655  {
2656  windows->pixel_info->pen_index=(unsigned short) i;
2657  break;
2658  }
2659  /*
2660  Define the annotate geometry string.
2661  */
2662  annotate_info->x=(int)
2663  width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2664  annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2665  windows->image.y)/windows->image.ximage->height;
2666  (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2667  "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2668  height*annotate_info->height/windows->image.ximage->height,
2669  annotate_info->x+x,annotate_info->y+y);
2670  /*
2671  Annotate image with text.
2672  */
2673  status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
2674  if (status == 0)
2675  return(MagickFalse);
2676  /*
2677  Free up memory.
2678  */
2679  previous_info=annotate_info->previous;
2680  annotate_info->text=DestroyString(annotate_info->text);
2681  annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2682  annotate_info=previous_info;
2683  }
2684  (void) XSetForeground(display,annotate_context,
2685  windows->pixel_info->foreground_color.pixel);
2686  (void) XSetBackground(display,annotate_context,
2687  windows->pixel_info->background_color.pixel);
2688  (void) XSetFont(display,annotate_context,windows->font_info->fid);
2689  XSetCursorState(display,windows,MagickFalse);
2690  (void) XFreeFont(display,font_info);
2691  /*
2692  Update image configuration.
2693  */
2694  XConfigureImageColormap(display,resource_info,windows,image);
2695  (void) XConfigureImage(display,resource_info,windows,image);
2696  return(MagickTrue);
2697 }
2698 ␌
2699 /*
2700 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2701 % %
2702 % %
2703 % %
2704 + X B a c k g r o u n d I m a g e %
2705 % %
2706 % %
2707 % %
2708 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2709 %
2710 % XBackgroundImage() displays the image in the background of a window.
2711 %
2712 % The format of the XBackgroundImage method is:
2713 %
2714 % MagickBooleanType XBackgroundImage(Display *display,
2715 % XResourceInfo *resource_info,XWindows *windows,Image **image)
2716 %
2717 % A description of each parameter follows:
2718 %
2719 % o display: Specifies a connection to an X server; returned from
2720 % XOpenDisplay.
2721 %
2722 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2723 %
2724 % o windows: Specifies a pointer to a XWindows structure.
2725 %
2726 % o image: the image.
2727 %
2728 */
2729 static MagickBooleanType XBackgroundImage(Display *display,
2730  XResourceInfo *resource_info,XWindows *windows,Image **image)
2731 {
2732 #define BackgroundImageTag "Background/Image"
2733 
2734  int
2735  status;
2736 
2737  static char
2738  window_id[MaxTextExtent] = "root";
2739 
2740  XResourceInfo
2741  background_resources;
2742 
2743  /*
2744  Put image in background.
2745  */
2746  status=XDialogWidget(display,windows,"Background",
2747  "Enter window id (id 0x00 selects window with pointer):",window_id);
2748  if (*window_id == '\0')
2749  return(MagickFalse);
2750  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
2751  XInfoWidget(display,windows,BackgroundImageTag);
2752  XSetCursorState(display,windows,MagickTrue);
2753  XCheckRefreshWindows(display,windows);
2754  background_resources=(*resource_info);
2755  background_resources.window_id=window_id;
2756  background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2757  status=XDisplayBackgroundImage(display,&background_resources,*image);
2758  if (status != MagickFalse)
2759  XClientMessage(display,windows->image.id,windows->im_protocols,
2760  windows->im_retain_colors,CurrentTime);
2761  XSetCursorState(display,windows,MagickFalse);
2762  (void) XMagickCommand(display,resource_info,windows,UndoCommand,image);
2763  return(MagickTrue);
2764 }
2765 ␌
2766 /*
2767 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2768 % %
2769 % %
2770 % %
2771 + X C h o p I m a g e %
2772 % %
2773 % %
2774 % %
2775 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2776 %
2777 % XChopImage() chops the X image.
2778 %
2779 % The format of the XChopImage method is:
2780 %
2781 % MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2782 % XWindows *windows,Image **image)
2783 %
2784 % A description of each parameter follows:
2785 %
2786 % o display: Specifies a connection to an X server; returned from
2787 % XOpenDisplay.
2788 %
2789 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2790 %
2791 % o windows: Specifies a pointer to a XWindows structure.
2792 %
2793 % o image: the image.
2794 %
2795 */
2796 static MagickBooleanType XChopImage(Display *display,
2797  XResourceInfo *resource_info,XWindows *windows,Image **image)
2798 {
2799  const char
2800  *const ChopMenu[] =
2801  {
2802  "Direction",
2803  "Help",
2804  "Dismiss",
2805  (char *) NULL
2806  };
2807 
2808  static ModeType
2809  direction = HorizontalChopCommand;
2810 
2811  static const ModeType
2812  ChopCommands[] =
2813  {
2814  ChopDirectionCommand,
2815  ChopHelpCommand,
2816  ChopDismissCommand
2817  },
2818  DirectionCommands[] =
2819  {
2820  HorizontalChopCommand,
2821  VerticalChopCommand
2822  };
2823 
2824  char
2825  text[MaxTextExtent];
2826 
2827  Image
2828  *chop_image;
2829 
2830  int
2831  id,
2832  x,
2833  y;
2834 
2835  MagickRealType
2836  scale_factor;
2837 
2839  chop_info;
2840 
2841  unsigned int
2842  distance,
2843  height,
2844  width;
2845 
2846  size_t
2847  state;
2848 
2849  XEvent
2850  event;
2851 
2852  XSegment
2853  segment_info;
2854 
2855  /*
2856  Map Command widget.
2857  */
2858  (void) CloneString(&windows->command.name,"Chop");
2859  windows->command.data=1;
2860  (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2861  (void) XMapRaised(display,windows->command.id);
2862  XClientMessage(display,windows->image.id,windows->im_protocols,
2863  windows->im_update_widget,CurrentTime);
2864  /*
2865  Track pointer until button 1 is pressed.
2866  */
2867  XQueryPosition(display,windows->image.id,&x,&y);
2868  (void) XSelectInput(display,windows->image.id,
2869  windows->image.attributes.event_mask | PointerMotionMask);
2870  state=DefaultState;
2871  (void) memset(&segment_info,0,sizeof(segment_info));
2872  do
2873  {
2874  if (windows->info.mapped != MagickFalse)
2875  {
2876  /*
2877  Display pointer position.
2878  */
2879  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2880  x+windows->image.x,y+windows->image.y);
2881  XInfoWidget(display,windows,text);
2882  }
2883  /*
2884  Wait for next event.
2885  */
2886  XScreenEvent(display,windows,&event);
2887  if (event.xany.window == windows->command.id)
2888  {
2889  /*
2890  Select a command from the Command widget.
2891  */
2892  id=XCommandWidget(display,windows,ChopMenu,&event);
2893  if (id < 0)
2894  continue;
2895  switch (ChopCommands[id])
2896  {
2897  case ChopDirectionCommand:
2898  {
2899  char
2900  command[MaxTextExtent];
2901 
2902  const char
2903  *const Directions[] =
2904  {
2905  "horizontal",
2906  "vertical",
2907  (char *) NULL,
2908  };
2909 
2910  /*
2911  Select a command from the pop-up menu.
2912  */
2913  id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2914  if (id >= 0)
2915  direction=DirectionCommands[id];
2916  break;
2917  }
2918  case ChopHelpCommand:
2919  {
2920  XTextViewHelp(display,resource_info,windows,MagickFalse,
2921  "Help Viewer - Image Chop",ImageChopHelp);
2922  break;
2923  }
2924  case ChopDismissCommand:
2925  {
2926  /*
2927  Prematurely exit.
2928  */
2929  state|=EscapeState;
2930  state|=ExitState;
2931  break;
2932  }
2933  default:
2934  break;
2935  }
2936  continue;
2937  }
2938  switch (event.type)
2939  {
2940  case ButtonPress:
2941  {
2942  if (event.xbutton.button != Button1)
2943  break;
2944  if (event.xbutton.window != windows->image.id)
2945  break;
2946  /*
2947  User has committed to start point of chopping line.
2948  */
2949  segment_info.x1=(short int) event.xbutton.x;
2950  segment_info.x2=(short int) event.xbutton.x;
2951  segment_info.y1=(short int) event.xbutton.y;
2952  segment_info.y2=(short int) event.xbutton.y;
2953  state|=ExitState;
2954  break;
2955  }
2956  case ButtonRelease:
2957  break;
2958  case Expose:
2959  break;
2960  case KeyPress:
2961  {
2962  char
2963  command[MaxTextExtent];
2964 
2965  KeySym
2966  key_symbol;
2967 
2968  if (event.xkey.window != windows->image.id)
2969  break;
2970  /*
2971  Respond to a user key press.
2972  */
2973  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2974  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2975  switch ((int) key_symbol)
2976  {
2977  case XK_Escape:
2978  case XK_F20:
2979  {
2980  /*
2981  Prematurely exit.
2982  */
2983  state|=EscapeState;
2984  state|=ExitState;
2985  break;
2986  }
2987  case XK_F1:
2988  case XK_Help:
2989  {
2990  (void) XSetFunction(display,windows->image.highlight_context,
2991  GXcopy);
2992  XTextViewHelp(display,resource_info,windows,MagickFalse,
2993  "Help Viewer - Image Chop",ImageChopHelp);
2994  (void) XSetFunction(display,windows->image.highlight_context,
2995  GXinvert);
2996  break;
2997  }
2998  default:
2999  {
3000  (void) XBell(display,0);
3001  break;
3002  }
3003  }
3004  break;
3005  }
3006  case MotionNotify:
3007  {
3008  /*
3009  Map and unmap Info widget as text cursor crosses its boundaries.
3010  */
3011  x=event.xmotion.x;
3012  y=event.xmotion.y;
3013  if (windows->info.mapped != MagickFalse)
3014  {
3015  if ((x < (int) (windows->info.x+windows->info.width)) &&
3016  (y < (int) (windows->info.y+windows->info.height)))
3017  (void) XWithdrawWindow(display,windows->info.id,
3018  windows->info.screen);
3019  }
3020  else
3021  if ((x > (int) (windows->info.x+windows->info.width)) ||
3022  (y > (int) (windows->info.y+windows->info.height)))
3023  (void) XMapWindow(display,windows->info.id);
3024  }
3025  }
3026  } while ((state & ExitState) == 0);
3027  (void) XSelectInput(display,windows->image.id,
3028  windows->image.attributes.event_mask);
3029  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3030  if ((state & EscapeState) != 0)
3031  return(MagickTrue);
3032  /*
3033  Draw line as pointer moves until the mouse button is released.
3034  */
3035  chop_info.width=0;
3036  chop_info.height=0;
3037  chop_info.x=0;
3038  chop_info.y=0;
3039  distance=0;
3040  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3041  state=DefaultState;
3042  do
3043  {
3044  if (distance > 9)
3045  {
3046  /*
3047  Display info and draw chopping line.
3048  */
3049  if (windows->info.mapped == MagickFalse)
3050  (void) XMapWindow(display,windows->info.id);
3051  (void) FormatLocaleString(text,MaxTextExtent,
3052  " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3053  chop_info.height,(double) chop_info.x,(double) chop_info.y);
3054  XInfoWidget(display,windows,text);
3055  XHighlightLine(display,windows->image.id,
3056  windows->image.highlight_context,&segment_info);
3057  }
3058  else
3059  if (windows->info.mapped != MagickFalse)
3060  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3061  /*
3062  Wait for next event.
3063  */
3064  XScreenEvent(display,windows,&event);
3065  if (distance > 9)
3066  XHighlightLine(display,windows->image.id,
3067  windows->image.highlight_context,&segment_info);
3068  switch (event.type)
3069  {
3070  case ButtonPress:
3071  {
3072  segment_info.x2=(short int) event.xmotion.x;
3073  segment_info.y2=(short int) event.xmotion.y;
3074  break;
3075  }
3076  case ButtonRelease:
3077  {
3078  /*
3079  User has committed to chopping line.
3080  */
3081  segment_info.x2=(short int) event.xbutton.x;
3082  segment_info.y2=(short int) event.xbutton.y;
3083  state|=ExitState;
3084  break;
3085  }
3086  case Expose:
3087  break;
3088  case MotionNotify:
3089  {
3090  segment_info.x2=(short int) event.xmotion.x;
3091  segment_info.y2=(short int) event.xmotion.y;
3092  }
3093  default:
3094  break;
3095  }
3096  /*
3097  Check boundary conditions.
3098  */
3099  if (segment_info.x2 < 0)
3100  segment_info.x2=0;
3101  else
3102  if (segment_info.x2 > windows->image.ximage->width)
3103  segment_info.x2=windows->image.ximage->width;
3104  if (segment_info.y2 < 0)
3105  segment_info.y2=0;
3106  else
3107  if (segment_info.y2 > windows->image.ximage->height)
3108  segment_info.y2=windows->image.ximage->height;
3109  distance=(unsigned int)
3110  (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3111  ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3112  /*
3113  Compute chopping geometry.
3114  */
3115  if (direction == HorizontalChopCommand)
3116  {
3117  chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3118  chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3119  chop_info.height=0;
3120  chop_info.y=0;
3121  if (segment_info.x1 > (int) segment_info.x2)
3122  {
3123  chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3124  chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3125  }
3126  }
3127  else
3128  {
3129  chop_info.width=0;
3130  chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3131  chop_info.x=0;
3132  chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3133  if (segment_info.y1 > segment_info.y2)
3134  {
3135  chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3136  chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3137  }
3138  }
3139  } while ((state & ExitState) == 0);
3140  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3141  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3142  if (distance <= 9)
3143  return(MagickTrue);
3144  /*
3145  Image chopping is relative to image configuration.
3146  */
3147  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
3148  XSetCursorState(display,windows,MagickTrue);
3149  XCheckRefreshWindows(display,windows);
3150  windows->image.window_changes.width=windows->image.ximage->width-
3151  (unsigned int) chop_info.width;
3152  windows->image.window_changes.height=windows->image.ximage->height-
3153  (unsigned int) chop_info.height;
3154  width=(unsigned int) (*image)->columns;
3155  height=(unsigned int) (*image)->rows;
3156  x=0;
3157  y=0;
3158  if (windows->image.crop_geometry != (char *) NULL)
3159  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3160  scale_factor=(MagickRealType) width/windows->image.ximage->width;
3161  chop_info.x+=x;
3162  chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3163  chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3164  scale_factor=(MagickRealType) height/windows->image.ximage->height;
3165  chop_info.y+=y;
3166  chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3167  chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3168  /*
3169  Chop image.
3170  */
3171  chop_image=ChopImage(*image,&chop_info,&(*image)->exception);
3172  XSetCursorState(display,windows,MagickFalse);
3173  if (chop_image == (Image *) NULL)
3174  return(MagickFalse);
3175  *image=DestroyImage(*image);
3176  *image=chop_image;
3177  /*
3178  Update image configuration.
3179  */
3180  XConfigureImageColormap(display,resource_info,windows,*image);
3181  (void) XConfigureImage(display,resource_info,windows,*image);
3182  return(MagickTrue);
3183 }
3184 ␌
3185 /*
3186 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3187 % %
3188 % %
3189 % %
3190 + X C o l o r E d i t I m a g e %
3191 % %
3192 % %
3193 % %
3194 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3195 %
3196 % XColorEditImage() allows the user to interactively change the color of one
3197 % pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3198 %
3199 % The format of the XColorEditImage method is:
3200 %
3201 % MagickBooleanType XColorEditImage(Display *display,
3202 % XResourceInfo *resource_info,XWindows *windows,Image **image)
3203 %
3204 % A description of each parameter follows:
3205 %
3206 % o display: Specifies a connection to an X server; returned from
3207 % XOpenDisplay.
3208 %
3209 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3210 %
3211 % o windows: Specifies a pointer to a XWindows structure.
3212 %
3213 % o image: the image; returned from ReadImage.
3214 %
3215 */
3216 
3217 
3218 static MagickBooleanType XColorEditImage(Display *display,
3219  XResourceInfo *resource_info,XWindows *windows,Image **image)
3220 {
3221  const char
3222  *const ColorEditMenu[] =
3223  {
3224  "Method",
3225  "Pixel Color",
3226  "Border Color",
3227  "Fuzz",
3228  "Undo",
3229  "Help",
3230  "Dismiss",
3231  (char *) NULL
3232  };
3233 
3234  static const ModeType
3235  ColorEditCommands[] =
3236  {
3237  ColorEditMethodCommand,
3238  ColorEditColorCommand,
3239  ColorEditBorderCommand,
3240  ColorEditFuzzCommand,
3241  ColorEditUndoCommand,
3242  ColorEditHelpCommand,
3243  ColorEditDismissCommand
3244  };
3245 
3246  static PaintMethod
3247  method = PointMethod;
3248 
3249  static unsigned int
3250  pen_id = 0;
3251 
3252  static XColor
3253  border_color = { 0, 0, 0, 0, 0, 0 };
3254 
3255  char
3256  command[MaxTextExtent] = "",
3257  text[MaxTextExtent] = "";
3258 
3259  Cursor
3260  cursor;
3261 
3263  *exception;
3264 
3265  int
3266  entry,
3267  id,
3268  x,
3269  x_offset,
3270  y,
3271  y_offset;
3272 
3273  PixelPacket
3274  *q;
3275 
3276  size_t
3277  state;
3278 
3279  ssize_t
3280  i;
3281 
3282  unsigned int
3283  height,
3284  width;
3285 
3286  XColor
3287  color;
3288 
3289  XEvent
3290  event;
3291 
3292  /*
3293  Map Command widget.
3294  */
3295  (void) CloneString(&windows->command.name,"Color Edit");
3296  windows->command.data=4;
3297  (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3298  (void) XMapRaised(display,windows->command.id);
3299  XClientMessage(display,windows->image.id,windows->im_protocols,
3300  windows->im_update_widget,CurrentTime);
3301  /*
3302  Make cursor.
3303  */
3304  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3305  resource_info->background_color,resource_info->foreground_color);
3306  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3307  /*
3308  Track pointer until button 1 is pressed.
3309  */
3310  XQueryPosition(display,windows->image.id,&x,&y);
3311  (void) XSelectInput(display,windows->image.id,
3312  windows->image.attributes.event_mask | PointerMotionMask);
3313  state=DefaultState;
3314  do
3315  {
3316  if (windows->info.mapped != MagickFalse)
3317  {
3318  /*
3319  Display pointer position.
3320  */
3321  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3322  x+windows->image.x,y+windows->image.y);
3323  XInfoWidget(display,windows,text);
3324  }
3325  /*
3326  Wait for next event.
3327  */
3328  XScreenEvent(display,windows,&event);
3329  if (event.xany.window == windows->command.id)
3330  {
3331  /*
3332  Select a command from the Command widget.
3333  */
3334  id=XCommandWidget(display,windows,ColorEditMenu,&event);
3335  if (id < 0)
3336  {
3337  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3338  continue;
3339  }
3340  switch (ColorEditCommands[id])
3341  {
3342  case ColorEditMethodCommand:
3343  {
3344  char
3345  **methods;
3346 
3347  /*
3348  Select a method from the pop-up menu.
3349  */
3350  methods=(char **) GetCommandOptions(MagickMethodOptions);
3351  if (methods == (char **) NULL)
3352  break;
3353  entry=XMenuWidget(display,windows,ColorEditMenu[id],
3354  (const char **) methods,command);
3355  if (entry >= 0)
3356  method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3357  MagickFalse,methods[entry]);
3358  methods=DestroyStringList(methods);
3359  break;
3360  }
3361  case ColorEditColorCommand:
3362  {
3363  const char
3364  *ColorMenu[MaxNumberPens];
3365 
3366  int
3367  pen_number;
3368 
3369  /*
3370  Initialize menu selections.
3371  */
3372  for (i=0; i < (int) (MaxNumberPens-2); i++)
3373  ColorMenu[i]=resource_info->pen_colors[i];
3374  ColorMenu[MaxNumberPens-2]="Browser...";
3375  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3376  /*
3377  Select a pen color from the pop-up menu.
3378  */
3379  pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3380  (const char **) ColorMenu,command);
3381  if (pen_number < 0)
3382  break;
3383  if (pen_number == (MaxNumberPens-2))
3384  {
3385  static char
3386  color_name[MaxTextExtent] = "gray";
3387 
3388  /*
3389  Select a pen color from a dialog.
3390  */
3391  resource_info->pen_colors[pen_number]=color_name;
3392  XColorBrowserWidget(display,windows,"Select",color_name);
3393  if (*color_name == '\0')
3394  break;
3395  }
3396  /*
3397  Set pen color.
3398  */
3399  (void) XParseColor(display,windows->map_info->colormap,
3400  resource_info->pen_colors[pen_number],&color);
3401  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3402  (unsigned int) MaxColors,&color);
3403  windows->pixel_info->pen_colors[pen_number]=color;
3404  pen_id=(unsigned int) pen_number;
3405  break;
3406  }
3407  case ColorEditBorderCommand:
3408  {
3409  const char
3410  *ColorMenu[MaxNumberPens];
3411 
3412  int
3413  pen_number;
3414 
3415  /*
3416  Initialize menu selections.
3417  */
3418  for (i=0; i < (int) (MaxNumberPens-2); i++)
3419  ColorMenu[i]=resource_info->pen_colors[i];
3420  ColorMenu[MaxNumberPens-2]="Browser...";
3421  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3422  /*
3423  Select a pen color from the pop-up menu.
3424  */
3425  pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3426  (const char **) ColorMenu,command);
3427  if (pen_number < 0)
3428  break;
3429  if (pen_number == (MaxNumberPens-2))
3430  {
3431  static char
3432  color_name[MaxTextExtent] = "gray";
3433 
3434  /*
3435  Select a pen color from a dialog.
3436  */
3437  resource_info->pen_colors[pen_number]=color_name;
3438  XColorBrowserWidget(display,windows,"Select",color_name);
3439  if (*color_name == '\0')
3440  break;
3441  }
3442  /*
3443  Set border color.
3444  */
3445  (void) XParseColor(display,windows->map_info->colormap,
3446  resource_info->pen_colors[pen_number],&border_color);
3447  break;
3448  }
3449  case ColorEditFuzzCommand:
3450  {
3451  static char
3452  fuzz[MaxTextExtent];
3453 
3454  static const char
3455  *FuzzMenu[] =
3456  {
3457  "0%",
3458  "2%",
3459  "5%",
3460  "10%",
3461  "15%",
3462  "Dialog...",
3463  (char *) NULL,
3464  };
3465 
3466  /*
3467  Select a command from the pop-up menu.
3468  */
3469  entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3470  command);
3471  if (entry < 0)
3472  break;
3473  if (entry != 5)
3474  {
3475  (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3476  QuantumRange+1.0);
3477  break;
3478  }
3479  (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3480  (void) XDialogWidget(display,windows,"Ok",
3481  "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3482  if (*fuzz == '\0')
3483  break;
3484  (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3485  (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3486  1.0);
3487  break;
3488  }
3489  case ColorEditUndoCommand:
3490  {
3491  (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3492  image);
3493  break;
3494  }
3495  case ColorEditHelpCommand:
3496  default:
3497  {
3498  XTextViewHelp(display,resource_info,windows,MagickFalse,
3499  "Help Viewer - Image Annotation",ImageColorEditHelp);
3500  break;
3501  }
3502  case ColorEditDismissCommand:
3503  {
3504  /*
3505  Prematurely exit.
3506  */
3507  state|=EscapeState;
3508  state|=ExitState;
3509  break;
3510  }
3511  }
3512  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3513  continue;
3514  }
3515  switch (event.type)
3516  {
3517  case ButtonPress:
3518  {
3519  if (event.xbutton.button != Button1)
3520  break;
3521  if ((event.xbutton.window != windows->image.id) &&
3522  (event.xbutton.window != windows->magnify.id))
3523  break;
3524  /*
3525  exit loop.
3526  */
3527  x=event.xbutton.x;
3528  y=event.xbutton.y;
3529  (void) XMagickCommand(display,resource_info,windows,
3530  SaveToUndoBufferCommand,image);
3531  state|=UpdateConfigurationState;
3532  break;
3533  }
3534  case ButtonRelease:
3535  {
3536  if (event.xbutton.button != Button1)
3537  break;
3538  if ((event.xbutton.window != windows->image.id) &&
3539  (event.xbutton.window != windows->magnify.id))
3540  break;
3541  /*
3542  Update colormap information.
3543  */
3544  x=event.xbutton.x;
3545  y=event.xbutton.y;
3546  XConfigureImageColormap(display,resource_info,windows,*image);
3547  (void) XConfigureImage(display,resource_info,windows,*image);
3548  XInfoWidget(display,windows,text);
3549  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3550  state&=(~UpdateConfigurationState);
3551  break;
3552  }
3553  case Expose:
3554  break;
3555  case KeyPress:
3556  {
3557  KeySym
3558  key_symbol;
3559 
3560  if (event.xkey.window == windows->magnify.id)
3561  {
3562  Window
3563  window;
3564 
3565  window=windows->magnify.id;
3566  while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3567  }
3568  if (event.xkey.window != windows->image.id)
3569  break;
3570  /*
3571  Respond to a user key press.
3572  */
3573  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3574  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3575  switch ((int) key_symbol)
3576  {
3577  case XK_Escape:
3578  case XK_F20:
3579  {
3580  /*
3581  Prematurely exit.
3582  */
3583  state|=ExitState;
3584  break;
3585  }
3586  case XK_F1:
3587  case XK_Help:
3588  {
3589  XTextViewHelp(display,resource_info,windows,MagickFalse,
3590  "Help Viewer - Image Annotation",ImageColorEditHelp);
3591  break;
3592  }
3593  default:
3594  {
3595  (void) XBell(display,0);
3596  break;
3597  }
3598  }
3599  break;
3600  }
3601  case MotionNotify:
3602  {
3603  /*
3604  Map and unmap Info widget as cursor crosses its boundaries.
3605  */
3606  x=event.xmotion.x;
3607  y=event.xmotion.y;
3608  if (windows->info.mapped != MagickFalse)
3609  {
3610  if ((x < (int) (windows->info.x+windows->info.width)) &&
3611  (y < (int) (windows->info.y+windows->info.height)))
3612  (void) XWithdrawWindow(display,windows->info.id,
3613  windows->info.screen);
3614  }
3615  else
3616  if ((x > (int) (windows->info.x+windows->info.width)) ||
3617  (y > (int) (windows->info.y+windows->info.height)))
3618  (void) XMapWindow(display,windows->info.id);
3619  break;
3620  }
3621  default:
3622  break;
3623  }
3624  if (event.xany.window == windows->magnify.id)
3625  {
3626  x=windows->magnify.x-windows->image.x;
3627  y=windows->magnify.y-windows->image.y;
3628  }
3629  x_offset=x;
3630  y_offset=y;
3631  if ((state & UpdateConfigurationState) != 0)
3632  {
3633  CacheView
3634  *image_view;
3635 
3636  int
3637  x,
3638  y;
3639 
3640  /*
3641  Pixel edit is relative to image configuration.
3642  */
3643  (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3644  MagickTrue);
3645  color=windows->pixel_info->pen_colors[pen_id];
3646  XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3647  width=(unsigned int) (*image)->columns;
3648  height=(unsigned int) (*image)->rows;
3649  x=0;
3650  y=0;
3651  if (windows->image.crop_geometry != (char *) NULL)
3652  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3653  &width,&height);
3654  x_offset=(int)
3655  (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3656  y_offset=(int)
3657  (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3658  if ((x_offset < 0) || (y_offset < 0))
3659  continue;
3660  if ((x_offset >= (int) (*image)->columns) ||
3661  (y_offset >= (int) (*image)->rows))
3662  continue;
3663  exception=(&(*image)->exception);
3664  image_view=AcquireAuthenticCacheView(*image,exception);
3665  switch (method)
3666  {
3667  case PointMethod:
3668  default:
3669  {
3670  /*
3671  Update color information using point algorithm.
3672  */
3673  if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3674  return(MagickFalse);
3675  q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3676  (ssize_t)y_offset,1,1,exception);
3677  if (q == (PixelPacket *) NULL)
3678  break;
3679  q->red=ScaleShortToQuantum(color.red);
3680  q->green=ScaleShortToQuantum(color.green);
3681  q->blue=ScaleShortToQuantum(color.blue);
3682  (void) SyncCacheViewAuthenticPixels(image_view,
3683  &(*image)->exception);
3684  break;
3685  }
3686  case ReplaceMethod:
3687  {
3688  PixelPacket
3689  target;
3690 
3691  /*
3692  Update color information using replace algorithm.
3693  */
3694  (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3695  (ssize_t) y_offset,&target,&(*image)->exception);
3696  if ((*image)->storage_class == DirectClass)
3697  {
3698  for (y=0; y < (int) (*image)->rows; y++)
3699  {
3700  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3701  (*image)->columns,1,exception);
3702  if (q == (PixelPacket *) NULL)
3703  break;
3704  for (x=0; x < (int) (*image)->columns; x++)
3705  {
3706  if (IsColorSimilar(*image,q,&target) != MagickFalse)
3707  {
3708  q->red=ScaleShortToQuantum(color.red);
3709  q->green=ScaleShortToQuantum(color.green);
3710  q->blue=ScaleShortToQuantum(color.blue);
3711  }
3712  q++;
3713  }
3714  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3715  break;
3716  }
3717  }
3718  else
3719  {
3720  for (i=0; i < (ssize_t) (*image)->colors; i++)
3721  if (IsColorSimilar(*image,(*image)->colormap+i,&target) != MagickFalse)
3722  {
3723  (*image)->colormap[i].red=ScaleShortToQuantum(color.red);
3724  (*image)->colormap[i].green=ScaleShortToQuantum(
3725  color.green);
3726  (*image)->colormap[i].blue=ScaleShortToQuantum(
3727  color.blue);
3728  }
3729  (void) SyncImage(*image);
3730  }
3731  break;
3732  }
3733  case FloodfillMethod:
3734  case FillToBorderMethod:
3735  {
3736  DrawInfo
3737  *draw_info;
3738 
3740  target;
3741 
3742  /*
3743  Update color information using floodfill algorithm.
3744  */
3745  (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
3746  (ssize_t) y_offset,&target,exception);
3747  if (method == FillToBorderMethod)
3748  {
3749  target.red=(MagickRealType)
3750  ScaleShortToQuantum(border_color.red);
3751  target.green=(MagickRealType)
3752  ScaleShortToQuantum(border_color.green);
3753  target.blue=(MagickRealType)
3754  ScaleShortToQuantum(border_color.blue);
3755  }
3756  draw_info=CloneDrawInfo(resource_info->image_info,
3757  (DrawInfo *) NULL);
3758  (void) QueryColorDatabase(resource_info->pen_colors[pen_id],
3759  &draw_info->fill,exception);
3760  (void) FloodfillPaintImage(*image,DefaultChannels,draw_info,&target,
3761  (ssize_t) x_offset,(ssize_t) y_offset,
3762  method == FloodfillMethod ? MagickFalse : MagickTrue);
3763  draw_info=DestroyDrawInfo(draw_info);
3764  break;
3765  }
3766  case ResetMethod:
3767  {
3768  /*
3769  Update color information using reset algorithm.
3770  */
3771  if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3772  return(MagickFalse);
3773  for (y=0; y < (int) (*image)->rows; y++)
3774  {
3775  q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3776  (*image)->columns,1,exception);
3777  if (q == (PixelPacket *) NULL)
3778  break;
3779  for (x=0; x < (int) (*image)->columns; x++)
3780  {
3781  q->red=ScaleShortToQuantum(color.red);
3782  q->green=ScaleShortToQuantum(color.green);
3783  q->blue=ScaleShortToQuantum(color.blue);
3784  q++;
3785  }
3786  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3787  break;
3788  }
3789  break;
3790  }
3791  }
3792  image_view=DestroyCacheView(image_view);
3793  state&=(~UpdateConfigurationState);
3794  }
3795  } while ((state & ExitState) == 0);
3796  (void) XSelectInput(display,windows->image.id,
3797  windows->image.attributes.event_mask);
3798  XSetCursorState(display,windows,MagickFalse);
3799  (void) XFreeCursor(display,cursor);
3800  return(MagickTrue);
3801 }
3802 ␌
3803 /*
3804 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3805 % %
3806 % %
3807 % %
3808 + X C o m p o s i t e I m a g e %
3809 % %
3810 % %
3811 % %
3812 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3813 %
3814 % XCompositeImage() requests an image name from the user, reads the image and
3815 % composites it with the X window image at a location the user chooses with
3816 % the pointer.
3817 %
3818 % The format of the XCompositeImage method is:
3819 %
3820 % MagickBooleanType XCompositeImage(Display *display,
3821 % XResourceInfo *resource_info,XWindows *windows,Image *image)
3822 %
3823 % A description of each parameter follows:
3824 %
3825 % o display: Specifies a connection to an X server; returned from
3826 % XOpenDisplay.
3827 %
3828 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3829 %
3830 % o windows: Specifies a pointer to a XWindows structure.
3831 %
3832 % o image: the image; returned from ReadImage.
3833 %
3834 */
3835 static MagickBooleanType XCompositeImage(Display *display,
3836  XResourceInfo *resource_info,XWindows *windows,Image *image)
3837 {
3838  const char
3839  *const CompositeMenu[] =
3840  {
3841  "Operators",
3842  "Dissolve",
3843  "Displace",
3844  "Help",
3845  "Dismiss",
3846  (char *) NULL
3847  };
3848 
3849  static char
3850  displacement_geometry[MaxTextExtent] = "30x30",
3851  filename[MaxTextExtent] = "\0";
3852 
3853  static CompositeOperator
3854  compose = CopyCompositeOp;
3855 
3856  static const ModeType
3857  CompositeCommands[] =
3858  {
3859  CompositeOperatorsCommand,
3860  CompositeDissolveCommand,
3861  CompositeDisplaceCommand,
3862  CompositeHelpCommand,
3863  CompositeDismissCommand
3864  };
3865 
3866  char
3867  text[MaxTextExtent];
3868 
3869  Cursor
3870  cursor;
3871 
3872  Image
3873  *composite_image;
3874 
3875  int
3876  entry,
3877  id,
3878  x,
3879  y;
3880 
3881  MagickRealType
3882  blend,
3883  scale_factor;
3884 
3886  highlight_info,
3887  composite_info;
3888 
3889  unsigned int
3890  height,
3891  width;
3892 
3893  size_t
3894  state;
3895 
3896  XEvent
3897  event;
3898 
3899  /*
3900  Request image file name from user.
3901  */
3902  XFileBrowserWidget(display,windows,"Composite",filename);
3903  if (*filename == '\0')
3904  return(MagickTrue);
3905  /*
3906  Read image.
3907  */
3908  XSetCursorState(display,windows,MagickTrue);
3909  XCheckRefreshWindows(display,windows);
3910  (void) CopyMagickString(resource_info->image_info->filename,filename,
3911  MaxTextExtent);
3912  composite_image=ReadImage(resource_info->image_info,&image->exception);
3913  CatchException(&image->exception);
3914  XSetCursorState(display,windows,MagickFalse);
3915  if (composite_image == (Image *) NULL)
3916  return(MagickFalse);
3917  /*
3918  Map Command widget.
3919  */
3920  (void) CloneString(&windows->command.name,"Composite");
3921  windows->command.data=1;
3922  (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3923  (void) XMapRaised(display,windows->command.id);
3924  XClientMessage(display,windows->image.id,windows->im_protocols,
3925  windows->im_update_widget,CurrentTime);
3926  /*
3927  Track pointer until button 1 is pressed.
3928  */
3929  XQueryPosition(display,windows->image.id,&x,&y);
3930  (void) XSelectInput(display,windows->image.id,
3931  windows->image.attributes.event_mask | PointerMotionMask);
3932  composite_info.x=(ssize_t) windows->image.x+x;
3933  composite_info.y=(ssize_t) windows->image.y+y;
3934  composite_info.width=0;
3935  composite_info.height=0;
3936  cursor=XCreateFontCursor(display,XC_ul_angle);
3937  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3938  blend=0.0;
3939  state=DefaultState;
3940  do
3941  {
3942  if (windows->info.mapped != MagickFalse)
3943  {
3944  /*
3945  Display pointer position.
3946  */
3947  (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
3948  (long) composite_info.x,(long) composite_info.y);
3949  XInfoWidget(display,windows,text);
3950  }
3951  highlight_info=composite_info;
3952  highlight_info.x=composite_info.x-windows->image.x;
3953  highlight_info.y=composite_info.y-windows->image.y;
3954  XHighlightRectangle(display,windows->image.id,
3955  windows->image.highlight_context,&highlight_info);
3956  /*
3957  Wait for next event.
3958  */
3959  XScreenEvent(display,windows,&event);
3960  XHighlightRectangle(display,windows->image.id,
3961  windows->image.highlight_context,&highlight_info);
3962  if (event.xany.window == windows->command.id)
3963  {
3964  /*
3965  Select a command from the Command widget.
3966  */
3967  id=XCommandWidget(display,windows,CompositeMenu,&event);
3968  if (id < 0)
3969  continue;
3970  switch (CompositeCommands[id])
3971  {
3972  case CompositeOperatorsCommand:
3973  {
3974  char
3975  command[MaxTextExtent],
3976  **operators;
3977 
3978  /*
3979  Select a command from the pop-up menu.
3980  */
3981  operators=GetCommandOptions(MagickComposeOptions);
3982  if (operators == (char **) NULL)
3983  break;
3984  entry=XMenuWidget(display,windows,CompositeMenu[id],
3985  (const char **) operators,command);
3986  if (entry >= 0)
3987  compose=(CompositeOperator) ParseCommandOption(
3988  MagickComposeOptions,MagickFalse,operators[entry]);
3989  operators=DestroyStringList(operators);
3990  break;
3991  }
3992  case CompositeDissolveCommand:
3993  {
3994  static char
3995  factor[MaxTextExtent] = "20.0";
3996 
3997  /*
3998  Dissolve the two images a given percent.
3999  */
4000  (void) XSetFunction(display,windows->image.highlight_context,
4001  GXcopy);
4002  (void) XDialogWidget(display,windows,"Dissolve",
4003  "Enter the blend factor (0.0 - 99.9%):",factor);
4004  (void) XSetFunction(display,windows->image.highlight_context,
4005  GXinvert);
4006  if (*factor == '\0')
4007  break;
4008  blend=StringToDouble(factor,(char **) NULL);
4009  compose=DissolveCompositeOp;
4010  break;
4011  }
4012  case CompositeDisplaceCommand:
4013  {
4014  /*
4015  Get horizontal and vertical scale displacement geometry.
4016  */
4017  (void) XSetFunction(display,windows->image.highlight_context,
4018  GXcopy);
4019  (void) XDialogWidget(display,windows,"Displace",
4020  "Enter the horizontal and vertical scale:",displacement_geometry);
4021  (void) XSetFunction(display,windows->image.highlight_context,
4022  GXinvert);
4023  if (*displacement_geometry == '\0')
4024  break;
4025  compose=DisplaceCompositeOp;
4026  break;
4027  }
4028  case CompositeHelpCommand:
4029  {
4030  (void) XSetFunction(display,windows->image.highlight_context,
4031  GXcopy);
4032  XTextViewHelp(display,resource_info,windows,MagickFalse,
4033  "Help Viewer - Image Composite",ImageCompositeHelp);
4034  (void) XSetFunction(display,windows->image.highlight_context,
4035  GXinvert);
4036  break;
4037  }
4038  case CompositeDismissCommand:
4039  {
4040  /*
4041  Prematurely exit.
4042  */
4043  state|=EscapeState;
4044  state|=ExitState;
4045  break;
4046  }
4047  default:
4048  break;
4049  }
4050  continue;
4051  }
4052  switch (event.type)
4053  {
4054  case ButtonPress:
4055  {
4056  if (resource_info->debug != MagickFalse)
4057  (void) LogMagickEvent(X11Event,GetMagickModule(),
4058  "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4059  event.xbutton.button,event.xbutton.x,event.xbutton.y);
4060  if (event.xbutton.button != Button1)
4061  break;
4062  if (event.xbutton.window != windows->image.id)
4063  break;
4064  /*
4065  Change cursor.
4066  */
4067  composite_info.width=composite_image->columns;
4068  composite_info.height=composite_image->rows;
4069  (void) XCheckDefineCursor(display,windows->image.id,cursor);
4070  composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4071  composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4072  break;
4073  }
4074  case ButtonRelease:
4075  {
4076  if (resource_info->debug != MagickFalse)
4077  (void) LogMagickEvent(X11Event,GetMagickModule(),
4078  "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4079  event.xbutton.button,event.xbutton.x,event.xbutton.y);
4080  if (event.xbutton.button != Button1)
4081  break;
4082  if (event.xbutton.window != windows->image.id)
4083  break;
4084  if ((composite_info.width != 0) && (composite_info.height != 0))
4085  {
4086  /*
4087  User has selected the location of the composite image.
4088  */
4089  composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4090  composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4091  state|=ExitState;
4092  }
4093  break;
4094  }
4095  case Expose:
4096  break;
4097  case KeyPress:
4098  {
4099  char
4100  command[MaxTextExtent];
4101 
4102  KeySym
4103  key_symbol;
4104 
4105  int
4106  length;
4107 
4108  if (event.xkey.window != windows->image.id)
4109  break;
4110  /*
4111  Respond to a user key press.
4112  */
4113  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4114  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4115  *(command+length)='\0';
4116  if (resource_info->debug != MagickFalse)
4117  (void) LogMagickEvent(X11Event,GetMagickModule(),
4118  "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4119  switch ((int) key_symbol)
4120  {
4121  case XK_Escape:
4122  case XK_F20:
4123  {
4124  /*
4125  Prematurely exit.
4126  */
4127  composite_image=DestroyImage(composite_image);
4128  state|=EscapeState;
4129  state|=ExitState;
4130  break;
4131  }
4132  case XK_F1:
4133  case XK_Help:
4134  {
4135  (void) XSetFunction(display,windows->image.highlight_context,
4136  GXcopy);
4137  XTextViewHelp(display,resource_info,windows,MagickFalse,
4138  "Help Viewer - Image Composite",ImageCompositeHelp);
4139  (void) XSetFunction(display,windows->image.highlight_context,
4140  GXinvert);
4141  break;
4142  }
4143  default:
4144  {
4145  (void) XBell(display,0);
4146  break;
4147  }
4148  }
4149  break;
4150  }
4151  case MotionNotify:
4152  {
4153  /*
4154  Map and unmap Info widget as text cursor crosses its boundaries.
4155  */
4156  x=event.xmotion.x;
4157  y=event.xmotion.y;
4158  if (windows->info.mapped != MagickFalse)
4159  {
4160  if ((x < (int) (windows->info.x+windows->info.width)) &&
4161  (y < (int) (windows->info.y+windows->info.height)))
4162  (void) XWithdrawWindow(display,windows->info.id,
4163  windows->info.screen);
4164  }
4165  else
4166  if ((x > (int) (windows->info.x+windows->info.width)) ||
4167  (y > (int) (windows->info.y+windows->info.height)))
4168  (void) XMapWindow(display,windows->info.id);
4169  composite_info.x=(ssize_t) windows->image.x+x;
4170  composite_info.y=(ssize_t) windows->image.y+y;
4171  break;
4172  }
4173  default:
4174  {
4175  if (resource_info->debug != MagickFalse)
4176  (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4177  event.type);
4178  break;
4179  }
4180  }
4181  } while ((state & ExitState) == 0);
4182  (void) XSelectInput(display,windows->image.id,
4183  windows->image.attributes.event_mask);
4184  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4185  XSetCursorState(display,windows,MagickFalse);
4186  (void) XFreeCursor(display,cursor);
4187  if ((state & EscapeState) != 0)
4188  return(MagickTrue);
4189  /*
4190  Image compositing is relative to image configuration.
4191  */
4192  XSetCursorState(display,windows,MagickTrue);
4193  XCheckRefreshWindows(display,windows);
4194  width=(unsigned int) image->columns;
4195  height=(unsigned int) image->rows;
4196  x=0;
4197  y=0;
4198  if (windows->image.crop_geometry != (char *) NULL)
4199  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4200  scale_factor=(MagickRealType) width/windows->image.ximage->width;
4201  composite_info.x+=x;
4202  composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4203  composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4204  scale_factor=(MagickRealType) height/windows->image.ximage->height;
4205  composite_info.y+=y;
4206  composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4207  composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4208  if ((composite_info.width != composite_image->columns) ||
4209  (composite_info.height != composite_image->rows))
4210  {
4211  Image
4212  *resize_image;
4213 
4214  /*
4215  Scale composite image.
4216  */
4217  resize_image=ResizeImage(composite_image,composite_info.width,
4218  composite_info.height,composite_image->filter,composite_image->blur,
4219  &image->exception);
4220  composite_image=DestroyImage(composite_image);
4221  if (resize_image == (Image *) NULL)
4222  {
4223  XSetCursorState(display,windows,MagickFalse);
4224  return(MagickFalse);
4225  }
4226  composite_image=resize_image;
4227  }
4228  if (compose == DisplaceCompositeOp)
4229  (void) SetImageArtifact(composite_image,"compose:args",
4230  displacement_geometry);
4231  if (blend != 0.0)
4232  {
4233  CacheView
4234  *image_view;
4235 
4237  *exception;
4238 
4239  int
4240  y;
4241 
4242  Quantum
4243  opacity;
4244 
4245  int
4246  x;
4247 
4248  PixelPacket
4249  *q;
4250 
4251  /*
4252  Create mattes for blending.
4253  */
4254  (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel);
4255  opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4256  ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4257  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
4258  return(MagickFalse);
4259  image->matte=MagickTrue;
4260  exception=(&image->exception);
4261  image_view=AcquireAuthenticCacheView(image,exception);
4262  for (y=0; y < (int) image->rows; y++)
4263  {
4264  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4265  exception);
4266  if (q == (PixelPacket *) NULL)
4267  break;
4268  for (x=0; x < (int) image->columns; x++)
4269  {
4270  q->opacity=opacity;
4271  q++;
4272  }
4273  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4274  break;
4275  }
4276  image_view=DestroyCacheView(image_view);
4277  }
4278  /*
4279  Composite image with X Image window.
4280  */
4281  (void) CompositeImage(image,compose,composite_image,composite_info.x,
4282  composite_info.y);
4283  composite_image=DestroyImage(composite_image);
4284  XSetCursorState(display,windows,MagickFalse);
4285  /*
4286  Update image configuration.
4287  */
4288  XConfigureImageColormap(display,resource_info,windows,image);
4289  (void) XConfigureImage(display,resource_info,windows,image);
4290  return(MagickTrue);
4291 }
4292 ␌
4293 /*
4294 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4295 % %
4296 % %
4297 % %
4298 + X C o n f i g u r e I m a g e %
4299 % %
4300 % %
4301 % %
4302 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4303 %
4304 % XConfigureImage() creates a new X image. It also notifies the window
4305 % manager of the new image size and configures the transient widows.
4306 %
4307 % The format of the XConfigureImage method is:
4308 %
4309 % MagickBooleanType XConfigureImage(Display *display,
4310 % XResourceInfo *resource_info,XWindows *windows,Image *image)
4311 %
4312 % A description of each parameter follows:
4313 %
4314 % o display: Specifies a connection to an X server; returned from
4315 % XOpenDisplay.
4316 %
4317 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4318 %
4319 % o windows: Specifies a pointer to a XWindows structure.
4320 %
4321 % o image: the image.
4322 %
4323 %
4324 */
4325 static MagickBooleanType XConfigureImage(Display *display,
4326  XResourceInfo *resource_info,XWindows *windows,Image *image)
4327 {
4328  char
4329  geometry[MaxTextExtent];
4330 
4331  MagickStatusType
4332  status;
4333 
4334  size_t
4335  mask,
4336  height,
4337  width;
4338 
4339  ssize_t
4340  x,
4341  y;
4342 
4343  XSizeHints
4344  *size_hints;
4345 
4346  XWindowChanges
4347  window_changes;
4348 
4349  /*
4350  Dismiss if window dimensions are zero.
4351  */
4352  width=(unsigned int) windows->image.window_changes.width;
4353  height=(unsigned int) windows->image.window_changes.height;
4354  if (resource_info->debug != MagickFalse)
4355  (void) LogMagickEvent(X11Event,GetMagickModule(),
4356  "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4357  windows->image.ximage->height,(double) width,(double) height);
4358  if ((width*height) == 0)
4359  return(MagickTrue);
4360  x=0;
4361  y=0;
4362  /*
4363  Resize image to fit Image window dimensions.
4364  */
4365  XSetCursorState(display,windows,MagickTrue);
4366  (void) XFlush(display);
4367  if (((int) width != windows->image.ximage->width) ||
4368  ((int) height != windows->image.ximage->height))
4369  image->taint=MagickTrue;
4370  windows->magnify.x=(int)
4371  width*windows->magnify.x/windows->image.ximage->width;
4372  windows->magnify.y=(int)
4373  height*windows->magnify.y/windows->image.ximage->height;
4374  windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4375  windows->image.y=(int)
4376  (height*windows->image.y/windows->image.ximage->height);
4377  status=XMakeImage(display,resource_info,&windows->image,image,
4378  (unsigned int) width,(unsigned int) height);
4379  if (status == MagickFalse)
4380  XNoticeWidget(display,windows,"Unable to configure X image:",
4381  windows->image.name);
4382  /*
4383  Notify window manager of the new configuration.
4384  */
4385  if (resource_info->image_geometry != (char *) NULL)
4386  (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4387  resource_info->image_geometry);
4388  else
4389  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4390  XDisplayWidth(display,windows->image.screen),
4391  XDisplayHeight(display,windows->image.screen));
4392  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4393  window_changes.width=(int) width;
4394  if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4395  window_changes.width=XDisplayWidth(display,windows->image.screen);
4396  window_changes.height=(int) height;
4397  if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4398  window_changes.height=XDisplayHeight(display,windows->image.screen);
4399  mask=(size_t) (CWWidth | CWHeight);
4400  if (resource_info->backdrop)
4401  {
4402  mask|=CWX | CWY;
4403  window_changes.x=(int)
4404  ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4405  window_changes.y=(int)
4406  ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4407  }
4408  (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4409  (unsigned int) mask,&window_changes);
4410  (void) XClearWindow(display,windows->image.id);
4411  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4412  /*
4413  Update Magnify window configuration.
4414  */
4415  if (windows->magnify.mapped != MagickFalse)
4416  XMakeMagnifyImage(display,windows);
4417  windows->pan.crop_geometry=windows->image.crop_geometry;
4418  XBestIconSize(display,&windows->pan,image);
4419  while (((windows->pan.width << 1) < MaxIconSize) &&
4420  ((windows->pan.height << 1) < MaxIconSize))
4421  {
4422  windows->pan.width<<=1;
4423  windows->pan.height<<=1;
4424  }
4425  if (windows->pan.geometry != (char *) NULL)
4426  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4427  &windows->pan.width,&windows->pan.height);
4428  window_changes.width=(int) windows->pan.width;
4429  window_changes.height=(int) windows->pan.height;
4430  size_hints=XAllocSizeHints();
4431  if (size_hints != (XSizeHints *) NULL)
4432  {
4433  /*
4434  Set new size hints.
4435  */
4436  size_hints->flags=PSize | PMinSize | PMaxSize;
4437  size_hints->width=window_changes.width;
4438  size_hints->height=window_changes.height;
4439  size_hints->min_width=size_hints->width;
4440  size_hints->min_height=size_hints->height;
4441  size_hints->max_width=size_hints->width;
4442  size_hints->max_height=size_hints->height;
4443  (void) XSetNormalHints(display,windows->pan.id,size_hints);
4444  (void) XFree((void *) size_hints);
4445  }
4446  (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4447  (unsigned int) (CWWidth | CWHeight),&window_changes);
4448  /*
4449  Update icon window configuration.
4450  */
4451  windows->icon.crop_geometry=windows->image.crop_geometry;
4452  XBestIconSize(display,&windows->icon,image);
4453  window_changes.width=(int) windows->icon.width;
4454  window_changes.height=(int) windows->icon.height;
4455  (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4456  (unsigned int) (CWWidth | CWHeight),&window_changes);
4457  XSetCursorState(display,windows,MagickFalse);
4458  return(status != 0 ? MagickTrue : MagickFalse);
4459 }
4460 ␌
4461 /*
4462 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4463 % %
4464 % %
4465 % %
4466 + X C r o p I m a g e %
4467 % %
4468 % %
4469 % %
4470 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4471 %
4472 % XCropImage() allows the user to select a region of the image and crop, copy,
4473 % or cut it. For copy or cut, the image can subsequently be composited onto
4474 % the image with XPasteImage.
4475 %
4476 % The format of the XCropImage method is:
4477 %
4478 % MagickBooleanType XCropImage(Display *display,
4479 % XResourceInfo *resource_info,XWindows *windows,Image *image,
4480 % const ClipboardMode mode)
4481 %
4482 % A description of each parameter follows:
4483 %
4484 % o display: Specifies a connection to an X server; returned from
4485 % XOpenDisplay.
4486 %
4487 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4488 %
4489 % o windows: Specifies a pointer to a XWindows structure.
4490 %
4491 % o image: the image; returned from ReadImage.
4492 %
4493 % o mode: This unsigned value specified whether the image should be
4494 % cropped, copied, or cut.
4495 %
4496 */
4497 static MagickBooleanType XCropImage(Display *display,
4498  XResourceInfo *resource_info,XWindows *windows,Image *image,
4499  const ClipboardMode mode)
4500 {
4501  static const char
4502  *CropModeMenu[] =
4503  {
4504  "Help",
4505  "Dismiss",
4506  (char *) NULL
4507  },
4508  *RectifyModeMenu[] =
4509  {
4510  "Crop",
4511  "Help",
4512  "Dismiss",
4513  (char *) NULL
4514  };
4515 
4516  static const ModeType
4517  CropCommands[] =
4518  {
4519  CropHelpCommand,
4520  CropDismissCommand
4521  },
4522  RectifyCommands[] =
4523  {
4524  RectifyCopyCommand,
4525  RectifyHelpCommand,
4526  RectifyDismissCommand
4527  };
4528 
4529  CacheView
4530  *image_view;
4531 
4532  char
4533  command[MaxTextExtent],
4534  text[MaxTextExtent];
4535 
4536  Cursor
4537  cursor;
4538 
4540  *exception;
4541 
4542  int
4543  id,
4544  x,
4545  y;
4546 
4547  KeySym
4548  key_symbol;
4549 
4550  Image
4551  *crop_image;
4552 
4553  MagickRealType
4554  scale_factor;
4555 
4557  crop_info,
4558  highlight_info;
4559 
4560  PixelPacket
4561  *q;
4562 
4563  unsigned int
4564  height,
4565  width;
4566 
4567  size_t
4568  state;
4569 
4570  XEvent
4571  event;
4572 
4573  /*
4574  Map Command widget.
4575  */
4576  switch (mode)
4577  {
4578  case CopyMode:
4579  {
4580  (void) CloneString(&windows->command.name,"Copy");
4581  break;
4582  }
4583  case CropMode:
4584  {
4585  (void) CloneString(&windows->command.name,"Crop");
4586  break;
4587  }
4588  case CutMode:
4589  {
4590  (void) CloneString(&windows->command.name,"Cut");
4591  break;
4592  }
4593  }
4594  RectifyModeMenu[0]=windows->command.name;
4595  windows->command.data=0;
4596  (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4597  (void) XMapRaised(display,windows->command.id);
4598  XClientMessage(display,windows->image.id,windows->im_protocols,
4599  windows->im_update_widget,CurrentTime);
4600  /*
4601  Track pointer until button 1 is pressed.
4602  */
4603  XQueryPosition(display,windows->image.id,&x,&y);
4604  (void) XSelectInput(display,windows->image.id,
4605  windows->image.attributes.event_mask | PointerMotionMask);
4606  crop_info.x=(ssize_t) windows->image.x+x;
4607  crop_info.y=(ssize_t) windows->image.y+y;
4608  crop_info.width=0;
4609  crop_info.height=0;
4610  cursor=XCreateFontCursor(display,XC_fleur);
4611  state=DefaultState;
4612  do
4613  {
4614  if (windows->info.mapped != MagickFalse)
4615  {
4616  /*
4617  Display pointer position.
4618  */
4619  (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4620  (long) crop_info.x,(long) crop_info.y);
4621  XInfoWidget(display,windows,text);
4622  }
4623  /*
4624  Wait for next event.
4625  */
4626  XScreenEvent(display,windows,&event);
4627  if (event.xany.window == windows->command.id)
4628  {
4629  /*
4630  Select a command from the Command widget.
4631  */
4632  id=XCommandWidget(display,windows,CropModeMenu,&event);
4633  if (id < 0)
4634  continue;
4635  switch (CropCommands[id])
4636  {
4637  case CropHelpCommand:
4638  {
4639  switch (mode)
4640  {
4641  case CopyMode:
4642  {
4643  XTextViewHelp(display,resource_info,windows,MagickFalse,
4644  "Help Viewer - Image Copy",ImageCopyHelp);
4645  break;
4646  }
4647  case CropMode:
4648  {
4649  XTextViewHelp(display,resource_info,windows,MagickFalse,
4650  "Help Viewer - Image Crop",ImageCropHelp);
4651  break;
4652  }
4653  case CutMode:
4654  {
4655  XTextViewHelp(display,resource_info,windows,MagickFalse,
4656  "Help Viewer - Image Cut",ImageCutHelp);
4657  break;
4658  }
4659  }
4660  break;
4661  }
4662  case CropDismissCommand:
4663  {
4664  /*
4665  Prematurely exit.
4666  */
4667  state|=EscapeState;
4668  state|=ExitState;
4669  break;
4670  }
4671  default:
4672  break;
4673  }
4674  continue;
4675  }
4676  switch (event.type)
4677  {
4678  case ButtonPress:
4679  {
4680  if (event.xbutton.button != Button1)
4681  break;
4682  if (event.xbutton.window != windows->image.id)
4683  break;
4684  /*
4685  Note first corner of cropping rectangle-- exit loop.
4686  */
4687  (void) XCheckDefineCursor(display,windows->image.id,cursor);
4688  crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4689  crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4690  state|=ExitState;
4691  break;
4692  }
4693  case ButtonRelease:
4694  break;
4695  case Expose:
4696  break;
4697  case KeyPress:
4698  {
4699  if (event.xkey.window != windows->image.id)
4700  break;
4701  /*
4702  Respond to a user key press.
4703  */
4704  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4705  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4706  switch ((int) key_symbol)
4707  {
4708  case XK_Escape:
4709  case XK_F20:
4710  {
4711  /*
4712  Prematurely exit.
4713  */
4714  state|=EscapeState;
4715  state|=ExitState;
4716  break;
4717  }
4718  case XK_F1:
4719  case XK_Help:
4720  {
4721  switch (mode)
4722  {
4723  case CopyMode:
4724  {
4725  XTextViewHelp(display,resource_info,windows,MagickFalse,
4726  "Help Viewer - Image Copy",ImageCopyHelp);
4727  break;
4728  }
4729  case CropMode:
4730  {
4731  XTextViewHelp(display,resource_info,windows,MagickFalse,
4732  "Help Viewer - Image Crop",ImageCropHelp);
4733  break;
4734  }
4735  case CutMode:
4736  {
4737  XTextViewHelp(display,resource_info,windows,MagickFalse,
4738  "Help Viewer - Image Cut",ImageCutHelp);
4739  break;
4740  }
4741  }
4742  break;
4743  }
4744  default:
4745  {
4746  (void) XBell(display,0);
4747  break;
4748  }
4749  }
4750  break;
4751  }
4752  case MotionNotify:
4753  {
4754  if (event.xmotion.window != windows->image.id)
4755  break;
4756  /*
4757  Map and unmap Info widget as text cursor crosses its boundaries.
4758  */
4759  x=event.xmotion.x;
4760  y=event.xmotion.y;
4761  if (windows->info.mapped != MagickFalse)
4762  {
4763  if ((x < (int) (windows->info.x+windows->info.width)) &&
4764  (y < (int) (windows->info.y+windows->info.height)))
4765  (void) XWithdrawWindow(display,windows->info.id,
4766  windows->info.screen);
4767  }
4768  else
4769  if ((x > (int) (windows->info.x+windows->info.width)) ||
4770  (y > (int) (windows->info.y+windows->info.height)))
4771  (void) XMapWindow(display,windows->info.id);
4772  crop_info.x=(ssize_t) windows->image.x+x;
4773  crop_info.y=(ssize_t) windows->image.y+y;
4774  break;
4775  }
4776  default:
4777  break;
4778  }
4779  } while ((state & ExitState) == 0);
4780  (void) XSelectInput(display,windows->image.id,
4781  windows->image.attributes.event_mask);
4782  if ((state & EscapeState) != 0)
4783  {
4784  /*
4785  User want to exit without cropping.
4786  */
4787  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4788  (void) XFreeCursor(display,cursor);
4789  return(MagickTrue);
4790  }
4791  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4792  do
4793  {
4794  /*
4795  Size rectangle as pointer moves until the mouse button is released.
4796  */
4797  x=(int) crop_info.x;
4798  y=(int) crop_info.y;
4799  crop_info.width=0;
4800  crop_info.height=0;
4801  state=DefaultState;
4802  do
4803  {
4804  highlight_info=crop_info;
4805  highlight_info.x=crop_info.x-windows->image.x;
4806  highlight_info.y=crop_info.y-windows->image.y;
4807  if ((highlight_info.width > 3) && (highlight_info.height > 3))
4808  {
4809  /*
4810  Display info and draw cropping rectangle.
4811  */
4812  if (windows->info.mapped == MagickFalse)
4813  (void) XMapWindow(display,windows->info.id);
4814  (void) FormatLocaleString(text,MaxTextExtent,
4815  " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4816  crop_info.height,(double) crop_info.x,(double) crop_info.y);
4817  XInfoWidget(display,windows,text);
4818  XHighlightRectangle(display,windows->image.id,
4819  windows->image.highlight_context,&highlight_info);
4820  }
4821  else
4822  if (windows->info.mapped != MagickFalse)
4823  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4824  /*
4825  Wait for next event.
4826  */
4827  XScreenEvent(display,windows,&event);
4828  if ((highlight_info.width > 3) && (highlight_info.height > 3))
4829  XHighlightRectangle(display,windows->image.id,
4830  windows->image.highlight_context,&highlight_info);
4831  switch (event.type)
4832  {
4833  case ButtonPress:
4834  {
4835  crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4836  crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4837  break;
4838  }
4839  case ButtonRelease:
4840  {
4841  /*
4842  User has committed to cropping rectangle.
4843  */
4844  crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4845  crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4846  XSetCursorState(display,windows,MagickFalse);
4847  state|=ExitState;
4848  windows->command.data=0;
4849  (void) XCommandWidget(display,windows,RectifyModeMenu,
4850  (XEvent *) NULL);
4851  break;
4852  }
4853  case Expose:
4854  break;
4855  case MotionNotify:
4856  {
4857  crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4858  crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4859  }
4860  default:
4861  break;
4862  }
4863  if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4864  ((state & ExitState) != 0))
4865  {
4866  /*
4867  Check boundary conditions.
4868  */
4869  if (crop_info.x < 0)
4870  crop_info.x=0;
4871  else
4872  if (crop_info.x > (ssize_t) windows->image.ximage->width)
4873  crop_info.x=(ssize_t) windows->image.ximage->width;
4874  if ((int) crop_info.x < x)
4875  crop_info.width=(unsigned int) (x-crop_info.x);
4876  else
4877  {
4878  crop_info.width=(unsigned int) (crop_info.x-x);
4879  crop_info.x=(ssize_t) x;
4880  }
4881  if (crop_info.y < 0)
4882  crop_info.y=0;
4883  else
4884  if (crop_info.y > (ssize_t) windows->image.ximage->height)
4885  crop_info.y=(ssize_t) windows->image.ximage->height;
4886  if ((int) crop_info.y < y)
4887  crop_info.height=(unsigned int) (y-crop_info.y);
4888  else
4889  {
4890  crop_info.height=(unsigned int) (crop_info.y-y);
4891  crop_info.y=(ssize_t) y;
4892  }
4893  }
4894  } while ((state & ExitState) == 0);
4895  /*
4896  Wait for user to grab a corner of the rectangle or press return.
4897  */
4898  state=DefaultState;
4899  (void) XMapWindow(display,windows->info.id);
4900  do
4901  {
4902  if (windows->info.mapped != MagickFalse)
4903  {
4904  /*
4905  Display pointer position.
4906  */
4907  (void) FormatLocaleString(text,MaxTextExtent,
4908  " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4909  crop_info.height,(double) crop_info.x,(double) crop_info.y);
4910  XInfoWidget(display,windows,text);
4911  }
4912  highlight_info=crop_info;
4913  highlight_info.x=crop_info.x-windows->image.x;
4914  highlight_info.y=crop_info.y-windows->image.y;
4915  if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4916  {
4917  state|=EscapeState;
4918  state|=ExitState;
4919  break;
4920  }
4921  XHighlightRectangle(display,windows->image.id,
4922  windows->image.highlight_context,&highlight_info);
4923  XScreenEvent(display,windows,&event);
4924  if (event.xany.window == windows->command.id)
4925  {
4926  /*
4927  Select a command from the Command widget.
4928  */
4929  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4930  id=XCommandWidget(display,windows,RectifyModeMenu,&event);
4931  (void) XSetFunction(display,windows->image.highlight_context,
4932  GXinvert);
4933  XHighlightRectangle(display,windows->image.id,
4934  windows->image.highlight_context,&highlight_info);
4935  if (id >= 0)
4936  switch (RectifyCommands[id])
4937  {
4938  case RectifyCopyCommand:
4939  {
4940  state|=ExitState;
4941  break;
4942  }
4943  case RectifyHelpCommand:
4944  {
4945  (void) XSetFunction(display,windows->image.highlight_context,
4946  GXcopy);
4947  switch (mode)
4948  {
4949  case CopyMode:
4950  {
4951  XTextViewHelp(display,resource_info,windows,MagickFalse,
4952  "Help Viewer - Image Copy",ImageCopyHelp);
4953  break;
4954  }
4955  case CropMode:
4956  {
4957  XTextViewHelp(display,resource_info,windows,MagickFalse,
4958  "Help Viewer - Image Crop",ImageCropHelp);
4959  break;
4960  }
4961  case CutMode:
4962  {
4963  XTextViewHelp(display,resource_info,windows,MagickFalse,
4964  "Help Viewer - Image Cut",ImageCutHelp);
4965  break;
4966  }
4967  }
4968  (void) XSetFunction(display,windows->image.highlight_context,
4969  GXinvert);
4970  break;
4971  }
4972  case RectifyDismissCommand:
4973  {
4974  /*
4975  Prematurely exit.
4976  */
4977  state|=EscapeState;
4978  state|=ExitState;
4979  break;
4980  }
4981  default:
4982  break;
4983  }
4984  continue;
4985  }
4986  XHighlightRectangle(display,windows->image.id,
4987  windows->image.highlight_context,&highlight_info);
4988  switch (event.type)
4989  {
4990  case ButtonPress:
4991  {
4992  if (event.xbutton.button != Button1)
4993  break;
4994  if (event.xbutton.window != windows->image.id)
4995  break;
4996  x=windows->image.x+event.xbutton.x;
4997  y=windows->image.y+event.xbutton.y;
4998  if ((x < (int) (crop_info.x+RoiDelta)) &&
4999  (x > (int) (crop_info.x-RoiDelta)) &&
5000  (y < (int) (crop_info.y+RoiDelta)) &&
5001  (y > (int) (crop_info.y-RoiDelta)))
5002  {
5003  crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5004  crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5005  state|=UpdateConfigurationState;
5006  break;
5007  }
5008  if ((x < (int) (crop_info.x+RoiDelta)) &&
5009  (x > (int) (crop_info.x-RoiDelta)) &&
5010  (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5011  (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5012  {
5013  crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5014  state|=UpdateConfigurationState;
5015  break;
5016  }
5017  if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5018  (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5019  (y < (int) (crop_info.y+RoiDelta)) &&
5020  (y > (int) (crop_info.y-RoiDelta)))
5021  {
5022  crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5023  state|=UpdateConfigurationState;
5024  break;
5025  }
5026  if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5027  (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5028  (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5029  (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5030  {
5031  state|=UpdateConfigurationState;
5032  break;
5033  }
5034  magick_fallthrough;
5035  }
5036  case ButtonRelease:
5037  {
5038  if (event.xbutton.window == windows->pan.id)
5039  if ((highlight_info.x != crop_info.x-windows->image.x) ||
5040  (highlight_info.y != crop_info.y-windows->image.y))
5041  XHighlightRectangle(display,windows->image.id,
5042  windows->image.highlight_context,&highlight_info);
5043  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5044  event.xbutton.time);
5045  break;
5046  }
5047  case Expose:
5048  {
5049  if (event.xexpose.window == windows->image.id)
5050  if (event.xexpose.count == 0)
5051  {
5052  event.xexpose.x=(int) highlight_info.x;
5053  event.xexpose.y=(int) highlight_info.y;
5054  event.xexpose.width=(int) highlight_info.width;
5055  event.xexpose.height=(int) highlight_info.height;
5056  XRefreshWindow(display,&windows->image,&event);
5057  }
5058  if (event.xexpose.window == windows->info.id)
5059  if (event.xexpose.count == 0)
5060  XInfoWidget(display,windows,text);
5061  break;
5062  }
5063  case KeyPress:
5064  {
5065  if (event.xkey.window != windows->image.id)
5066  break;
5067  /*
5068  Respond to a user key press.
5069  */
5070  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5071  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5072  switch ((int) key_symbol)
5073  {
5074  case XK_Escape:
5075  case XK_F20:
5076  {
5077  state|=EscapeState;
5078  magick_fallthrough;
5079  }
5080  case XK_Return:
5081  {
5082  state|=ExitState;
5083  break;
5084  }
5085  case XK_Home:
5086  case XK_KP_Home:
5087  {
5088  crop_info.x=(ssize_t) (windows->image.width/2L-crop_info.width/
5089  2L);
5090  crop_info.y=(ssize_t) (windows->image.height/2L-crop_info.height/
5091  2L);
5092  break;
5093  }
5094  case XK_Left:
5095  case XK_KP_Left:
5096  {
5097  crop_info.x--;
5098  break;
5099  }
5100  case XK_Up:
5101  case XK_KP_Up:
5102  case XK_Next:
5103  {
5104  crop_info.y--;
5105  break;
5106  }
5107  case XK_Right:
5108  case XK_KP_Right:
5109  {
5110  crop_info.x++;
5111  break;
5112  }
5113  case XK_Prior:
5114  case XK_Down:
5115  case XK_KP_Down:
5116  {
5117  crop_info.y++;
5118  break;
5119  }
5120  case XK_F1:
5121  case XK_Help:
5122  {
5123  (void) XSetFunction(display,windows->image.highlight_context,
5124  GXcopy);
5125  switch (mode)
5126  {
5127  case CopyMode:
5128  {
5129  XTextViewHelp(display,resource_info,windows,MagickFalse,
5130  "Help Viewer - Image Copy",ImageCopyHelp);
5131  break;
5132  }
5133  case CropMode:
5134  {
5135  XTextViewHelp(display,resource_info,windows,MagickFalse,
5136  "Help Viewer - Image Cropg",ImageCropHelp);
5137  break;
5138  }
5139  case CutMode:
5140  {
5141  XTextViewHelp(display,resource_info,windows,MagickFalse,
5142  "Help Viewer - Image Cutg",ImageCutHelp);
5143  break;
5144  }
5145  }
5146  (void) XSetFunction(display,windows->image.highlight_context,
5147  GXinvert);
5148  break;
5149  }
5150  default:
5151  {
5152  (void) XBell(display,0);
5153  break;
5154  }
5155  }
5156  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5157  event.xkey.time);
5158  break;
5159  }
5160  case KeyRelease:
5161  break;
5162  case MotionNotify:
5163  {
5164  if (event.xmotion.window != windows->image.id)
5165  break;
5166  /*
5167  Map and unmap Info widget as text cursor crosses its boundaries.
5168  */
5169  x=event.xmotion.x;
5170  y=event.xmotion.y;
5171  if (windows->info.mapped != MagickFalse)
5172  {
5173  if ((x < (int) (windows->info.x+windows->info.width)) &&
5174  (y < (int) (windows->info.y+windows->info.height)))
5175  (void) XWithdrawWindow(display,windows->info.id,
5176  windows->info.screen);
5177  }
5178  else
5179  if ((x > (int) (windows->info.x+windows->info.width)) ||
5180  (y > (int) (windows->info.y+windows->info.height)))
5181  (void) XMapWindow(display,windows->info.id);
5182  crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5183  crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5184  break;
5185  }
5186  case SelectionRequest:
5187  {
5188  XSelectionEvent
5189  notify;
5190 
5191  XSelectionRequestEvent
5192  *request;
5193 
5194  /*
5195  Set primary selection.
5196  */
5197  (void) FormatLocaleString(text,MaxTextExtent,
5198  "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5199  crop_info.height,(double) crop_info.x,(double) crop_info.y);
5200  request=(&(event.xselectionrequest));
5201  (void) XChangeProperty(request->display,request->requestor,
5202  request->property,request->target,8,PropModeReplace,
5203  (unsigned char *) text,(int) strlen(text));
5204  notify.type=SelectionNotify;
5205  notify.display=request->display;
5206  notify.requestor=request->requestor;
5207  notify.selection=request->selection;
5208  notify.target=request->target;
5209  notify.time=request->time;
5210  if (request->property == None)
5211  notify.property=request->target;
5212  else
5213  notify.property=request->property;
5214  (void) XSendEvent(request->display,request->requestor,False,0,
5215  (XEvent *) &notify);
5216  }
5217  default:
5218  break;
5219  }
5220  if ((state & UpdateConfigurationState) != 0)
5221  {
5222  (void) XPutBackEvent(display,&event);
5223  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5224  break;
5225  }
5226  } while ((state & ExitState) == 0);
5227  } while ((state & ExitState) == 0);
5228  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5229  XSetCursorState(display,windows,MagickFalse);
5230  if ((state & EscapeState) != 0)
5231  return(MagickTrue);
5232  if (mode == CropMode)
5233  if (((int) crop_info.width != windows->image.ximage->width) ||
5234  ((int) crop_info.height != windows->image.ximage->height))
5235  {
5236  /*
5237  Reconfigure Image window as defined by cropping rectangle.
5238  */
5239  XSetCropGeometry(display,windows,&crop_info,image);
5240  windows->image.window_changes.width=(int) crop_info.width;
5241  windows->image.window_changes.height=(int) crop_info.height;
5242  (void) XConfigureImage(display,resource_info,windows,image);
5243  return(MagickTrue);
5244  }
5245  /*
5246  Copy image before applying image transforms.
5247  */
5248  XSetCursorState(display,windows,MagickTrue);
5249  XCheckRefreshWindows(display,windows);
5250  width=(unsigned int) image->columns;
5251  height=(unsigned int) image->rows;
5252  x=0;
5253  y=0;
5254  if (windows->image.crop_geometry != (char *) NULL)
5255  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5256  scale_factor=(MagickRealType) width/windows->image.ximage->width;
5257  crop_info.x+=x;
5258  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5259  crop_info.x+=image->page.x;
5260  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5261  scale_factor=(MagickRealType) height/windows->image.ximage->height;
5262  crop_info.y+=y;
5263  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5264  crop_info.y+=image->page.y;
5265  crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5266  crop_image=CropImage(image,&crop_info,&image->exception);
5267  XSetCursorState(display,windows,MagickFalse);
5268  if (crop_image == (Image *) NULL)
5269  return(MagickFalse);
5270  if (resource_info->copy_image != (Image *) NULL)
5271  resource_info->copy_image=DestroyImage(resource_info->copy_image);
5272  resource_info->copy_image=crop_image;
5273  if (mode == CopyMode)
5274  {
5275  (void) XConfigureImage(display,resource_info,windows,image);
5276  return(MagickTrue);
5277  }
5278  /*
5279  Cut image.
5280  */
5281  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
5282  return(MagickFalse);
5283  image->matte=MagickTrue;
5284  exception=(&image->exception);
5285  image_view=AcquireAuthenticCacheView(image,exception);
5286  for (y=0; y < (int) crop_info.height; y++)
5287  {
5288  q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5289  crop_info.width,1,exception);
5290  if (q == (PixelPacket *) NULL)
5291  break;
5292  for (x=0; x < (int) crop_info.width; x++)
5293  {
5294  q->opacity=(Quantum) TransparentOpacity;
5295  q++;
5296  }
5297  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5298  break;
5299  }
5300  image_view=DestroyCacheView(image_view);
5301  /*
5302  Update image configuration.
5303  */
5304  XConfigureImageColormap(display,resource_info,windows,image);
5305  (void) XConfigureImage(display,resource_info,windows,image);
5306  return(MagickTrue);
5307 }
5308 ␌
5309 /*
5310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5311 % %
5312 % %
5313 % %
5314 + X D r a w I m a g e %
5315 % %
5316 % %
5317 % %
5318 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5319 %
5320 % XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5321 % the image.
5322 %
5323 % The format of the XDrawEditImage method is:
5324 %
5325 % MagickBooleanType XDrawEditImage(Display *display,
5326 % XResourceInfo *resource_info,XWindows *windows,Image **image)
5327 %
5328 % A description of each parameter follows:
5329 %
5330 % o display: Specifies a connection to an X server; returned from
5331 % XOpenDisplay.
5332 %
5333 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5334 %
5335 % o windows: Specifies a pointer to a XWindows structure.
5336 %
5337 % o image: the image.
5338 %
5339 */
5340 static MagickBooleanType XDrawEditImage(Display *display,
5341  XResourceInfo *resource_info,XWindows *windows,Image **image)
5342 {
5343  const char
5344  *const DrawMenu[] =
5345  {
5346  "Element",
5347  "Color",
5348  "Stipple",
5349  "Width",
5350  "Undo",
5351  "Help",
5352  "Dismiss",
5353  (char *) NULL
5354  };
5355 
5356  static ElementType
5357  element = PointElement;
5358 
5359  static const ModeType
5360  DrawCommands[] =
5361  {
5362  DrawElementCommand,
5363  DrawColorCommand,
5364  DrawStippleCommand,
5365  DrawWidthCommand,
5366  DrawUndoCommand,
5367  DrawHelpCommand,
5368  DrawDismissCommand
5369  };
5370 
5371  static Pixmap
5372  stipple = (Pixmap) NULL;
5373 
5374  static unsigned int
5375  pen_id = 0,
5376  line_width = 1;
5377 
5378  char
5379  command[MaxTextExtent],
5380  text[MaxTextExtent];
5381 
5382  Cursor
5383  cursor;
5384 
5385  int
5386  entry,
5387  id,
5388  number_coordinates,
5389  x,
5390  y;
5391 
5392  MagickRealType
5393  degrees;
5394 
5395  MagickStatusType
5396  status;
5397 
5399  rectangle_info;
5400 
5401  int
5402  i;
5403 
5404  unsigned int
5405  distance,
5406  height,
5407  max_coordinates,
5408  width;
5409 
5410  size_t
5411  state;
5412 
5413  Window
5414  root_window;
5415 
5416  XDrawInfo
5417  draw_info;
5418 
5419  XEvent
5420  event;
5421 
5422  XPoint
5423  *coordinate_info;
5424 
5425  XSegment
5426  line_info;
5427 
5428  /*
5429  Allocate polygon info.
5430  */
5431  max_coordinates=2048;
5432  coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5433  sizeof(*coordinate_info));
5434  if (coordinate_info == (XPoint *) NULL)
5435  {
5436  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
5437  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5438  return(MagickFalse);
5439  }
5440  /*
5441  Map Command widget.
5442  */
5443  (void) CloneString(&windows->command.name,"Draw");
5444  windows->command.data=4;
5445  (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5446  (void) XMapRaised(display,windows->command.id);
5447  XClientMessage(display,windows->image.id,windows->im_protocols,
5448  windows->im_update_widget,CurrentTime);
5449  /*
5450  Wait for first button press.
5451  */
5452  root_window=XRootWindow(display,XDefaultScreen(display));
5453  draw_info.stencil=OpaqueStencil;
5454  status=MagickTrue;
5455  cursor=XCreateFontCursor(display,XC_tcross);
5456  for ( ; ; )
5457  {
5458  XQueryPosition(display,windows->image.id,&x,&y);
5459  (void) XSelectInput(display,windows->image.id,
5460  windows->image.attributes.event_mask | PointerMotionMask);
5461  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5462  state=DefaultState;
5463  do
5464  {
5465  if (windows->info.mapped != MagickFalse)
5466  {
5467  /*
5468  Display pointer position.
5469  */
5470  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5471  x+windows->image.x,y+windows->image.y);
5472  XInfoWidget(display,windows,text);
5473  }
5474  /*
5475  Wait for next event.
5476  */
5477  XScreenEvent(display,windows,&event);
5478  if (event.xany.window == windows->command.id)
5479  {
5480  /*
5481  Select a command from the Command widget.
5482  */
5483  id=XCommandWidget(display,windows,DrawMenu,&event);
5484  if (id < 0)
5485  continue;
5486  switch (DrawCommands[id])
5487  {
5488  case DrawElementCommand:
5489  {
5490  const char
5491  *const Elements[] =
5492  {
5493  "point",
5494  "line",
5495  "rectangle",
5496  "fill rectangle",
5497  "circle",
5498  "fill circle",
5499  "ellipse",
5500  "fill ellipse",
5501  "polygon",
5502  "fill polygon",
5503  (char *) NULL,
5504  };
5505 
5506  /*
5507  Select a command from the pop-up menu.
5508  */
5509  element=(ElementType) (XMenuWidget(display,windows,
5510  DrawMenu[id],Elements,command)+1);
5511  break;
5512  }
5513  case DrawColorCommand:
5514  {
5515  const char
5516  *ColorMenu[MaxNumberPens+1];
5517 
5518  int
5519  pen_number;
5520 
5521  MagickBooleanType
5522  transparent;
5523 
5524  XColor
5525  color;
5526 
5527  /*
5528  Initialize menu selections.
5529  */
5530  for (i=0; i < (int) (MaxNumberPens-2); i++)
5531  ColorMenu[i]=resource_info->pen_colors[i];
5532  ColorMenu[MaxNumberPens-2]="transparent";
5533  ColorMenu[MaxNumberPens-1]="Browser...";
5534  ColorMenu[MaxNumberPens]=(char *) NULL;
5535  /*
5536  Select a pen color from the pop-up menu.
5537  */
5538  pen_number=XMenuWidget(display,windows,DrawMenu[id],
5539  (const char **) ColorMenu,command);
5540  if (pen_number < 0)
5541  break;
5542  transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5543  MagickFalse;
5544  if (transparent != MagickFalse)
5545  {
5546  draw_info.stencil=TransparentStencil;
5547  break;
5548  }
5549  if (pen_number == (MaxNumberPens-1))
5550  {
5551  static char
5552  color_name[MaxTextExtent] = "gray";
5553 
5554  /*
5555  Select a pen color from a dialog.
5556  */
5557  resource_info->pen_colors[pen_number]=color_name;
5558  XColorBrowserWidget(display,windows,"Select",color_name);
5559  if (*color_name == '\0')
5560  break;
5561  }
5562  /*
5563  Set pen color.
5564  */
5565  (void) XParseColor(display,windows->map_info->colormap,
5566  resource_info->pen_colors[pen_number],&color);
5567  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5568  (unsigned int) MaxColors,&color);
5569  windows->pixel_info->pen_colors[pen_number]=color;
5570  pen_id=(unsigned int) pen_number;
5571  draw_info.stencil=OpaqueStencil;
5572  break;
5573  }
5574  case DrawStippleCommand:
5575  {
5576  const char
5577  *StipplesMenu[] =
5578  {
5579  "Brick",
5580  "Diagonal",
5581  "Scales",
5582  "Vertical",
5583  "Wavy",
5584  "Translucent",
5585  "Opaque",
5586  (char *) NULL,
5587  (char *) NULL,
5588  };
5589 
5590  Image
5591  *stipple_image;
5592 
5593  ImageInfo
5594  *image_info;
5595 
5596  int
5597  status;
5598 
5599  static char
5600  filename[MaxTextExtent] = "\0";
5601 
5602  /*
5603  Select a command from the pop-up menu.
5604  */
5605  StipplesMenu[7]="Open...";
5606  entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5607  command);
5608  if (entry < 0)
5609  break;
5610  if (stipple != (Pixmap) NULL)
5611  (void) XFreePixmap(display,stipple);
5612  stipple=(Pixmap) NULL;
5613  if (entry != 7)
5614  {
5615  switch (entry)
5616  {
5617  case 0:
5618  {
5619  stipple=XCreateBitmapFromData(display,root_window,
5620  (char *) BricksBitmap,BricksWidth,BricksHeight);
5621  break;
5622  }
5623  case 1:
5624  {
5625  stipple=XCreateBitmapFromData(display,root_window,
5626  (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5627  break;
5628  }
5629  case 2:
5630  {
5631  stipple=XCreateBitmapFromData(display,root_window,
5632  (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5633  break;
5634  }
5635  case 3:
5636  {
5637  stipple=XCreateBitmapFromData(display,root_window,
5638  (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5639  break;
5640  }
5641  case 4:
5642  {
5643  stipple=XCreateBitmapFromData(display,root_window,
5644  (char *) WavyBitmap,WavyWidth,WavyHeight);
5645  break;
5646  }
5647  case 5:
5648  {
5649  stipple=XCreateBitmapFromData(display,root_window,
5650  (char *) HighlightBitmap,HighlightWidth,
5651  HighlightHeight);
5652  break;
5653  }
5654  case 6:
5655  default:
5656  {
5657  stipple=XCreateBitmapFromData(display,root_window,
5658  (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5659  break;
5660  }
5661  }
5662  break;
5663  }
5664  XFileBrowserWidget(display,windows,"Stipple",filename);
5665  if (*filename == '\0')
5666  break;
5667  /*
5668  Read image.
5669  */
5670  XSetCursorState(display,windows,MagickTrue);
5671  XCheckRefreshWindows(display,windows);
5672  image_info=AcquireImageInfo();
5673  (void) CopyMagickString(image_info->filename,filename,
5674  MaxTextExtent);
5675  stipple_image=ReadImage(image_info,&(*image)->exception);
5676  CatchException(&(*image)->exception);
5677  XSetCursorState(display,windows,MagickFalse);
5678  if (stipple_image == (Image *) NULL)
5679  break;
5680  (void) AcquireUniqueFileResource(filename);
5681  (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5682  "xbm:%s",filename);
5683  (void) WriteImage(image_info,stipple_image);
5684  stipple_image=DestroyImage(stipple_image);
5685  image_info=DestroyImageInfo(image_info);
5686  status=XReadBitmapFile(display,root_window,filename,&width,
5687  &height,&stipple,&x,&y);
5688  (void) RelinquishUniqueFileResource(filename);
5689  if ((status != BitmapSuccess) != 0)
5690  XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5691  filename);
5692  break;
5693  }
5694  case DrawWidthCommand:
5695  {
5696  const char
5697  *const WidthsMenu[] =
5698  {
5699  "1",
5700  "2",
5701  "4",
5702  "8",
5703  "16",
5704  "Dialog...",
5705  (char *) NULL,
5706  };
5707 
5708  static char
5709  width[MaxTextExtent] = "0";
5710 
5711  /*
5712  Select a command from the pop-up menu.
5713  */
5714  entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5715  command);
5716  if (entry < 0)
5717  break;
5718  if (entry != 5)
5719  {
5720  line_width=(unsigned int) StringToUnsignedLong(
5721  WidthsMenu[entry]);
5722  break;
5723  }
5724  (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5725  width);
5726  if (*width == '\0')
5727  break;
5728  line_width=(unsigned int) StringToUnsignedLong(width);
5729  break;
5730  }
5731  case DrawUndoCommand:
5732  {
5733  (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5734  image);
5735  break;
5736  }
5737  case DrawHelpCommand:
5738  {
5739  XTextViewHelp(display,resource_info,windows,MagickFalse,
5740  "Help Viewer - Image Rotation",ImageDrawHelp);
5741  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5742  break;
5743  }
5744  case DrawDismissCommand:
5745  {
5746  /*
5747  Prematurely exit.
5748  */
5749  state|=EscapeState;
5750  state|=ExitState;
5751  break;
5752  }
5753  default:
5754  break;
5755  }
5756  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5757  continue;
5758  }
5759  switch (event.type)
5760  {
5761  case ButtonPress:
5762  {
5763  if (event.xbutton.button != Button1)
5764  break;
5765  if (event.xbutton.window != windows->image.id)
5766  break;
5767  /*
5768  exit loop.
5769  */
5770  x=event.xbutton.x;
5771  y=event.xbutton.y;
5772  state|=ExitState;
5773  break;
5774  }
5775  case ButtonRelease:
5776  break;
5777  case Expose:
5778  break;
5779  case KeyPress:
5780  {
5781  KeySym
5782  key_symbol;
5783 
5784  if (event.xkey.window != windows->image.id)
5785  break;
5786  /*
5787  Respond to a user key press.
5788  */
5789  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5790  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5791  switch ((int) key_symbol)
5792  {
5793  case XK_Escape:
5794  case XK_F20:
5795  {
5796  /*
5797  Prematurely exit.
5798  */
5799  state|=EscapeState;
5800  state|=ExitState;
5801  break;
5802  }
5803  case XK_F1:
5804  case XK_Help:
5805  {
5806  XTextViewHelp(display,resource_info,windows,MagickFalse,
5807  "Help Viewer - Image Rotation",ImageDrawHelp);
5808  break;
5809  }
5810  default:
5811  {
5812  (void) XBell(display,0);
5813  break;
5814  }
5815  }
5816  break;
5817  }
5818  case MotionNotify:
5819  {
5820  /*
5821  Map and unmap Info widget as text cursor crosses its boundaries.
5822  */
5823  x=event.xmotion.x;
5824  y=event.xmotion.y;
5825  if (windows->info.mapped != MagickFalse)
5826  {
5827  if ((x < (int) (windows->info.x+windows->info.width)) &&
5828  (y < (int) (windows->info.y+windows->info.height)))
5829  (void) XWithdrawWindow(display,windows->info.id,
5830  windows->info.screen);
5831  }
5832  else
5833  if ((x > (int) (windows->info.x+windows->info.width)) ||
5834  (y > (int) (windows->info.y+windows->info.height)))
5835  (void) XMapWindow(display,windows->info.id);
5836  break;
5837  }
5838  }
5839  } while ((state & ExitState) == 0);
5840  (void) XSelectInput(display,windows->image.id,
5841  windows->image.attributes.event_mask);
5842  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5843  if ((state & EscapeState) != 0)
5844  break;
5845  /*
5846  Draw element as pointer moves until the button is released.
5847  */
5848  distance=0;
5849  degrees=0.0;
5850  line_info.x1=x;
5851  line_info.y1=y;
5852  line_info.x2=x;
5853  line_info.y2=y;
5854  rectangle_info.x=(ssize_t) x;
5855  rectangle_info.y=(ssize_t) y;
5856  rectangle_info.width=0;
5857  rectangle_info.height=0;
5858  number_coordinates=1;
5859  coordinate_info->x=x;
5860  coordinate_info->y=y;
5861  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5862  state=DefaultState;
5863  do
5864  {
5865  switch (element)
5866  {
5867  case PointElement:
5868  default:
5869  {
5870  if (number_coordinates > 1)
5871  {
5872  (void) XDrawLines(display,windows->image.id,
5873  windows->image.highlight_context,coordinate_info,
5874  number_coordinates,CoordModeOrigin);
5875  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5876  coordinate_info[number_coordinates-1].x,
5877  coordinate_info[number_coordinates-1].y);
5878  XInfoWidget(display,windows,text);
5879  }
5880  break;
5881  }
5882  case LineElement:
5883  {
5884  if (distance > 9)
5885  {
5886  /*
5887  Display angle of the line.
5888  */
5889  degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5890  line_info.y1),(double) (line_info.x2-line_info.x1)));
5891  (void) FormatLocaleString(text,MaxTextExtent," %g",
5892  (double) degrees);
5893  XInfoWidget(display,windows,text);
5894  XHighlightLine(display,windows->image.id,
5895  windows->image.highlight_context,&line_info);
5896  }
5897  else
5898  if (windows->info.mapped != MagickFalse)
5899  (void) XWithdrawWindow(display,windows->info.id,
5900  windows->info.screen);
5901  break;
5902  }
5903  case RectangleElement:
5904  case FillRectangleElement:
5905  {
5906  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5907  {
5908  /*
5909  Display info and draw drawing rectangle.
5910  */
5911  (void) FormatLocaleString(text,MaxTextExtent,
5912  " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5913  (double) rectangle_info.height,(double) rectangle_info.x,
5914  (double) rectangle_info.y);
5915  XInfoWidget(display,windows,text);
5916  XHighlightRectangle(display,windows->image.id,
5917  windows->image.highlight_context,&rectangle_info);
5918  }
5919  else
5920  if (windows->info.mapped != MagickFalse)
5921  (void) XWithdrawWindow(display,windows->info.id,
5922  windows->info.screen);
5923  break;
5924  }
5925  case CircleElement:
5926  case FillCircleElement:
5927  case EllipseElement:
5928  case FillEllipseElement:
5929  {
5930  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5931  {
5932  /*
5933  Display info and draw drawing rectangle.
5934  */
5935  (void) FormatLocaleString(text,MaxTextExtent,
5936  " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5937  (double) rectangle_info.height,(double) rectangle_info.x,
5938  (double) rectangle_info.y);
5939  XInfoWidget(display,windows,text);
5940  XHighlightEllipse(display,windows->image.id,
5941  windows->image.highlight_context,&rectangle_info);
5942  }
5943  else
5944  if (windows->info.mapped != MagickFalse)
5945  (void) XWithdrawWindow(display,windows->info.id,
5946  windows->info.screen);
5947  break;
5948  }
5949  case PolygonElement:
5950  case FillPolygonElement:
5951  {
5952  if (number_coordinates > 1)
5953  (void) XDrawLines(display,windows->image.id,
5954  windows->image.highlight_context,coordinate_info,
5955  number_coordinates,CoordModeOrigin);
5956  if (distance > 9)
5957  {
5958  /*
5959  Display angle of the line.
5960  */
5961  degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5962  line_info.y1),(double) (line_info.x2-line_info.x1)));
5963  (void) FormatLocaleString(text,MaxTextExtent," %g",
5964  (double) degrees);
5965  XInfoWidget(display,windows,text);
5966  XHighlightLine(display,windows->image.id,
5967  windows->image.highlight_context,&line_info);
5968  }
5969  else
5970  if (windows->info.mapped != MagickFalse)
5971  (void) XWithdrawWindow(display,windows->info.id,
5972  windows->info.screen);
5973  break;
5974  }
5975  }
5976  /*
5977  Wait for next event.
5978  */
5979  XScreenEvent(display,windows,&event);
5980  switch (element)
5981  {
5982  case PointElement:
5983  default:
5984  {
5985  if (number_coordinates > 1)
5986  (void) XDrawLines(display,windows->image.id,
5987  windows->image.highlight_context,coordinate_info,
5988  number_coordinates,CoordModeOrigin);
5989  break;
5990  }
5991  case LineElement:
5992  {
5993  if (distance > 9)
5994  XHighlightLine(display,windows->image.id,
5995  windows->image.highlight_context,&line_info);
5996  break;
5997  }
5998  case RectangleElement:
5999  case FillRectangleElement:
6000  {
6001  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6002  XHighlightRectangle(display,windows->image.id,
6003  windows->image.highlight_context,&rectangle_info);
6004  break;
6005  }
6006  case CircleElement:
6007  case FillCircleElement:
6008  case EllipseElement:
6009  case FillEllipseElement:
6010  {
6011  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6012  XHighlightEllipse(display,windows->image.id,
6013  windows->image.highlight_context,&rectangle_info);
6014  break;
6015  }
6016  case PolygonElement:
6017  case FillPolygonElement:
6018  {
6019  if (number_coordinates > 1)
6020  (void) XDrawLines(display,windows->image.id,
6021  windows->image.highlight_context,coordinate_info,
6022  number_coordinates,CoordModeOrigin);
6023  if (distance > 9)
6024  XHighlightLine(display,windows->image.id,
6025  windows->image.highlight_context,&line_info);
6026  break;
6027  }
6028  }
6029  switch (event.type)
6030  {
6031  case ButtonPress:
6032  break;
6033  case ButtonRelease:
6034  {
6035  /*
6036  User has committed to element.
6037  */
6038  line_info.x2=event.xbutton.x;
6039  line_info.y2=event.xbutton.y;
6040  rectangle_info.x=(ssize_t) event.xbutton.x;
6041  rectangle_info.y=(ssize_t) event.xbutton.y;
6042  coordinate_info[number_coordinates].x=event.xbutton.x;
6043  coordinate_info[number_coordinates].y=event.xbutton.y;
6044  if (((element != PolygonElement) &&
6045  (element != FillPolygonElement)) || (distance <= 9))
6046  {
6047  state|=ExitState;
6048  break;
6049  }
6050  number_coordinates++;
6051  if (number_coordinates < (int) max_coordinates)
6052  {
6053  line_info.x1=event.xbutton.x;
6054  line_info.y1=event.xbutton.y;
6055  break;
6056  }
6057  max_coordinates<<=1;
6058  coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6059  max_coordinates,sizeof(*coordinate_info));
6060  if (coordinate_info == (XPoint *) NULL)
6061  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6062  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6063  break;
6064  }
6065  case Expose:
6066  break;
6067  case MotionNotify:
6068  {
6069  if (event.xmotion.window != windows->image.id)
6070  break;
6071  if (element != PointElement)
6072  {
6073  line_info.x2=event.xmotion.x;
6074  line_info.y2=event.xmotion.y;
6075  rectangle_info.x=(ssize_t) event.xmotion.x;
6076  rectangle_info.y=(ssize_t) event.xmotion.y;
6077  break;
6078  }
6079  coordinate_info[number_coordinates].x=event.xbutton.x;
6080  coordinate_info[number_coordinates].y=event.xbutton.y;
6081  number_coordinates++;
6082  if (number_coordinates < (int) max_coordinates)
6083  break;
6084  max_coordinates<<=1;
6085  coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6086  max_coordinates,sizeof(*coordinate_info));
6087  if (coordinate_info == (XPoint *) NULL)
6088  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6089  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6090  break;
6091  }
6092  default:
6093  break;
6094  }
6095  /*
6096  Check boundary conditions.
6097  */
6098  if (line_info.x2 < 0)
6099  line_info.x2=0;
6100  else
6101  if (line_info.x2 > (int) windows->image.width)
6102  line_info.x2=(short) windows->image.width;
6103  if (line_info.y2 < 0)
6104  line_info.y2=0;
6105  else
6106  if (line_info.y2 > (int) windows->image.height)
6107  line_info.y2=(short) windows->image.height;
6108  distance=(unsigned int)
6109  (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6110  ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6111  if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6112  ((state & ExitState) != 0))
6113  {
6114  if (rectangle_info.x < 0)
6115  rectangle_info.x=0;
6116  else
6117  if (rectangle_info.x > (ssize_t) windows->image.width)
6118  rectangle_info.x=(ssize_t) windows->image.width;
6119  if ((int) rectangle_info.x < x)
6120  rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6121  else
6122  {
6123  rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6124  rectangle_info.x=(ssize_t) x;
6125  }
6126  if (rectangle_info.y < 0)
6127  rectangle_info.y=0;
6128  else
6129  if (rectangle_info.y > (ssize_t) windows->image.height)
6130  rectangle_info.y=(ssize_t) windows->image.height;
6131  if ((int) rectangle_info.y < y)
6132  rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6133  else
6134  {
6135  rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6136  rectangle_info.y=(ssize_t) y;
6137  }
6138  }
6139  } while ((state & ExitState) == 0);
6140  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6141  if ((element == PointElement) || (element == PolygonElement) ||
6142  (element == FillPolygonElement))
6143  {
6144  /*
6145  Determine polygon bounding box.
6146  */
6147  rectangle_info.x=(ssize_t) coordinate_info->x;
6148  rectangle_info.y=(ssize_t) coordinate_info->y;
6149  x=coordinate_info->x;
6150  y=coordinate_info->y;
6151  for (i=1; i < number_coordinates; i++)
6152  {
6153  if (coordinate_info[i].x > x)
6154  x=coordinate_info[i].x;
6155  if (coordinate_info[i].y > y)
6156  y=coordinate_info[i].y;
6157  if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6158  rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6159  if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6160  rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6161  }
6162  rectangle_info.width=(size_t) (x-rectangle_info.x);
6163  rectangle_info.height=(size_t) (y-rectangle_info.y);
6164  for (i=0; i < number_coordinates; i++)
6165  {
6166  coordinate_info[i].x-=rectangle_info.x;
6167  coordinate_info[i].y-=rectangle_info.y;
6168  }
6169  }
6170  else
6171  if (distance <= 9)
6172  continue;
6173  else
6174  if ((element == RectangleElement) ||
6175  (element == CircleElement) || (element == EllipseElement))
6176  {
6177  rectangle_info.width--;
6178  rectangle_info.height--;
6179  }
6180  /*
6181  Drawing is relative to image configuration.
6182  */
6183  draw_info.x=(int) rectangle_info.x;
6184  draw_info.y=(int) rectangle_info.y;
6185  (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6186  image);
6187  width=(unsigned int) (*image)->columns;
6188  height=(unsigned int) (*image)->rows;
6189  x=0;
6190  y=0;
6191  if (windows->image.crop_geometry != (char *) NULL)
6192  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6193  draw_info.x+=windows->image.x-(line_width/2);
6194  if (draw_info.x < 0)
6195  draw_info.x=0;
6196  draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6197  draw_info.y+=windows->image.y-(line_width/2);
6198  if (draw_info.y < 0)
6199  draw_info.y=0;
6200  draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6201  draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6202  if (draw_info.width > (unsigned int) (*image)->columns)
6203  draw_info.width=(unsigned int) (*image)->columns;
6204  draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6205  if (draw_info.height > (unsigned int) (*image)->rows)
6206  draw_info.height=(unsigned int) (*image)->rows;
6207  (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6208  width*draw_info.width/windows->image.ximage->width,
6209  height*draw_info.height/windows->image.ximage->height,
6210  draw_info.x+x,draw_info.y+y);
6211  /*
6212  Initialize drawing attributes.
6213  */
6214  draw_info.degrees=0.0;
6215  draw_info.element=element;
6216  draw_info.stipple=stipple;
6217  draw_info.line_width=line_width;
6218  draw_info.line_info=line_info;
6219  if (line_info.x1 > (int) (line_width/2))
6220  draw_info.line_info.x1=(short) line_width/2;
6221  if (line_info.y1 > (int) (line_width/2))
6222  draw_info.line_info.y1=(short) line_width/2;
6223  draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6224  draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6225  if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6226  {
6227  draw_info.line_info.x2=(-draw_info.line_info.x2);
6228  draw_info.line_info.y2=(-draw_info.line_info.y2);
6229  }
6230  if (draw_info.line_info.x2 < 0)
6231  {
6232  draw_info.line_info.x2=(-draw_info.line_info.x2);
6233  Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6234  }
6235  if (draw_info.line_info.y2 < 0)
6236  {
6237  draw_info.line_info.y2=(-draw_info.line_info.y2);
6238  Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6239  }
6240  draw_info.rectangle_info=rectangle_info;
6241  if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6242  draw_info.rectangle_info.x=(ssize_t) line_width/2;
6243  if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6244  draw_info.rectangle_info.y=(ssize_t) line_width/2;
6245  draw_info.number_coordinates=(unsigned int) number_coordinates;
6246  draw_info.coordinate_info=coordinate_info;
6247  windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6248  /*
6249  Draw element on image.
6250  */
6251  XSetCursorState(display,windows,MagickTrue);
6252  XCheckRefreshWindows(display,windows);
6253  status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6254  XSetCursorState(display,windows,MagickFalse);
6255  /*
6256  Update image colormap and return to image drawing.
6257  */
6258  XConfigureImageColormap(display,resource_info,windows,*image);
6259  (void) XConfigureImage(display,resource_info,windows,*image);
6260  }
6261  XSetCursorState(display,windows,MagickFalse);
6262  coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6263  return(status != 0 ? MagickTrue : MagickFalse);
6264 }
6265 ␌
6266 /*
6267 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6268 % %
6269 % %
6270 % %
6271 + X D r a w P a n R e c t a n g l e %
6272 % %
6273 % %
6274 % %
6275 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6276 %
6277 % XDrawPanRectangle() draws a rectangle in the pan window. The pan window
6278 % displays a zoom image and the rectangle shows which portion of the image is
6279 % displayed in the Image window.
6280 %
6281 % The format of the XDrawPanRectangle method is:
6282 %
6283 % XDrawPanRectangle(Display *display,XWindows *windows)
6284 %
6285 % A description of each parameter follows:
6286 %
6287 % o display: Specifies a connection to an X server; returned from
6288 % XOpenDisplay.
6289 %
6290 % o windows: Specifies a pointer to a XWindows structure.
6291 %
6292 */
6293 static void XDrawPanRectangle(Display *display,XWindows *windows)
6294 {
6295  MagickRealType
6296  scale_factor;
6297 
6299  highlight_info;
6300 
6301  /*
6302  Determine dimensions of the panning rectangle.
6303  */
6304  scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6305  highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6306  highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6307  scale_factor=(MagickRealType)
6308  windows->pan.height/windows->image.ximage->height;
6309  highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6310  highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6311  /*
6312  Display the panning rectangle.
6313  */
6314  (void) XClearWindow(display,windows->pan.id);
6315  XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6316  &highlight_info);
6317 }
6318 ␌
6319 /*
6320 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6321 % %
6322 % %
6323 % %
6324 + X I m a g e C a c h e %
6325 % %
6326 % %
6327 % %
6328 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6329 %
6330 % XImageCache() handles the creation, manipulation, and destruction of the
6331 % image cache (undo and redo buffers).
6332 %
6333 % The format of the XImageCache method is:
6334 %
6335 % void XImageCache(Display *display,XResourceInfo *resource_info,
6336 % XWindows *windows,const DisplayCommand command,Image **image)
6337 %
6338 % A description of each parameter follows:
6339 %
6340 % o display: Specifies a connection to an X server; returned from
6341 % XOpenDisplay.
6342 %
6343 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6344 %
6345 % o windows: Specifies a pointer to a XWindows structure.
6346 %
6347 % o command: Specifies a command to perform.
6348 %
6349 % o image: the image; XImageCache may transform the image and return a new
6350 % image pointer.
6351 %
6352 */
6353 static void XImageCache(Display *display,XResourceInfo *resource_info,
6354  XWindows *windows,const DisplayCommand command,Image **image)
6355 {
6356  Image
6357  *cache_image;
6358 
6359  static Image
6360  *redo_image = (Image *) NULL,
6361  *undo_image = (Image *) NULL;
6362 
6363  switch (command)
6364  {
6365  case FreeBuffersCommand:
6366  {
6367  /*
6368  Free memory from the undo and redo cache.
6369  */
6370  while (undo_image != (Image *) NULL)
6371  {
6372  cache_image=undo_image;
6373  undo_image=GetPreviousImageInList(undo_image);
6374  cache_image->list=DestroyImage(cache_image->list);
6375  cache_image=DestroyImage(cache_image);
6376  }
6377  undo_image=NewImageList();
6378  if (redo_image != (Image *) NULL)
6379  redo_image=DestroyImage(redo_image);
6380  redo_image=NewImageList();
6381  return;
6382  }
6383  case UndoCommand:
6384  {
6385  char
6386  image_geometry[MaxTextExtent];
6387 
6388  /*
6389  Undo the last image transformation.
6390  */
6391  if (undo_image == (Image *) NULL)
6392  {
6393  (void) XBell(display,0);
6394  ThrowXWindowException(ImageError,"NoImagesWereFound",
6395  (*image)->filename);
6396  return;
6397  }
6398  cache_image=undo_image;
6399  undo_image=GetPreviousImageInList(undo_image);
6400  windows->image.window_changes.width=(int) cache_image->columns;
6401  windows->image.window_changes.height=(int) cache_image->rows;
6402  (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6403  windows->image.ximage->width,windows->image.ximage->height);
6404  (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
6405  if (windows->image.crop_geometry != (char *) NULL)
6406  windows->image.crop_geometry=(char *)
6407  RelinquishMagickMemory(windows->image.crop_geometry);
6408  windows->image.crop_geometry=cache_image->geometry;
6409  if (redo_image != (Image *) NULL)
6410  redo_image=DestroyImage(redo_image);
6411  redo_image=(*image);
6412  *image=cache_image->list;
6413  cache_image=DestroyImage(cache_image);
6414  if (windows->image.orphan != MagickFalse)
6415  return;
6416  XConfigureImageColormap(display,resource_info,windows,*image);
6417  (void) XConfigureImage(display,resource_info,windows,*image);
6418  return;
6419  }
6420  case CutCommand:
6421  case PasteCommand:
6422  case ApplyCommand:
6423  case HalfSizeCommand:
6424  case OriginalSizeCommand:
6425  case DoubleSizeCommand:
6426  case ResizeCommand:
6427  case TrimCommand:
6428  case CropCommand:
6429  case ChopCommand:
6430  case FlipCommand:
6431  case FlopCommand:
6432  case RotateRightCommand:
6433  case RotateLeftCommand:
6434  case RotateCommand:
6435  case ShearCommand:
6436  case RollCommand:
6437  case NegateCommand:
6438  case ContrastStretchCommand:
6439  case SigmoidalContrastCommand:
6440  case NormalizeCommand:
6441  case EqualizeCommand:
6442  case HueCommand:
6443  case SaturationCommand:
6444  case BrightnessCommand:
6445  case GammaCommand:
6446  case SpiffCommand:
6447  case DullCommand:
6448  case GrayscaleCommand:
6449  case MapCommand:
6450  case QuantizeCommand:
6451  case DespeckleCommand:
6452  case EmbossCommand:
6453  case ReduceNoiseCommand:
6454  case AddNoiseCommand:
6455  case SharpenCommand:
6456  case BlurCommand:
6457  case ThresholdCommand:
6458  case EdgeDetectCommand:
6459  case SpreadCommand:
6460  case ShadeCommand:
6461  case RaiseCommand:
6462  case SegmentCommand:
6463  case SolarizeCommand:
6464  case SepiaToneCommand:
6465  case SwirlCommand:
6466  case ImplodeCommand:
6467  case VignetteCommand:
6468  case WaveCommand:
6469  case OilPaintCommand:
6470  case CharcoalDrawCommand:
6471  case AnnotateCommand:
6472  case AddBorderCommand:
6473  case AddFrameCommand:
6474  case CompositeCommand:
6475  case CommentCommand:
6476  case LaunchCommand:
6477  case RegionOfInterestCommand:
6478  case SaveToUndoBufferCommand:
6479  case RedoCommand:
6480  {
6481  Image
6482  *previous_image;
6483 
6484  ssize_t
6485  bytes;
6486 
6487  bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
6488  if (undo_image != (Image *) NULL)
6489  {
6490  /*
6491  Ensure the undo cache has enough memory available.
6492  */
6493  previous_image=undo_image;
6494  while (previous_image != (Image *) NULL)
6495  {
6496  bytes+=previous_image->list->columns*previous_image->list->rows*
6497  sizeof(PixelPacket);
6498  if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6499  {
6500  previous_image=GetPreviousImageInList(previous_image);
6501  continue;
6502  }
6503  bytes-=previous_image->list->columns*previous_image->list->rows*
6504  sizeof(PixelPacket);
6505  if (previous_image == undo_image)
6506  undo_image=NewImageList();
6507  else
6508  previous_image->next->previous=NewImageList();
6509  break;
6510  }
6511  while (previous_image != (Image *) NULL)
6512  {
6513  /*
6514  Delete any excess memory from undo cache.
6515  */
6516  cache_image=previous_image;
6517  previous_image=GetPreviousImageInList(previous_image);
6518  cache_image->list=DestroyImage(cache_image->list);
6519  cache_image=DestroyImage(cache_image);
6520  }
6521  }
6522  if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6523  break;
6524  /*
6525  Save image before transformations are applied.
6526  */
6527  cache_image=AcquireImage((ImageInfo *) NULL);
6528  if (cache_image == (Image *) NULL)
6529  break;
6530  XSetCursorState(display,windows,MagickTrue);
6531  XCheckRefreshWindows(display,windows);
6532  cache_image->list=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
6533  XSetCursorState(display,windows,MagickFalse);
6534  if (cache_image->list == (Image *) NULL)
6535  {
6536  cache_image=DestroyImage(cache_image);
6537  break;
6538  }
6539  cache_image->columns=(size_t) windows->image.ximage->width;
6540  cache_image->rows=(size_t) windows->image.ximage->height;
6541  cache_image->geometry=windows->image.crop_geometry;
6542  if (windows->image.crop_geometry != (char *) NULL)
6543  {
6544  cache_image->geometry=AcquireString((char *) NULL);
6545  (void) CopyMagickString(cache_image->geometry,
6546  windows->image.crop_geometry,MaxTextExtent);
6547  }
6548  if (undo_image == (Image *) NULL)
6549  {
6550  undo_image=cache_image;
6551  break;
6552  }
6553  undo_image->next=cache_image;
6554  undo_image->next->previous=undo_image;
6555  undo_image=undo_image->next;
6556  break;
6557  }
6558  default:
6559  break;
6560  }
6561  if (command == RedoCommand)
6562  {
6563  /*
6564  Redo the last image transformation.
6565  */
6566  if (redo_image == (Image *) NULL)
6567  {
6568  (void) XBell(display,0);
6569  return;
6570  }
6571  windows->image.window_changes.width=(int) redo_image->columns;
6572  windows->image.window_changes.height=(int) redo_image->rows;
6573  if (windows->image.crop_geometry != (char *) NULL)
6574  windows->image.crop_geometry=(char *)
6575  RelinquishMagickMemory(windows->image.crop_geometry);
6576  windows->image.crop_geometry=redo_image->geometry;
6577  *image=DestroyImage(*image);
6578  *image=redo_image;
6579  redo_image=NewImageList();
6580  if (windows->image.orphan != MagickFalse)
6581  return;
6582  XConfigureImageColormap(display,resource_info,windows,*image);
6583  (void) XConfigureImage(display,resource_info,windows,*image);
6584  return;
6585  }
6586  if (command != InfoCommand)
6587  return;
6588  /*
6589  Display image info.
6590  */
6591  XSetCursorState(display,windows,MagickTrue);
6592  XCheckRefreshWindows(display,windows);
6593  XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6594  XSetCursorState(display,windows,MagickFalse);
6595 }
6596 ␌
6597 /*
6598 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6599 % %
6600 % %
6601 % %
6602 + X I m a g e W i n d o w C o m m a n d %
6603 % %
6604 % %
6605 % %
6606 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6607 %
6608 % XImageWindowCommand() makes a transform to the image or Image window as
6609 % specified by a user menu button or keyboard command.
6610 %
6611 % The format of the XMagickCommand method is:
6612 %
6613 % DisplayCommand XImageWindowCommand(Display *display,
6614 % XResourceInfo *resource_info,XWindows *windows,
6615 % const MagickStatusType state,KeySym key_symbol,Image **image)
6616 %
6617 % A description of each parameter follows:
6618 %
6619 % o nexus: Method XImageWindowCommand returns an image when the
6620 % user chooses 'Open Image' from the command menu. Otherwise a null
6621 % image is returned.
6622 %
6623 % o display: Specifies a connection to an X server; returned from
6624 % XOpenDisplay.
6625 %
6626 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6627 %
6628 % o windows: Specifies a pointer to a XWindows structure.
6629 %
6630 % o state: key mask.
6631 %
6632 % o key_symbol: Specifies a command to perform.
6633 %
6634 % o image: the image; XImageWIndowCommand
6635 % may transform the image and return a new image pointer.
6636 %
6637 */
6638 static DisplayCommand XImageWindowCommand(Display *display,
6639  XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6640  KeySym key_symbol,Image **image)
6641 {
6642  static char
6643  delta[MaxTextExtent] = "";
6644 
6645  static const char
6646  Digits[] = "01234567890";
6647 
6648  static KeySym
6649  last_symbol = XK_0;
6650 
6651  if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6652  {
6653  if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6654  {
6655  *delta='\0';
6656  resource_info->quantum=1;
6657  }
6658  last_symbol=key_symbol;
6659  delta[strlen(delta)+1]='\0';
6660  delta[strlen(delta)]=Digits[key_symbol-XK_0];
6661  resource_info->quantum=StringToLong(delta);
6662  return(NullCommand);
6663  }
6664  last_symbol=key_symbol;
6665  if (resource_info->immutable)
6666  {
6667  /*
6668  Virtual image window has a restricted command set.
6669  */
6670  switch (key_symbol)
6671  {
6672  case XK_question:
6673  return(InfoCommand);
6674  case XK_p:
6675  case XK_Print:
6676  return(PrintCommand);
6677  case XK_space:
6678  return(NextCommand);
6679  case XK_q:
6680  case XK_Escape:
6681  return(QuitCommand);
6682  default:
6683  break;
6684  }
6685  return(NullCommand);
6686  }
6687  switch ((int) key_symbol)
6688  {
6689  case XK_o:
6690  {
6691  if ((state & ControlMask) == 0)
6692  break;
6693  return(OpenCommand);
6694  }
6695  case XK_space:
6696  return(NextCommand);
6697  case XK_BackSpace:
6698  return(FormerCommand);
6699  case XK_s:
6700  {
6701  if ((state & Mod1Mask) != 0)
6702  return(SwirlCommand);
6703  if ((state & ControlMask) == 0)
6704  return(ShearCommand);
6705  return(SaveCommand);
6706  }
6707  case XK_p:
6708  case XK_Print:
6709  {
6710  if ((state & Mod1Mask) != 0)
6711  return(OilPaintCommand);
6712  if ((state & Mod4Mask) != 0)
6713  return(ColorCommand);
6714  if ((state & ControlMask) == 0)
6715  return(NullCommand);
6716  return(PrintCommand);
6717  }
6718  case XK_d:
6719  {
6720  if ((state & Mod4Mask) != 0)
6721  return(DrawCommand);
6722  if ((state & ControlMask) == 0)
6723  return(NullCommand);
6724  return(DeleteCommand);
6725  }
6726  case XK_Select:
6727  {
6728  if ((state & ControlMask) == 0)
6729  return(NullCommand);
6730  return(SelectCommand);
6731  }
6732  case XK_n:
6733  {
6734  if ((state & ControlMask) == 0)
6735  return(NullCommand);
6736  return(NewCommand);
6737  }
6738  case XK_q:
6739  case XK_Escape:
6740  return(QuitCommand);
6741  case XK_z:
6742  case XK_Undo:
6743  {
6744  if ((state & ControlMask) == 0)
6745  return(NullCommand);
6746  return(UndoCommand);
6747  }
6748  case XK_r:
6749  case XK_Redo:
6750  {
6751  if ((state & ControlMask) == 0)
6752  return(RollCommand);
6753  return(RedoCommand);
6754  }
6755  case XK_x:
6756  {
6757  if ((state & ControlMask) == 0)
6758  return(NullCommand);
6759  return(CutCommand);
6760  }
6761  case XK_c:
6762  {
6763  if ((state & Mod1Mask) != 0)
6764  return(CharcoalDrawCommand);
6765  if ((state & ControlMask) == 0)
6766  return(CropCommand);
6767  return(CopyCommand);
6768  }
6769  case XK_v:
6770  case XK_Insert:
6771  {
6772  if ((state & Mod4Mask) != 0)
6773  return(CompositeCommand);
6774  if ((state & ControlMask) == 0)
6775  return(FlipCommand);
6776  return(PasteCommand);
6777  }
6778  case XK_less:
6779  return(HalfSizeCommand);
6780  case XK_minus:
6781  return(OriginalSizeCommand);
6782  case XK_greater:
6783  return(DoubleSizeCommand);
6784  case XK_percent:
6785  return(ResizeCommand);
6786  case XK_at:
6787  return(RefreshCommand);
6788  case XK_bracketleft:
6789  return(ChopCommand);
6790  case XK_h:
6791  return(FlopCommand);
6792  case XK_slash:
6793  return(RotateRightCommand);
6794  case XK_backslash:
6795  return(RotateLeftCommand);
6796  case XK_asterisk:
6797  return(RotateCommand);
6798  case XK_t:
6799  return(TrimCommand);
6800  case XK_H:
6801  return(HueCommand);
6802  case XK_S:
6803  return(SaturationCommand);
6804  case XK_L:
6805  return(BrightnessCommand);
6806  case XK_G:
6807  return(GammaCommand);
6808  case XK_C:
6809  return(SpiffCommand);
6810  case XK_Z:
6811  return(DullCommand);
6812  case XK_N:
6813  return(NormalizeCommand);
6814  case XK_equal:
6815  return(EqualizeCommand);
6816  case XK_asciitilde:
6817  return(NegateCommand);
6818  case XK_period:
6819  return(GrayscaleCommand);
6820  case XK_numbersign:
6821  return(QuantizeCommand);
6822  case XK_F2:
6823  return(DespeckleCommand);
6824  case XK_F3:
6825  return(EmbossCommand);
6826  case XK_F4:
6827  return(ReduceNoiseCommand);
6828  case XK_F5:
6829  return(AddNoiseCommand);
6830  case XK_F6:
6831  return(SharpenCommand);
6832  case XK_F7:
6833  return(BlurCommand);
6834  case XK_F8:
6835  return(ThresholdCommand);
6836  case XK_F9:
6837  return(EdgeDetectCommand);
6838  case XK_F10:
6839  return(SpreadCommand);
6840  case XK_F11:
6841  return(ShadeCommand);
6842  case XK_F12:
6843  return(RaiseCommand);
6844  case XK_F13:
6845  return(SegmentCommand);
6846  case XK_i:
6847  {
6848  if ((state & Mod1Mask) == 0)
6849  return(NullCommand);
6850  return(ImplodeCommand);
6851  }
6852  case XK_w:
6853  {
6854  if ((state & Mod1Mask) == 0)
6855  return(NullCommand);
6856  return(WaveCommand);
6857  }
6858  case XK_m:
6859  {
6860  if ((state & Mod4Mask) == 0)
6861  return(NullCommand);
6862  return(MatteCommand);
6863  }
6864  case XK_b:
6865  {
6866  if ((state & Mod4Mask) == 0)
6867  return(NullCommand);
6868  return(AddBorderCommand);
6869  }
6870  case XK_f:
6871  {
6872  if ((state & Mod4Mask) == 0)
6873  return(NullCommand);
6874  return(AddFrameCommand);
6875  }
6876  case XK_exclam:
6877  {
6878  if ((state & Mod4Mask) == 0)
6879  return(NullCommand);
6880  return(CommentCommand);
6881  }
6882  case XK_a:
6883  {
6884  if ((state & Mod1Mask) != 0)
6885  return(ApplyCommand);
6886  if ((state & Mod4Mask) != 0)
6887  return(AnnotateCommand);
6888  if ((state & ControlMask) == 0)
6889  return(NullCommand);
6890  return(RegionOfInterestCommand);
6891  }
6892  case XK_question:
6893  return(InfoCommand);
6894  case XK_plus:
6895  return(ZoomCommand);
6896  case XK_P:
6897  {
6898  if ((state & ShiftMask) == 0)
6899  return(NullCommand);
6900  return(ShowPreviewCommand);
6901  }
6902  case XK_Execute:
6903  return(LaunchCommand);
6904  case XK_F1:
6905  return(HelpCommand);
6906  case XK_Find:
6907  return(BrowseDocumentationCommand);
6908  case XK_Menu:
6909  {
6910  (void) XMapRaised(display,windows->command.id);
6911  return(NullCommand);
6912  }
6913  case XK_Next:
6914  case XK_Prior:
6915  case XK_Home:
6916  case XK_KP_Home:
6917  {
6918  XTranslateImage(display,windows,*image,key_symbol);
6919  return(NullCommand);
6920  }
6921  case XK_Up:
6922  case XK_KP_Up:
6923  case XK_Down:
6924  case XK_KP_Down:
6925  case XK_Left:
6926  case XK_KP_Left:
6927  case XK_Right:
6928  case XK_KP_Right:
6929  {
6930  if ((state & Mod1Mask) != 0)
6931  {
6933  crop_info;
6934 
6935  /*
6936  Trim one pixel from edge of image.
6937  */
6938  crop_info.x=0;
6939  crop_info.y=0;
6940  crop_info.width=(size_t) windows->image.ximage->width;
6941  crop_info.height=(size_t) windows->image.ximage->height;
6942  if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
6943  {
6944  if (resource_info->quantum >= (int) crop_info.height)
6945  resource_info->quantum=(int) crop_info.height-1;
6946  crop_info.height-=resource_info->quantum;
6947  }
6948  if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
6949  {
6950  if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
6951  resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
6952  crop_info.y+=resource_info->quantum;
6953  crop_info.height-=resource_info->quantum;
6954  }
6955  if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
6956  {
6957  if (resource_info->quantum >= (int) crop_info.width)
6958  resource_info->quantum=(int) crop_info.width-1;
6959  crop_info.width-=resource_info->quantum;
6960  }
6961  if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
6962  {
6963  if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
6964  resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
6965  crop_info.x+=resource_info->quantum;
6966  crop_info.width-=resource_info->quantum;
6967  }
6968  if ((int) (windows->image.x+windows->image.width) >
6969  (int) crop_info.width)
6970  windows->image.x=(int) (crop_info.width-windows->image.width);
6971  if ((int) (windows->image.y+windows->image.height) >
6972  (int) crop_info.height)
6973  windows->image.y=(int) (crop_info.height-windows->image.height);
6974  XSetCropGeometry(display,windows,&crop_info,*image);
6975  windows->image.window_changes.width=(int) crop_info.width;
6976  windows->image.window_changes.height=(int) crop_info.height;
6977  (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
6978  (void) XConfigureImage(display,resource_info,windows,*image);
6979  return(NullCommand);
6980  }
6981  XTranslateImage(display,windows,*image,key_symbol);
6982  return(NullCommand);
6983  }
6984  default:
6985  return(NullCommand);
6986  }
6987  return(NullCommand);
6988 }
6989 ␌
6990 /*
6991 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6992 % %
6993 % %
6994 % %
6995 + X M a g i c k C o m m a n d %
6996 % %
6997 % %
6998 % %
6999 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7000 %
7001 % XMagickCommand() makes a transform to the image or Image window as
7002 % specified by a user menu button or keyboard command.
7003 %
7004 % The format of the XMagickCommand method is:
7005 %
7006 % Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7007 % XWindows *windows,const DisplayCommand command,Image **image)
7008 %
7009 % A description of each parameter follows:
7010 %
7011 % o nexus: Method XMagickCommand returns an image when the
7012 % user chooses 'Load Image' from the command menu. Otherwise a null
7013 % image is returned.
7014 %
7015 % o display: Specifies a connection to an X server; returned from
7016 % XOpenDisplay.
7017 %
7018 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7019 %
7020 % o windows: Specifies a pointer to a XWindows structure.
7021 %
7022 % o command: Specifies a command to perform.
7023 %
7024 % o image: the image; XMagickCommand
7025 % may transform the image and return a new image pointer.
7026 %
7027 */
7028 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7029  XWindows *windows,const DisplayCommand command,Image **image)
7030 {
7031  char
7032  filename[MaxTextExtent],
7033  geometry[MaxTextExtent],
7034  modulate_factors[MaxTextExtent];
7035 
7036  GeometryInfo
7037  geometry_info;
7038 
7039  Image
7040  *nexus;
7041 
7042  ImageInfo
7043  *image_info;
7044 
7045  int
7046  x,
7047  y;
7048 
7049  MagickStatusType
7050  flags,
7051  status;
7052 
7053  QuantizeInfo
7054  quantize_info;
7055 
7057  page_geometry;
7058 
7059  int
7060  i;
7061 
7062  static char
7063  color[MaxTextExtent] = "gray";
7064 
7065  unsigned int
7066  height,
7067  width;
7068 
7069  /*
7070  Process user command.
7071  */
7072  XCheckRefreshWindows(display,windows);
7073  XImageCache(display,resource_info,windows,command,image);
7074  nexus=NewImageList();
7075  windows->image.window_changes.width=windows->image.ximage->width;
7076  windows->image.window_changes.height=windows->image.ximage->height;
7077  image_info=CloneImageInfo(resource_info->image_info);
7078  SetGeometryInfo(&geometry_info);
7079  GetQuantizeInfo(&quantize_info);
7080  switch (command)
7081  {
7082  case OpenCommand:
7083  {
7084  /*
7085  Load image.
7086  */
7087  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7088  break;
7089  }
7090  case NextCommand:
7091  {
7092  /*
7093  Display next image.
7094  */
7095  for (i=0; i < resource_info->quantum; i++)
7096  XClientMessage(display,windows->image.id,windows->im_protocols,
7097  windows->im_next_image,CurrentTime);
7098  break;
7099  }
7100  case FormerCommand:
7101  {
7102  /*
7103  Display former image.
7104  */
7105  for (i=0; i < resource_info->quantum; i++)
7106  XClientMessage(display,windows->image.id,windows->im_protocols,
7107  windows->im_former_image,CurrentTime);
7108  break;
7109  }
7110  case SelectCommand:
7111  {
7112  int
7113  status;
7114 
7115  /*
7116  Select image.
7117  */
7118  if (*resource_info->home_directory == '\0')
7119  (void) CopyMagickString(resource_info->home_directory,".",
7120  MaxTextExtent);
7121  status=chdir(resource_info->home_directory);
7122  if (status == -1)
7123  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
7124  FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
7125  nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7126  break;
7127  }
7128  case SaveCommand:
7129  {
7130  /*
7131  Save image.
7132  */
7133  status=XSaveImage(display,resource_info,windows,*image);
7134  if (status == MagickFalse)
7135  {
7136  char
7137  message[MaxTextExtent];
7138 
7139  (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7140  (*image)->exception.reason != (char *) NULL ?
7141  (*image)->exception.reason : "",
7142  (*image)->exception.description != (char *) NULL ?
7143  (*image)->exception.description : "");
7144  XNoticeWidget(display,windows,"Unable to save file:",message);
7145  break;
7146  }
7147  break;
7148  }
7149  case PrintCommand:
7150  {
7151  /*
7152  Print image.
7153  */
7154  status=XPrintImage(display,resource_info,windows,*image);
7155  if (status == MagickFalse)
7156  {
7157  char
7158  message[MaxTextExtent];
7159 
7160  (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7161  (*image)->exception.reason != (char *) NULL ?
7162  (*image)->exception.reason : "",
7163  (*image)->exception.description != (char *) NULL ?
7164  (*image)->exception.description : "");
7165  XNoticeWidget(display,windows,"Unable to print file:",message);
7166  break;
7167  }
7168  break;
7169  }
7170  case DeleteCommand:
7171  {
7172  static char
7173  filename[MaxTextExtent] = "\0";
7174 
7175  /*
7176  Delete image file.
7177  */
7178  XFileBrowserWidget(display,windows,"Delete",filename);
7179  if (*filename == '\0')
7180  break;
7181  status=ShredFile(filename);
7182  status|=remove_utf8(filename);
7183  if (status != MagickFalse)
7184  XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7185  break;
7186  }
7187  case NewCommand:
7188  {
7189  int
7190  status;
7191 
7192  static char
7193  color[MaxTextExtent] = "gray",
7194  geometry[MaxTextExtent] = "640x480";
7195 
7196  static const char
7197  *format = "gradient";
7198 
7199  /*
7200  Query user for canvas geometry.
7201  */
7202  status=XDialogWidget(display,windows,"New","Enter image geometry:",
7203  geometry);
7204  if (*geometry == '\0')
7205  break;
7206  if (status == 0)
7207  format="xc";
7208  XColorBrowserWidget(display,windows,"Select",color);
7209  if (*color == '\0')
7210  break;
7211  /*
7212  Create canvas.
7213  */
7214  (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7215  "%s:%s",format,color);
7216  (void) CloneString(&image_info->size,geometry);
7217  nexus=ReadImage(image_info,&(*image)->exception);
7218  CatchException(&(*image)->exception);
7219  XClientMessage(display,windows->image.id,windows->im_protocols,
7220  windows->im_next_image,CurrentTime);
7221  break;
7222  }
7223  case VisualDirectoryCommand:
7224  {
7225  /*
7226  Visual Image directory.
7227  */
7228  nexus=XVisualDirectoryImage(display,resource_info,windows);
7229  break;
7230  }
7231  case QuitCommand:
7232  {
7233  /*
7234  exit program.
7235  */
7236  if (resource_info->confirm_exit == MagickFalse)
7237  XClientMessage(display,windows->image.id,windows->im_protocols,
7238  windows->im_exit,CurrentTime);
7239  else
7240  {
7241  int
7242  status;
7243 
7244  /*
7245  Confirm program exit.
7246  */
7247  status=XConfirmWidget(display,windows,"Do you really want to exit",
7248  resource_info->client_name);
7249  if (status > 0)
7250  XClientMessage(display,windows->image.id,windows->im_protocols,
7251  windows->im_exit,CurrentTime);
7252  }
7253  break;
7254  }
7255  case CutCommand:
7256  {
7257  /*
7258  Cut image.
7259  */
7260  (void) XCropImage(display,resource_info,windows,*image,CutMode);
7261  break;
7262  }
7263  case CopyCommand:
7264  {
7265  /*
7266  Copy image.
7267  */
7268  (void) XCropImage(display,resource_info,windows,*image,CopyMode);
7269  break;
7270  }
7271  case PasteCommand:
7272  {
7273  /*
7274  Paste image.
7275  */
7276  status=XPasteImage(display,resource_info,windows,*image);
7277  if (status == MagickFalse)
7278  {
7279  XNoticeWidget(display,windows,"Unable to paste X image",
7280  (*image)->filename);
7281  break;
7282  }
7283  break;
7284  }
7285  case HalfSizeCommand:
7286  {
7287  /*
7288  Half image size.
7289  */
7290  windows->image.window_changes.width=windows->image.ximage->width/2;
7291  windows->image.window_changes.height=windows->image.ximage->height/2;
7292  (void) XConfigureImage(display,resource_info,windows,*image);
7293  break;
7294  }
7295  case OriginalSizeCommand:
7296  {
7297  /*
7298  Original image size.
7299  */
7300  windows->image.window_changes.width=(int) (*image)->columns;
7301  windows->image.window_changes.height=(int) (*image)->rows;
7302  (void) XConfigureImage(display,resource_info,windows,*image);
7303  break;
7304  }
7305  case DoubleSizeCommand:
7306  {
7307  /*
7308  Double the image size.
7309  */
7310  windows->image.window_changes.width=windows->image.ximage->width << 1;
7311  windows->image.window_changes.height=windows->image.ximage->height << 1;
7312  (void) XConfigureImage(display,resource_info,windows,*image);
7313  break;
7314  }
7315  case ResizeCommand:
7316  {
7317  int
7318  status;
7319 
7320  size_t
7321  height,
7322  width;
7323 
7324  ssize_t
7325  x,
7326  y;
7327 
7328  /*
7329  Resize image.
7330  */
7331  width=(size_t) windows->image.ximage->width;
7332  height=(size_t) windows->image.ximage->height;
7333  x=0;
7334  y=0;
7335  (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7336  (double) width,(double) height);
7337  status=XDialogWidget(display,windows,"Resize",
7338  "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7339  if (*geometry == '\0')
7340  break;
7341  if (status == 0)
7342  (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7343  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7344  windows->image.window_changes.width=(int) width;
7345  windows->image.window_changes.height=(int) height;
7346  (void) XConfigureImage(display,resource_info,windows,*image);
7347  break;
7348  }
7349  case ApplyCommand:
7350  {
7351  char
7352  image_geometry[MaxTextExtent];
7353 
7354  if ((windows->image.crop_geometry == (char *) NULL) &&
7355  ((int) (*image)->columns == windows->image.ximage->width) &&
7356  ((int) (*image)->rows == windows->image.ximage->height))
7357  break;
7358  /*
7359  Apply size transforms to image.
7360  */
7361  XSetCursorState(display,windows,MagickTrue);
7362  XCheckRefreshWindows(display,windows);
7363  /*
7364  Crop and/or scale displayed image.
7365  */
7366  (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7367  windows->image.ximage->width,windows->image.ximage->height);
7368  (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7369  if (windows->image.crop_geometry != (char *) NULL)
7370  windows->image.crop_geometry=(char *)
7371  RelinquishMagickMemory(windows->image.crop_geometry);
7372  windows->image.x=0;
7373  windows->image.y=0;
7374  XConfigureImageColormap(display,resource_info,windows,*image);
7375  (void) XConfigureImage(display,resource_info,windows,*image);
7376  break;
7377  }
7378  case RefreshCommand:
7379  {
7380  (void) XConfigureImage(display,resource_info,windows,*image);
7381  break;
7382  }
7383  case RestoreCommand:
7384  {
7385  /*
7386  Restore Image window to its original size.
7387  */
7388  if ((windows->image.width == (unsigned int) (*image)->columns) &&
7389  (windows->image.height == (unsigned int) (*image)->rows) &&
7390  (windows->image.crop_geometry == (char *) NULL))
7391  {
7392  (void) XBell(display,0);
7393  break;
7394  }
7395  windows->image.window_changes.width=(int) (*image)->columns;
7396  windows->image.window_changes.height=(int) (*image)->rows;
7397  if (windows->image.crop_geometry != (char *) NULL)
7398  {
7399  windows->image.crop_geometry=(char *)
7400  RelinquishMagickMemory(windows->image.crop_geometry);
7401  windows->image.crop_geometry=(char *) NULL;
7402  windows->image.x=0;
7403  windows->image.y=0;
7404  }
7405  XConfigureImageColormap(display,resource_info,windows,*image);
7406  (void) XConfigureImage(display,resource_info,windows,*image);
7407  break;
7408  }
7409  case CropCommand:
7410  {
7411  /*
7412  Crop image.
7413  */
7414  (void) XCropImage(display,resource_info,windows,*image,CropMode);
7415  break;
7416  }
7417  case ChopCommand:
7418  {
7419  /*
7420  Chop image.
7421  */
7422  status=XChopImage(display,resource_info,windows,image);
7423  if (status == MagickFalse)
7424  {
7425  XNoticeWidget(display,windows,"Unable to cut X image",
7426  (*image)->filename);
7427  break;
7428  }
7429  break;
7430  }
7431  case FlopCommand:
7432  {
7433  Image
7434  *flop_image;
7435 
7436  /*
7437  Flop image scanlines.
7438  */
7439  XSetCursorState(display,windows,MagickTrue);
7440  XCheckRefreshWindows(display,windows);
7441  flop_image=FlopImage(*image,&(*image)->exception);
7442  if (flop_image != (Image *) NULL)
7443  {
7444  *image=DestroyImage(*image);
7445  *image=flop_image;
7446  }
7447  CatchException(&(*image)->exception);
7448  XSetCursorState(display,windows,MagickFalse);
7449  if (windows->image.crop_geometry != (char *) NULL)
7450  {
7451  /*
7452  Flop crop geometry.
7453  */
7454  width=(unsigned int) (*image)->columns;
7455  height=(unsigned int) (*image)->rows;
7456  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7457  &width,&height);
7458  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7459  "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7460  }
7461  if (windows->image.orphan != MagickFalse)
7462  break;
7463  (void) XConfigureImage(display,resource_info,windows,*image);
7464  break;
7465  }
7466  case FlipCommand:
7467  {
7468  Image
7469  *flip_image;
7470 
7471  /*
7472  Flip image scanlines.
7473  */
7474  XSetCursorState(display,windows,MagickTrue);
7475  XCheckRefreshWindows(display,windows);
7476  flip_image=FlipImage(*image,&(*image)->exception);
7477  if (flip_image != (Image *) NULL)
7478  {
7479  *image=DestroyImage(*image);
7480  *image=flip_image;
7481  }
7482  CatchException(&(*image)->exception);
7483  XSetCursorState(display,windows,MagickFalse);
7484  if (windows->image.crop_geometry != (char *) NULL)
7485  {
7486  /*
7487  Flip crop geometry.
7488  */
7489  width=(unsigned int) (*image)->columns;
7490  height=(unsigned int) (*image)->rows;
7491  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7492  &width,&height);
7493  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7494  "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7495  }
7496  if (windows->image.orphan != MagickFalse)
7497  break;
7498  (void) XConfigureImage(display,resource_info,windows,*image);
7499  break;
7500  }
7501  case RotateRightCommand:
7502  {
7503  /*
7504  Rotate image 90 degrees clockwise.
7505  */
7506  status=XRotateImage(display,resource_info,windows,90.0,image);
7507  if (status == MagickFalse)
7508  {
7509  XNoticeWidget(display,windows,"Unable to rotate X image",
7510  (*image)->filename);
7511  break;
7512  }
7513  break;
7514  }
7515  case RotateLeftCommand:
7516  {
7517  /*
7518  Rotate image 90 degrees counter-clockwise.
7519  */
7520  status=XRotateImage(display,resource_info,windows,-90.0,image);
7521  if (status == MagickFalse)
7522  {
7523  XNoticeWidget(display,windows,"Unable to rotate X image",
7524  (*image)->filename);
7525  break;
7526  }
7527  break;
7528  }
7529  case RotateCommand:
7530  {
7531  /*
7532  Rotate image.
7533  */
7534  status=XRotateImage(display,resource_info,windows,0.0,image);
7535  if (status == MagickFalse)
7536  {
7537  XNoticeWidget(display,windows,"Unable to rotate X image",
7538  (*image)->filename);
7539  break;
7540  }
7541  break;
7542  }
7543  case ShearCommand:
7544  {
7545  Image
7546  *shear_image;
7547 
7548  static char
7549  geometry[MaxTextExtent] = "45.0x45.0";
7550 
7551  /*
7552  Query user for shear color and geometry.
7553  */
7554  XColorBrowserWidget(display,windows,"Select",color);
7555  if (*color == '\0')
7556  break;
7557  (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7558  geometry);
7559  if (*geometry == '\0')
7560  break;
7561  /*
7562  Shear image.
7563  */
7564  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7565  XSetCursorState(display,windows,MagickTrue);
7566  XCheckRefreshWindows(display,windows);
7567  (void) QueryColorDatabase(color,&(*image)->background_color,
7568  &(*image)->exception);
7569  flags=ParseGeometry(geometry,&geometry_info);
7570  if ((flags & SigmaValue) == 0)
7571  geometry_info.sigma=geometry_info.rho;
7572  shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7573  &(*image)->exception);
7574  if (shear_image != (Image *) NULL)
7575  {
7576  *image=DestroyImage(*image);
7577  *image=shear_image;
7578  }
7579  CatchException(&(*image)->exception);
7580  XSetCursorState(display,windows,MagickFalse);
7581  if (windows->image.orphan != MagickFalse)
7582  break;
7583  windows->image.window_changes.width=(int) (*image)->columns;
7584  windows->image.window_changes.height=(int) (*image)->rows;
7585  XConfigureImageColormap(display,resource_info,windows,*image);
7586  (void) XConfigureImage(display,resource_info,windows,*image);
7587  break;
7588  }
7589  case RollCommand:
7590  {
7591  Image
7592  *roll_image;
7593 
7594  static char
7595  geometry[MaxTextExtent] = "+2+2";
7596 
7597  /*
7598  Query user for the roll geometry.
7599  */
7600  (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7601  geometry);
7602  if (*geometry == '\0')
7603  break;
7604  /*
7605  Roll image.
7606  */
7607  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7608  XSetCursorState(display,windows,MagickTrue);
7609  XCheckRefreshWindows(display,windows);
7610  (void) ParsePageGeometry(*image,geometry,&page_geometry,
7611  &(*image)->exception);
7612  roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7613  &(*image)->exception);
7614  if (roll_image != (Image *) NULL)
7615  {
7616  *image=DestroyImage(*image);
7617  *image=roll_image;
7618  }
7619  CatchException(&(*image)->exception);
7620  XSetCursorState(display,windows,MagickFalse);
7621  if (windows->image.orphan != MagickFalse)
7622  break;
7623  windows->image.window_changes.width=(int) (*image)->columns;
7624  windows->image.window_changes.height=(int) (*image)->rows;
7625  XConfigureImageColormap(display,resource_info,windows,*image);
7626  (void) XConfigureImage(display,resource_info,windows,*image);
7627  break;
7628  }
7629  case TrimCommand:
7630  {
7631  static char
7632  fuzz[MaxTextExtent];
7633 
7634  /*
7635  Query user for the fuzz factor.
7636  */
7637  (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7638  (*image)->fuzz/((double) QuantumRange+1.0));
7639  (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7640  if (*fuzz == '\0')
7641  break;
7642  (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7643  /*
7644  Trim image.
7645  */
7646  status=XTrimImage(display,resource_info,windows,*image);
7647  if (status == MagickFalse)
7648  {
7649  XNoticeWidget(display,windows,"Unable to trim X image",
7650  (*image)->filename);
7651  break;
7652  }
7653  break;
7654  }
7655  case HueCommand:
7656  {
7657  static char
7658  hue_percent[MaxTextExtent] = "110";
7659 
7660  /*
7661  Query user for percent hue change.
7662  */
7663  (void) XDialogWidget(display,windows,"Apply",
7664  "Enter percent change in image hue (0-200):",hue_percent);
7665  if (*hue_percent == '\0')
7666  break;
7667  /*
7668  Vary the image hue.
7669  */
7670  XSetCursorState(display,windows,MagickTrue);
7671  XCheckRefreshWindows(display,windows);
7672  (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7673  (void) ConcatenateMagickString(modulate_factors,hue_percent,
7674  MaxTextExtent);
7675  (void) ModulateImage(*image,modulate_factors);
7676  XSetCursorState(display,windows,MagickFalse);
7677  if (windows->image.orphan != MagickFalse)
7678  break;
7679  XConfigureImageColormap(display,resource_info,windows,*image);
7680  (void) XConfigureImage(display,resource_info,windows,*image);
7681  break;
7682  }
7683  case SaturationCommand:
7684  {
7685  static char
7686  saturation_percent[MaxTextExtent] = "110";
7687 
7688  /*
7689  Query user for percent saturation change.
7690  */
7691  (void) XDialogWidget(display,windows,"Apply",
7692  "Enter percent change in color saturation (0-200):",saturation_percent);
7693  if (*saturation_percent == '\0')
7694  break;
7695  /*
7696  Vary color saturation.
7697  */
7698  XSetCursorState(display,windows,MagickTrue);
7699  XCheckRefreshWindows(display,windows);
7700  (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7701  (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7702  MaxTextExtent);
7703  (void) ModulateImage(*image,modulate_factors);
7704  XSetCursorState(display,windows,MagickFalse);
7705  if (windows->image.orphan != MagickFalse)
7706  break;
7707  XConfigureImageColormap(display,resource_info,windows,*image);
7708  (void) XConfigureImage(display,resource_info,windows,*image);
7709  break;
7710  }
7711  case BrightnessCommand:
7712  {
7713  static char
7714  brightness_percent[MaxTextExtent] = "110";
7715 
7716  /*
7717  Query user for percent brightness change.
7718  */
7719  (void) XDialogWidget(display,windows,"Apply",
7720  "Enter percent change in color brightness (0-200):",brightness_percent);
7721  if (*brightness_percent == '\0')
7722  break;
7723  /*
7724  Vary the color brightness.
7725  */
7726  XSetCursorState(display,windows,MagickTrue);
7727  XCheckRefreshWindows(display,windows);
7728  (void) CopyMagickString(modulate_factors,brightness_percent,
7729  MaxTextExtent);
7730  (void) ModulateImage(*image,modulate_factors);
7731  XSetCursorState(display,windows,MagickFalse);
7732  if (windows->image.orphan != MagickFalse)
7733  break;
7734  XConfigureImageColormap(display,resource_info,windows,*image);
7735  (void) XConfigureImage(display,resource_info,windows,*image);
7736  break;
7737  }
7738  case GammaCommand:
7739  {
7740  static char
7741  factor[MaxTextExtent] = "1.6";
7742 
7743  /*
7744  Query user for gamma value.
7745  */
7746  (void) XDialogWidget(display,windows,"Gamma",
7747  "Enter gamma value (e.g. 1.0,1.0,1.6):",factor);
7748  if (*factor == '\0')
7749  break;
7750  /*
7751  Gamma correct image.
7752  */
7753  XSetCursorState(display,windows,MagickTrue);
7754  XCheckRefreshWindows(display,windows);
7755  (void) GammaImage(*image,factor);
7756  XSetCursorState(display,windows,MagickFalse);
7757  if (windows->image.orphan != MagickFalse)
7758  break;
7759  XConfigureImageColormap(display,resource_info,windows,*image);
7760  (void) XConfigureImage(display,resource_info,windows,*image);
7761  break;
7762  }
7763  case SpiffCommand:
7764  {
7765  /*
7766  Sharpen the image contrast.
7767  */
7768  XSetCursorState(display,windows,MagickTrue);
7769  XCheckRefreshWindows(display,windows);
7770  (void) ContrastImage(*image,MagickTrue);
7771  XSetCursorState(display,windows,MagickFalse);
7772  if (windows->image.orphan != MagickFalse)
7773  break;
7774  XConfigureImageColormap(display,resource_info,windows,*image);
7775  (void) XConfigureImage(display,resource_info,windows,*image);
7776  break;
7777  }
7778  case DullCommand:
7779  {
7780  /*
7781  Dull the image contrast.
7782  */
7783  XSetCursorState(display,windows,MagickTrue);
7784  XCheckRefreshWindows(display,windows);
7785  (void) ContrastImage(*image,MagickFalse);
7786  XSetCursorState(display,windows,MagickFalse);
7787  if (windows->image.orphan != MagickFalse)
7788  break;
7789  XConfigureImageColormap(display,resource_info,windows,*image);
7790  (void) XConfigureImage(display,resource_info,windows,*image);
7791  break;
7792  }
7793  case ContrastStretchCommand:
7794  {
7795  double
7796  black_point,
7797  white_point;
7798 
7799  static char
7800  levels[MaxTextExtent] = "1%";
7801 
7802  /*
7803  Query user for gamma value.
7804  */
7805  (void) XDialogWidget(display,windows,"Contrast Stretch",
7806  "Enter black and white points:",levels);
7807  if (*levels == '\0')
7808  break;
7809  /*
7810  Contrast stretch image.
7811  */
7812  XSetCursorState(display,windows,MagickTrue);
7813  XCheckRefreshWindows(display,windows);
7814  flags=ParseGeometry(levels,&geometry_info);
7815  black_point=geometry_info.rho;
7816  white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7817  if ((flags & PercentValue) != 0)
7818  {
7819  black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7820  white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7821  }
7822  white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7823  (void) ContrastStretchImageChannel(*image,DefaultChannels,black_point,
7824  white_point);
7825  XSetCursorState(display,windows,MagickFalse);
7826  if (windows->image.orphan != MagickFalse)
7827  break;
7828  XConfigureImageColormap(display,resource_info,windows,*image);
7829  (void) XConfigureImage(display,resource_info,windows,*image);
7830  break;
7831  }
7832  case SigmoidalContrastCommand:
7833  {
7834  static char
7835  levels[MaxTextExtent] = "3x50%";
7836 
7837  /*
7838  Query user for gamma value.
7839  */
7840  (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7841  "Enter contrast and midpoint:",levels);
7842  if (*levels == '\0')
7843  break;
7844  /*
7845  Contrast stretch image.
7846  */
7847  XSetCursorState(display,windows,MagickTrue);
7848  XCheckRefreshWindows(display,windows);
7849  (void) SigmoidalContrastImage(*image,MagickTrue,levels);
7850  XSetCursorState(display,windows,MagickFalse);
7851  if (windows->image.orphan != MagickFalse)
7852  break;
7853  XConfigureImageColormap(display,resource_info,windows,*image);
7854  (void) XConfigureImage(display,resource_info,windows,*image);
7855  break;
7856  }
7857  case NormalizeCommand:
7858  {
7859  /*
7860  Perform histogram normalization on the image.
7861  */
7862  XSetCursorState(display,windows,MagickTrue);
7863  XCheckRefreshWindows(display,windows);
7864  (void) NormalizeImage(*image);
7865  XSetCursorState(display,windows,MagickFalse);
7866  if (windows->image.orphan != MagickFalse)
7867  break;
7868  XConfigureImageColormap(display,resource_info,windows,*image);
7869  (void) XConfigureImage(display,resource_info,windows,*image);
7870  break;
7871  }
7872  case EqualizeCommand:
7873  {
7874  /*
7875  Perform histogram equalization on the image.
7876  */
7877  XSetCursorState(display,windows,MagickTrue);
7878  XCheckRefreshWindows(display,windows);
7879  (void) EqualizeImage(*image);
7880  XSetCursorState(display,windows,MagickFalse);
7881  if (windows->image.orphan != MagickFalse)
7882  break;
7883  XConfigureImageColormap(display,resource_info,windows,*image);
7884  (void) XConfigureImage(display,resource_info,windows,*image);
7885  break;
7886  }
7887  case NegateCommand:
7888  {
7889  /*
7890  Negate colors in image.
7891  */
7892  XSetCursorState(display,windows,MagickTrue);
7893  XCheckRefreshWindows(display,windows);
7894  (void) NegateImage(*image,MagickFalse);
7895  XSetCursorState(display,windows,MagickFalse);
7896  if (windows->image.orphan != MagickFalse)
7897  break;
7898  XConfigureImageColormap(display,resource_info,windows,*image);
7899  (void) XConfigureImage(display,resource_info,windows,*image);
7900  break;
7901  }
7902  case GrayscaleCommand:
7903  {
7904  /*
7905  Convert image to grayscale.
7906  */
7907  XSetCursorState(display,windows,MagickTrue);
7908  XCheckRefreshWindows(display,windows);
7909  (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7910  GrayscaleType : GrayscaleMatteType);
7911  XSetCursorState(display,windows,MagickFalse);
7912  if (windows->image.orphan != MagickFalse)
7913  break;
7914  XConfigureImageColormap(display,resource_info,windows,*image);
7915  (void) XConfigureImage(display,resource_info,windows,*image);
7916  break;
7917  }
7918  case MapCommand:
7919  {
7920  Image
7921  *affinity_image;
7922 
7923  static char
7924  filename[MaxTextExtent] = "\0";
7925 
7926  /*
7927  Request image file name from user.
7928  */
7929  XFileBrowserWidget(display,windows,"Map",filename);
7930  if (*filename == '\0')
7931  break;
7932  /*
7933  Map image.
7934  */
7935  XSetCursorState(display,windows,MagickTrue);
7936  XCheckRefreshWindows(display,windows);
7937  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
7938  affinity_image=ReadImage(image_info,&(*image)->exception);
7939  if (affinity_image != (Image *) NULL)
7940  {
7941  (void) RemapImage(&quantize_info,*image,affinity_image);
7942  affinity_image=DestroyImage(affinity_image);
7943  }
7944  CatchException(&(*image)->exception);
7945  XSetCursorState(display,windows,MagickFalse);
7946  if (windows->image.orphan != MagickFalse)
7947  break;
7948  XConfigureImageColormap(display,resource_info,windows,*image);
7949  (void) XConfigureImage(display,resource_info,windows,*image);
7950  break;
7951  }
7952  case QuantizeCommand:
7953  {
7954  int
7955  status;
7956 
7957  static char
7958  colors[MaxTextExtent] = "256";
7959 
7960  /*
7961  Query user for maximum number of colors.
7962  */
7963  status=XDialogWidget(display,windows,"Quantize",
7964  "Maximum number of colors:",colors);
7965  if (*colors == '\0')
7966  break;
7967  /*
7968  Color reduce the image.
7969  */
7970  XSetCursorState(display,windows,MagickTrue);
7971  XCheckRefreshWindows(display,windows);
7972  quantize_info.number_colors=StringToUnsignedLong(colors);
7973  quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
7974  (void) QuantizeImage(&quantize_info,*image);
7975  XSetCursorState(display,windows,MagickFalse);
7976  if (windows->image.orphan != MagickFalse)
7977  break;
7978  XConfigureImageColormap(display,resource_info,windows,*image);
7979  (void) XConfigureImage(display,resource_info,windows,*image);
7980  break;
7981  }
7982  case DespeckleCommand:
7983  {
7984  Image
7985  *despeckle_image;
7986 
7987  /*
7988  Despeckle image.
7989  */
7990  XSetCursorState(display,windows,MagickTrue);
7991  XCheckRefreshWindows(display,windows);
7992  despeckle_image=DespeckleImage(*image,&(*image)->exception);
7993  if (despeckle_image != (Image *) NULL)
7994  {
7995  *image=DestroyImage(*image);
7996  *image=despeckle_image;
7997  }
7998  CatchException(&(*image)->exception);
7999  XSetCursorState(display,windows,MagickFalse);
8000  if (windows->image.orphan != MagickFalse)
8001  break;
8002  XConfigureImageColormap(display,resource_info,windows,*image);
8003  (void) XConfigureImage(display,resource_info,windows,*image);
8004  break;
8005  }
8006  case EmbossCommand:
8007  {
8008  Image
8009  *emboss_image;
8010 
8011  static char
8012  radius[MaxTextExtent] = "0.0x1.0";
8013 
8014  /*
8015  Query user for emboss radius.
8016  */
8017  (void) XDialogWidget(display,windows,"Emboss",
8018  "Enter the emboss radius and standard deviation:",radius);
8019  if (*radius == '\0')
8020  break;
8021  /*
8022  Reduce noise in the image.
8023  */
8024  XSetCursorState(display,windows,MagickTrue);
8025  XCheckRefreshWindows(display,windows);
8026  flags=ParseGeometry(radius,&geometry_info);
8027  if ((flags & SigmaValue) == 0)
8028  geometry_info.sigma=1.0;
8029  emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8030  &(*image)->exception);
8031  if (emboss_image != (Image *) NULL)
8032  {
8033  *image=DestroyImage(*image);
8034  *image=emboss_image;
8035  }
8036  CatchException(&(*image)->exception);
8037  XSetCursorState(display,windows,MagickFalse);
8038  if (windows->image.orphan != MagickFalse)
8039  break;
8040  XConfigureImageColormap(display,resource_info,windows,*image);
8041  (void) XConfigureImage(display,resource_info,windows,*image);
8042  break;
8043  }
8044  case ReduceNoiseCommand:
8045  {
8046  Image
8047  *noise_image;
8048 
8049  static char
8050  radius[MaxTextExtent] = "0";
8051 
8052  /*
8053  Query user for noise radius.
8054  */
8055  (void) XDialogWidget(display,windows,"Reduce Noise",
8056  "Enter the noise radius:",radius);
8057  if (*radius == '\0')
8058  break;
8059  /*
8060  Reduce noise in the image.
8061  */
8062  XSetCursorState(display,windows,MagickTrue);
8063  XCheckRefreshWindows(display,windows);
8064  flags=ParseGeometry(radius,&geometry_info);
8065  noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8066  geometry_info.rho,(size_t) geometry_info.rho,&(*image)->exception);
8067  if (noise_image != (Image *) NULL)
8068  {
8069  *image=DestroyImage(*image);
8070  *image=noise_image;
8071  }
8072  CatchException(&(*image)->exception);
8073  XSetCursorState(display,windows,MagickFalse);
8074  if (windows->image.orphan != MagickFalse)
8075  break;
8076  XConfigureImageColormap(display,resource_info,windows,*image);
8077  (void) XConfigureImage(display,resource_info,windows,*image);
8078  break;
8079  }
8080  case AddNoiseCommand:
8081  {
8082  char
8083  **noises;
8084 
8085  Image
8086  *noise_image;
8087 
8088  static char
8089  noise_type[MaxTextExtent] = "Gaussian";
8090 
8091  /*
8092  Add noise to the image.
8093  */
8094  noises=GetCommandOptions(MagickNoiseOptions);
8095  if (noises == (char **) NULL)
8096  break;
8097  XListBrowserWidget(display,windows,&windows->widget,
8098  (const char **) noises,"Add Noise",
8099  "Select a type of noise to add to your image:",noise_type);
8100  noises=DestroyStringList(noises);
8101  if (*noise_type == '\0')
8102  break;
8103  XSetCursorState(display,windows,MagickTrue);
8104  XCheckRefreshWindows(display,windows);
8105  noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8106  MagickNoiseOptions,MagickFalse,noise_type),&(*image)->exception);
8107  if (noise_image != (Image *) NULL)
8108  {
8109  *image=DestroyImage(*image);
8110  *image=noise_image;
8111  }
8112  CatchException(&(*image)->exception);
8113  XSetCursorState(display,windows,MagickFalse);
8114  if (windows->image.orphan != MagickFalse)
8115  break;
8116  XConfigureImageColormap(display,resource_info,windows,*image);
8117  (void) XConfigureImage(display,resource_info,windows,*image);
8118  break;
8119  }
8120  case SharpenCommand:
8121  {
8122  Image
8123  *sharp_image;
8124 
8125  static char
8126  radius[MaxTextExtent] = "0.0x1.0";
8127 
8128  /*
8129  Query user for sharpen radius.
8130  */
8131  (void) XDialogWidget(display,windows,"Sharpen",
8132  "Enter the sharpen radius and standard deviation:",radius);
8133  if (*radius == '\0')
8134  break;
8135  /*
8136  Sharpen image scanlines.
8137  */
8138  XSetCursorState(display,windows,MagickTrue);
8139  XCheckRefreshWindows(display,windows);
8140  flags=ParseGeometry(radius,&geometry_info);
8141  sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8142  &(*image)->exception);
8143  if (sharp_image != (Image *) NULL)
8144  {
8145  *image=DestroyImage(*image);
8146  *image=sharp_image;
8147  }
8148  CatchException(&(*image)->exception);
8149  XSetCursorState(display,windows,MagickFalse);
8150  if (windows->image.orphan != MagickFalse)
8151  break;
8152  XConfigureImageColormap(display,resource_info,windows,*image);
8153  (void) XConfigureImage(display,resource_info,windows,*image);
8154  break;
8155  }
8156  case BlurCommand:
8157  {
8158  Image
8159  *blur_image;
8160 
8161  static char
8162  radius[MaxTextExtent] = "0.0x1.0";
8163 
8164  /*
8165  Query user for blur radius.
8166  */
8167  (void) XDialogWidget(display,windows,"Blur",
8168  "Enter the blur radius and standard deviation:",radius);
8169  if (*radius == '\0')
8170  break;
8171  /*
8172  Blur an image.
8173  */
8174  XSetCursorState(display,windows,MagickTrue);
8175  XCheckRefreshWindows(display,windows);
8176  flags=ParseGeometry(radius,&geometry_info);
8177  blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8178  &(*image)->exception);
8179  if (blur_image != (Image *) NULL)
8180  {
8181  *image=DestroyImage(*image);
8182  *image=blur_image;
8183  }
8184  CatchException(&(*image)->exception);
8185  XSetCursorState(display,windows,MagickFalse);
8186  if (windows->image.orphan != MagickFalse)
8187  break;
8188  XConfigureImageColormap(display,resource_info,windows,*image);
8189  (void) XConfigureImage(display,resource_info,windows,*image);
8190  break;
8191  }
8192  case ThresholdCommand:
8193  {
8194  double
8195  threshold;
8196 
8197  static char
8198  factor[MaxTextExtent] = "128";
8199 
8200  /*
8201  Query user for threshold value.
8202  */
8203  (void) XDialogWidget(display,windows,"Threshold",
8204  "Enter threshold value:",factor);
8205  if (*factor == '\0')
8206  break;
8207  /*
8208  Gamma correct image.
8209  */
8210  XSetCursorState(display,windows,MagickTrue);
8211  XCheckRefreshWindows(display,windows);
8212  threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8213  (void) BilevelImage(*image,threshold);
8214  XSetCursorState(display,windows,MagickFalse);
8215  if (windows->image.orphan != MagickFalse)
8216  break;
8217  XConfigureImageColormap(display,resource_info,windows,*image);
8218  (void) XConfigureImage(display,resource_info,windows,*image);
8219  break;
8220  }
8221  case EdgeDetectCommand:
8222  {
8223  Image
8224  *edge_image;
8225 
8226  static char
8227  radius[MaxTextExtent] = "0";
8228 
8229  /*
8230  Query user for edge factor.
8231  */
8232  (void) XDialogWidget(display,windows,"Detect Edges",
8233  "Enter the edge detect radius:",radius);
8234  if (*radius == '\0')
8235  break;
8236  /*
8237  Detect edge in image.
8238  */
8239  XSetCursorState(display,windows,MagickTrue);
8240  XCheckRefreshWindows(display,windows);
8241  flags=ParseGeometry(radius,&geometry_info);
8242  edge_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8243  if (edge_image != (Image *) NULL)
8244  {
8245  *image=DestroyImage(*image);
8246  *image=edge_image;
8247  }
8248  CatchException(&(*image)->exception);
8249  XSetCursorState(display,windows,MagickFalse);
8250  if (windows->image.orphan != MagickFalse)
8251  break;
8252  XConfigureImageColormap(display,resource_info,windows,*image);
8253  (void) XConfigureImage(display,resource_info,windows,*image);
8254  break;
8255  }
8256  case SpreadCommand:
8257  {
8258  Image
8259  *spread_image;
8260 
8261  static char
8262  amount[MaxTextExtent] = "2";
8263 
8264  /*
8265  Query user for spread amount.
8266  */
8267  (void) XDialogWidget(display,windows,"Spread",
8268  "Enter the displacement amount:",amount);
8269  if (*amount == '\0')
8270  break;
8271  /*
8272  Displace image pixels by a random amount.
8273  */
8274  XSetCursorState(display,windows,MagickTrue);
8275  XCheckRefreshWindows(display,windows);
8276  flags=ParseGeometry(amount,&geometry_info);
8277  spread_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8278  if (spread_image != (Image *) NULL)
8279  {
8280  *image=DestroyImage(*image);
8281  *image=spread_image;
8282  }
8283  CatchException(&(*image)->exception);
8284  XSetCursorState(display,windows,MagickFalse);
8285  if (windows->image.orphan != MagickFalse)
8286  break;
8287  XConfigureImageColormap(display,resource_info,windows,*image);
8288  (void) XConfigureImage(display,resource_info,windows,*image);
8289  break;
8290  }
8291  case ShadeCommand:
8292  {
8293  Image
8294  *shade_image;
8295 
8296  int
8297  status;
8298 
8299  static char
8300  geometry[MaxTextExtent] = "30x30";
8301 
8302  /*
8303  Query user for the shade geometry.
8304  */
8305  status=XDialogWidget(display,windows,"Shade",
8306  "Enter the azimuth and elevation of the light source:",geometry);
8307  if (*geometry == '\0')
8308  break;
8309  /*
8310  Shade image pixels.
8311  */
8312  XSetCursorState(display,windows,MagickTrue);
8313  XCheckRefreshWindows(display,windows);
8314  flags=ParseGeometry(geometry,&geometry_info);
8315  if ((flags & SigmaValue) == 0)
8316  geometry_info.sigma=1.0;
8317  shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8318  geometry_info.rho,geometry_info.sigma,&(*image)->exception);
8319  if (shade_image != (Image *) NULL)
8320  {
8321  *image=DestroyImage(*image);
8322  *image=shade_image;
8323  }
8324  CatchException(&(*image)->exception);
8325  XSetCursorState(display,windows,MagickFalse);
8326  if (windows->image.orphan != MagickFalse)
8327  break;
8328  XConfigureImageColormap(display,resource_info,windows,*image);
8329  (void) XConfigureImage(display,resource_info,windows,*image);
8330  break;
8331  }
8332  case RaiseCommand:
8333  {
8334  static char
8335  bevel_width[MaxTextExtent] = "10";
8336 
8337  /*
8338  Query user for bevel width.
8339  */
8340  (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8341  if (*bevel_width == '\0')
8342  break;
8343  /*
8344  Raise an image.
8345  */
8346  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8347  XSetCursorState(display,windows,MagickTrue);
8348  XCheckRefreshWindows(display,windows);
8349  (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8350  &(*image)->exception);
8351  (void) RaiseImage(*image,&page_geometry,MagickTrue);
8352  XSetCursorState(display,windows,MagickFalse);
8353  if (windows->image.orphan != MagickFalse)
8354  break;
8355  XConfigureImageColormap(display,resource_info,windows,*image);
8356  (void) XConfigureImage(display,resource_info,windows,*image);
8357  break;
8358  }
8359  case SegmentCommand:
8360  {
8361  static char
8362  threshold[MaxTextExtent] = "1.0x1.5";
8363 
8364  /*
8365  Query user for smoothing threshold.
8366  */
8367  (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8368  threshold);
8369  if (*threshold == '\0')
8370  break;
8371  /*
8372  Segment an image.
8373  */
8374  XSetCursorState(display,windows,MagickTrue);
8375  XCheckRefreshWindows(display,windows);
8376  flags=ParseGeometry(threshold,&geometry_info);
8377  if ((flags & SigmaValue) == 0)
8378  geometry_info.sigma=1.0;
8379  (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8380  geometry_info.sigma);
8381  XSetCursorState(display,windows,MagickFalse);
8382  if (windows->image.orphan != MagickFalse)
8383  break;
8384  XConfigureImageColormap(display,resource_info,windows,*image);
8385  (void) XConfigureImage(display,resource_info,windows,*image);
8386  break;
8387  }
8388  case SepiaToneCommand:
8389  {
8390  double
8391  threshold;
8392 
8393  Image
8394  *sepia_image;
8395 
8396  static char
8397  factor[MaxTextExtent] = "80%";
8398 
8399  /*
8400  Query user for sepia-tone factor.
8401  */
8402  (void) XDialogWidget(display,windows,"Sepia Tone",
8403  "Enter the sepia tone factor (0 - 99.9%):",factor);
8404  if (*factor == '\0')
8405  break;
8406  /*
8407  Sepia tone image pixels.
8408  */
8409  XSetCursorState(display,windows,MagickTrue);
8410  XCheckRefreshWindows(display,windows);
8411  threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8412  sepia_image=SepiaToneImage(*image,threshold,&(*image)->exception);
8413  if (sepia_image != (Image *) NULL)
8414  {
8415  *image=DestroyImage(*image);
8416  *image=sepia_image;
8417  }
8418  CatchException(&(*image)->exception);
8419  XSetCursorState(display,windows,MagickFalse);
8420  if (windows->image.orphan != MagickFalse)
8421  break;
8422  XConfigureImageColormap(display,resource_info,windows,*image);
8423  (void) XConfigureImage(display,resource_info,windows,*image);
8424  break;
8425  }
8426  case SolarizeCommand:
8427  {
8428  double
8429  threshold;
8430 
8431  static char
8432  factor[MaxTextExtent] = "60%";
8433 
8434  /*
8435  Query user for solarize factor.
8436  */
8437  (void) XDialogWidget(display,windows,"Solarize",
8438  "Enter the solarize factor (0 - 99.9%):",factor);
8439  if (*factor == '\0')
8440  break;
8441  /*
8442  Solarize image pixels.
8443  */
8444  XSetCursorState(display,windows,MagickTrue);
8445  XCheckRefreshWindows(display,windows);
8446  threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8447  (void) SolarizeImage(*image,threshold);
8448  XSetCursorState(display,windows,MagickFalse);
8449  if (windows->image.orphan != MagickFalse)
8450  break;
8451  XConfigureImageColormap(display,resource_info,windows,*image);
8452  (void) XConfigureImage(display,resource_info,windows,*image);
8453  break;
8454  }
8455  case SwirlCommand:
8456  {
8457  Image
8458  *swirl_image;
8459 
8460  static char
8461  degrees[MaxTextExtent] = "60";
8462 
8463  /*
8464  Query user for swirl angle.
8465  */
8466  (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8467  degrees);
8468  if (*degrees == '\0')
8469  break;
8470  /*
8471  Swirl image pixels about the center.
8472  */
8473  XSetCursorState(display,windows,MagickTrue);
8474  XCheckRefreshWindows(display,windows);
8475  flags=ParseGeometry(degrees,&geometry_info);
8476  swirl_image=SwirlImage(*image,geometry_info.rho,&(*image)->exception);
8477  if (swirl_image != (Image *) NULL)
8478  {
8479  *image=DestroyImage(*image);
8480  *image=swirl_image;
8481  }
8482  CatchException(&(*image)->exception);
8483  XSetCursorState(display,windows,MagickFalse);
8484  if (windows->image.orphan != MagickFalse)
8485  break;
8486  XConfigureImageColormap(display,resource_info,windows,*image);
8487  (void) XConfigureImage(display,resource_info,windows,*image);
8488  break;
8489  }
8490  case ImplodeCommand:
8491  {
8492  Image
8493  *implode_image;
8494 
8495  static char
8496  factor[MaxTextExtent] = "0.3";
8497 
8498  /*
8499  Query user for implode factor.
8500  */
8501  (void) XDialogWidget(display,windows,"Implode",
8502  "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8503  if (*factor == '\0')
8504  break;
8505  /*
8506  Implode image pixels about the center.
8507  */
8508  XSetCursorState(display,windows,MagickTrue);
8509  XCheckRefreshWindows(display,windows);
8510  flags=ParseGeometry(factor,&geometry_info);
8511  implode_image=ImplodeImage(*image,geometry_info.rho,&(*image)->exception);
8512  if (implode_image != (Image *) NULL)
8513  {
8514  *image=DestroyImage(*image);
8515  *image=implode_image;
8516  }
8517  CatchException(&(*image)->exception);
8518  XSetCursorState(display,windows,MagickFalse);
8519  if (windows->image.orphan != MagickFalse)
8520  break;
8521  XConfigureImageColormap(display,resource_info,windows,*image);
8522  (void) XConfigureImage(display,resource_info,windows,*image);
8523  break;
8524  }
8525  case VignetteCommand:
8526  {
8527  Image
8528  *vignette_image;
8529 
8530  static char
8531  geometry[MaxTextExtent] = "0x20";
8532 
8533  /*
8534  Query user for the vignette geometry.
8535  */
8536  (void) XDialogWidget(display,windows,"Vignette",
8537  "Enter the radius, sigma, and x and y offsets:",geometry);
8538  if (*geometry == '\0')
8539  break;
8540  /*
8541  Soften the edges of the image in vignette style
8542  */
8543  XSetCursorState(display,windows,MagickTrue);
8544  XCheckRefreshWindows(display,windows);
8545  flags=ParseGeometry(geometry,&geometry_info);
8546  if ((flags & SigmaValue) == 0)
8547  geometry_info.sigma=1.0;
8548  if ((flags & XiValue) == 0)
8549  geometry_info.xi=0.1*(*image)->columns;
8550  if ((flags & PsiValue) == 0)
8551  geometry_info.psi=0.1*(*image)->rows;
8552  vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8553  (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8554  0.5),&(*image)->exception);
8555  if (vignette_image != (Image *) NULL)
8556  {
8557  *image=DestroyImage(*image);
8558  *image=vignette_image;
8559  }
8560  CatchException(&(*image)->exception);
8561  XSetCursorState(display,windows,MagickFalse);
8562  if (windows->image.orphan != MagickFalse)
8563  break;
8564  XConfigureImageColormap(display,resource_info,windows,*image);
8565  (void) XConfigureImage(display,resource_info,windows,*image);
8566  break;
8567  }
8568  case WaveCommand:
8569  {
8570  Image
8571  *wave_image;
8572 
8573  static char
8574  geometry[MaxTextExtent] = "25x150";
8575 
8576  /*
8577  Query user for the wave geometry.
8578  */
8579  (void) XDialogWidget(display,windows,"Wave",
8580  "Enter the amplitude and length of the wave:",geometry);
8581  if (*geometry == '\0')
8582  break;
8583  /*
8584  Alter an image along a sine wave.
8585  */
8586  XSetCursorState(display,windows,MagickTrue);
8587  XCheckRefreshWindows(display,windows);
8588  flags=ParseGeometry(geometry,&geometry_info);
8589  if ((flags & SigmaValue) == 0)
8590  geometry_info.sigma=1.0;
8591  wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8592  &(*image)->exception);
8593  if (wave_image != (Image *) NULL)
8594  {
8595  *image=DestroyImage(*image);
8596  *image=wave_image;
8597  }
8598  CatchException(&(*image)->exception);
8599  XSetCursorState(display,windows,MagickFalse);
8600  if (windows->image.orphan != MagickFalse)
8601  break;
8602  XConfigureImageColormap(display,resource_info,windows,*image);
8603  (void) XConfigureImage(display,resource_info,windows,*image);
8604  break;
8605  }
8606  case OilPaintCommand:
8607  {
8608  Image
8609  *paint_image;
8610 
8611  static char
8612  radius[MaxTextExtent] = "0";
8613 
8614  /*
8615  Query user for circular neighborhood radius.
8616  */
8617  (void) XDialogWidget(display,windows,"Oil Paint",
8618  "Enter the mask radius:",radius);
8619  if (*radius == '\0')
8620  break;
8621  /*
8622  OilPaint image scanlines.
8623  */
8624  XSetCursorState(display,windows,MagickTrue);
8625  XCheckRefreshWindows(display,windows);
8626  flags=ParseGeometry(radius,&geometry_info);
8627  paint_image=OilPaintImage(*image,geometry_info.rho,&(*image)->exception);
8628  if (paint_image != (Image *) NULL)
8629  {
8630  *image=DestroyImage(*image);
8631  *image=paint_image;
8632  }
8633  CatchException(&(*image)->exception);
8634  XSetCursorState(display,windows,MagickFalse);
8635  if (windows->image.orphan != MagickFalse)
8636  break;
8637  XConfigureImageColormap(display,resource_info,windows,*image);
8638  (void) XConfigureImage(display,resource_info,windows,*image);
8639  break;
8640  }
8641  case CharcoalDrawCommand:
8642  {
8643  Image
8644  *charcoal_image;
8645 
8646  static char
8647  radius[MaxTextExtent] = "0x1";
8648 
8649  /*
8650  Query user for charcoal radius.
8651  */
8652  (void) XDialogWidget(display,windows,"Charcoal Draw",
8653  "Enter the charcoal radius and sigma:",radius);
8654  if (*radius == '\0')
8655  break;
8656  /*
8657  Charcoal the image.
8658  */
8659  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8660  XSetCursorState(display,windows,MagickTrue);
8661  XCheckRefreshWindows(display,windows);
8662  flags=ParseGeometry(radius,&geometry_info);
8663  if ((flags & SigmaValue) == 0)
8664  geometry_info.sigma=geometry_info.rho;
8665  charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8666  &(*image)->exception);
8667  if (charcoal_image != (Image *) NULL)
8668  {
8669  *image=DestroyImage(*image);
8670  *image=charcoal_image;
8671  }
8672  CatchException(&(*image)->exception);
8673  XSetCursorState(display,windows,MagickFalse);
8674  if (windows->image.orphan != MagickFalse)
8675  break;
8676  XConfigureImageColormap(display,resource_info,windows,*image);
8677  (void) XConfigureImage(display,resource_info,windows,*image);
8678  break;
8679  }
8680  case AnnotateCommand:
8681  {
8682  /*
8683  Annotate the image with text.
8684  */
8685  status=XAnnotateEditImage(display,resource_info,windows,*image);
8686  if (status == MagickFalse)
8687  {
8688  XNoticeWidget(display,windows,"Unable to annotate X image",
8689  (*image)->filename);
8690  break;
8691  }
8692  break;
8693  }
8694  case DrawCommand:
8695  {
8696  /*
8697  Draw image.
8698  */
8699  status=XDrawEditImage(display,resource_info,windows,image);
8700  if (status == MagickFalse)
8701  {
8702  XNoticeWidget(display,windows,"Unable to draw on the X image",
8703  (*image)->filename);
8704  break;
8705  }
8706  break;
8707  }
8708  case ColorCommand:
8709  {
8710  /*
8711  Color edit.
8712  */
8713  status=XColorEditImage(display,resource_info,windows,image);
8714  if (status == MagickFalse)
8715  {
8716  XNoticeWidget(display,windows,"Unable to pixel edit X image",
8717  (*image)->filename);
8718  break;
8719  }
8720  break;
8721  }
8722  case MatteCommand:
8723  {
8724  /*
8725  Matte edit.
8726  */
8727  status=XMatteEditImage(display,resource_info,windows,image);
8728  if (status == MagickFalse)
8729  {
8730  XNoticeWidget(display,windows,"Unable to matte edit X image",
8731  (*image)->filename);
8732  break;
8733  }
8734  break;
8735  }
8736  case CompositeCommand:
8737  {
8738  /*
8739  Composite image.
8740  */
8741  status=XCompositeImage(display,resource_info,windows,*image);
8742  if (status == MagickFalse)
8743  {
8744  XNoticeWidget(display,windows,"Unable to composite X image",
8745  (*image)->filename);
8746  break;
8747  }
8748  break;
8749  }
8750  case AddBorderCommand:
8751  {
8752  Image
8753  *border_image;
8754 
8755  static char
8756  geometry[MaxTextExtent] = "6x6";
8757 
8758  /*
8759  Query user for border color and geometry.
8760  */
8761  XColorBrowserWidget(display,windows,"Select",color);
8762  if (*color == '\0')
8763  break;
8764  (void) XDialogWidget(display,windows,"Add Border",
8765  "Enter border geometry:",geometry);
8766  if (*geometry == '\0')
8767  break;
8768  /*
8769  Add a border to the image.
8770  */
8771  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8772  XSetCursorState(display,windows,MagickTrue);
8773  XCheckRefreshWindows(display,windows);
8774  (void) QueryColorDatabase(color,&(*image)->border_color,
8775  &(*image)->exception);
8776  (void) ParsePageGeometry(*image,geometry,&page_geometry,
8777  &(*image)->exception);
8778  border_image=BorderImage(*image,&page_geometry,&(*image)->exception);
8779  if (border_image != (Image *) NULL)
8780  {
8781  *image=DestroyImage(*image);
8782  *image=border_image;
8783  }
8784  CatchException(&(*image)->exception);
8785  XSetCursorState(display,windows,MagickFalse);
8786  if (windows->image.orphan != MagickFalse)
8787  break;
8788  windows->image.window_changes.width=(int) (*image)->columns;
8789  windows->image.window_changes.height=(int) (*image)->rows;
8790  XConfigureImageColormap(display,resource_info,windows,*image);
8791  (void) XConfigureImage(display,resource_info,windows,*image);
8792  break;
8793  }
8794  case AddFrameCommand:
8795  {
8796  FrameInfo
8797  frame_info;
8798 
8799  Image
8800  *frame_image;
8801 
8802  static char
8803  geometry[MaxTextExtent] = "6x6";
8804 
8805  /*
8806  Query user for frame color and geometry.
8807  */
8808  XColorBrowserWidget(display,windows,"Select",color);
8809  if (*color == '\0')
8810  break;
8811  (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8812  geometry);
8813  if (*geometry == '\0')
8814  break;
8815  /*
8816  Surround image with an ornamental border.
8817  */
8818  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8819  XSetCursorState(display,windows,MagickTrue);
8820  XCheckRefreshWindows(display,windows);
8821  (void) QueryColorDatabase(color,&(*image)->matte_color,
8822  &(*image)->exception);
8823  (void) ParsePageGeometry(*image,geometry,&page_geometry,
8824  &(*image)->exception);
8825  frame_info.width=page_geometry.width;
8826  frame_info.height=page_geometry.height;
8827  frame_info.outer_bevel=page_geometry.x;
8828  frame_info.inner_bevel=page_geometry.y;
8829  frame_info.x=(ssize_t) frame_info.width;
8830  frame_info.y=(ssize_t) frame_info.height;
8831  frame_info.width=(*image)->columns+2*frame_info.width;
8832  frame_info.height=(*image)->rows+2*frame_info.height;
8833  frame_image=FrameImage(*image,&frame_info,&(*image)->exception);
8834  if (frame_image != (Image *) NULL)
8835  {
8836  *image=DestroyImage(*image);
8837  *image=frame_image;
8838  }
8839  CatchException(&(*image)->exception);
8840  XSetCursorState(display,windows,MagickFalse);
8841  if (windows->image.orphan != MagickFalse)
8842  break;
8843  windows->image.window_changes.width=(int) (*image)->columns;
8844  windows->image.window_changes.height=(int) (*image)->rows;
8845  XConfigureImageColormap(display,resource_info,windows,*image);
8846  (void) XConfigureImage(display,resource_info,windows,*image);
8847  break;
8848  }
8849  case CommentCommand:
8850  {
8851  const char
8852  *value;
8853 
8854  FILE
8855  *file;
8856 
8857  int
8858  unique_file;
8859 
8860  /*
8861  Edit image comment.
8862  */
8863  unique_file=AcquireUniqueFileResource(image_info->filename);
8864  if (unique_file == -1)
8865  {
8866  XNoticeWidget(display,windows,"Unable to edit image comment",
8867  image_info->filename);
8868  break;
8869  }
8870  value=GetImageProperty(*image,"comment");
8871  if (value == (char *) NULL)
8872  unique_file=close(unique_file)-1;
8873  else
8874  {
8875  const char
8876  *p;
8877 
8878  file=fdopen(unique_file,"w");
8879  if (file == (FILE *) NULL)
8880  {
8881  XNoticeWidget(display,windows,"Unable to edit image comment",
8882  image_info->filename);
8883  break;
8884  }
8885  for (p=value; *p != '\0'; p++)
8886  (void) fputc((int) *p,file);
8887  (void) fputc('\n',file);
8888  (void) fclose(file);
8889  }
8890  XSetCursorState(display,windows,MagickTrue);
8891  XCheckRefreshWindows(display,windows);
8892  status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8893  &(*image)->exception);
8894  if (status == MagickFalse)
8895  XNoticeWidget(display,windows,"Unable to edit image comment",
8896  (char *) NULL);
8897  else
8898  {
8899  char
8900  *comment;
8901 
8902  comment=FileToString(image_info->filename,~0UL,&(*image)->exception);
8903  if (comment != (char *) NULL)
8904  {
8905  (void) SetImageProperty(*image,"comment",comment);
8906  (*image)->taint=MagickTrue;
8907  }
8908  }
8909  (void) RelinquishUniqueFileResource(image_info->filename);
8910  XSetCursorState(display,windows,MagickFalse);
8911  break;
8912  }
8913  case LaunchCommand:
8914  {
8915  /*
8916  Launch program.
8917  */
8918  XSetCursorState(display,windows,MagickTrue);
8919  XCheckRefreshWindows(display,windows);
8920  (void) AcquireUniqueFilename(filename);
8921  (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
8922  filename);
8923  status=WriteImage(image_info,*image);
8924  if (status == MagickFalse)
8925  XNoticeWidget(display,windows,"Unable to launch image editor",
8926  (char *) NULL);
8927  else
8928  {
8929  nexus=ReadImage(resource_info->image_info,&(*image)->exception);
8930  CatchException(&(*image)->exception);
8931  XClientMessage(display,windows->image.id,windows->im_protocols,
8932  windows->im_next_image,CurrentTime);
8933  }
8934  (void) RelinquishUniqueFileResource(filename);
8935  XSetCursorState(display,windows,MagickFalse);
8936  break;
8937  }
8938  case RegionOfInterestCommand:
8939  {
8940  /*
8941  Apply an image processing technique to a region of interest.
8942  */
8943  (void) XROIImage(display,resource_info,windows,image);
8944  break;
8945  }
8946  case InfoCommand:
8947  break;
8948  case ZoomCommand:
8949  {
8950  /*
8951  Zoom image.
8952  */
8953  if (windows->magnify.mapped != MagickFalse)
8954  (void) XRaiseWindow(display,windows->magnify.id);
8955  else
8956  {
8957  /*
8958  Make magnify image.
8959  */
8960  XSetCursorState(display,windows,MagickTrue);
8961  (void) XMapRaised(display,windows->magnify.id);
8962  XSetCursorState(display,windows,MagickFalse);
8963  }
8964  break;
8965  }
8966  case ShowPreviewCommand:
8967  {
8968  char
8969  **previews;
8970 
8971  Image
8972  *preview_image;
8973 
8974  static char
8975  preview_type[MaxTextExtent] = "Gamma";
8976 
8977  /*
8978  Select preview type from menu.
8979  */
8980  previews=GetCommandOptions(MagickPreviewOptions);
8981  if (previews == (char **) NULL)
8982  break;
8983  XListBrowserWidget(display,windows,&windows->widget,
8984  (const char **) previews,"Preview",
8985  "Select an enhancement, effect, or F/X:",preview_type);
8986  previews=DestroyStringList(previews);
8987  if (*preview_type == '\0')
8988  break;
8989  /*
8990  Show image preview.
8991  */
8992  XSetCursorState(display,windows,MagickTrue);
8993  XCheckRefreshWindows(display,windows);
8994  image_info->preview_type=(PreviewType)
8995  ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
8996  image_info->group=(ssize_t) windows->image.id;
8997  (void) DeleteImageProperty(*image,"label");
8998  (void) SetImageProperty(*image,"label","Preview");
8999  (void) AcquireUniqueFilename(filename);
9000  (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9001  filename);
9002  status=WriteImage(image_info,*image);
9003  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9004  preview_image=ReadImage(image_info,&(*image)->exception);
9005  (void) RelinquishUniqueFileResource(filename);
9006  if (preview_image == (Image *) NULL)
9007  break;
9008  (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9009  filename);
9010  status=WriteImage(image_info,preview_image);
9011  preview_image=DestroyImage(preview_image);
9012  if (status == MagickFalse)
9013  XNoticeWidget(display,windows,"Unable to show image preview",
9014  (*image)->filename);
9015  XDelay(display,1500);
9016  XSetCursorState(display,windows,MagickFalse);
9017  break;
9018  }
9019  case ShowHistogramCommand:
9020  {
9021  Image
9022  *histogram_image;
9023 
9024  /*
9025  Show image histogram.
9026  */
9027  XSetCursorState(display,windows,MagickTrue);
9028  XCheckRefreshWindows(display,windows);
9029  image_info->group=(ssize_t) windows->image.id;
9030  (void) DeleteImageProperty(*image,"label");
9031  (void) SetImageProperty(*image,"label","Histogram");
9032  (void) AcquireUniqueFilename(filename);
9033  (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9034  filename);
9035  status=WriteImage(image_info,*image);
9036  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9037  histogram_image=ReadImage(image_info,&(*image)->exception);
9038  (void) RelinquishUniqueFileResource(filename);
9039  if (histogram_image == (Image *) NULL)
9040  break;
9041  (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9042  "show:%s",filename);
9043  status=WriteImage(image_info,histogram_image);
9044  histogram_image=DestroyImage(histogram_image);
9045  if (status == MagickFalse)
9046  XNoticeWidget(display,windows,"Unable to show histogram",
9047  (*image)->filename);
9048  XDelay(display,1500);
9049  XSetCursorState(display,windows,MagickFalse);
9050  break;
9051  }
9052  case ShowMatteCommand:
9053  {
9054  Image
9055  *matte_image;
9056 
9057  if ((*image)->matte == MagickFalse)
9058  {
9059  XNoticeWidget(display,windows,
9060  "Image does not have any matte information",(*image)->filename);
9061  break;
9062  }
9063  /*
9064  Show image matte.
9065  */
9066  XSetCursorState(display,windows,MagickTrue);
9067  XCheckRefreshWindows(display,windows);
9068  image_info->group=(ssize_t) windows->image.id;
9069  (void) DeleteImageProperty(*image,"label");
9070  (void) SetImageProperty(*image,"label","Matte");
9071  (void) AcquireUniqueFilename(filename);
9072  (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9073  filename);
9074  status=WriteImage(image_info,*image);
9075  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9076  matte_image=ReadImage(image_info,&(*image)->exception);
9077  (void) RelinquishUniqueFileResource(filename);
9078  if (matte_image == (Image *) NULL)
9079  break;
9080  (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9081  filename);
9082  status=WriteImage(image_info,matte_image);
9083  matte_image=DestroyImage(matte_image);
9084  if (status == MagickFalse)
9085  XNoticeWidget(display,windows,"Unable to show matte",
9086  (*image)->filename);
9087  XDelay(display,1500);
9088  XSetCursorState(display,windows,MagickFalse);
9089  break;
9090  }
9091  case BackgroundCommand:
9092  {
9093  /*
9094  Background image.
9095  */
9096  status=XBackgroundImage(display,resource_info,windows,image);
9097  if (status == MagickFalse)
9098  break;
9099  nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9100  if (nexus != (Image *) NULL)
9101  XClientMessage(display,windows->image.id,windows->im_protocols,
9102  windows->im_next_image,CurrentTime);
9103  break;
9104  }
9105  case SlideShowCommand:
9106  {
9107  static char
9108  delay[MaxTextExtent] = "5";
9109 
9110  /*
9111  Display next image after pausing.
9112  */
9113  (void) XDialogWidget(display,windows,"Slide Show",
9114  "Pause how many 1/100ths of a second between images:",delay);
9115  if (*delay == '\0')
9116  break;
9117  resource_info->delay=StringToUnsignedLong(delay);
9118  XClientMessage(display,windows->image.id,windows->im_protocols,
9119  windows->im_next_image,CurrentTime);
9120  break;
9121  }
9122  case PreferencesCommand:
9123  {
9124  /*
9125  Set user preferences.
9126  */
9127  status=XPreferencesWidget(display,resource_info,windows);
9128  if (status == MagickFalse)
9129  break;
9130  nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9131  if (nexus != (Image *) NULL)
9132  XClientMessage(display,windows->image.id,windows->im_protocols,
9133  windows->im_next_image,CurrentTime);
9134  break;
9135  }
9136  case HelpCommand:
9137  {
9138  /*
9139  User requested help.
9140  */
9141  XTextViewHelp(display,resource_info,windows,MagickFalse,
9142  "Help Viewer - Display",DisplayHelp);
9143  break;
9144  }
9145  case BrowseDocumentationCommand:
9146  {
9147  Atom
9148  mozilla_atom;
9149 
9150  Window
9151  mozilla_window,
9152  root_window;
9153 
9154  /*
9155  Browse the ImageMagick documentation.
9156  */
9157  root_window=XRootWindow(display,XDefaultScreen(display));
9158  mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9159  mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9160  if (mozilla_window != (Window) NULL)
9161  {
9162  char
9163  command[MaxTextExtent];
9164 
9165  /*
9166  Display documentation using Netscape remote control.
9167  */
9168  (void) FormatLocaleString(command,MaxTextExtent,
9169  "openurl(%s,new-tab)",MagickAuthoritativeURL);
9170  mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9171  (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9172  8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9173  XSetCursorState(display,windows,MagickFalse);
9174  break;
9175  }
9176  XSetCursorState(display,windows,MagickTrue);
9177  XCheckRefreshWindows(display,windows);
9178  status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9179  &(*image)->exception);
9180  if (status == MagickFalse)
9181  XNoticeWidget(display,windows,"Unable to browse documentation",
9182  (char *) NULL);
9183  XDelay(display,1500);
9184  XSetCursorState(display,windows,MagickFalse);
9185  break;
9186  }
9187  case VersionCommand:
9188  {
9189  XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9190  GetMagickCopyright());
9191  break;
9192  }
9193  case SaveToUndoBufferCommand:
9194  break;
9195  default:
9196  {
9197  (void) XBell(display,0);
9198  break;
9199  }
9200  }
9201  image_info=DestroyImageInfo(image_info);
9202  return(nexus);
9203 }
9204 ␌
9205 /*
9206 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9207 % %
9208 % %
9209 % %
9210 + X M a g n i f y I m a g e %
9211 % %
9212 % %
9213 % %
9214 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9215 %
9216 % XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9217 % The magnified portion is displayed in a separate window.
9218 %
9219 % The format of the XMagnifyImage method is:
9220 %
9221 % void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9222 %
9223 % A description of each parameter follows:
9224 %
9225 % o display: Specifies a connection to an X server; returned from
9226 % XOpenDisplay.
9227 %
9228 % o windows: Specifies a pointer to a XWindows structure.
9229 %
9230 % o event: Specifies a pointer to a XEvent structure. If it is NULL,
9231 % the entire image is refreshed.
9232 %
9233 */
9234 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9235 {
9236  char
9237  text[MaxTextExtent];
9238 
9239  int
9240  x,
9241  y;
9242 
9243  size_t
9244  state;
9245 
9246  /*
9247  Update magnified image until the mouse button is released.
9248  */
9249  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9250  state=DefaultState;
9251  x=event->xbutton.x;
9252  y=event->xbutton.y;
9253  windows->magnify.x=(int) windows->image.x+x;
9254  windows->magnify.y=(int) windows->image.y+y;
9255  do
9256  {
9257  /*
9258  Map and unmap Info widget as text cursor crosses its boundaries.
9259  */
9260  if (windows->info.mapped != MagickFalse)
9261  {
9262  if ((x < (int) (windows->info.x+windows->info.width)) &&
9263  (y < (int) (windows->info.y+windows->info.height)))
9264  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9265  }
9266  else
9267  if ((x > (int) (windows->info.x+windows->info.width)) ||
9268  (y > (int) (windows->info.y+windows->info.height)))
9269  (void) XMapWindow(display,windows->info.id);
9270  if (windows->info.mapped != MagickFalse)
9271  {
9272  /*
9273  Display pointer position.
9274  */
9275  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9276  windows->magnify.x,windows->magnify.y);
9277  XInfoWidget(display,windows,text);
9278  }
9279  /*
9280  Wait for next event.
9281  */
9282  XScreenEvent(display,windows,event);
9283  switch (event->type)
9284  {
9285  case ButtonPress:
9286  break;
9287  case ButtonRelease:
9288  {
9289  /*
9290  User has finished magnifying image.
9291  */
9292  x=event->xbutton.x;
9293  y=event->xbutton.y;
9294  state|=ExitState;
9295  break;
9296  }
9297  case Expose:
9298  break;
9299  case MotionNotify:
9300  {
9301  x=event->xmotion.x;
9302  y=event->xmotion.y;
9303  break;
9304  }
9305  default:
9306  break;
9307  }
9308  /*
9309  Check boundary conditions.
9310  */
9311  if (x < 0)
9312  x=0;
9313  else
9314  if (x >= (int) windows->image.width)
9315  x=(int) windows->image.width-1;
9316  if (y < 0)
9317  y=0;
9318  else
9319  if (y >= (int) windows->image.height)
9320  y=(int) windows->image.height-1;
9321  } while ((state & ExitState) == 0);
9322  /*
9323  Display magnified image.
9324  */
9325  XSetCursorState(display,windows,MagickFalse);
9326 }
9327 ␌
9328 /*
9329 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9330 % %
9331 % %
9332 % %
9333 + X M a g n i f y W i n d o w C o m m a n d %
9334 % %
9335 % %
9336 % %
9337 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9338 %
9339 % XMagnifyWindowCommand() moves the image within an Magnify window by one
9340 % pixel as specified by the key symbol.
9341 %
9342 % The format of the XMagnifyWindowCommand method is:
9343 %
9344 % void XMagnifyWindowCommand(Display *display,XWindows *windows,
9345 % const MagickStatusType state,const KeySym key_symbol)
9346 %
9347 % A description of each parameter follows:
9348 %
9349 % o display: Specifies a connection to an X server; returned from
9350 % XOpenDisplay.
9351 %
9352 % o windows: Specifies a pointer to a XWindows structure.
9353 %
9354 % o state: key mask.
9355 %
9356 % o key_symbol: Specifies a KeySym which indicates which side of the image
9357 % to trim.
9358 %
9359 */
9360 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9361  const MagickStatusType state,const KeySym key_symbol)
9362 {
9363  unsigned int
9364  quantum;
9365 
9366  /*
9367  User specified a magnify factor or position.
9368  */
9369  quantum=1;
9370  if ((state & Mod1Mask) != 0)
9371  quantum=10;
9372  switch ((int) key_symbol)
9373  {
9374  case QuitCommand:
9375  {
9376  (void) XWithdrawWindow(display,windows->magnify.id,
9377  windows->magnify.screen);
9378  break;
9379  }
9380  case XK_Home:
9381  case XK_KP_Home:
9382  {
9383  windows->magnify.x=(int) windows->image.width/2;
9384  windows->magnify.y=(int) windows->image.height/2;
9385  break;
9386  }
9387  case XK_Left:
9388  case XK_KP_Left:
9389  {
9390  if (windows->magnify.x > 0)
9391  windows->magnify.x-=quantum;
9392  break;
9393  }
9394  case XK_Up:
9395  case XK_KP_Up:
9396  {
9397  if (windows->magnify.y > 0)
9398  windows->magnify.y-=quantum;
9399  break;
9400  }
9401  case XK_Right:
9402  case XK_KP_Right:
9403  {
9404  if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9405  windows->magnify.x+=quantum;
9406  break;
9407  }
9408  case XK_Down:
9409  case XK_KP_Down:
9410  {
9411  if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9412  windows->magnify.y+=quantum;
9413  break;
9414  }
9415  case XK_0:
9416  case XK_1:
9417  case XK_2:
9418  case XK_3:
9419  case XK_4:
9420  case XK_5:
9421  case XK_6:
9422  case XK_7:
9423  case XK_8:
9424  case XK_9:
9425  {
9426  windows->magnify.data=(key_symbol-XK_0);
9427  break;
9428  }
9429  case XK_KP_0:
9430  case XK_KP_1:
9431  case XK_KP_2:
9432  case XK_KP_3:
9433  case XK_KP_4:
9434  case XK_KP_5:
9435  case XK_KP_6:
9436  case XK_KP_7:
9437  case XK_KP_8:
9438  case XK_KP_9:
9439  {
9440  windows->magnify.data=(key_symbol-XK_KP_0);
9441  break;
9442  }
9443  default:
9444  break;
9445  }
9446  XMakeMagnifyImage(display,windows);
9447 }
9448 ␌
9449 /*
9450 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9451 % %
9452 % %
9453 % %
9454 + X M a k e P a n I m a g e %
9455 % %
9456 % %
9457 % %
9458 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9459 %
9460 % XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9461 % icon window.
9462 %
9463 % The format of the XMakePanImage method is:
9464 %
9465 % void XMakePanImage(Display *display,XResourceInfo *resource_info,
9466 % XWindows *windows,Image *image)
9467 %
9468 % A description of each parameter follows:
9469 %
9470 % o display: Specifies a connection to an X server; returned from
9471 % XOpenDisplay.
9472 %
9473 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9474 %
9475 % o windows: Specifies a pointer to a XWindows structure.
9476 %
9477 % o image: the image.
9478 %
9479 */
9480 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9481  XWindows *windows,Image *image)
9482 {
9483  MagickStatusType
9484  status;
9485 
9486  /*
9487  Create and display image for panning icon.
9488  */
9489  XSetCursorState(display,windows,MagickTrue);
9490  XCheckRefreshWindows(display,windows);
9491  windows->pan.x=(int) windows->image.x;
9492  windows->pan.y=(int) windows->image.y;
9493  status=XMakeImage(display,resource_info,&windows->pan,image,
9494  windows->pan.width,windows->pan.height);
9495  if (status == MagickFalse)
9496  ThrowXWindowFatalException(XServerFatalError,image->exception.reason,
9497  image->exception.description);
9498  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9499  windows->pan.pixmap);
9500  (void) XClearWindow(display,windows->pan.id);
9501  XDrawPanRectangle(display,windows);
9502  XSetCursorState(display,windows,MagickFalse);
9503 }
9504 ␌
9505 /*
9506 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9507 % %
9508 % %
9509 % %
9510 + X M a t t a E d i t I m a g e %
9511 % %
9512 % %
9513 % %
9514 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9515 %
9516 % XMatteEditImage() allows the user to interactively change the Matte channel
9517 % of an image. If the image is PseudoClass it is promoted to DirectClass
9518 % before the matte information is stored.
9519 %
9520 % The format of the XMatteEditImage method is:
9521 %
9522 % MagickBooleanType XMatteEditImage(Display *display,
9523 % XResourceInfo *resource_info,XWindows *windows,Image **image)
9524 %
9525 % A description of each parameter follows:
9526 %
9527 % o display: Specifies a connection to an X server; returned from
9528 % XOpenDisplay.
9529 %
9530 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9531 %
9532 % o windows: Specifies a pointer to a XWindows structure.
9533 %
9534 % o image: the image; returned from ReadImage.
9535 %
9536 */
9537 static MagickBooleanType XMatteEditImage(Display *display,
9538  XResourceInfo *resource_info,XWindows *windows,Image **image)
9539 {
9540  const char
9541  *const MatteEditMenu[] =
9542  {
9543  "Method",
9544  "Border Color",
9545  "Fuzz",
9546  "Matte Value",
9547  "Undo",
9548  "Help",
9549  "Dismiss",
9550  (char *) NULL
9551  };
9552 
9553  static char
9554  matte[MaxTextExtent] = "0";
9555 
9556  static const ModeType
9557  MatteEditCommands[] =
9558  {
9559  MatteEditMethod,
9560  MatteEditBorderCommand,
9561  MatteEditFuzzCommand,
9562  MatteEditValueCommand,
9563  MatteEditUndoCommand,
9564  MatteEditHelpCommand,
9565  MatteEditDismissCommand
9566  };
9567 
9568  static PaintMethod
9569  method = PointMethod;
9570 
9571  static XColor
9572  border_color = { 0, 0, 0, 0, 0, 0 };
9573 
9574  char
9575  command[MaxTextExtent],
9576  text[MaxTextExtent] = "";
9577 
9578  Cursor
9579  cursor;
9580 
9581  int
9582  entry,
9583  id,
9584  x,
9585  x_offset,
9586  y,
9587  y_offset;
9588 
9589  int
9590  i;
9591 
9592  PixelPacket
9593  *q;
9594 
9595  unsigned int
9596  height,
9597  width;
9598 
9599  size_t
9600  state;
9601 
9602  XEvent
9603  event;
9604 
9605  /*
9606  Map Command widget.
9607  */
9608  (void) CloneString(&windows->command.name,"Matte Edit");
9609  windows->command.data=4;
9610  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9611  (void) XMapRaised(display,windows->command.id);
9612  XClientMessage(display,windows->image.id,windows->im_protocols,
9613  windows->im_update_widget,CurrentTime);
9614  /*
9615  Make cursor.
9616  */
9617  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9618  resource_info->background_color,resource_info->foreground_color);
9619  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9620  /*
9621  Track pointer until button 1 is pressed.
9622  */
9623  XQueryPosition(display,windows->image.id,&x,&y);
9624  (void) XSelectInput(display,windows->image.id,
9625  windows->image.attributes.event_mask | PointerMotionMask);
9626  state=DefaultState;
9627  do
9628  {
9629  if (windows->info.mapped != MagickFalse)
9630  {
9631  /*
9632  Display pointer position.
9633  */
9634  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9635  x+windows->image.x,y+windows->image.y);
9636  XInfoWidget(display,windows,text);
9637  }
9638  /*
9639  Wait for next event.
9640  */
9641  XScreenEvent(display,windows,&event);
9642  if (event.xany.window == windows->command.id)
9643  {
9644  /*
9645  Select a command from the Command widget.
9646  */
9647  id=XCommandWidget(display,windows,MatteEditMenu,&event);
9648  if (id < 0)
9649  {
9650  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9651  continue;
9652  }
9653  switch (MatteEditCommands[id])
9654  {
9655  case MatteEditMethod:
9656  {
9657  char
9658  **methods;
9659 
9660  /*
9661  Select a method from the pop-up menu.
9662  */
9663  methods=GetCommandOptions(MagickMethodOptions);
9664  if (methods == (char **) NULL)
9665  break;
9666  entry=XMenuWidget(display,windows,MatteEditMenu[id],
9667  (const char **) methods,command);
9668  if (entry >= 0)
9669  method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9670  MagickFalse,methods[entry]);
9671  methods=DestroyStringList(methods);
9672  break;
9673  }
9674  case MatteEditBorderCommand:
9675  {
9676  const char
9677  *ColorMenu[MaxNumberPens];
9678 
9679  int
9680  pen_number;
9681 
9682  /*
9683  Initialize menu selections.
9684  */
9685  for (i=0; i < (int) (MaxNumberPens-2); i++)
9686  ColorMenu[i]=resource_info->pen_colors[i];
9687  ColorMenu[MaxNumberPens-2]="Browser...";
9688  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9689  /*
9690  Select a pen color from the pop-up menu.
9691  */
9692  pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9693  (const char **) ColorMenu,command);
9694  if (pen_number < 0)
9695  break;
9696  if (pen_number == (MaxNumberPens-2))
9697  {
9698  static char
9699  color_name[MaxTextExtent] = "gray";
9700 
9701  /*
9702  Select a pen color from a dialog.
9703  */
9704  resource_info->pen_colors[pen_number]=color_name;
9705  XColorBrowserWidget(display,windows,"Select",color_name);
9706  if (*color_name == '\0')
9707  break;
9708  }
9709  /*
9710  Set border color.
9711  */
9712  (void) XParseColor(display,windows->map_info->colormap,
9713  resource_info->pen_colors[pen_number],&border_color);
9714  break;
9715  }
9716  case MatteEditFuzzCommand:
9717  {
9718  const char
9719  *const FuzzMenu[] =
9720  {
9721  "0%",
9722  "2%",
9723  "5%",
9724  "10%",
9725  "15%",
9726  "Dialog...",
9727  (char *) NULL,
9728  };
9729 
9730  static char
9731  fuzz[MaxTextExtent];
9732 
9733  /*
9734  Select a command from the pop-up menu.
9735  */
9736  entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9737  command);
9738  if (entry < 0)
9739  break;
9740  if (entry != 5)
9741  {
9742  (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9743  QuantumRange+1.0);
9744  break;
9745  }
9746  (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9747  (void) XDialogWidget(display,windows,"Ok",
9748  "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9749  if (*fuzz == '\0')
9750  break;
9751  (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9752  (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9753  1.0);
9754  break;
9755  }
9756  case MatteEditValueCommand:
9757  {
9758  const char
9759  *const MatteMenu[] =
9760  {
9761  "Opaque",
9762  "Transparent",
9763  "Dialog...",
9764  (char *) NULL,
9765  };
9766 
9767  static char
9768  message[MaxTextExtent];
9769 
9770  /*
9771  Select a command from the pop-up menu.
9772  */
9773  entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9774  command);
9775  if (entry < 0)
9776  break;
9777  if (entry != 2)
9778  {
9779  (void) FormatLocaleString(matte,MaxTextExtent,"%g",
9780  (double) OpaqueOpacity);
9781  if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9782  (void) FormatLocaleString(matte,MaxTextExtent,"%g",
9783  (double) TransparentOpacity);
9784  break;
9785  }
9786  (void) FormatLocaleString(message,MaxTextExtent,
9787  "Enter matte value (0 - " "%g" "):",(double) QuantumRange);
9788  (void) XDialogWidget(display,windows,"Matte",message,matte);
9789  if (*matte == '\0')
9790  break;
9791  break;
9792  }
9793  case MatteEditUndoCommand:
9794  {
9795  (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9796  image);
9797  break;
9798  }
9799  case MatteEditHelpCommand:
9800  {
9801  XTextViewHelp(display,resource_info,windows,MagickFalse,
9802  "Help Viewer - Matte Edit",ImageMatteEditHelp);
9803  break;
9804  }
9805  case MatteEditDismissCommand:
9806  {
9807  /*
9808  Prematurely exit.
9809  */
9810  state|=EscapeState;
9811  state|=ExitState;
9812  break;
9813  }
9814  default:
9815  break;
9816  }
9817  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9818  continue;
9819  }
9820  switch (event.type)
9821  {
9822  case ButtonPress:
9823  {
9824  if (event.xbutton.button != Button1)
9825  break;
9826  if ((event.xbutton.window != windows->image.id) &&
9827  (event.xbutton.window != windows->magnify.id))
9828  break;
9829  /*
9830  Update matte data.
9831  */
9832  x=event.xbutton.x;
9833  y=event.xbutton.y;
9834  (void) XMagickCommand(display,resource_info,windows,
9835  SaveToUndoBufferCommand,image);
9836  state|=UpdateConfigurationState;
9837  break;
9838  }
9839  case ButtonRelease:
9840  {
9841  if (event.xbutton.button != Button1)
9842  break;
9843  if ((event.xbutton.window != windows->image.id) &&
9844  (event.xbutton.window != windows->magnify.id))
9845  break;
9846  /*
9847  Update colormap information.
9848  */
9849  x=event.xbutton.x;
9850  y=event.xbutton.y;
9851  XConfigureImageColormap(display,resource_info,windows,*image);
9852  (void) XConfigureImage(display,resource_info,windows,*image);
9853  XInfoWidget(display,windows,text);
9854  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9855  state&=(~UpdateConfigurationState);
9856  break;
9857  }
9858  case Expose:
9859  break;
9860  case KeyPress:
9861  {
9862  char
9863  command[MaxTextExtent];
9864 
9865  KeySym
9866  key_symbol;
9867 
9868  if (event.xkey.window == windows->magnify.id)
9869  {
9870  Window
9871  window;
9872 
9873  window=windows->magnify.id;
9874  while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9875  }
9876  if (event.xkey.window != windows->image.id)
9877  break;
9878  /*
9879  Respond to a user key press.
9880  */
9881  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9882  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9883  switch ((int) key_symbol)
9884  {
9885  case XK_Escape:
9886  case XK_F20:
9887  {
9888  /*
9889  Prematurely exit.
9890  */
9891  state|=ExitState;
9892  break;
9893  }
9894  case XK_F1:
9895  case XK_Help:
9896  {
9897  XTextViewHelp(display,resource_info,windows,MagickFalse,
9898  "Help Viewer - Matte Edit",ImageMatteEditHelp);
9899  break;
9900  }
9901  default:
9902  {
9903  (void) XBell(display,0);
9904  break;
9905  }
9906  }
9907  break;
9908  }
9909  case MotionNotify:
9910  {
9911  /*
9912  Map and unmap Info widget as cursor crosses its boundaries.
9913  */
9914  x=event.xmotion.x;
9915  y=event.xmotion.y;
9916  if (windows->info.mapped != MagickFalse)
9917  {
9918  if ((x < (int) (windows->info.x+windows->info.width)) &&
9919  (y < (int) (windows->info.y+windows->info.height)))
9920  (void) XWithdrawWindow(display,windows->info.id,
9921  windows->info.screen);
9922  }
9923  else
9924  if ((x > (int) (windows->info.x+windows->info.width)) ||
9925  (y > (int) (windows->info.y+windows->info.height)))
9926  (void) XMapWindow(display,windows->info.id);
9927  break;
9928  }
9929  default:
9930  break;
9931  }
9932  if (event.xany.window == windows->magnify.id)
9933  {
9934  x=windows->magnify.x-windows->image.x;
9935  y=windows->magnify.y-windows->image.y;
9936  }
9937  x_offset=x;
9938  y_offset=y;
9939  if ((state & UpdateConfigurationState) != 0)
9940  {
9941  CacheView
9942  *image_view;
9943 
9945  *exception;
9946 
9947  int
9948  x,
9949  y;
9950 
9951  /*
9952  Matte edit is relative to image configuration.
9953  */
9954  (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
9955  MagickTrue);
9956  XPutPixel(windows->image.ximage,x_offset,y_offset,
9957  windows->pixel_info->background_color.pixel);
9958  width=(unsigned int) (*image)->columns;
9959  height=(unsigned int) (*image)->rows;
9960  x=0;
9961  y=0;
9962  if (windows->image.crop_geometry != (char *) NULL)
9963  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
9964  &width,&height);
9965  x_offset=(int)
9966  (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
9967  y_offset=(int)
9968  (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
9969  if ((x_offset < 0) || (y_offset < 0))
9970  continue;
9971  if ((x_offset >= (int) (*image)->columns) ||
9972  (y_offset >= (int) (*image)->rows))
9973  continue;
9974  if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
9975  return(MagickFalse);
9976  (*image)->matte=MagickTrue;
9977  exception=(&(*image)->exception);
9978  image_view=AcquireAuthenticCacheView(*image,exception);
9979  switch (method)
9980  {
9981  case PointMethod:
9982  default:
9983  {
9984  /*
9985  Update matte information using point algorithm.
9986  */
9987  q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
9988  (ssize_t) y_offset,1,1,exception);
9989  if (q == (PixelPacket *) NULL)
9990  break;
9991  q->opacity=(Quantum) StringToLong(matte);
9992  (void) SyncCacheViewAuthenticPixels(image_view,exception);
9993  break;
9994  }
9995  case ReplaceMethod:
9996  {
9997  PixelPacket
9998  target;
9999 
10000  /*
10001  Update matte information using replace algorithm.
10002  */
10003  (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10004  (ssize_t) y_offset,&target,exception);
10005  for (y=0; y < (int) (*image)->rows; y++)
10006  {
10007  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10008  (*image)->columns,1,&(*image)->exception);
10009  if (q == (PixelPacket *) NULL)
10010  break;
10011  for (x=0; x < (int) (*image)->columns; x++)
10012  {
10013  if (IsColorSimilar(*image,q,&target))
10014  q->opacity=(Quantum) StringToLong(matte);
10015  q++;
10016  }
10017  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10018  break;
10019  }
10020  break;
10021  }
10022  case FloodfillMethod:
10023  case FillToBorderMethod:
10024  {
10025  DrawInfo
10026  *draw_info;
10027 
10029  target;
10030 
10031  /*
10032  Update matte information using floodfill algorithm.
10033  */
10034  (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10035  (ssize_t) y_offset,&target,exception);
10036  if (method == FillToBorderMethod)
10037  {
10038  target.red=(MagickRealType)
10039  ScaleShortToQuantum(border_color.red);
10040  target.green=(MagickRealType)
10041  ScaleShortToQuantum(border_color.green);
10042  target.blue=(MagickRealType)
10043  ScaleShortToQuantum(border_color.blue);
10044  }
10045  draw_info=CloneDrawInfo(resource_info->image_info,
10046  (DrawInfo *) NULL);
10047  draw_info->fill.opacity=ClampToQuantum(StringToDouble(matte,
10048  (char **) NULL));
10049  (void) FloodfillPaintImage(*image,OpacityChannel,draw_info,&target,
10050  (ssize_t) x_offset,(ssize_t) y_offset,
10051  method == FloodfillMethod ? MagickFalse : MagickTrue);
10052  draw_info=DestroyDrawInfo(draw_info);
10053  break;
10054  }
10055  case ResetMethod:
10056  {
10057  /*
10058  Update matte information using reset algorithm.
10059  */
10060  if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
10061  return(MagickFalse);
10062  for (y=0; y < (int) (*image)->rows; y++)
10063  {
10064  q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10065  (*image)->columns,1,exception);
10066  if (q == (PixelPacket *) NULL)
10067  break;
10068  for (x=0; x < (int) (*image)->columns; x++)
10069  {
10070  q->opacity=(Quantum) StringToLong(matte);
10071  q++;
10072  }
10073  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10074  break;
10075  }
10076  if (StringToLong(matte) == OpaqueOpacity)
10077  (*image)->matte=MagickFalse;
10078  break;
10079  }
10080  }
10081  image_view=DestroyCacheView(image_view);
10082  state&=(~UpdateConfigurationState);
10083  }
10084  } while ((state & ExitState) == 0);
10085  (void) XSelectInput(display,windows->image.id,
10086  windows->image.attributes.event_mask);
10087  XSetCursorState(display,windows,MagickFalse);
10088  (void) XFreeCursor(display,cursor);
10089  return(MagickTrue);
10090 }
10091 ␌
10092 /*
10093 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10094 % %
10095 % %
10096 % %
10097 + X O p e n I m a g e %
10098 % %
10099 % %
10100 % %
10101 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10102 %
10103 % XOpenImage() loads an image from a file.
10104 %
10105 % The format of the XOpenImage method is:
10106 %
10107 % Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10108 % XWindows *windows,const unsigned int command)
10109 %
10110 % A description of each parameter follows:
10111 %
10112 % o display: Specifies a connection to an X server; returned from
10113 % XOpenDisplay.
10114 %
10115 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10116 %
10117 % o windows: Specifies a pointer to a XWindows structure.
10118 %
10119 % o command: A value other than zero indicates that the file is selected
10120 % from the command line argument list.
10121 %
10122 */
10123 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10124  XWindows *windows,const MagickBooleanType command)
10125 {
10126  const MagickInfo
10127  *magick_info;
10128 
10130  *exception;
10131 
10132  Image
10133  *nexus;
10134 
10135  ImageInfo
10136  *image_info;
10137 
10138  static char
10139  filename[MaxTextExtent] = "\0";
10140 
10141  /*
10142  Request file name from user.
10143  */
10144  if (command == MagickFalse)
10145  XFileBrowserWidget(display,windows,"Open",filename);
10146  else
10147  {
10148  char
10149  **filelist,
10150  **files;
10151 
10152  int
10153  count,
10154  status;
10155 
10156  int
10157  i,
10158  j;
10159 
10160  /*
10161  Select next image from the command line.
10162  */
10163  status=XGetCommand(display,windows->image.id,&files,&count);
10164  if (status == 0)
10165  ThrowXWindowException(XServerError,"UnableToGetProperty","...");
10166  filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10167  if (filelist == (char **) NULL)
10168  {
10169  (void) XFreeStringList(files);
10170  ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
10171  "...");
10172  return((Image *) NULL);
10173  }
10174  j=0;
10175  for (i=1; i < count; i++)
10176  if (*files[i] != '-')
10177  filelist[j++]=files[i];
10178  filelist[j]=(char *) NULL;
10179  XListBrowserWidget(display,windows,&windows->widget,
10180  (const char **) filelist,"Load","Select Image to Load:",filename);
10181  filelist=(char **) RelinquishMagickMemory(filelist);
10182  (void) XFreeStringList(files);
10183  }
10184  if (*filename == '\0')
10185  return((Image *) NULL);
10186  image_info=CloneImageInfo(resource_info->image_info);
10187  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10188  (void *) NULL);
10189  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10190  exception=AcquireExceptionInfo();
10191  (void) SetImageInfo(image_info,0,exception);
10192  if (LocaleCompare(image_info->magick,"X") == 0)
10193  {
10194  char
10195  seconds[MaxTextExtent];
10196 
10197  /*
10198  User may want to delay the X server screen grab.
10199  */
10200  (void) CopyMagickString(seconds,"0",MaxTextExtent);
10201  (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10202  seconds);
10203  if (*seconds == '\0')
10204  return((Image *) NULL);
10205  XDelay(display,(size_t) (1000*StringToLong(seconds)));
10206  }
10207  magick_info=GetMagickInfo(image_info->magick,exception);
10208  if ((magick_info != (const MagickInfo *) NULL) &&
10209  (magick_info->raw != MagickFalse))
10210  {
10211  char
10212  geometry[MaxTextExtent];
10213 
10214  /*
10215  Request image size from the user.
10216  */
10217  (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10218  if (image_info->size != (char *) NULL)
10219  (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10220  (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10221  geometry);
10222  (void) CloneString(&image_info->size,geometry);
10223  }
10224  /*
10225  Load the image.
10226  */
10227  XSetCursorState(display,windows,MagickTrue);
10228  XCheckRefreshWindows(display,windows);
10229  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10230  nexus=ReadImage(image_info,exception);
10231  CatchException(exception);
10232  XSetCursorState(display,windows,MagickFalse);
10233  if (nexus != (Image *) NULL)
10234  XClientMessage(display,windows->image.id,windows->im_protocols,
10235  windows->im_next_image,CurrentTime);
10236  else
10237  {
10238  char
10239  *text,
10240  **textlist;
10241 
10242  /*
10243  Unknown image format.
10244  */
10245  text=FileToString(filename,~0UL,exception);
10246  if (text == (char *) NULL)
10247  return((Image *) NULL);
10248  textlist=StringToList(text);
10249  if (textlist != (char **) NULL)
10250  {
10251  char
10252  title[MaxTextExtent];
10253 
10254  int
10255  i;
10256 
10257  (void) FormatLocaleString(title,MaxTextExtent,
10258  "Unknown format: %s",filename);
10259  XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10260  (const char **) textlist);
10261  for (i=0; textlist[i] != (char *) NULL; i++)
10262  textlist[i]=DestroyString(textlist[i]);
10263  textlist=(char **) RelinquishMagickMemory(textlist);
10264  }
10265  text=DestroyString(text);
10266  }
10267  exception=DestroyExceptionInfo(exception);
10268  image_info=DestroyImageInfo(image_info);
10269  return(nexus);
10270 }
10271 ␌
10272 /*
10273 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10274 % %
10275 % %
10276 % %
10277 + X P a n I m a g e %
10278 % %
10279 % %
10280 % %
10281 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10282 %
10283 % XPanImage() pans the image until the mouse button is released.
10284 %
10285 % The format of the XPanImage method is:
10286 %
10287 % void XPanImage(Display *display,XWindows *windows,XEvent *event)
10288 %
10289 % A description of each parameter follows:
10290 %
10291 % o display: Specifies a connection to an X server; returned from
10292 % XOpenDisplay.
10293 %
10294 % o windows: Specifies a pointer to a XWindows structure.
10295 %
10296 % o event: Specifies a pointer to a XEvent structure. If it is NULL,
10297 % the entire image is refreshed.
10298 %
10299 */
10300 static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10301 {
10302  char
10303  text[MaxTextExtent];
10304 
10305  Cursor
10306  cursor;
10307 
10308  MagickRealType
10309  x_factor,
10310  y_factor;
10311 
10313  pan_info;
10314 
10315  size_t
10316  state;
10317 
10318  /*
10319  Define cursor.
10320  */
10321  if ((windows->image.ximage->width > (int) windows->image.width) &&
10322  (windows->image.ximage->height > (int) windows->image.height))
10323  cursor=XCreateFontCursor(display,XC_fleur);
10324  else
10325  if (windows->image.ximage->width > (int) windows->image.width)
10326  cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10327  else
10328  if (windows->image.ximage->height > (int) windows->image.height)
10329  cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10330  else
10331  cursor=XCreateFontCursor(display,XC_arrow);
10332  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10333  /*
10334  Pan image as pointer moves until the mouse button is released.
10335  */
10336  x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10337  y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10338  pan_info.width=windows->pan.width*windows->image.width/
10339  windows->image.ximage->width;
10340  pan_info.height=windows->pan.height*windows->image.height/
10341  windows->image.ximage->height;
10342  pan_info.x=0;
10343  pan_info.y=0;
10344  state=UpdateConfigurationState;
10345  do
10346  {
10347  switch (event->type)
10348  {
10349  case ButtonPress:
10350  {
10351  /*
10352  User choose an initial pan location.
10353  */
10354  pan_info.x=(ssize_t) event->xbutton.x;
10355  pan_info.y=(ssize_t) event->xbutton.y;
10356  state|=UpdateConfigurationState;
10357  break;
10358  }
10359  case ButtonRelease:
10360  {
10361  /*
10362  User has finished panning the image.
10363  */
10364  pan_info.x=(ssize_t) event->xbutton.x;
10365  pan_info.y=(ssize_t) event->xbutton.y;
10366  state|=UpdateConfigurationState | ExitState;
10367  break;
10368  }
10369  case MotionNotify:
10370  {
10371  pan_info.x=(ssize_t) event->xmotion.x;
10372  pan_info.y=(ssize_t) event->xmotion.y;
10373  state|=UpdateConfigurationState;
10374  }
10375  default:
10376  break;
10377  }
10378  if ((state & UpdateConfigurationState) != 0)
10379  {
10380  /*
10381  Check boundary conditions.
10382  */
10383  if (pan_info.x < (ssize_t) (pan_info.width/2))
10384  pan_info.x=0;
10385  else
10386  pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10387  if (pan_info.x < 0)
10388  pan_info.x=0;
10389  else
10390  if ((int) (pan_info.x+windows->image.width) >
10391  windows->image.ximage->width)
10392  pan_info.x=(ssize_t)
10393  (windows->image.ximage->width-windows->image.width);
10394  if (pan_info.y < (ssize_t) (pan_info.height/2))
10395  pan_info.y=0;
10396  else
10397  pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10398  if (pan_info.y < 0)
10399  pan_info.y=0;
10400  else
10401  if ((int) (pan_info.y+windows->image.height) >
10402  windows->image.ximage->height)
10403  pan_info.y=(ssize_t)
10404  (windows->image.ximage->height-windows->image.height);
10405  if ((windows->image.x != (int) pan_info.x) ||
10406  (windows->image.y != (int) pan_info.y))
10407  {
10408  /*
10409  Display image pan offset.
10410  */
10411  windows->image.x=(int) pan_info.x;
10412  windows->image.y=(int) pan_info.y;
10413  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10414  windows->image.width,windows->image.height,windows->image.x,
10415  windows->image.y);
10416  XInfoWidget(display,windows,text);
10417  /*
10418  Refresh Image window.
10419  */
10420  XDrawPanRectangle(display,windows);
10421  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10422  }
10423  state&=(~UpdateConfigurationState);
10424  }
10425  /*
10426  Wait for next event.
10427  */
10428  if ((state & ExitState) == 0)
10429  XScreenEvent(display,windows,event);
10430  } while ((state & ExitState) == 0);
10431  /*
10432  Restore cursor.
10433  */
10434  (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10435  (void) XFreeCursor(display,cursor);
10436  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10437 }
10438 ␌
10439 /*
10440 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10441 % %
10442 % %
10443 % %
10444 + X P a s t e I m a g e %
10445 % %
10446 % %
10447 % %
10448 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10449 %
10450 % XPasteImage() pastes an image previously saved with XCropImage in the X
10451 % window image at a location the user chooses with the pointer.
10452 %
10453 % The format of the XPasteImage method is:
10454 %
10455 % MagickBooleanType XPasteImage(Display *display,
10456 % XResourceInfo *resource_info,XWindows *windows,Image *image)
10457 %
10458 % A description of each parameter follows:
10459 %
10460 % o display: Specifies a connection to an X server; returned from
10461 % XOpenDisplay.
10462 %
10463 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10464 %
10465 % o windows: Specifies a pointer to a XWindows structure.
10466 %
10467 % o image: the image; returned from ReadImage.
10468 %
10469 */
10470 static MagickBooleanType XPasteImage(Display *display,
10471  XResourceInfo *resource_info,XWindows *windows,Image *image)
10472 {
10473  const char
10474  *const PasteMenu[] =
10475  {
10476  "Operator",
10477  "Help",
10478  "Dismiss",
10479  (char *) NULL
10480  };
10481 
10482  static const ModeType
10483  PasteCommands[] =
10484  {
10485  PasteOperatorsCommand,
10486  PasteHelpCommand,
10487  PasteDismissCommand
10488  };
10489 
10490  static CompositeOperator
10491  compose = CopyCompositeOp;
10492 
10493  char
10494  text[MaxTextExtent];
10495 
10496  Cursor
10497  cursor;
10498 
10499  Image
10500  *paste_image;
10501 
10502  int
10503  entry,
10504  id,
10505  x,
10506  y;
10507 
10508  MagickRealType
10509  scale_factor;
10510 
10512  highlight_info,
10513  paste_info;
10514 
10515  unsigned int
10516  height,
10517  width;
10518 
10519  size_t
10520  state;
10521 
10522  XEvent
10523  event;
10524 
10525  /*
10526  Copy image.
10527  */
10528  if (resource_info->copy_image == (Image *) NULL)
10529  return(MagickFalse);
10530  paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,
10531  &image->exception);
10532  if (paste_image == (Image *) NULL)
10533  return(MagickFalse);
10534  /*
10535  Map Command widget.
10536  */
10537  (void) CloneString(&windows->command.name,"Paste");
10538  windows->command.data=1;
10539  (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10540  (void) XMapRaised(display,windows->command.id);
10541  XClientMessage(display,windows->image.id,windows->im_protocols,
10542  windows->im_update_widget,CurrentTime);
10543  /*
10544  Track pointer until button 1 is pressed.
10545  */
10546  XSetCursorState(display,windows,MagickFalse);
10547  XQueryPosition(display,windows->image.id,&x,&y);
10548  (void) XSelectInput(display,windows->image.id,
10549  windows->image.attributes.event_mask | PointerMotionMask);
10550  paste_info.x=(ssize_t) windows->image.x+x;
10551  paste_info.y=(ssize_t) windows->image.y+y;
10552  paste_info.width=0;
10553  paste_info.height=0;
10554  cursor=XCreateFontCursor(display,XC_ul_angle);
10555  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10556  state=DefaultState;
10557  do
10558  {
10559  if (windows->info.mapped != MagickFalse)
10560  {
10561  /*
10562  Display pointer position.
10563  */
10564  (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10565  (long) paste_info.x,(long) paste_info.y);
10566  XInfoWidget(display,windows,text);
10567  }
10568  highlight_info=paste_info;
10569  highlight_info.x=paste_info.x-windows->image.x;
10570  highlight_info.y=paste_info.y-windows->image.y;
10571  XHighlightRectangle(display,windows->image.id,
10572  windows->image.highlight_context,&highlight_info);
10573  /*
10574  Wait for next event.
10575  */
10576  XScreenEvent(display,windows,&event);
10577  XHighlightRectangle(display,windows->image.id,
10578  windows->image.highlight_context,&highlight_info);
10579  if (event.xany.window == windows->command.id)
10580  {
10581  /*
10582  Select a command from the Command widget.
10583  */
10584  id=XCommandWidget(display,windows,PasteMenu,&event);
10585  if (id < 0)
10586  continue;
10587  switch (PasteCommands[id])
10588  {
10589  case PasteOperatorsCommand:
10590  {
10591  char
10592  command[MaxTextExtent],
10593  **operators;
10594 
10595  /*
10596  Select a command from the pop-up menu.
10597  */
10598  operators=GetCommandOptions(MagickComposeOptions);
10599  if (operators == (char **) NULL)
10600  break;
10601  entry=XMenuWidget(display,windows,PasteMenu[id],
10602  (const char **) operators,command);
10603  if (entry >= 0)
10604  compose=(CompositeOperator) ParseCommandOption(
10605  MagickComposeOptions,MagickFalse,operators[entry]);
10606  operators=DestroyStringList(operators);
10607  break;
10608  }
10609  case PasteHelpCommand:
10610  {
10611  XTextViewHelp(display,resource_info,windows,MagickFalse,
10612  "Help Viewer - Image Composite",ImagePasteHelp);
10613  break;
10614  }
10615  case PasteDismissCommand:
10616  {
10617  /*
10618  Prematurely exit.
10619  */
10620  state|=EscapeState;
10621  state|=ExitState;
10622  break;
10623  }
10624  default:
10625  break;
10626  }
10627  continue;
10628  }
10629  switch (event.type)
10630  {
10631  case ButtonPress:
10632  {
10633  if (resource_info->debug != MagickFalse)
10634  (void) LogMagickEvent(X11Event,GetMagickModule(),
10635  "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10636  event.xbutton.button,event.xbutton.x,event.xbutton.y);
10637  if (event.xbutton.button != Button1)
10638  break;
10639  if (event.xbutton.window != windows->image.id)
10640  break;
10641  /*
10642  Paste rectangle is relative to image configuration.
10643  */
10644  width=(unsigned int) image->columns;
10645  height=(unsigned int) image->rows;
10646  x=0;
10647  y=0;
10648  if (windows->image.crop_geometry != (char *) NULL)
10649  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10650  &width,&height);
10651  scale_factor=(MagickRealType) windows->image.ximage->width/width;
10652  paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10653  scale_factor=(MagickRealType) windows->image.ximage->height/height;
10654  paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10655  (void) XCheckDefineCursor(display,windows->image.id,cursor);
10656  paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10657  paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10658  break;
10659  }
10660  case ButtonRelease:
10661  {
10662  if (resource_info->debug != MagickFalse)
10663  (void) LogMagickEvent(X11Event,GetMagickModule(),
10664  "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10665  event.xbutton.button,event.xbutton.x,event.xbutton.y);
10666  if (event.xbutton.button != Button1)
10667  break;
10668  if (event.xbutton.window != windows->image.id)
10669  break;
10670  if ((paste_info.width != 0) && (paste_info.height != 0))
10671  {
10672  /*
10673  User has selected the location of the paste image.
10674  */
10675  paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10676  paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10677  state|=ExitState;
10678  }
10679  break;
10680  }
10681  case Expose:
10682  break;
10683  case KeyPress:
10684  {
10685  char
10686  command[MaxTextExtent];
10687 
10688  KeySym
10689  key_symbol;
10690 
10691  int
10692  length;
10693 
10694  if (event.xkey.window != windows->image.id)
10695  break;
10696  /*
10697  Respond to a user key press.
10698  */
10699  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10700  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10701  *(command+length)='\0';
10702  if (resource_info->debug != MagickFalse)
10703  (void) LogMagickEvent(X11Event,GetMagickModule(),
10704  "Key press: 0x%lx (%s)",(long) key_symbol,command);
10705  switch ((int) key_symbol)
10706  {
10707  case XK_Escape:
10708  case XK_F20:
10709  {
10710  /*
10711  Prematurely exit.
10712  */
10713  paste_image=DestroyImage(paste_image);
10714  state|=EscapeState;
10715  state|=ExitState;
10716  break;
10717  }
10718  case XK_F1:
10719  case XK_Help:
10720  {
10721  (void) XSetFunction(display,windows->image.highlight_context,
10722  GXcopy);
10723  XTextViewHelp(display,resource_info,windows,MagickFalse,
10724  "Help Viewer - Image Composite",ImagePasteHelp);
10725  (void) XSetFunction(display,windows->image.highlight_context,
10726  GXinvert);
10727  break;
10728  }
10729  default:
10730  {
10731  (void) XBell(display,0);
10732  break;
10733  }
10734  }
10735  break;
10736  }
10737  case MotionNotify:
10738  {
10739  /*
10740  Map and unmap Info widget as text cursor crosses its boundaries.
10741  */
10742  x=event.xmotion.x;
10743  y=event.xmotion.y;
10744  if (windows->info.mapped != MagickFalse)
10745  {
10746  if ((x < (int) (windows->info.x+windows->info.width)) &&
10747  (y < (int) (windows->info.y+windows->info.height)))
10748  (void) XWithdrawWindow(display,windows->info.id,
10749  windows->info.screen);
10750  }
10751  else
10752  if ((x > (int) (windows->info.x+windows->info.width)) ||
10753  (y > (int) (windows->info.y+windows->info.height)))
10754  (void) XMapWindow(display,windows->info.id);
10755  paste_info.x=(ssize_t) windows->image.x+x;
10756  paste_info.y=(ssize_t) windows->image.y+y;
10757  break;
10758  }
10759  default:
10760  {
10761  if (resource_info->debug != MagickFalse)
10762  (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10763  event.type);
10764  break;
10765  }
10766  }
10767  } while ((state & ExitState) == 0);
10768  (void) XSelectInput(display,windows->image.id,
10769  windows->image.attributes.event_mask);
10770  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10771  XSetCursorState(display,windows,MagickFalse);
10772  (void) XFreeCursor(display,cursor);
10773  if ((state & EscapeState) != 0)
10774  return(MagickTrue);
10775  /*
10776  Image pasting is relative to image configuration.
10777  */
10778  XSetCursorState(display,windows,MagickTrue);
10779  XCheckRefreshWindows(display,windows);
10780  width=(unsigned int) image->columns;
10781  height=(unsigned int) image->rows;
10782  x=0;
10783  y=0;
10784  if (windows->image.crop_geometry != (char *) NULL)
10785  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10786  scale_factor=(MagickRealType) width/windows->image.ximage->width;
10787  paste_info.x+=x;
10788  paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10789  paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10790  scale_factor=(MagickRealType) height/windows->image.ximage->height;
10791  paste_info.y+=y;
10792  paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10793  paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10794  /*
10795  Paste image with X Image window.
10796  */
10797  (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10798  paste_image=DestroyImage(paste_image);
10799  XSetCursorState(display,windows,MagickFalse);
10800  /*
10801  Update image colormap.
10802  */
10803  XConfigureImageColormap(display,resource_info,windows,image);
10804  (void) XConfigureImage(display,resource_info,windows,image);
10805  return(MagickTrue);
10806 }
10807 ␌
10808 /*
10809 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10810 % %
10811 % %
10812 % %
10813 + X P r i n t I m a g e %
10814 % %
10815 % %
10816 % %
10817 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10818 %
10819 % XPrintImage() prints an image to a Postscript printer.
10820 %
10821 % The format of the XPrintImage method is:
10822 %
10823 % MagickBooleanType XPrintImage(Display *display,
10824 % XResourceInfo *resource_info,XWindows *windows,Image *image)
10825 %
10826 % A description of each parameter follows:
10827 %
10828 % o display: Specifies a connection to an X server; returned from
10829 % XOpenDisplay.
10830 %
10831 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10832 %
10833 % o windows: Specifies a pointer to a XWindows structure.
10834 %
10835 % o image: the image.
10836 %
10837 */
10838 static MagickBooleanType XPrintImage(Display *display,
10839  XResourceInfo *resource_info,XWindows *windows,Image *image)
10840 {
10841  char
10842  filename[MaxTextExtent],
10843  geometry[MaxTextExtent];
10844 
10845  const char
10846  *const PageSizes[] =
10847  {
10848  "Letter",
10849  "Tabloid",
10850  "Ledger",
10851  "Legal",
10852  "Statement",
10853  "Executive",
10854  "A3",
10855  "A4",
10856  "A5",
10857  "B4",
10858  "B5",
10859  "Folio",
10860  "Quarto",
10861  "10x14",
10862  (char *) NULL
10863  };
10864 
10865  Image
10866  *print_image;
10867 
10868  ImageInfo
10869  *image_info;
10870 
10871  MagickStatusType
10872  status;
10873 
10874  /*
10875  Request Postscript page geometry from user.
10876  */
10877  image_info=CloneImageInfo(resource_info->image_info);
10878  (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10879  if (image_info->page != (char *) NULL)
10880  (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10881  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10882  "Select Postscript Page Geometry:",geometry);
10883  if (*geometry == '\0')
10884  return(MagickTrue);
10885  image_info->page=GetPageGeometry(geometry);
10886  /*
10887  Apply image transforms.
10888  */
10889  XSetCursorState(display,windows,MagickTrue);
10890  XCheckRefreshWindows(display,windows);
10891  print_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10892  if (print_image == (Image *) NULL)
10893  return(MagickFalse);
10894  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
10895  windows->image.ximage->width,windows->image.ximage->height);
10896  (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10897  /*
10898  Print image.
10899  */
10900  (void) AcquireUniqueFilename(filename);
10901  (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
10902  filename);
10903  status=WriteImage(image_info,print_image);
10904  (void) RelinquishUniqueFileResource(filename);
10905  print_image=DestroyImage(print_image);
10906  image_info=DestroyImageInfo(image_info);
10907  XSetCursorState(display,windows,MagickFalse);
10908  return(status != 0 ? MagickTrue : MagickFalse);
10909 }
10910 ␌
10911 /*
10912 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10913 % %
10914 % %
10915 % %
10916 + X R O I I m a g e %
10917 % %
10918 % %
10919 % %
10920 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10921 %
10922 % XROIImage() applies an image processing technique to a region of interest.
10923 %
10924 % The format of the XROIImage method is:
10925 %
10926 % MagickBooleanType XROIImage(Display *display,
10927 % XResourceInfo *resource_info,XWindows *windows,Image **image)
10928 %
10929 % A description of each parameter follows:
10930 %
10931 % o display: Specifies a connection to an X server; returned from
10932 % XOpenDisplay.
10933 %
10934 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10935 %
10936 % o windows: Specifies a pointer to a XWindows structure.
10937 %
10938 % o image: the image; returned from ReadImage.
10939 %
10940 */
10941 static MagickBooleanType XROIImage(Display *display,
10942  XResourceInfo *resource_info,XWindows *windows,Image **image)
10943 {
10944 #define ApplyMenus 7
10945 
10946  const char
10947  *const ROIMenu[] =
10948  {
10949  "Help",
10950  "Dismiss",
10951  (char *) NULL
10952  },
10953  *const ApplyMenu[] =
10954  {
10955  "File",
10956  "Edit",
10957  "Transform",
10958  "Enhance",
10959  "Effects",
10960  "F/X",
10961  "Miscellany",
10962  "Help",
10963  "Dismiss",
10964  (char *) NULL
10965  },
10966  *const FileMenu[] =
10967  {
10968  "Save...",
10969  "Print...",
10970  (char *) NULL
10971  },
10972  *const EditMenu[] =
10973  {
10974  "Undo",
10975  "Redo",
10976  (char *) NULL
10977  },
10978  *const TransformMenu[] =
10979  {
10980  "Flop",
10981  "Flip",
10982  "Rotate Right",
10983  "Rotate Left",
10984  (char *) NULL
10985  },
10986  *const EnhanceMenu[] =
10987  {
10988  "Hue...",
10989  "Saturation...",
10990  "Brightness...",
10991  "Gamma...",
10992  "Spiff",
10993  "Dull",
10994  "Contrast Stretch...",
10995  "Sigmoidal Contrast...",
10996  "Normalize",
10997  "Equalize",
10998  "Negate",
10999  "Grayscale",
11000  "Map...",
11001  "Quantize...",
11002  (char *) NULL
11003  },
11004  *const EffectsMenu[] =
11005  {
11006  "Despeckle",
11007  "Emboss",
11008  "Reduce Noise",
11009  "Add Noise",
11010  "Sharpen...",
11011  "Blur...",
11012  "Threshold...",
11013  "Edge Detect...",
11014  "Spread...",
11015  "Shade...",
11016  "Raise...",
11017  "Segment...",
11018  (char *) NULL
11019  },
11020  *const FXMenu[] =
11021  {
11022  "Solarize...",
11023  "Sepia Tone...",
11024  "Swirl...",
11025  "Implode...",
11026  "Vignette...",
11027  "Wave...",
11028  "Oil Paint...",
11029  "Charcoal Draw...",
11030  (char *) NULL
11031  },
11032  *const MiscellanyMenu[] =
11033  {
11034  "Image Info",
11035  "Zoom Image",
11036  "Show Preview...",
11037  "Show Histogram",
11038  "Show Matte",
11039  (char *) NULL
11040  };
11041 
11042  const char
11043  *const *Menus[ApplyMenus] =
11044  {
11045  FileMenu,
11046  EditMenu,
11047  TransformMenu,
11048  EnhanceMenu,
11049  EffectsMenu,
11050  FXMenu,
11051  MiscellanyMenu
11052  };
11053 
11054  static const DisplayCommand
11055  ApplyCommands[] =
11056  {
11057  NullCommand,
11058  NullCommand,
11059  NullCommand,
11060  NullCommand,
11061  NullCommand,
11062  NullCommand,
11063  NullCommand,
11064  HelpCommand,
11065  QuitCommand
11066  },
11067  FileCommands[] =
11068  {
11069  SaveCommand,
11070  PrintCommand
11071  },
11072  EditCommands[] =
11073  {
11074  UndoCommand,
11075  RedoCommand
11076  },
11077  TransformCommands[] =
11078  {
11079  FlopCommand,
11080  FlipCommand,
11081  RotateRightCommand,
11082  RotateLeftCommand
11083  },
11084  EnhanceCommands[] =
11085  {
11086  HueCommand,
11087  SaturationCommand,
11088  BrightnessCommand,
11089  GammaCommand,
11090  SpiffCommand,
11091  DullCommand,
11092  ContrastStretchCommand,
11093  SigmoidalContrastCommand,
11094  NormalizeCommand,
11095  EqualizeCommand,
11096  NegateCommand,
11097  GrayscaleCommand,
11098  MapCommand,
11099  QuantizeCommand
11100  },
11101  EffectsCommands[] =
11102  {
11103  DespeckleCommand,
11104  EmbossCommand,
11105  ReduceNoiseCommand,
11106  AddNoiseCommand,
11107  SharpenCommand,
11108  BlurCommand,
11109  ThresholdCommand,
11110  EdgeDetectCommand,
11111  SpreadCommand,
11112  ShadeCommand,
11113  RaiseCommand,
11114  SegmentCommand
11115  },
11116  FXCommands[] =
11117  {
11118  SolarizeCommand,
11119  SepiaToneCommand,
11120  SwirlCommand,
11121  ImplodeCommand,
11122  VignetteCommand,
11123  WaveCommand,
11124  OilPaintCommand,
11125  CharcoalDrawCommand
11126  },
11127  MiscellanyCommands[] =
11128  {
11129  InfoCommand,
11130  ZoomCommand,
11131  ShowPreviewCommand,
11132  ShowHistogramCommand,
11133  ShowMatteCommand
11134  },
11135  ROICommands[] =
11136  {
11137  ROIHelpCommand,
11138  ROIDismissCommand
11139  };
11140 
11141  static const DisplayCommand
11142  *Commands[ApplyMenus] =
11143  {
11144  FileCommands,
11145  EditCommands,
11146  TransformCommands,
11147  EnhanceCommands,
11148  EffectsCommands,
11149  FXCommands,
11150  MiscellanyCommands
11151  };
11152 
11153  char
11154  command[MaxTextExtent],
11155  text[MaxTextExtent];
11156 
11157  DisplayCommand
11158  display_command;
11159 
11160  Cursor
11161  cursor;
11162 
11163  Image
11164  *roi_image;
11165 
11166  int
11167  entry,
11168  id,
11169  x,
11170  y;
11171 
11172  MagickRealType
11173  scale_factor;
11174 
11175  MagickProgressMonitor
11176  progress_monitor;
11177 
11179  crop_info,
11180  highlight_info,
11181  roi_info;
11182 
11183  unsigned int
11184  height,
11185  width;
11186 
11187  size_t
11188  state;
11189 
11190  XEvent
11191  event;
11192 
11193  /*
11194  Map Command widget.
11195  */
11196  (void) CloneString(&windows->command.name,"ROI");
11197  windows->command.data=0;
11198  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11199  (void) XMapRaised(display,windows->command.id);
11200  XClientMessage(display,windows->image.id,windows->im_protocols,
11201  windows->im_update_widget,CurrentTime);
11202  /*
11203  Track pointer until button 1 is pressed.
11204  */
11205  XQueryPosition(display,windows->image.id,&x,&y);
11206  (void) XSelectInput(display,windows->image.id,
11207  windows->image.attributes.event_mask | PointerMotionMask);
11208  crop_info.width=0;
11209  crop_info.height=0;
11210  crop_info.x=0;
11211  crop_info.y=0;
11212  roi_info.x=(ssize_t) windows->image.x+x;
11213  roi_info.y=(ssize_t) windows->image.y+y;
11214  roi_info.width=0;
11215  roi_info.height=0;
11216  cursor=XCreateFontCursor(display,XC_fleur);
11217  state=DefaultState;
11218  do
11219  {
11220  if (windows->info.mapped != MagickFalse)
11221  {
11222  /*
11223  Display pointer position.
11224  */
11225  (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11226  (long) roi_info.x,(long) roi_info.y);
11227  XInfoWidget(display,windows,text);
11228  }
11229  /*
11230  Wait for next event.
11231  */
11232  XScreenEvent(display,windows,&event);
11233  if (event.xany.window == windows->command.id)
11234  {
11235  /*
11236  Select a command from the Command widget.
11237  */
11238  id=XCommandWidget(display,windows,ROIMenu,&event);
11239  if (id < 0)
11240  continue;
11241  switch (ROICommands[id])
11242  {
11243  case ROIHelpCommand:
11244  {
11245  XTextViewHelp(display,resource_info,windows,MagickFalse,
11246  "Help Viewer - Region of Interest",ImageROIHelp);
11247  break;
11248  }
11249  case ROIDismissCommand:
11250  {
11251  /*
11252  Prematurely exit.
11253  */
11254  state|=EscapeState;
11255  state|=ExitState;
11256  break;
11257  }
11258  default:
11259  break;
11260  }
11261  continue;
11262  }
11263  switch (event.type)
11264  {
11265  case ButtonPress:
11266  {
11267  if (event.xbutton.button != Button1)
11268  break;
11269  if (event.xbutton.window != windows->image.id)
11270  break;
11271  /*
11272  Note first corner of region of interest rectangle-- exit loop.
11273  */
11274  (void) XCheckDefineCursor(display,windows->image.id,cursor);
11275  roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11276  roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11277  state|=ExitState;
11278  break;
11279  }
11280  case ButtonRelease:
11281  break;
11282  case Expose:
11283  break;
11284  case KeyPress:
11285  {
11286  KeySym
11287  key_symbol;
11288 
11289  if (event.xkey.window != windows->image.id)
11290  break;
11291  /*
11292  Respond to a user key press.
11293  */
11294  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11295  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11296  switch ((int) key_symbol)
11297  {
11298  case XK_Escape:
11299  case XK_F20:
11300  {
11301  /*
11302  Prematurely exit.
11303  */
11304  state|=EscapeState;
11305  state|=ExitState;
11306  break;
11307  }
11308  case XK_F1:
11309  case XK_Help:
11310  {
11311  XTextViewHelp(display,resource_info,windows,MagickFalse,
11312  "Help Viewer - Region of Interest",ImageROIHelp);
11313  break;
11314  }
11315  default:
11316  {
11317  (void) XBell(display,0);
11318  break;
11319  }
11320  }
11321  break;
11322  }
11323  case MotionNotify:
11324  {
11325  /*
11326  Map and unmap Info widget as text cursor crosses its boundaries.
11327  */
11328  x=event.xmotion.x;
11329  y=event.xmotion.y;
11330  if (windows->info.mapped != MagickFalse)
11331  {
11332  if ((x < (int) (windows->info.x+windows->info.width)) &&
11333  (y < (int) (windows->info.y+windows->info.height)))
11334  (void) XWithdrawWindow(display,windows->info.id,
11335  windows->info.screen);
11336  }
11337  else
11338  if ((x > (int) (windows->info.x+windows->info.width)) ||
11339  (y > (int) (windows->info.y+windows->info.height)))
11340  (void) XMapWindow(display,windows->info.id);
11341  roi_info.x=(ssize_t) windows->image.x+x;
11342  roi_info.y=(ssize_t) windows->image.y+y;
11343  break;
11344  }
11345  default:
11346  break;
11347  }
11348  } while ((state & ExitState) == 0);
11349  (void) XSelectInput(display,windows->image.id,
11350  windows->image.attributes.event_mask);
11351  if ((state & EscapeState) != 0)
11352  {
11353  /*
11354  User want to exit without region of interest.
11355  */
11356  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11357  (void) XFreeCursor(display,cursor);
11358  return(MagickTrue);
11359  }
11360  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11361  do
11362  {
11363  /*
11364  Size rectangle as pointer moves until the mouse button is released.
11365  */
11366  x=(int) roi_info.x;
11367  y=(int) roi_info.y;
11368  roi_info.width=0;
11369  roi_info.height=0;
11370  state=DefaultState;
11371  do
11372  {
11373  highlight_info=roi_info;
11374  highlight_info.x=roi_info.x-windows->image.x;
11375  highlight_info.y=roi_info.y-windows->image.y;
11376  if ((highlight_info.width > 3) && (highlight_info.height > 3))
11377  {
11378  /*
11379  Display info and draw region of interest rectangle.
11380  */
11381  if (windows->info.mapped == MagickFalse)
11382  (void) XMapWindow(display,windows->info.id);
11383  (void) FormatLocaleString(text,MaxTextExtent,
11384  " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11385  roi_info.height,(double) roi_info.x,(double) roi_info.y);
11386  XInfoWidget(display,windows,text);
11387  XHighlightRectangle(display,windows->image.id,
11388  windows->image.highlight_context,&highlight_info);
11389  }
11390  else
11391  if (windows->info.mapped != MagickFalse)
11392  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11393  /*
11394  Wait for next event.
11395  */
11396  XScreenEvent(display,windows,&event);
11397  if ((highlight_info.width > 3) && (highlight_info.height > 3))
11398  XHighlightRectangle(display,windows->image.id,
11399  windows->image.highlight_context,&highlight_info);
11400  switch (event.type)
11401  {
11402  case ButtonPress:
11403  {
11404  roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11405  roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11406  break;
11407  }
11408  case ButtonRelease:
11409  {
11410  /*
11411  User has committed to region of interest rectangle.
11412  */
11413  roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11414  roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11415  XSetCursorState(display,windows,MagickFalse);
11416  state|=ExitState;
11417  if (LocaleCompare(windows->command.name,"Apply") == 0)
11418  break;
11419  (void) CloneString(&windows->command.name,"Apply");
11420  windows->command.data=ApplyMenus;
11421  (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11422  break;
11423  }
11424  case Expose:
11425  break;
11426  case MotionNotify:
11427  {
11428  roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11429  roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11430  }
11431  default:
11432  break;
11433  }
11434  if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11435  ((state & ExitState) != 0))
11436  {
11437  /*
11438  Check boundary conditions.
11439  */
11440  if (roi_info.x < 0)
11441  roi_info.x=0;
11442  else
11443  if (roi_info.x > (ssize_t) windows->image.ximage->width)
11444  roi_info.x=(ssize_t) windows->image.ximage->width;
11445  if ((int) roi_info.x < x)
11446  roi_info.width=(unsigned int) (x-roi_info.x);
11447  else
11448  {
11449  roi_info.width=(unsigned int) (roi_info.x-x);
11450  roi_info.x=(ssize_t) x;
11451  }
11452  if (roi_info.y < 0)
11453  roi_info.y=0;
11454  else
11455  if (roi_info.y > (ssize_t) windows->image.ximage->height)
11456  roi_info.y=(ssize_t) windows->image.ximage->height;
11457  if ((int) roi_info.y < y)
11458  roi_info.height=(unsigned int) (y-roi_info.y);
11459  else
11460  {
11461  roi_info.height=(unsigned int) (roi_info.y-y);
11462  roi_info.y=(ssize_t) y;
11463  }
11464  }
11465  } while ((state & ExitState) == 0);
11466  /*
11467  Wait for user to grab a corner of the rectangle or press return.
11468  */
11469  state=DefaultState;
11470  display_command=NullCommand;
11471  (void) XMapWindow(display,windows->info.id);
11472  do
11473  {
11474  if (windows->info.mapped != MagickFalse)
11475  {
11476  /*
11477  Display pointer position.
11478  */
11479  (void) FormatLocaleString(text,MaxTextExtent,
11480  " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11481  roi_info.height,(double) roi_info.x,(double) roi_info.y);
11482  XInfoWidget(display,windows,text);
11483  }
11484  highlight_info=roi_info;
11485  highlight_info.x=roi_info.x-windows->image.x;
11486  highlight_info.y=roi_info.y-windows->image.y;
11487  if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11488  {
11489  state|=EscapeState;
11490  state|=ExitState;
11491  break;
11492  }
11493  if ((state & UpdateRegionState) != 0)
11494  {
11495  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11496  switch (display_command)
11497  {
11498  case UndoCommand:
11499  case RedoCommand:
11500  {
11501  (void) XMagickCommand(display,resource_info,windows,
11502  display_command,image);
11503  break;
11504  }
11505  default:
11506  {
11507  /*
11508  Region of interest is relative to image configuration.
11509  */
11510  progress_monitor=SetImageProgressMonitor(*image,
11511  (MagickProgressMonitor) NULL,(*image)->client_data);
11512  crop_info=roi_info;
11513  width=(unsigned int) (*image)->columns;
11514  height=(unsigned int) (*image)->rows;
11515  x=0;
11516  y=0;
11517  if (windows->image.crop_geometry != (char *) NULL)
11518  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11519  &width,&height);
11520  scale_factor=(MagickRealType) width/windows->image.ximage->width;
11521  crop_info.x+=x;
11522  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11523  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11524  scale_factor=(MagickRealType)
11525  height/windows->image.ximage->height;
11526  crop_info.y+=y;
11527  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11528  crop_info.height=(unsigned int)
11529  (scale_factor*crop_info.height+0.5);
11530  roi_image=CropImage(*image,&crop_info,&(*image)->exception);
11531  (void) SetImageProgressMonitor(*image,progress_monitor,
11532  (*image)->client_data);
11533  if (roi_image == (Image *) NULL)
11534  continue;
11535  /*
11536  Apply image processing technique to the region of interest.
11537  */
11538  windows->image.orphan=MagickTrue;
11539  (void) XMagickCommand(display,resource_info,windows,
11540  display_command,&roi_image);
11541  progress_monitor=SetImageProgressMonitor(*image,
11542  (MagickProgressMonitor) NULL,(*image)->client_data);
11543  (void) XMagickCommand(display,resource_info,windows,
11544  SaveToUndoBufferCommand,image);
11545  windows->image.orphan=MagickFalse;
11546  (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11547  crop_info.x,crop_info.y);
11548  roi_image=DestroyImage(roi_image);
11549  (void) SetImageProgressMonitor(*image,progress_monitor,
11550  (*image)->client_data);
11551  break;
11552  }
11553  }
11554  if (display_command != InfoCommand)
11555  {
11556  XConfigureImageColormap(display,resource_info,windows,*image);
11557  (void) XConfigureImage(display,resource_info,windows,*image);
11558  }
11559  XCheckRefreshWindows(display,windows);
11560  XInfoWidget(display,windows,text);
11561  (void) XSetFunction(display,windows->image.highlight_context,
11562  GXinvert);
11563  state&=(~UpdateRegionState);
11564  }
11565  XHighlightRectangle(display,windows->image.id,
11566  windows->image.highlight_context,&highlight_info);
11567  XScreenEvent(display,windows,&event);
11568  if (event.xany.window == windows->command.id)
11569  {
11570  /*
11571  Select a command from the Command widget.
11572  */
11573  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11574  display_command=NullCommand;
11575  id=XCommandWidget(display,windows,ApplyMenu,&event);
11576  if (id >= 0)
11577  {
11578  (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11579  display_command=ApplyCommands[id];
11580  if (id < ApplyMenus)
11581  {
11582  /*
11583  Select a command from a pop-up menu.
11584  */
11585  entry=XMenuWidget(display,windows,ApplyMenu[id],
11586  (const char **) Menus[id],command);
11587  if (entry >= 0)
11588  {
11589  (void) CopyMagickString(command,Menus[id][entry],
11590  MaxTextExtent);
11591  display_command=Commands[id][entry];
11592  }
11593  }
11594  }
11595  (void) XSetFunction(display,windows->image.highlight_context,
11596  GXinvert);
11597  XHighlightRectangle(display,windows->image.id,
11598  windows->image.highlight_context,&highlight_info);
11599  if (display_command == HelpCommand)
11600  {
11601  (void) XSetFunction(display,windows->image.highlight_context,
11602  GXcopy);
11603  XTextViewHelp(display,resource_info,windows,MagickFalse,
11604  "Help Viewer - Region of Interest",ImageROIHelp);
11605  (void) XSetFunction(display,windows->image.highlight_context,
11606  GXinvert);
11607  continue;
11608  }
11609  if (display_command == QuitCommand)
11610  {
11611  /*
11612  exit.
11613  */
11614  state|=EscapeState;
11615  state|=ExitState;
11616  continue;
11617  }
11618  if (display_command != NullCommand)
11619  state|=UpdateRegionState;
11620  continue;
11621  }
11622  XHighlightRectangle(display,windows->image.id,
11623  windows->image.highlight_context,&highlight_info);
11624  switch (event.type)
11625  {
11626  case ButtonPress:
11627  {
11628  x=windows->image.x;
11629  y=windows->image.y;
11630  if (event.xbutton.button != Button1)
11631  break;
11632  if (event.xbutton.window != windows->image.id)
11633  break;
11634  x=windows->image.x+event.xbutton.x;
11635  y=windows->image.y+event.xbutton.y;
11636  if ((x < (int) (roi_info.x+RoiDelta)) &&
11637  (x > (int) (roi_info.x-RoiDelta)) &&
11638  (y < (int) (roi_info.y+RoiDelta)) &&
11639  (y > (int) (roi_info.y-RoiDelta)))
11640  {
11641  roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11642  roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11643  state|=UpdateConfigurationState;
11644  break;
11645  }
11646  if ((x < (int) (roi_info.x+RoiDelta)) &&
11647  (x > (int) (roi_info.x-RoiDelta)) &&
11648  (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11649  (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11650  {
11651  roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11652  state|=UpdateConfigurationState;
11653  break;
11654  }
11655  if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11656  (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11657  (y < (int) (roi_info.y+RoiDelta)) &&
11658  (y > (int) (roi_info.y-RoiDelta)))
11659  {
11660  roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11661  state|=UpdateConfigurationState;
11662  break;
11663  }
11664  if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11665  (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11666  (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11667  (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11668  {
11669  state|=UpdateConfigurationState;
11670  break;
11671  }
11672  magick_fallthrough;
11673  }
11674  case ButtonRelease:
11675  {
11676  if (event.xbutton.window == windows->pan.id)
11677  if ((highlight_info.x != crop_info.x-windows->image.x) ||
11678  (highlight_info.y != crop_info.y-windows->image.y))
11679  XHighlightRectangle(display,windows->image.id,
11680  windows->image.highlight_context,&highlight_info);
11681  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11682  event.xbutton.time);
11683  break;
11684  }
11685  case Expose:
11686  {
11687  if (event.xexpose.window == windows->image.id)
11688  if (event.xexpose.count == 0)
11689  {
11690  event.xexpose.x=(int) highlight_info.x;
11691  event.xexpose.y=(int) highlight_info.y;
11692  event.xexpose.width=(int) highlight_info.width;
11693  event.xexpose.height=(int) highlight_info.height;
11694  XRefreshWindow(display,&windows->image,&event);
11695  }
11696  if (event.xexpose.window == windows->info.id)
11697  if (event.xexpose.count == 0)
11698  XInfoWidget(display,windows,text);
11699  break;
11700  }
11701  case KeyPress:
11702  {
11703  KeySym
11704  key_symbol;
11705 
11706  if (event.xkey.window != windows->image.id)
11707  break;
11708  /*
11709  Respond to a user key press.
11710  */
11711  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11712  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11713  switch ((int) key_symbol)
11714  {
11715  case XK_Shift_L:
11716  case XK_Shift_R:
11717  break;
11718  case XK_Escape:
11719  case XK_F20:
11720  {
11721  state|=EscapeState;
11722  magick_fallthrough;
11723  }
11724  case XK_Return:
11725  {
11726  state|=ExitState;
11727  break;
11728  }
11729  case XK_Home:
11730  case XK_KP_Home:
11731  {
11732  roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11733  roi_info.y=(ssize_t) (windows->image.height/2L-
11734  roi_info.height/2L);
11735  break;
11736  }
11737  case XK_Left:
11738  case XK_KP_Left:
11739  {
11740  roi_info.x--;
11741  break;
11742  }
11743  case XK_Up:
11744  case XK_KP_Up:
11745  case XK_Next:
11746  {
11747  roi_info.y--;
11748  break;
11749  }
11750  case XK_Right:
11751  case XK_KP_Right:
11752  {
11753  roi_info.x++;
11754  break;
11755  }
11756  case XK_Prior:
11757  case XK_Down:
11758  case XK_KP_Down:
11759  {
11760  roi_info.y++;
11761  break;
11762  }
11763  case XK_F1:
11764  case XK_Help:
11765  {
11766  (void) XSetFunction(display,windows->image.highlight_context,
11767  GXcopy);
11768  XTextViewHelp(display,resource_info,windows,MagickFalse,
11769  "Help Viewer - Region of Interest",ImageROIHelp);
11770  (void) XSetFunction(display,windows->image.highlight_context,
11771  GXinvert);
11772  break;
11773  }
11774  default:
11775  {
11776  display_command=XImageWindowCommand(display,resource_info,windows,
11777  event.xkey.state,key_symbol,image);
11778  if (display_command != NullCommand)
11779  state|=UpdateRegionState;
11780  break;
11781  }
11782  }
11783  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11784  event.xkey.time);
11785  break;
11786  }
11787  case KeyRelease:
11788  break;
11789  case MotionNotify:
11790  {
11791  if (event.xbutton.window != windows->image.id)
11792  break;
11793  /*
11794  Map and unmap Info widget as text cursor crosses its boundaries.
11795  */
11796  x=event.xmotion.x;
11797  y=event.xmotion.y;
11798  if (windows->info.mapped != MagickFalse)
11799  {
11800  if ((x < (int) (windows->info.x+windows->info.width)) &&
11801  (y < (int) (windows->info.y+windows->info.height)))
11802  (void) XWithdrawWindow(display,windows->info.id,
11803  windows->info.screen);
11804  }
11805  else
11806  if ((x > (int) (windows->info.x+windows->info.width)) ||
11807  (y > (int) (windows->info.y+windows->info.height)))
11808  (void) XMapWindow(display,windows->info.id);
11809  roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11810  roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11811  break;
11812  }
11813  case SelectionRequest:
11814  {
11815  XSelectionEvent
11816  notify;
11817 
11818  XSelectionRequestEvent
11819  *request;
11820 
11821  /*
11822  Set primary selection.
11823  */
11824  (void) FormatLocaleString(text,MaxTextExtent,
11825  "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11826  roi_info.height,(double) roi_info.x,(double) roi_info.y);
11827  request=(&(event.xselectionrequest));
11828  (void) XChangeProperty(request->display,request->requestor,
11829  request->property,request->target,8,PropModeReplace,
11830  (unsigned char *) text,(int) strlen(text));
11831  notify.type=SelectionNotify;
11832  notify.display=request->display;
11833  notify.requestor=request->requestor;
11834  notify.selection=request->selection;
11835  notify.target=request->target;
11836  notify.time=request->time;
11837  if (request->property == None)
11838  notify.property=request->target;
11839  else
11840  notify.property=request->property;
11841  (void) XSendEvent(request->display,request->requestor,False,0,
11842  (XEvent *) &notify);
11843  }
11844  default:
11845  break;
11846  }
11847  if ((state & UpdateConfigurationState) != 0)
11848  {
11849  (void) XPutBackEvent(display,&event);
11850  (void) XCheckDefineCursor(display,windows->image.id,cursor);
11851  break;
11852  }
11853  } while ((state & ExitState) == 0);
11854  } while ((state & ExitState) == 0);
11855  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11856  XSetCursorState(display,windows,MagickFalse);
11857  if ((state & EscapeState) != 0)
11858  return(MagickTrue);
11859  return(MagickTrue);
11860 }
11861 ␌
11862 /*
11863 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11864 % %
11865 % %
11866 % %
11867 + X R o t a t e I m a g e %
11868 % %
11869 % %
11870 % %
11871 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11872 %
11873 % XRotateImage() rotates the X image. If the degrees parameter if zero, the
11874 % rotation angle is computed from the slope of a line drawn by the user.
11875 %
11876 % The format of the XRotateImage method is:
11877 %
11878 % MagickBooleanType XRotateImage(Display *display,
11879 % XResourceInfo *resource_info,XWindows *windows,double degrees,
11880 % Image **image)
11881 %
11882 % A description of each parameter follows:
11883 %
11884 % o display: Specifies a connection to an X server; returned from
11885 % XOpenDisplay.
11886 %
11887 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11888 %
11889 % o windows: Specifies a pointer to a XWindows structure.
11890 %
11891 % o degrees: Specifies the number of degrees to rotate the image.
11892 %
11893 % o image: the image.
11894 %
11895 */
11896 static MagickBooleanType XRotateImage(Display *display,
11897  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image)
11898 {
11899  const char
11900  *const RotateMenu[] =
11901  {
11902  "Pixel Color",
11903  "Direction",
11904  "Help",
11905  "Dismiss",
11906  (char *) NULL
11907  };
11908 
11909  static ModeType
11910  direction = HorizontalRotateCommand;
11911 
11912  static const ModeType
11913  DirectionCommands[] =
11914  {
11915  HorizontalRotateCommand,
11916  VerticalRotateCommand
11917  },
11918  RotateCommands[] =
11919  {
11920  RotateColorCommand,
11921  RotateDirectionCommand,
11922  RotateHelpCommand,
11923  RotateDismissCommand
11924  };
11925 
11926  static unsigned int
11927  pen_id = 0;
11928 
11929  char
11930  command[MaxTextExtent],
11931  text[MaxTextExtent];
11932 
11933  Image
11934  *rotate_image;
11935 
11936  int
11937  id,
11938  x,
11939  y;
11940 
11941  MagickRealType
11942  normalized_degrees;
11943 
11944  int
11945  i;
11946 
11947  unsigned int
11948  height,
11949  rotations,
11950  width;
11951 
11952  if (degrees == 0.0)
11953  {
11954  unsigned int
11955  distance;
11956 
11957  size_t
11958  state;
11959 
11960  XEvent
11961  event;
11962 
11963  XSegment
11964  rotate_info;
11965 
11966  /*
11967  Map Command widget.
11968  */
11969  (void) CloneString(&windows->command.name,"Rotate");
11970  windows->command.data=2;
11971  (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
11972  (void) XMapRaised(display,windows->command.id);
11973  XClientMessage(display,windows->image.id,windows->im_protocols,
11974  windows->im_update_widget,CurrentTime);
11975  /*
11976  Wait for first button press.
11977  */
11978  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11979  XQueryPosition(display,windows->image.id,&x,&y);
11980  rotate_info.x1=x;
11981  rotate_info.y1=y;
11982  rotate_info.x2=x;
11983  rotate_info.y2=y;
11984  state=DefaultState;
11985  do
11986  {
11987  XHighlightLine(display,windows->image.id,
11988  windows->image.highlight_context,&rotate_info);
11989  /*
11990  Wait for next event.
11991  */
11992  XScreenEvent(display,windows,&event);
11993  XHighlightLine(display,windows->image.id,
11994  windows->image.highlight_context,&rotate_info);
11995  if (event.xany.window == windows->command.id)
11996  {
11997  /*
11998  Select a command from the Command widget.
11999  */
12000  id=XCommandWidget(display,windows,RotateMenu,&event);
12001  if (id < 0)
12002  continue;
12003  (void) XSetFunction(display,windows->image.highlight_context,
12004  GXcopy);
12005  switch (RotateCommands[id])
12006  {
12007  case RotateColorCommand:
12008  {
12009  const char
12010  *ColorMenu[MaxNumberPens];
12011 
12012  int
12013  pen_number;
12014 
12015  XColor
12016  color;
12017 
12018  /*
12019  Initialize menu selections.
12020  */
12021  for (i=0; i < (int) (MaxNumberPens-2); i++)
12022  ColorMenu[i]=resource_info->pen_colors[i];
12023  ColorMenu[MaxNumberPens-2]="Browser...";
12024  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12025  /*
12026  Select a pen color from the pop-up menu.
12027  */
12028  pen_number=XMenuWidget(display,windows,RotateMenu[id],
12029  (const char **) ColorMenu,command);
12030  if (pen_number < 0)
12031  break;
12032  if (pen_number == (MaxNumberPens-2))
12033  {
12034  static char
12035  color_name[MaxTextExtent] = "gray";
12036 
12037  /*
12038  Select a pen color from a dialog.
12039  */
12040  resource_info->pen_colors[pen_number]=color_name;
12041  XColorBrowserWidget(display,windows,"Select",color_name);
12042  if (*color_name == '\0')
12043  break;
12044  }
12045  /*
12046  Set pen color.
12047  */
12048  (void) XParseColor(display,windows->map_info->colormap,
12049  resource_info->pen_colors[pen_number],&color);
12050  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12051  (unsigned int) MaxColors,&color);
12052  windows->pixel_info->pen_colors[pen_number]=color;
12053  pen_id=(unsigned int) pen_number;
12054  break;
12055  }
12056  case RotateDirectionCommand:
12057  {
12058  const char
12059  *const Directions[] =
12060  {
12061  "horizontal",
12062  "vertical",
12063  (char *) NULL,
12064  };
12065 
12066  /*
12067  Select a command from the pop-up menu.
12068  */
12069  id=XMenuWidget(display,windows,RotateMenu[id],
12070  Directions,command);
12071  if (id >= 0)
12072  direction=DirectionCommands[id];
12073  break;
12074  }
12075  case RotateHelpCommand:
12076  {
12077  XTextViewHelp(display,resource_info,windows,MagickFalse,
12078  "Help Viewer - Image Rotation",ImageRotateHelp);
12079  break;
12080  }
12081  case RotateDismissCommand:
12082  {
12083  /*
12084  Prematurely exit.
12085  */
12086  state|=EscapeState;
12087  state|=ExitState;
12088  break;
12089  }
12090  default:
12091  break;
12092  }
12093  (void) XSetFunction(display,windows->image.highlight_context,
12094  GXinvert);
12095  continue;
12096  }
12097  switch (event.type)
12098  {
12099  case ButtonPress:
12100  {
12101  if (event.xbutton.button != Button1)
12102  break;
12103  if (event.xbutton.window != windows->image.id)
12104  break;
12105  /*
12106  exit loop.
12107  */
12108  (void) XSetFunction(display,windows->image.highlight_context,
12109  GXcopy);
12110  rotate_info.x1=event.xbutton.x;
12111  rotate_info.y1=event.xbutton.y;
12112  state|=ExitState;
12113  break;
12114  }
12115  case ButtonRelease:
12116  break;
12117  case Expose:
12118  break;
12119  case KeyPress:
12120  {
12121  char
12122  command[MaxTextExtent];
12123 
12124  KeySym
12125  key_symbol;
12126 
12127  if (event.xkey.window != windows->image.id)
12128  break;
12129  /*
12130  Respond to a user key press.
12131  */
12132  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12133  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12134  switch ((int) key_symbol)
12135  {
12136  case XK_Escape:
12137  case XK_F20:
12138  {
12139  /*
12140  Prematurely exit.
12141  */
12142  state|=EscapeState;
12143  state|=ExitState;
12144  break;
12145  }
12146  case XK_F1:
12147  case XK_Help:
12148  {
12149  (void) XSetFunction(display,windows->image.highlight_context,
12150  GXcopy);
12151  XTextViewHelp(display,resource_info,windows,MagickFalse,
12152  "Help Viewer - Image Rotation",ImageRotateHelp);
12153  (void) XSetFunction(display,windows->image.highlight_context,
12154  GXinvert);
12155  break;
12156  }
12157  default:
12158  {
12159  (void) XBell(display,0);
12160  break;
12161  }
12162  }
12163  break;
12164  }
12165  case MotionNotify:
12166  {
12167  rotate_info.x1=event.xmotion.x;
12168  rotate_info.y1=event.xmotion.y;
12169  }
12170  }
12171  rotate_info.x2=rotate_info.x1;
12172  rotate_info.y2=rotate_info.y1;
12173  if (direction == HorizontalRotateCommand)
12174  rotate_info.x2+=32;
12175  else
12176  rotate_info.y2-=32;
12177  } while ((state & ExitState) == 0);
12178  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12179  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12180  if ((state & EscapeState) != 0)
12181  return(MagickTrue);
12182  /*
12183  Draw line as pointer moves until the mouse button is released.
12184  */
12185  distance=0;
12186  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12187  state=DefaultState;
12188  do
12189  {
12190  if (distance > 9)
12191  {
12192  /*
12193  Display info and draw rotation line.
12194  */
12195  if (windows->info.mapped == MagickFalse)
12196  (void) XMapWindow(display,windows->info.id);
12197  (void) FormatLocaleString(text,MaxTextExtent," %g",
12198  direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12199  XInfoWidget(display,windows,text);
12200  XHighlightLine(display,windows->image.id,
12201  windows->image.highlight_context,&rotate_info);
12202  }
12203  else
12204  if (windows->info.mapped != MagickFalse)
12205  (void) XWithdrawWindow(display,windows->info.id,
12206  windows->info.screen);
12207  /*
12208  Wait for next event.
12209  */
12210  XScreenEvent(display,windows,&event);
12211  if (distance > 9)
12212  XHighlightLine(display,windows->image.id,
12213  windows->image.highlight_context,&rotate_info);
12214  switch (event.type)
12215  {
12216  case ButtonPress:
12217  break;
12218  case ButtonRelease:
12219  {
12220  /*
12221  User has committed to rotation line.
12222  */
12223  rotate_info.x2=event.xbutton.x;
12224  rotate_info.y2=event.xbutton.y;
12225  state|=ExitState;
12226  break;
12227  }
12228  case Expose:
12229  break;
12230  case MotionNotify:
12231  {
12232  rotate_info.x2=event.xmotion.x;
12233  rotate_info.y2=event.xmotion.y;
12234  }
12235  default:
12236  break;
12237  }
12238  /*
12239  Check boundary conditions.
12240  */
12241  if (rotate_info.x2 < 0)
12242  rotate_info.x2=0;
12243  else
12244  if (rotate_info.x2 > (int) windows->image.width)
12245  rotate_info.x2=(short) windows->image.width;
12246  if (rotate_info.y2 < 0)
12247  rotate_info.y2=0;
12248  else
12249  if (rotate_info.y2 > (int) windows->image.height)
12250  rotate_info.y2=(short) windows->image.height;
12251  /*
12252  Compute rotation angle from the slope of the line.
12253  */
12254  degrees=0.0;
12255  distance=(unsigned int)
12256  ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12257  ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12258  if (distance > 9)
12259  degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12260  rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12261  } while ((state & ExitState) == 0);
12262  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12263  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12264  if (distance <= 9)
12265  return(MagickTrue);
12266  }
12267  if (direction == VerticalRotateCommand)
12268  degrees-=90.0;
12269  if (degrees == 0.0)
12270  return(MagickTrue);
12271  /*
12272  Rotate image.
12273  */
12274  normalized_degrees=degrees;
12275  while (normalized_degrees < -45.0)
12276  normalized_degrees+=360.0;
12277  for (rotations=0; normalized_degrees > 45.0; rotations++)
12278  normalized_degrees-=90.0;
12279  if (normalized_degrees != 0.0)
12280  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
12281  XSetCursorState(display,windows,MagickTrue);
12282  XCheckRefreshWindows(display,windows);
12283  (*image)->background_color.red=ScaleShortToQuantum(
12284  windows->pixel_info->pen_colors[pen_id].red);
12285  (*image)->background_color.green=ScaleShortToQuantum(
12286  windows->pixel_info->pen_colors[pen_id].green);
12287  (*image)->background_color.blue=ScaleShortToQuantum(
12288  windows->pixel_info->pen_colors[pen_id].blue);
12289  rotate_image=RotateImage(*image,degrees,&(*image)->exception);
12290  XSetCursorState(display,windows,MagickFalse);
12291  if (rotate_image == (Image *) NULL)
12292  return(MagickFalse);
12293  *image=DestroyImage(*image);
12294  *image=rotate_image;
12295  if (windows->image.crop_geometry != (char *) NULL)
12296  {
12297  /*
12298  Rotate crop geometry.
12299  */
12300  width=(unsigned int) (*image)->columns;
12301  height=(unsigned int) (*image)->rows;
12302  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12303  switch (rotations % 4)
12304  {
12305  default:
12306  case 0:
12307  break;
12308  case 1:
12309  {
12310  /*
12311  Rotate 90 degrees.
12312  */
12313  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12314  "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12315  (int) height-y,x);
12316  break;
12317  }
12318  case 2:
12319  {
12320  /*
12321  Rotate 180 degrees.
12322  */
12323  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12324  "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12325  break;
12326  }
12327  case 3:
12328  {
12329  /*
12330  Rotate 270 degrees.
12331  */
12332  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12333  "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12334  break;
12335  }
12336  }
12337  }
12338  if (windows->image.orphan != MagickFalse)
12339  return(MagickTrue);
12340  if (normalized_degrees != 0.0)
12341  {
12342  /*
12343  Update image colormap.
12344  */
12345  windows->image.window_changes.width=(int) (*image)->columns;
12346  windows->image.window_changes.height=(int) (*image)->rows;
12347  if (windows->image.crop_geometry != (char *) NULL)
12348  {
12349  /*
12350  Obtain dimensions of image from crop geometry.
12351  */
12352  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12353  &width,&height);
12354  windows->image.window_changes.width=(int) width;
12355  windows->image.window_changes.height=(int) height;
12356  }
12357  XConfigureImageColormap(display,resource_info,windows,*image);
12358  }
12359  else
12360  if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12361  {
12362  windows->image.window_changes.width=windows->image.ximage->height;
12363  windows->image.window_changes.height=windows->image.ximage->width;
12364  }
12365  /*
12366  Update image configuration.
12367  */
12368  (void) XConfigureImage(display,resource_info,windows,*image);
12369  return(MagickTrue);
12370 }
12371 ␌
12372 /*
12373 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12374 % %
12375 % %
12376 % %
12377 + X S a v e I m a g e %
12378 % %
12379 % %
12380 % %
12381 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12382 %
12383 % XSaveImage() saves an image to a file.
12384 %
12385 % The format of the XSaveImage method is:
12386 %
12387 % MagickBooleanType XSaveImage(Display *display,
12388 % XResourceInfo *resource_info,XWindows *windows,Image *image)
12389 %
12390 % A description of each parameter follows:
12391 %
12392 % o display: Specifies a connection to an X server; returned from
12393 % XOpenDisplay.
12394 %
12395 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12396 %
12397 % o windows: Specifies a pointer to a XWindows structure.
12398 %
12399 % o image: the image.
12400 %
12401 */
12402 static MagickBooleanType XSaveImage(Display *display,
12403  XResourceInfo *resource_info,XWindows *windows,Image *image)
12404 {
12405  char
12406  filename[MaxTextExtent],
12407  geometry[MaxTextExtent];
12408 
12409  Image
12410  *save_image;
12411 
12412  ImageInfo
12413  *image_info;
12414 
12415  MagickStatusType
12416  status;
12417 
12418  /*
12419  Request file name from user.
12420  */
12421  if (resource_info->write_filename != (char *) NULL)
12422  (void) CopyMagickString(filename,resource_info->write_filename,
12423  MaxTextExtent);
12424  else
12425  {
12426  char
12427  path[MaxTextExtent];
12428 
12429  int
12430  status;
12431 
12432  GetPathComponent(image->filename,HeadPath,path);
12433  GetPathComponent(image->filename,TailPath,filename);
12434  if (*path != '\0')
12435  {
12436  status=chdir(path);
12437  if (status == -1)
12438  (void) ThrowMagickException(&image->exception,GetMagickModule(),
12439  FileOpenError,"UnableToOpenFile","%s",path);
12440  }
12441  }
12442  XFileBrowserWidget(display,windows,"Save",filename);
12443  if (*filename == '\0')
12444  return(MagickTrue);
12445  if (IsPathAccessible(filename) != MagickFalse)
12446  {
12447  int
12448  status;
12449 
12450  /*
12451  File exists-- seek user's permission before overwriting.
12452  */
12453  status=XConfirmWidget(display,windows,"Overwrite",filename);
12454  if (status <= 0)
12455  return(MagickTrue);
12456  }
12457  image_info=CloneImageInfo(resource_info->image_info);
12458  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12459  (void) SetImageInfo(image_info,1,&image->exception);
12460  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12461  (LocaleCompare(image_info->magick,"JPG") == 0))
12462  {
12463  char
12464  quality[MaxTextExtent];
12465 
12466  int
12467  status;
12468 
12469  /*
12470  Request JPEG quality from user.
12471  */
12472  (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12473  image->quality);
12474  status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12475  quality);
12476  if (*quality == '\0')
12477  return(MagickTrue);
12478  image->quality=StringToUnsignedLong(quality);
12479  image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12480  }
12481  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12482  (LocaleCompare(image_info->magick,"PDF") == 0) ||
12483  (LocaleCompare(image_info->magick,"PS") == 0) ||
12484  (LocaleCompare(image_info->magick,"PS2") == 0))
12485  {
12486  char
12487  geometry[MaxTextExtent];
12488 
12489  const char
12490  *const PageSizes[] =
12491  {
12492  "Letter",
12493  "Tabloid",
12494  "Ledger",
12495  "Legal",
12496  "Statement",
12497  "Executive",
12498  "A3",
12499  "A4",
12500  "A5",
12501  "B4",
12502  "B5",
12503  "Folio",
12504  "Quarto",
12505  "10x14",
12506  (char *) NULL
12507  };
12508 
12509  /*
12510  Request page geometry from user.
12511  */
12512  (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12513  if (LocaleCompare(image_info->magick,"PDF") == 0)
12514  (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12515  if (image_info->page != (char *) NULL)
12516  (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12517  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12518  "Select page geometry:",geometry);
12519  if (*geometry != '\0')
12520  image_info->page=GetPageGeometry(geometry);
12521  }
12522  /*
12523  Apply image transforms.
12524  */
12525  XSetCursorState(display,windows,MagickTrue);
12526  XCheckRefreshWindows(display,windows);
12527  save_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12528  if (save_image == (Image *) NULL)
12529  return(MagickFalse);
12530  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12531  windows->image.ximage->width,windows->image.ximage->height);
12532  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12533  /*
12534  Write image.
12535  */
12536  (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12537  status=WriteImage(image_info,save_image);
12538  if (status != MagickFalse)
12539  image->taint=MagickFalse;
12540  save_image=DestroyImage(save_image);
12541  image_info=DestroyImageInfo(image_info);
12542  XSetCursorState(display,windows,MagickFalse);
12543  return(status != 0 ? MagickTrue : MagickFalse);
12544 }
12545 ␌
12546 /*
12547 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12548 % %
12549 % %
12550 % %
12551 + X S c r e e n E v e n t %
12552 % %
12553 % %
12554 % %
12555 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12556 %
12557 % XScreenEvent() handles global events associated with the Pan and Magnify
12558 % windows.
12559 %
12560 % The format of the XScreenEvent function is:
12561 %
12562 % void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12563 %
12564 % A description of each parameter follows:
12565 %
12566 % o display: Specifies a pointer to the Display structure; returned from
12567 % XOpenDisplay.
12568 %
12569 % o windows: Specifies a pointer to a XWindows structure.
12570 %
12571 % o event: Specifies a pointer to a X11 XEvent structure.
12572 %
12573 %
12574 */
12575 
12576 #if defined(__cplusplus) || defined(c_plusplus)
12577 extern "C" {
12578 #endif
12579 
12580 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12581 {
12582  XWindows
12583  *windows;
12584 
12585  magick_unreferenced(display);
12586 
12587  windows=(XWindows *) data;
12588  if ((event->type == ClientMessage) &&
12589  (event->xclient.window == windows->image.id))
12590  return(MagickFalse);
12591  return(MagickTrue);
12592 }
12593 
12594 #if defined(__cplusplus) || defined(c_plusplus)
12595 }
12596 #endif
12597 
12598 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12599 {
12600  int
12601  x,
12602  y;
12603 
12604  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12605  if (event->xany.window == windows->command.id)
12606  return;
12607  switch (event->type)
12608  {
12609  case ButtonPress:
12610  case ButtonRelease:
12611  {
12612  if ((event->xbutton.button == Button3) &&
12613  (event->xbutton.state & Mod1Mask))
12614  {
12615  /*
12616  Convert Alt-Button3 to Button2.
12617  */
12618  event->xbutton.button=Button2;
12619  event->xbutton.state&=(~Mod1Mask);
12620  }
12621  if (event->xbutton.window == windows->backdrop.id)
12622  {
12623  (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12624  event->xbutton.time);
12625  break;
12626  }
12627  if (event->xbutton.window == windows->pan.id)
12628  {
12629  XPanImage(display,windows,event);
12630  break;
12631  }
12632  if (event->xbutton.window == windows->image.id)
12633  if (event->xbutton.button == Button2)
12634  {
12635  /*
12636  Update magnified image.
12637  */
12638  x=event->xbutton.x;
12639  y=event->xbutton.y;
12640  if (x < 0)
12641  x=0;
12642  else
12643  if (x >= (int) windows->image.width)
12644  x=(int) (windows->image.width-1);
12645  windows->magnify.x=(int) windows->image.x+x;
12646  if (y < 0)
12647  y=0;
12648  else
12649  if (y >= (int) windows->image.height)
12650  y=(int) (windows->image.height-1);
12651  windows->magnify.y=windows->image.y+y;
12652  if (windows->magnify.mapped == MagickFalse)
12653  (void) XMapRaised(display,windows->magnify.id);
12654  XMakeMagnifyImage(display,windows);
12655  if (event->type == ButtonRelease)
12656  (void) XWithdrawWindow(display,windows->info.id,
12657  windows->info.screen);
12658  break;
12659  }
12660  break;
12661  }
12662  case ClientMessage:
12663  {
12664  /*
12665  If client window delete message, exit.
12666  */
12667  if (event->xclient.message_type != windows->wm_protocols)
12668  break;
12669  if (*event->xclient.data.l != (long) windows->wm_delete_window)
12670  break;
12671  if (event->xclient.window == windows->magnify.id)
12672  {
12673  (void) XWithdrawWindow(display,windows->magnify.id,
12674  windows->magnify.screen);
12675  break;
12676  }
12677  break;
12678  }
12679  case ConfigureNotify:
12680  {
12681  if (event->xconfigure.window == windows->magnify.id)
12682  {
12683  unsigned int
12684  magnify;
12685 
12686  /*
12687  Magnify window has a new configuration.
12688  */
12689  windows->magnify.width=(unsigned int) event->xconfigure.width;
12690  windows->magnify.height=(unsigned int) event->xconfigure.height;
12691  if (windows->magnify.mapped == MagickFalse)
12692  break;
12693  magnify=1;
12694  while ((int) magnify <= event->xconfigure.width)
12695  magnify<<=1;
12696  while ((int) magnify <= event->xconfigure.height)
12697  magnify<<=1;
12698  magnify>>=1;
12699  if (((int) magnify != event->xconfigure.width) ||
12700  ((int) magnify != event->xconfigure.height))
12701  {
12702  XWindowChanges
12703  window_changes;
12704 
12705  window_changes.width=(int) magnify;
12706  window_changes.height=(int) magnify;
12707  (void) XReconfigureWMWindow(display,windows->magnify.id,
12708  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12709  &window_changes);
12710  break;
12711  }
12712  XMakeMagnifyImage(display,windows);
12713  break;
12714  }
12715  break;
12716  }
12717  case Expose:
12718  {
12719  if (event->xexpose.window == windows->image.id)
12720  {
12721  XRefreshWindow(display,&windows->image,event);
12722  break;
12723  }
12724  if (event->xexpose.window == windows->pan.id)
12725  if (event->xexpose.count == 0)
12726  {
12727  XDrawPanRectangle(display,windows);
12728  break;
12729  }
12730  if (event->xexpose.window == windows->magnify.id)
12731  if (event->xexpose.count == 0)
12732  {
12733  XMakeMagnifyImage(display,windows);
12734  break;
12735  }
12736  break;
12737  }
12738  case KeyPress:
12739  {
12740  char
12741  command[MaxTextExtent];
12742 
12743  KeySym
12744  key_symbol;
12745 
12746  if (event->xkey.window != windows->magnify.id)
12747  break;
12748  /*
12749  Respond to a user key press.
12750  */
12751  (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12752  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12753  XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12754  break;
12755  }
12756  case MapNotify:
12757  {
12758  if (event->xmap.window == windows->magnify.id)
12759  {
12760  windows->magnify.mapped=MagickTrue;
12761  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12762  break;
12763  }
12764  if (event->xmap.window == windows->info.id)
12765  {
12766  windows->info.mapped=MagickTrue;
12767  break;
12768  }
12769  break;
12770  }
12771  case MotionNotify:
12772  {
12773  while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12774  if (event->xmotion.window == windows->image.id)
12775  if (windows->magnify.mapped != MagickFalse)
12776  {
12777  /*
12778  Update magnified image.
12779  */
12780  x=event->xmotion.x;
12781  y=event->xmotion.y;
12782  if (x < 0)
12783  x=0;
12784  else
12785  if (x >= (int) windows->image.width)
12786  x=(int) (windows->image.width-1);
12787  windows->magnify.x=(int) windows->image.x+x;
12788  if (y < 0)
12789  y=0;
12790  else
12791  if (y >= (int) windows->image.height)
12792  y=(int) (windows->image.height-1);
12793  windows->magnify.y=windows->image.y+y;
12794  XMakeMagnifyImage(display,windows);
12795  }
12796  break;
12797  }
12798  case UnmapNotify:
12799  {
12800  if (event->xunmap.window == windows->magnify.id)
12801  {
12802  windows->magnify.mapped=MagickFalse;
12803  break;
12804  }
12805  if (event->xunmap.window == windows->info.id)
12806  {
12807  windows->info.mapped=MagickFalse;
12808  break;
12809  }
12810  break;
12811  }
12812  default:
12813  break;
12814  }
12815 }
12816 ␌
12817 /*
12818 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12819 % %
12820 % %
12821 % %
12822 + X S e t C r o p G e o m e t r y %
12823 % %
12824 % %
12825 % %
12826 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12827 %
12828 % XSetCropGeometry() accepts a cropping geometry relative to the Image window
12829 % and translates it to a cropping geometry relative to the image.
12830 %
12831 % The format of the XSetCropGeometry method is:
12832 %
12833 % void XSetCropGeometry(Display *display,XWindows *windows,
12834 % RectangleInfo *crop_info,Image *image)
12835 %
12836 % A description of each parameter follows:
12837 %
12838 % o display: Specifies a connection to an X server; returned from
12839 % XOpenDisplay.
12840 %
12841 % o windows: Specifies a pointer to a XWindows structure.
12842 %
12843 % o crop_info: A pointer to a RectangleInfo that defines a region of the
12844 % Image window to crop.
12845 %
12846 % o image: the image.
12847 %
12848 */
12849 static void XSetCropGeometry(Display *display,XWindows *windows,
12850  RectangleInfo *crop_info,Image *image)
12851 {
12852  char
12853  text[MaxTextExtent];
12854 
12855  int
12856  x,
12857  y;
12858 
12859  MagickRealType
12860  scale_factor;
12861 
12862  unsigned int
12863  height,
12864  width;
12865 
12866  if (windows->info.mapped != MagickFalse)
12867  {
12868  /*
12869  Display info on cropping rectangle.
12870  */
12871  (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12872  (double) crop_info->width,(double) crop_info->height,(double)
12873  crop_info->x,(double) crop_info->y);
12874  XInfoWidget(display,windows,text);
12875  }
12876  /*
12877  Cropping geometry is relative to any previous crop geometry.
12878  */
12879  x=0;
12880  y=0;
12881  width=(unsigned int) image->columns;
12882  height=(unsigned int) image->rows;
12883  if (windows->image.crop_geometry != (char *) NULL)
12884  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12885  else
12886  windows->image.crop_geometry=AcquireString((char *) NULL);
12887  /*
12888  Define the crop geometry string from the cropping rectangle.
12889  */
12890  scale_factor=(MagickRealType) width/windows->image.ximage->width;
12891  if (crop_info->x > 0)
12892  x+=(int) (scale_factor*crop_info->x+0.5);
12893  width=(unsigned int) (scale_factor*crop_info->width+0.5);
12894  if (width == 0)
12895  width=1;
12896  scale_factor=(MagickRealType) height/windows->image.ximage->height;
12897  if (crop_info->y > 0)
12898  y+=(int) (scale_factor*crop_info->y+0.5);
12899  height=(unsigned int) (scale_factor*crop_info->height+0.5);
12900  if (height == 0)
12901  height=1;
12902  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12903  "%ux%u%+d%+d",width,height,x,y);
12904 }
12905 ␌
12906 /*
12907 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12908 % %
12909 % %
12910 % %
12911 + X T i l e I m a g e %
12912 % %
12913 % %
12914 % %
12915 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12916 %
12917 % XTileImage() loads or deletes a selected tile from a visual image directory.
12918 % The load or delete command is chosen from a menu.
12919 %
12920 % The format of the XTileImage method is:
12921 %
12922 % Image *XTileImage(Display *display,XResourceInfo *resource_info,
12923 % XWindows *windows,Image *image,XEvent *event)
12924 %
12925 % A description of each parameter follows:
12926 %
12927 % o tile_image: XTileImage reads or deletes the tile image
12928 % and returns it. A null image is returned if an error occurs.
12929 %
12930 % o display: Specifies a connection to an X server; returned from
12931 % XOpenDisplay.
12932 %
12933 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12934 %
12935 % o windows: Specifies a pointer to a XWindows structure.
12936 %
12937 % o image: the image; returned from ReadImage.
12938 %
12939 % o event: Specifies a pointer to a XEvent structure. If it is NULL,
12940 % the entire image is refreshed.
12941 %
12942 */
12943 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
12944  XWindows *windows,Image *image,XEvent *event)
12945 {
12946  const char
12947  *const VerbMenu[] =
12948  {
12949  "Load",
12950  "Next",
12951  "Former",
12952  "Delete",
12953  "Update",
12954  (char *) NULL,
12955  };
12956 
12957  static const ModeType
12958  TileCommands[] =
12959  {
12960  TileLoadCommand,
12961  TileNextCommand,
12962  TileFormerCommand,
12963  TileDeleteCommand,
12964  TileUpdateCommand
12965  };
12966 
12967  char
12968  command[MaxTextExtent],
12969  filename[MaxTextExtent];
12970 
12971  Image
12972  *tile_image;
12973 
12974  int
12975  id,
12976  status,
12977  tile,
12978  x,
12979  y;
12980 
12981  MagickRealType
12982  scale_factor;
12983 
12984  char
12985  *p,
12986  *q;
12987 
12988  int
12989  i;
12990 
12991  unsigned int
12992  height,
12993  width;
12994 
12995  /*
12996  Tile image is relative to montage image configuration.
12997  */
12998  x=0;
12999  y=0;
13000  width=(unsigned int) image->columns;
13001  height=(unsigned int) image->rows;
13002  if (windows->image.crop_geometry != (char *) NULL)
13003  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13004  scale_factor=(MagickRealType) width/windows->image.ximage->width;
13005  event->xbutton.x+=windows->image.x;
13006  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13007  scale_factor=(MagickRealType) height/windows->image.ximage->height;
13008  event->xbutton.y+=windows->image.y;
13009  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13010  /*
13011  Determine size and location of each tile in the visual image directory.
13012  */
13013  width=(unsigned int) image->columns;
13014  height=(unsigned int) image->rows;
13015  x=0;
13016  y=0;
13017  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13018  tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13019  (event->xbutton.x-x)/width;
13020  if (tile < 0)
13021  {
13022  /*
13023  Button press is outside any tile.
13024  */
13025  (void) XBell(display,0);
13026  return((Image *) NULL);
13027  }
13028  /*
13029  Determine file name from the tile directory.
13030  */
13031  p=image->directory;
13032  for (i=tile; (i != 0) && (*p != '\0'); )
13033  {
13034  if (*p == '\xff')
13035  i--;
13036  p++;
13037  }
13038  if (*p == '\0')
13039  {
13040  /*
13041  Button press is outside any tile.
13042  */
13043  (void) XBell(display,0);
13044  return((Image *) NULL);
13045  }
13046  /*
13047  Select a command from the pop-up menu.
13048  */
13049  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13050  if (id < 0)
13051  return((Image *) NULL);
13052  q=p;
13053  while ((*q != '\xff') && (*q != '\0'))
13054  q++;
13055  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13056  /*
13057  Perform command for the selected tile.
13058  */
13059  XSetCursorState(display,windows,MagickTrue);
13060  XCheckRefreshWindows(display,windows);
13061  tile_image=NewImageList();
13062  switch (TileCommands[id])
13063  {
13064  case TileLoadCommand:
13065  {
13066  /*
13067  Load tile image.
13068  */
13069  XCheckRefreshWindows(display,windows);
13070  (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13071  MaxTextExtent);
13072  (void) CopyMagickString(resource_info->image_info->filename,filename,
13073  MaxTextExtent);
13074  tile_image=ReadImage(resource_info->image_info,&image->exception);
13075  CatchException(&image->exception);
13076  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13077  break;
13078  }
13079  case TileNextCommand:
13080  {
13081  /*
13082  Display next image.
13083  */
13084  XClientMessage(display,windows->image.id,windows->im_protocols,
13085  windows->im_next_image,CurrentTime);
13086  break;
13087  }
13088  case TileFormerCommand:
13089  {
13090  /*
13091  Display former image.
13092  */
13093  XClientMessage(display,windows->image.id,windows->im_protocols,
13094  windows->im_former_image,CurrentTime);
13095  break;
13096  }
13097  case TileDeleteCommand:
13098  {
13099  /*
13100  Delete tile image.
13101  */
13102  if (IsPathAccessible(filename) == MagickFalse)
13103  {
13104  XNoticeWidget(display,windows,"Image file does not exist:",filename);
13105  break;
13106  }
13107  status=XConfirmWidget(display,windows,"Really delete tile",filename);
13108  if (status <= 0)
13109  break;
13110  status=ShredFile(filename);
13111  status|=remove_utf8(filename);
13112  if (status != MagickFalse)
13113  {
13114  XNoticeWidget(display,windows,"Unable to delete image file:",
13115  filename);
13116  break;
13117  }
13118  magick_fallthrough;
13119  }
13120  case TileUpdateCommand:
13121  {
13123  *exception;
13124 
13125  int
13126  x_offset,
13127  y_offset;
13128 
13129  PixelPacket
13130  pixel;
13131 
13132  int
13133  j;
13134 
13135  PixelPacket
13136  *s;
13137 
13138  /*
13139  Ensure all the images exist.
13140  */
13141  tile=0;
13142  for (p=image->directory; *p != '\0'; p++)
13143  {
13144  CacheView
13145  *image_view;
13146 
13147  q=p;
13148  while ((*q != '\xff') && (*q != '\0'))
13149  q++;
13150  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13151  p=q;
13152  if (IsPathAccessible(filename) != MagickFalse)
13153  {
13154  tile++;
13155  continue;
13156  }
13157  /*
13158  Overwrite tile with background color.
13159  */
13160  x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13161  y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13162  exception=(&image->exception);
13163  image_view=AcquireAuthenticCacheView(image,exception);
13164  (void) GetOneCacheViewVirtualPixel(image_view,0,0,&pixel,exception);
13165  for (i=0; i < (int) height; i++)
13166  {
13167  s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13168  y_offset+i,width,1,exception);
13169  if (s == (PixelPacket *) NULL)
13170  break;
13171  for (j=0; j < (int) width; j++)
13172  *s++=pixel;
13173  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13174  break;
13175  }
13176  image_view=DestroyCacheView(image_view);
13177  tile++;
13178  }
13179  windows->image.window_changes.width=(int) image->columns;
13180  windows->image.window_changes.height=(int) image->rows;
13181  XConfigureImageColormap(display,resource_info,windows,image);
13182  (void) XConfigureImage(display,resource_info,windows,image);
13183  break;
13184  }
13185  default:
13186  break;
13187  }
13188  XSetCursorState(display,windows,MagickFalse);
13189  return(tile_image);
13190 }
13191 ␌
13192 /*
13193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13194 % %
13195 % %
13196 % %
13197 + X T r a n s l a t e I m a g e %
13198 % %
13199 % %
13200 % %
13201 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13202 %
13203 % XTranslateImage() translates the image within an Image window by one pixel
13204 % as specified by the key symbol. If the image has a `montage string the
13205 % translation is respect to the width and height contained within the string.
13206 %
13207 % The format of the XTranslateImage method is:
13208 %
13209 % void XTranslateImage(Display *display,XWindows *windows,
13210 % Image *image,const KeySym key_symbol)
13211 %
13212 % A description of each parameter follows:
13213 %
13214 % o display: Specifies a connection to an X server; returned from
13215 % XOpenDisplay.
13216 %
13217 % o windows: Specifies a pointer to a XWindows structure.
13218 %
13219 % o image: the image.
13220 %
13221 % o key_symbol: Specifies a KeySym which indicates which side of the image
13222 % to trim.
13223 %
13224 */
13225 static void XTranslateImage(Display *display,XWindows *windows,
13226  Image *image,const KeySym key_symbol)
13227 {
13228  char
13229  text[MaxTextExtent];
13230 
13231  int
13232  x,
13233  y;
13234 
13235  unsigned int
13236  x_offset,
13237  y_offset;
13238 
13239  /*
13240  User specified a pan position offset.
13241  */
13242  x_offset=windows->image.width;
13243  y_offset=windows->image.height;
13244  if (image->montage != (char *) NULL)
13245  (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13246  switch ((int) key_symbol)
13247  {
13248  case XK_Home:
13249  case XK_KP_Home:
13250  {
13251  windows->image.x=(int) windows->image.width/2;
13252  windows->image.y=(int) windows->image.height/2;
13253  break;
13254  }
13255  case XK_Left:
13256  case XK_KP_Left:
13257  {
13258  windows->image.x-=x_offset;
13259  break;
13260  }
13261  case XK_Next:
13262  case XK_Up:
13263  case XK_KP_Up:
13264  {
13265  windows->image.y-=y_offset;
13266  break;
13267  }
13268  case XK_Right:
13269  case XK_KP_Right:
13270  {
13271  windows->image.x+=x_offset;
13272  break;
13273  }
13274  case XK_Prior:
13275  case XK_Down:
13276  case XK_KP_Down:
13277  {
13278  windows->image.y+=y_offset;
13279  break;
13280  }
13281  default:
13282  return;
13283  }
13284  /*
13285  Check boundary conditions.
13286  */
13287  if (windows->image.x < 0)
13288  windows->image.x=0;
13289  else
13290  if ((int) (windows->image.x+windows->image.width) >
13291  windows->image.ximage->width)
13292  windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13293  if (windows->image.y < 0)
13294  windows->image.y=0;
13295  else
13296  if ((int) (windows->image.y+windows->image.height) >
13297  windows->image.ximage->height)
13298  windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13299  /*
13300  Refresh Image window.
13301  */
13302  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13303  windows->image.width,windows->image.height,windows->image.x,
13304  windows->image.y);
13305  XInfoWidget(display,windows,text);
13306  XCheckRefreshWindows(display,windows);
13307  XDrawPanRectangle(display,windows);
13308  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13309  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13310 }
13311 ␌
13312 /*
13313 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13314 % %
13315 % %
13316 % %
13317 + X T r i m I m a g e %
13318 % %
13319 % %
13320 % %
13321 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13322 %
13323 % XTrimImage() trims the edges from the Image window.
13324 %
13325 % The format of the XTrimImage method is:
13326 %
13327 % MagickBooleanType XTrimImage(Display *display,
13328 % XResourceInfo *resource_info,XWindows *windows,Image *image)
13329 %
13330 % A description of each parameter follows:
13331 %
13332 % o display: Specifies a connection to an X server; returned from
13333 % XOpenDisplay.
13334 %
13335 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13336 %
13337 % o windows: Specifies a pointer to a XWindows structure.
13338 %
13339 % o image: the image.
13340 %
13341 */
13342 static MagickBooleanType XTrimImage(Display *display,
13343  XResourceInfo *resource_info,XWindows *windows,Image *image)
13344 {
13346  trim_info;
13347 
13348  int
13349  x,
13350  y;
13351 
13352  size_t
13353  background,
13354  pixel;
13355 
13356  /*
13357  Trim edges from image.
13358  */
13359  XSetCursorState(display,windows,MagickTrue);
13360  XCheckRefreshWindows(display,windows);
13361  /*
13362  Crop the left edge.
13363  */
13364  background=XGetPixel(windows->image.ximage,0,0);
13365  trim_info.width=(size_t) windows->image.ximage->width;
13366  for (x=0; x < windows->image.ximage->width; x++)
13367  {
13368  for (y=0; y < windows->image.ximage->height; y++)
13369  {
13370  pixel=XGetPixel(windows->image.ximage,x,y);
13371  if (pixel != background)
13372  break;
13373  }
13374  if (y < windows->image.ximage->height)
13375  break;
13376  }
13377  trim_info.x=(ssize_t) x;
13378  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13379  {
13380  XSetCursorState(display,windows,MagickFalse);
13381  return(MagickFalse);
13382  }
13383  /*
13384  Crop the right edge.
13385  */
13386  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13387  for (x=windows->image.ximage->width-1; x != 0; x--)
13388  {
13389  for (y=0; y < windows->image.ximage->height; y++)
13390  {
13391  pixel=XGetPixel(windows->image.ximage,x,y);
13392  if (pixel != background)
13393  break;
13394  }
13395  if (y < windows->image.ximage->height)
13396  break;
13397  }
13398  trim_info.width=(size_t) (x-trim_info.x+1);
13399  /*
13400  Crop the top edge.
13401  */
13402  background=XGetPixel(windows->image.ximage,0,0);
13403  trim_info.height=(size_t) windows->image.ximage->height;
13404  for (y=0; y < windows->image.ximage->height; y++)
13405  {
13406  for (x=0; x < windows->image.ximage->width; x++)
13407  {
13408  pixel=XGetPixel(windows->image.ximage,x,y);
13409  if (pixel != background)
13410  break;
13411  }
13412  if (x < windows->image.ximage->width)
13413  break;
13414  }
13415  trim_info.y=(ssize_t) y;
13416  /*
13417  Crop the bottom edge.
13418  */
13419  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13420  for (y=windows->image.ximage->height-1; y != 0; y--)
13421  {
13422  for (x=0; x < windows->image.ximage->width; x++)
13423  {
13424  pixel=XGetPixel(windows->image.ximage,x,y);
13425  if (pixel != background)
13426  break;
13427  }
13428  if (x < windows->image.ximage->width)
13429  break;
13430  }
13431  trim_info.height=(size_t) y-trim_info.y+1;
13432  if (((unsigned int) trim_info.width != windows->image.width) ||
13433  ((unsigned int) trim_info.height != windows->image.height))
13434  {
13435  /*
13436  Reconfigure Image window as defined by the trimming rectangle.
13437  */
13438  XSetCropGeometry(display,windows,&trim_info,image);
13439  windows->image.window_changes.width=(int) trim_info.width;
13440  windows->image.window_changes.height=(int) trim_info.height;
13441  (void) XConfigureImage(display,resource_info,windows,image);
13442  }
13443  XSetCursorState(display,windows,MagickFalse);
13444  return(MagickTrue);
13445 }
13446 ␌
13447 /*
13448 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13449 % %
13450 % %
13451 % %
13452 + X V i s u a l D i r e c t o r y I m a g e %
13453 % %
13454 % %
13455 % %
13456 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13457 %
13458 % XVisualDirectoryImage() creates a Visual Image Directory.
13459 %
13460 % The format of the XVisualDirectoryImage method is:
13461 %
13462 % Image *XVisualDirectoryImage(Display *display,
13463 % XResourceInfo *resource_info,XWindows *windows)
13464 %
13465 % A description of each parameter follows:
13466 %
13467 % o nexus: Method XVisualDirectoryImage returns a visual image
13468 % directory if it can be created successfully. Otherwise a null image
13469 % is returned.
13470 %
13471 % o display: Specifies a connection to an X server; returned from
13472 % XOpenDisplay.
13473 %
13474 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13475 %
13476 % o windows: Specifies a pointer to a XWindows structure.
13477 %
13478 */
13479 static Image *XVisualDirectoryImage(Display *display,
13480  XResourceInfo *resource_info,XWindows *windows)
13481 {
13482 #define TileImageTag "Scale/Image"
13483 #define XClientName "montage"
13484 
13485  char
13486  **filelist;
13487 
13489  *exception;
13490 
13491  Image
13492  *images,
13493  *montage_image,
13494  *next_image,
13495  *thumbnail_image;
13496 
13497  ImageInfo
13498  *read_info;
13499 
13500  int
13501  number_files;
13502 
13503  MagickBooleanType
13504  backdrop;
13505 
13506  MagickStatusType
13507  status;
13508 
13509  MontageInfo
13510  *montage_info;
13511 
13513  geometry;
13514 
13515  int
13516  i;
13517 
13518  static char
13519  filename[MaxTextExtent] = "\0",
13520  filenames[MaxTextExtent] = "*";
13521 
13522  XResourceInfo
13523  background_resources;
13524 
13525  /*
13526  Request file name from user.
13527  */
13528  XFileBrowserWidget(display,windows,"Directory",filenames);
13529  if (*filenames == '\0')
13530  return((Image *) NULL);
13531  /*
13532  Expand the filenames.
13533  */
13534  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13535  if (filelist == (char **) NULL)
13536  {
13537  ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13538  filenames);
13539  return((Image *) NULL);
13540  }
13541  number_files=1;
13542  filelist[0]=filenames;
13543  status=ExpandFilenames(&number_files,&filelist);
13544  if ((status == MagickFalse) || (number_files == 0))
13545  {
13546  if (number_files == 0)
13547  ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13548  else
13549  ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13550  filenames);
13551  return((Image *) NULL);
13552  }
13553  /*
13554  Set image background resources.
13555  */
13556  background_resources=(*resource_info);
13557  background_resources.window_id=AcquireString("");
13558  (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13559  "0x%lx",windows->image.id);
13560  background_resources.backdrop=MagickTrue;
13561  /*
13562  Read each image and convert them to a tile.
13563  */
13564  backdrop=(windows->visual_info->klass == TrueColor) ||
13565  (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13566  read_info=CloneImageInfo(resource_info->image_info);
13567  (void) SetImageOption(read_info,"jpeg:size","120x120");
13568  (void) CloneString(&read_info->size,DefaultTileGeometry);
13569  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13570  (void *) NULL);
13571  images=NewImageList();
13572  exception=AcquireExceptionInfo();
13573  XSetCursorState(display,windows,MagickTrue);
13574  XCheckRefreshWindows(display,windows);
13575  for (i=0; i < (int) number_files; i++)
13576  {
13577  (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13578  filelist[i]=DestroyString(filelist[i]);
13579  *read_info->magick='\0';
13580  next_image=ReadImage(read_info,exception);
13581  CatchException(exception);
13582  if (next_image != (Image *) NULL)
13583  {
13584  (void) DeleteImageProperty(next_image,"label");
13585  (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13586  read_info,next_image,DefaultTileLabel));
13587  (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13588  exception);
13589  thumbnail_image=ThumbnailImage(next_image,geometry.width,
13590  geometry.height,exception);
13591  if (thumbnail_image != (Image *) NULL)
13592  {
13593  next_image=DestroyImage(next_image);
13594  next_image=thumbnail_image;
13595  }
13596  if (backdrop)
13597  {
13598  (void) XDisplayBackgroundImage(display,&background_resources,
13599  next_image);
13600  XSetCursorState(display,windows,MagickTrue);
13601  }
13602  AppendImageToList(&images,next_image);
13603  if (images->progress_monitor != (MagickProgressMonitor) NULL)
13604  {
13605  MagickBooleanType
13606  proceed;
13607 
13608  proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13609  (MagickSizeType) number_files);
13610  if (proceed == MagickFalse)
13611  break;
13612  }
13613  }
13614  }
13615  exception=DestroyExceptionInfo(exception);
13616  filelist=(char **) RelinquishMagickMemory(filelist);
13617  if (images == (Image *) NULL)
13618  {
13619  read_info=DestroyImageInfo(read_info);
13620  XSetCursorState(display,windows,MagickFalse);
13621  ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13622  return((Image *) NULL);
13623  }
13624  /*
13625  Create the Visual Image Directory.
13626  */
13627  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13628  montage_info->pointsize=10;
13629  if (resource_info->font != (char *) NULL)
13630  (void) CloneString(&montage_info->font,resource_info->font);
13631  (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13632  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13633  images),&images->exception);
13634  images=DestroyImageList(images);
13635  montage_info=DestroyMontageInfo(montage_info);
13636  read_info=DestroyImageInfo(read_info);
13637  XSetCursorState(display,windows,MagickFalse);
13638  if (montage_image == (Image *) NULL)
13639  return(montage_image);
13640  XClientMessage(display,windows->image.id,windows->im_protocols,
13641  windows->im_next_image,CurrentTime);
13642  return(montage_image);
13643 }
13644 ␌
13645 /*
13646 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13647 % %
13648 % %
13649 % %
13650 % X D i s p l a y B a c k g r o u n d I m a g e %
13651 % %
13652 % %
13653 % %
13654 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13655 %
13656 % XDisplayBackgroundImage() displays an image in the background of a window.
13657 %
13658 % The format of the XDisplayBackgroundImage method is:
13659 %
13660 % MagickBooleanType XDisplayBackgroundImage(Display *display,
13661 % XResourceInfo *resource_info,Image *image)
13662 %
13663 % A description of each parameter follows:
13664 %
13665 % o display: Specifies a connection to an X server; returned from
13666 % XOpenDisplay.
13667 %
13668 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13669 %
13670 % o image: the image.
13671 %
13672 */
13673 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13674  XResourceInfo *resource_info,Image *image)
13675 {
13676  char
13677  geometry[MaxTextExtent],
13678  visual_type[MaxTextExtent];
13679 
13680  int
13681  height,
13682  status,
13683  width;
13684 
13686  geometry_info;
13687 
13688  static XPixelInfo
13689  pixel;
13690 
13691  static XStandardColormap
13692  *map_info;
13693 
13694  static XVisualInfo
13695  *visual_info = (XVisualInfo *) NULL;
13696 
13697  static XWindowInfo
13698  window_info;
13699 
13700  size_t
13701  delay;
13702 
13703  Window
13704  root_window;
13705 
13706  XGCValues
13707  context_values;
13708 
13709  XResourceInfo
13710  resources;
13711 
13712  XWindowAttributes
13713  window_attributes;
13714 
13715  /*
13716  Determine target window.
13717  */
13718  assert(image != (Image *) NULL);
13719  assert(image->signature == MagickCoreSignature);
13720  if (IsEventLogging() != MagickFalse)
13721  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13722  resources=(*resource_info);
13723  window_info.id=(Window) NULL;
13724  root_window=XRootWindow(display,XDefaultScreen(display));
13725  if (LocaleCompare(resources.window_id,"root") == 0)
13726  window_info.id=root_window;
13727  else
13728  {
13729  if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13730  window_info.id=XWindowByID(display,root_window,
13731  (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13732  if (window_info.id == (Window) NULL)
13733  window_info.id=XWindowByName(display,root_window,resources.window_id);
13734  }
13735  if (window_info.id == (Window) NULL)
13736  {
13737  ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13738  resources.window_id);
13739  }
13740  /*
13741  Determine window visual id.
13742  */
13743  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13744  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13745  (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13746  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13747  if (status != 0)
13748  (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13749  XVisualIDFromVisual(window_attributes.visual));
13750  if (visual_info == (XVisualInfo *) NULL)
13751  {
13752  /*
13753  Allocate standard colormap.
13754  */
13755  map_info=XAllocStandardColormap();
13756  if (map_info == (XStandardColormap *) NULL)
13757  ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13758  image->filename);
13759  map_info->colormap=(Colormap) NULL;
13760  pixel.pixels=(unsigned long *) NULL;
13761  /*
13762  Initialize visual info.
13763  */
13764  resources.map_type=(char *) NULL;
13765  resources.visual_type=visual_type;
13766  visual_info=XBestVisualInfo(display,map_info,&resources);
13767  if (visual_info == (XVisualInfo *) NULL)
13768  ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13769  resources.visual_type);
13770  /*
13771  Initialize window info.
13772  */
13773  window_info.ximage=(XImage *) NULL;
13774  window_info.matte_image=(XImage *) NULL;
13775  window_info.pixmap=(Pixmap) NULL;
13776  window_info.matte_pixmap=(Pixmap) NULL;
13777  }
13778  /*
13779  Free previous root colors.
13780  */
13781  if (window_info.id == root_window)
13782  (void) XDestroyWindowColors(display,root_window);
13783  /*
13784  Initialize Standard Colormap.
13785  */
13786  resources.colormap=SharedColormap;
13787  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13788  /*
13789  Graphic context superclass.
13790  */
13791  context_values.background=pixel.foreground_color.pixel;
13792  context_values.foreground=pixel.background_color.pixel;
13793  pixel.annotate_context=XCreateGC(display,window_info.id,
13794  (size_t) (GCBackground | GCForeground),&context_values);
13795  if (pixel.annotate_context == (GC) NULL)
13796  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13797  image->filename);
13798  /*
13799  Initialize Image window attributes.
13800  */
13801  window_info.name=AcquireString("\0");
13802  window_info.icon_name=AcquireString("\0");
13803  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13804  &resources,&window_info);
13805  /*
13806  Create the X image.
13807  */
13808  window_info.width=(unsigned int) image->columns;
13809  window_info.height=(unsigned int) image->rows;
13810  if ((image->columns != window_info.width) ||
13811  (image->rows != window_info.height))
13812  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13813  image->filename);
13814  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13815  window_attributes.width,window_attributes.height);
13816  geometry_info.width=window_info.width;
13817  geometry_info.height=window_info.height;
13818  geometry_info.x=(ssize_t) window_info.x;
13819  geometry_info.y=(ssize_t) window_info.y;
13820  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13821  &geometry_info.width,&geometry_info.height);
13822  window_info.width=(unsigned int) geometry_info.width;
13823  window_info.height=(unsigned int) geometry_info.height;
13824  window_info.x=(int) geometry_info.x;
13825  window_info.y=(int) geometry_info.y;
13826  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13827  window_info.height);
13828  if (status == MagickFalse)
13829  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13830  image->filename);
13831  window_info.x=0;
13832  window_info.y=0;
13833  if (resource_info->debug != MagickFalse)
13834  {
13835  (void) LogMagickEvent(X11Event,GetMagickModule(),
13836  "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13837  (double) image->columns,(double) image->rows);
13838  if (image->colors != 0)
13839  (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13840  image->colors);
13841  (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13842  }
13843  /*
13844  Adjust image dimensions as specified by backdrop or geometry options.
13845  */
13846  width=(int) window_info.width;
13847  height=(int) window_info.height;
13848  if (resources.backdrop != MagickFalse)
13849  {
13850  /*
13851  Center image on window.
13852  */
13853  window_info.x=(window_attributes.width/2)-(window_info.ximage->width/2);
13854  window_info.y=(window_attributes.height/2)-(window_info.ximage->height/2);
13855  width=window_attributes.width;
13856  height=window_attributes.height;
13857  }
13858  if ((resources.image_geometry != (char *) NULL) &&
13859  (*resources.image_geometry != '\0'))
13860  {
13861  char
13862  default_geometry[MaxTextExtent];
13863 
13864  int
13865  flags,
13866  gravity;
13867 
13868  XSizeHints
13869  *size_hints;
13870 
13871  /*
13872  User specified geometry.
13873  */
13874  size_hints=XAllocSizeHints();
13875  if (size_hints == (XSizeHints *) NULL)
13876  ThrowXWindowFatalException(ResourceLimitFatalError,
13877  "MemoryAllocationFailed",image->filename);
13878  size_hints->flags=0L;
13879  (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13880  width,height);
13881  flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13882  default_geometry,window_info.border_width,size_hints,&window_info.x,
13883  &window_info.y,&width,&height,&gravity);
13884  if (flags & (XValue | YValue))
13885  {
13886  width=window_attributes.width;
13887  height=window_attributes.height;
13888  }
13889  (void) XFree((void *) size_hints);
13890  }
13891  /*
13892  Create the X pixmap.
13893  */
13894  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13895  (unsigned int) height,window_info.depth);
13896  if (window_info.pixmap == (Pixmap) NULL)
13897  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13898  image->filename);
13899  /*
13900  Display pixmap on the window.
13901  */
13902  if (((unsigned int) width > window_info.width) ||
13903  ((unsigned int) height > window_info.height))
13904  (void) XFillRectangle(display,window_info.pixmap,
13905  window_info.annotate_context,0,0,(unsigned int) width,
13906  (unsigned int) height);
13907  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13908  window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13909  window_info.width,(unsigned int) window_info.height);
13910  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13911  (void) XClearWindow(display,window_info.id);
13912  delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13913  XDelay(display,delay == 0UL ? 10UL : delay);
13914  (void) XSync(display,MagickFalse);
13915  return(window_info.id == root_window ? MagickTrue : MagickFalse);
13916 }
13917 ␌
13918 /*
13919 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13920 % %
13921 % %
13922 % %
13923 + X D i s p l a y I m a g e %
13924 % %
13925 % %
13926 % %
13927 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13928 %
13929 % XDisplayImage() displays an image via X11. A new image is created and
13930 % returned if the user interactively transforms the displayed image.
13931 %
13932 % The format of the XDisplayImage method is:
13933 %
13934 % Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13935 % char **argv,int argc,Image **image,size_t *state)
13936 %
13937 % A description of each parameter follows:
13938 %
13939 % o nexus: Method XDisplayImage returns an image when the
13940 % user chooses 'Open Image' from the command menu or picks a tile
13941 % from the image directory. Otherwise a null image is returned.
13942 %
13943 % o display: Specifies a connection to an X server; returned from
13944 % XOpenDisplay.
13945 %
13946 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13947 %
13948 % o argv: Specifies the application's argument list.
13949 %
13950 % o argc: Specifies the number of arguments.
13951 %
13952 % o image: Specifies an address to an address of an Image structure;
13953 %
13954 */
13955 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13956  char **argv,int argc,Image **image,size_t *state)
13957 {
13958 #define MagnifySize 256 /* must be a power of 2 */
13959 #define MagickMenus 10
13960 #define MagickTitle "Commands"
13961 
13962  const char
13963  *const CommandMenu[] =
13964  {
13965  "File",
13966  "Edit",
13967  "View",
13968  "Transform",
13969  "Enhance",
13970  "Effects",
13971  "F/X",
13972  "Image Edit",
13973  "Miscellany",
13974  "Help",
13975  (char *) NULL
13976  },
13977  *const FileMenu[] =
13978  {
13979  "Open...",
13980  "Next",
13981  "Former",
13982  "Select...",
13983  "Save...",
13984  "Print...",
13985  "Delete...",
13986  "New...",
13987  "Visual Directory...",
13988  "Quit",
13989  (char *) NULL
13990  },
13991  *const EditMenu[] =
13992  {
13993  "Undo",
13994  "Redo",
13995  "Cut",
13996  "Copy",
13997  "Paste",
13998  (char *) NULL
13999  },
14000  *const ViewMenu[] =
14001  {
14002  "Half Size",
14003  "Original Size",
14004  "Double Size",
14005  "Resize...",
14006  "Apply",
14007  "Refresh",
14008  "Restore",
14009  (char *) NULL
14010  },
14011  *const TransformMenu[] =
14012  {
14013  "Crop",
14014  "Chop",
14015  "Flop",
14016  "Flip",
14017  "Rotate Right",
14018  "Rotate Left",
14019  "Rotate...",
14020  "Shear...",
14021  "Roll...",
14022  "Trim Edges",
14023  (char *) NULL
14024  },
14025  *const EnhanceMenu[] =
14026  {
14027  "Hue...",
14028  "Saturation...",
14029  "Brightness...",
14030  "Gamma...",
14031  "Spiff",
14032  "Dull",
14033  "Contrast Stretch...",
14034  "Sigmoidal Contrast...",
14035  "Normalize",
14036  "Equalize",
14037  "Negate",
14038  "Grayscale",
14039  "Map...",
14040  "Quantize...",
14041  (char *) NULL
14042  },
14043  *const EffectsMenu[] =
14044  {
14045  "Despeckle",
14046  "Emboss",
14047  "Reduce Noise",
14048  "Add Noise...",
14049  "Sharpen...",
14050  "Blur...",
14051  "Threshold...",
14052  "Edge Detect...",
14053  "Spread...",
14054  "Shade...",
14055  "Raise...",
14056  "Segment...",
14057  (char *) NULL
14058  },
14059  *const FXMenu[] =
14060  {
14061  "Solarize...",
14062  "Sepia Tone...",
14063  "Swirl...",
14064  "Implode...",
14065  "Vignette...",
14066  "Wave...",
14067  "Oil Paint...",
14068  "Charcoal Draw...",
14069  (char *) NULL
14070  },
14071  *const ImageEditMenu[] =
14072  {
14073  "Annotate...",
14074  "Draw...",
14075  "Color...",
14076  "Matte...",
14077  "Composite...",
14078  "Add Border...",
14079  "Add Frame...",
14080  "Comment...",
14081  "Launch...",
14082  "Region of Interest...",
14083  (char *) NULL
14084  },
14085  *const MiscellanyMenu[] =
14086  {
14087  "Image Info",
14088  "Zoom Image",
14089  "Show Preview...",
14090  "Show Histogram",
14091  "Show Matte",
14092  "Background...",
14093  "Slide Show...",
14094  "Preferences...",
14095  (char *) NULL
14096  },
14097  *const HelpMenu[] =
14098  {
14099  "Overview",
14100  "Browse Documentation",
14101  "About Display",
14102  (char *) NULL
14103  },
14104  *const ShortCutsMenu[] =
14105  {
14106  "Next",
14107  "Former",
14108  "Open...",
14109  "Save...",
14110  "Print...",
14111  "Undo",
14112  "Restore",
14113  "Image Info",
14114  "Quit",
14115  (char *) NULL
14116  },
14117  *const VirtualMenu[] =
14118  {
14119  "Image Info",
14120  "Print",
14121  "Next",
14122  "Quit",
14123  (char *) NULL
14124  };
14125 
14126  const char
14127  *const *Menus[MagickMenus] =
14128  {
14129  FileMenu,
14130  EditMenu,
14131  ViewMenu,
14132  TransformMenu,
14133  EnhanceMenu,
14134  EffectsMenu,
14135  FXMenu,
14136  ImageEditMenu,
14137  MiscellanyMenu,
14138  HelpMenu
14139  };
14140 
14141  static DisplayCommand
14142  CommandMenus[] =
14143  {
14144  NullCommand,
14145  NullCommand,
14146  NullCommand,
14147  NullCommand,
14148  NullCommand,
14149  NullCommand,
14150  NullCommand,
14151  NullCommand,
14152  NullCommand,
14153  NullCommand,
14154  },
14155  FileCommands[] =
14156  {
14157  OpenCommand,
14158  NextCommand,
14159  FormerCommand,
14160  SelectCommand,
14161  SaveCommand,
14162  PrintCommand,
14163  DeleteCommand,
14164  NewCommand,
14165  VisualDirectoryCommand,
14166  QuitCommand
14167  },
14168  EditCommands[] =
14169  {
14170  UndoCommand,
14171  RedoCommand,
14172  CutCommand,
14173  CopyCommand,
14174  PasteCommand
14175  },
14176  ViewCommands[] =
14177  {
14178  HalfSizeCommand,
14179  OriginalSizeCommand,
14180  DoubleSizeCommand,
14181  ResizeCommand,
14182  ApplyCommand,
14183  RefreshCommand,
14184  RestoreCommand
14185  },
14186  TransformCommands[] =
14187  {
14188  CropCommand,
14189  ChopCommand,
14190  FlopCommand,
14191  FlipCommand,
14192  RotateRightCommand,
14193  RotateLeftCommand,
14194  RotateCommand,
14195  ShearCommand,
14196  RollCommand,
14197  TrimCommand
14198  },
14199  EnhanceCommands[] =
14200  {
14201  HueCommand,
14202  SaturationCommand,
14203  BrightnessCommand,
14204  GammaCommand,
14205  SpiffCommand,
14206  DullCommand,
14207  ContrastStretchCommand,
14208  SigmoidalContrastCommand,
14209  NormalizeCommand,
14210  EqualizeCommand,
14211  NegateCommand,
14212  GrayscaleCommand,
14213  MapCommand,
14214  QuantizeCommand
14215  },
14216  EffectsCommands[] =
14217  {
14218  DespeckleCommand,
14219  EmbossCommand,
14220  ReduceNoiseCommand,
14221  AddNoiseCommand,
14222  SharpenCommand,
14223  BlurCommand,
14224  ThresholdCommand,
14225  EdgeDetectCommand,
14226  SpreadCommand,
14227  ShadeCommand,
14228  RaiseCommand,
14229  SegmentCommand
14230  },
14231  FXCommands[] =
14232  {
14233  SolarizeCommand,
14234  SepiaToneCommand,
14235  SwirlCommand,
14236  ImplodeCommand,
14237  VignetteCommand,
14238  WaveCommand,
14239  OilPaintCommand,
14240  CharcoalDrawCommand
14241  },
14242  ImageEditCommands[] =
14243  {
14244  AnnotateCommand,
14245  DrawCommand,
14246  ColorCommand,
14247  MatteCommand,
14248  CompositeCommand,
14249  AddBorderCommand,
14250  AddFrameCommand,
14251  CommentCommand,
14252  LaunchCommand,
14253  RegionOfInterestCommand
14254  },
14255  MiscellanyCommands[] =
14256  {
14257  InfoCommand,
14258  ZoomCommand,
14259  ShowPreviewCommand,
14260  ShowHistogramCommand,
14261  ShowMatteCommand,
14262  BackgroundCommand,
14263  SlideShowCommand,
14264  PreferencesCommand
14265  },
14266  HelpCommands[] =
14267  {
14268  HelpCommand,
14269  BrowseDocumentationCommand,
14270  VersionCommand
14271  },
14272  ShortCutsCommands[] =
14273  {
14274  NextCommand,
14275  FormerCommand,
14276  OpenCommand,
14277  SaveCommand,
14278  PrintCommand,
14279  UndoCommand,
14280  RestoreCommand,
14281  InfoCommand,
14282  QuitCommand
14283  },
14284  VirtualCommands[] =
14285  {
14286  InfoCommand,
14287  PrintCommand,
14288  NextCommand,
14289  QuitCommand
14290  };
14291 
14292  static DisplayCommand
14293  *Commands[MagickMenus] =
14294  {
14295  FileCommands,
14296  EditCommands,
14297  ViewCommands,
14298  TransformCommands,
14299  EnhanceCommands,
14300  EffectsCommands,
14301  FXCommands,
14302  ImageEditCommands,
14303  MiscellanyCommands,
14304  HelpCommands
14305  };
14306 
14307  char
14308  command[MaxTextExtent],
14309  *directory,
14310  geometry[MaxTextExtent],
14311  resource_name[MaxTextExtent];
14312 
14313  DisplayCommand
14314  display_command;
14315 
14316  Image
14317  *display_image,
14318  *nexus;
14319 
14320  int
14321  entry,
14322  id;
14323 
14324  KeySym
14325  key_symbol;
14326 
14327  MagickStatusType
14328  context_mask,
14329  status;
14330 
14332  geometry_info;
14333 
14334  int
14335  i;
14336 
14337  static char
14338  working_directory[MaxTextExtent];
14339 
14340  static XPoint
14341  vid_info;
14342 
14343  static XWindowInfo
14344  *magick_windows[MaxXWindows];
14345 
14346  static unsigned int
14347  number_windows;
14348 
14349  struct stat
14350  attributes;
14351 
14352  time_t
14353  timer,
14354  timestamp,
14355  update_time;
14356 
14357  unsigned int
14358  height,
14359  width;
14360 
14361  size_t
14362  delay;
14363 
14364  WarningHandler
14365  warning_handler;
14366 
14367  Window
14368  root_window;
14369 
14370  XClassHint
14371  *class_hints;
14372 
14373  XEvent
14374  event;
14375 
14376  XFontStruct
14377  *font_info;
14378 
14379  XGCValues
14380  context_values;
14381 
14382  XPixelInfo
14383  *icon_pixel,
14384  *pixel;
14385 
14386  XResourceInfo
14387  *icon_resources;
14388 
14389  XStandardColormap
14390  *icon_map,
14391  *map_info;
14392 
14393  XVisualInfo
14394  *icon_visual,
14395  *visual_info;
14396 
14397  XWindowChanges
14398  window_changes;
14399 
14400  XWindows
14401  *windows;
14402 
14403  XWMHints
14404  *manager_hints;
14405 
14406  assert(image != (Image **) NULL);
14407  assert((*image)->signature == MagickCoreSignature);
14408  if (IsEventLogging() != MagickFalse)
14409  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14410  display_image=(*image);
14411  warning_handler=(WarningHandler) NULL;
14412  windows=XSetWindows((XWindows *) ~0);
14413  if (windows != (XWindows *) NULL)
14414  {
14415  int
14416  status;
14417 
14418  if (*working_directory == '\0')
14419  (void) CopyMagickString(working_directory,".",MaxTextExtent);
14420  status=chdir(working_directory);
14421  if (status == -1)
14422  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
14423  FileOpenError,"UnableToOpenFile","%s",working_directory);
14424  warning_handler=resource_info->display_warnings ?
14425  SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14426  warning_handler=resource_info->display_warnings ?
14427  SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14428  }
14429  else
14430  {
14431  /*
14432  Allocate windows structure.
14433  */
14434  resource_info->colors=display_image->colors;
14435  windows=XSetWindows(XInitializeWindows(display,resource_info));
14436  if (windows == (XWindows *) NULL)
14437  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14438  (*image)->filename);
14439  /*
14440  Initialize window id's.
14441  */
14442  number_windows=0;
14443  magick_windows[number_windows++]=(&windows->icon);
14444  magick_windows[number_windows++]=(&windows->backdrop);
14445  magick_windows[number_windows++]=(&windows->image);
14446  magick_windows[number_windows++]=(&windows->info);
14447  magick_windows[number_windows++]=(&windows->command);
14448  magick_windows[number_windows++]=(&windows->widget);
14449  magick_windows[number_windows++]=(&windows->popup);
14450  magick_windows[number_windows++]=(&windows->magnify);
14451  magick_windows[number_windows++]=(&windows->pan);
14452  for (i=0; i < (int) number_windows; i++)
14453  magick_windows[i]->id=(Window) NULL;
14454  vid_info.x=0;
14455  vid_info.y=0;
14456  }
14457  /*
14458  Initialize font info.
14459  */
14460  if (windows->font_info != (XFontStruct *) NULL)
14461  (void) XFreeFont(display,windows->font_info);
14462  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14463  if (windows->font_info == (XFontStruct *) NULL)
14464  ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14465  resource_info->font);
14466  /*
14467  Initialize Standard Colormap.
14468  */
14469  map_info=windows->map_info;
14470  icon_map=windows->icon_map;
14471  visual_info=windows->visual_info;
14472  icon_visual=windows->icon_visual;
14473  pixel=windows->pixel_info;
14474  icon_pixel=windows->icon_pixel;
14475  font_info=windows->font_info;
14476  icon_resources=windows->icon_resources;
14477  class_hints=windows->class_hints;
14478  manager_hints=windows->manager_hints;
14479  root_window=XRootWindow(display,visual_info->screen);
14480  nexus=NewImageList();
14481  if (resource_info->debug != MagickFalse)
14482  {
14483  (void) LogMagickEvent(X11Event,GetMagickModule(),
14484  "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14485  (double) display_image->scene,(double) display_image->columns,
14486  (double) display_image->rows);
14487  if (display_image->colors != 0)
14488  (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14489  display_image->colors);
14490  (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14491  display_image->magick);
14492  }
14493  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14494  map_info,pixel);
14495  display_image->taint=MagickFalse;
14496  /*
14497  Initialize graphic context.
14498  */
14499  windows->context.id=(Window) NULL;
14500  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14501  resource_info,&windows->context);
14502  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14503  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14504  class_hints->res_class[0]=(char) LocaleToUppercase((int)
14505  class_hints->res_class[0]);
14506  manager_hints->flags=InputHint | StateHint;
14507  manager_hints->input=MagickFalse;
14508  manager_hints->initial_state=WithdrawnState;
14509  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14510  &windows->context);
14511  if (resource_info->debug != MagickFalse)
14512  (void) LogMagickEvent(X11Event,GetMagickModule(),
14513  "Window id: 0x%lx (context)",windows->context.id);
14514  context_values.background=pixel->background_color.pixel;
14515  context_values.font=font_info->fid;
14516  context_values.foreground=pixel->foreground_color.pixel;
14517  context_values.graphics_exposures=MagickFalse;
14518  context_mask=(MagickStatusType)
14519  (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14520  if (pixel->annotate_context != (GC) NULL)
14521  (void) XFreeGC(display,pixel->annotate_context);
14522  pixel->annotate_context=XCreateGC(display,windows->context.id,
14523  context_mask,&context_values);
14524  if (pixel->annotate_context == (GC) NULL)
14525  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14526  display_image->filename);
14527  context_values.background=pixel->depth_color.pixel;
14528  if (pixel->widget_context != (GC) NULL)
14529  (void) XFreeGC(display,pixel->widget_context);
14530  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14531  &context_values);
14532  if (pixel->widget_context == (GC) NULL)
14533  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14534  display_image->filename);
14535  context_values.background=pixel->foreground_color.pixel;
14536  context_values.foreground=pixel->background_color.pixel;
14537  context_values.plane_mask=context_values.background ^
14538  context_values.foreground;
14539  if (pixel->highlight_context != (GC) NULL)
14540  (void) XFreeGC(display,pixel->highlight_context);
14541  pixel->highlight_context=XCreateGC(display,windows->context.id,
14542  (size_t) (context_mask | GCPlaneMask),&context_values);
14543  if (pixel->highlight_context == (GC) NULL)
14544  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14545  display_image->filename);
14546  (void) XDestroyWindow(display,windows->context.id);
14547  /*
14548  Initialize icon window.
14549  */
14550  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14551  icon_resources,&windows->icon);
14552  windows->icon.geometry=resource_info->icon_geometry;
14553  XBestIconSize(display,&windows->icon,display_image);
14554  windows->icon.attributes.colormap=XDefaultColormap(display,
14555  icon_visual->screen);
14556  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14557  manager_hints->flags=InputHint | StateHint;
14558  manager_hints->input=MagickFalse;
14559  manager_hints->initial_state=IconicState;
14560  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14561  &windows->icon);
14562  if (resource_info->debug != MagickFalse)
14563  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14564  windows->icon.id);
14565  /*
14566  Initialize graphic context for icon window.
14567  */
14568  if (icon_pixel->annotate_context != (GC) NULL)
14569  (void) XFreeGC(display,icon_pixel->annotate_context);
14570  context_values.background=icon_pixel->background_color.pixel;
14571  context_values.foreground=icon_pixel->foreground_color.pixel;
14572  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14573  (size_t) (GCBackground | GCForeground),&context_values);
14574  if (icon_pixel->annotate_context == (GC) NULL)
14575  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14576  display_image->filename);
14577  windows->icon.annotate_context=icon_pixel->annotate_context;
14578  /*
14579  Initialize Image window.
14580  */
14581  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14582  &windows->image);
14583  windows->image.shape=MagickTrue; /* non-rectangular shape hint */
14584  if (resource_info->use_shared_memory == MagickFalse)
14585  windows->image.shared_memory=MagickFalse;
14586  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14587  {
14588  char
14589  *title;
14590 
14591  title=InterpretImageProperties(resource_info->image_info,display_image,
14592  resource_info->title);
14593  (void) CloneString(&windows->image.name,title);
14594  (void) CloneString(&windows->image.icon_name,title);
14595  title=DestroyString(title);
14596  }
14597  else
14598  {
14599  char
14600  filename[MaxTextExtent],
14601  window_name[MaxTextExtent];
14602 
14603  /*
14604  Window name is the base of the filename.
14605  */
14606  GetPathComponent(display_image->magick_filename,TailPath,filename);
14607  if (display_image->scene == 0)
14608  (void) FormatLocaleString(window_name,MaxTextExtent,"%s: %s",
14609  MagickPackageName,filename);
14610  else
14611  (void) FormatLocaleString(window_name,MaxTextExtent,
14612  "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14613  (double) display_image->scene,(double) GetImageListLength(
14614  display_image));
14615  (void) CloneString(&windows->image.name,window_name);
14616  (void) CloneString(&windows->image.icon_name,filename);
14617  }
14618  if (resource_info->immutable)
14619  windows->image.immutable=MagickTrue;
14620  windows->image.use_pixmap=resource_info->use_pixmap;
14621  windows->image.geometry=resource_info->image_geometry;
14622  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14623  XDisplayWidth(display,visual_info->screen),
14624  XDisplayHeight(display,visual_info->screen));
14625  geometry_info.width=display_image->columns;
14626  geometry_info.height=display_image->rows;
14627  geometry_info.x=0;
14628  geometry_info.y=0;
14629  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14630  &geometry_info.width,&geometry_info.height);
14631  windows->image.width=(unsigned int) geometry_info.width;
14632  windows->image.height=(unsigned int) geometry_info.height;
14633  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14634  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14635  KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14636  PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14637  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14638  resource_info,&windows->backdrop);
14639  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14640  {
14641  /*
14642  Initialize backdrop window.
14643  */
14644  windows->backdrop.x=0;
14645  windows->backdrop.y=0;
14646  (void) CloneString(&windows->backdrop.name,"Backdrop");
14647  windows->backdrop.flags=(size_t) (USSize | USPosition);
14648  windows->backdrop.width=(unsigned int)
14649  XDisplayWidth(display,visual_info->screen);
14650  windows->backdrop.height=(unsigned int)
14651  XDisplayHeight(display,visual_info->screen);
14652  windows->backdrop.border_width=0;
14653  windows->backdrop.immutable=MagickTrue;
14654  windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14655  ButtonReleaseMask;
14656  windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14657  StructureNotifyMask;
14658  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14659  manager_hints->icon_window=windows->icon.id;
14660  manager_hints->input=MagickTrue;
14661  manager_hints->initial_state=resource_info->iconic ? IconicState :
14662  NormalState;
14663  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14664  &windows->backdrop);
14665  if (resource_info->debug != MagickFalse)
14666  (void) LogMagickEvent(X11Event,GetMagickModule(),
14667  "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14668  (void) XMapWindow(display,windows->backdrop.id);
14669  (void) XClearWindow(display,windows->backdrop.id);
14670  if (windows->image.id != (Window) NULL)
14671  {
14672  (void) XDestroyWindow(display,windows->image.id);
14673  windows->image.id=(Window) NULL;
14674  }
14675  /*
14676  Position image in the center the backdrop.
14677  */
14678  windows->image.flags|=USPosition;
14679  windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14680  (windows->image.width/2);
14681  windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14682  (windows->image.height/2);
14683  }
14684  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14685  manager_hints->icon_window=windows->icon.id;
14686  manager_hints->input=MagickTrue;
14687  manager_hints->initial_state=resource_info->iconic ? IconicState :
14688  NormalState;
14689  if (windows->group_leader.id != (Window) NULL)
14690  {
14691  /*
14692  Follow the leader.
14693  */
14694  manager_hints->flags|=WindowGroupHint;
14695  manager_hints->window_group=windows->group_leader.id;
14696  (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14697  if (resource_info->debug != MagickFalse)
14698  (void) LogMagickEvent(X11Event,GetMagickModule(),
14699  "Window id: 0x%lx (group leader)",windows->group_leader.id);
14700  }
14701  XMakeWindow(display,
14702  (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14703  argv,argc,class_hints,manager_hints,&windows->image);
14704  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14705  XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14706  if (windows->group_leader.id != (Window) NULL)
14707  (void) XSetTransientForHint(display,windows->image.id,
14708  windows->group_leader.id);
14709  if (resource_info->debug != MagickFalse)
14710  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14711  windows->image.id);
14712  /*
14713  Initialize Info widget.
14714  */
14715  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14716  &windows->info);
14717  (void) CloneString(&windows->info.name,"Info");
14718  (void) CloneString(&windows->info.icon_name,"Info");
14719  windows->info.border_width=1;
14720  windows->info.x=2;
14721  windows->info.y=2;
14722  windows->info.flags|=PPosition;
14723  windows->info.attributes.win_gravity=UnmapGravity;
14724  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14725  StructureNotifyMask;
14726  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14727  manager_hints->input=MagickFalse;
14728  manager_hints->initial_state=NormalState;
14729  manager_hints->window_group=windows->image.id;
14730  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14731  &windows->info);
14732  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14733  windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14734  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14735  windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14736  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14737  if (windows->image.mapped != MagickFalse)
14738  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14739  if (resource_info->debug != MagickFalse)
14740  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14741  windows->info.id);
14742  /*
14743  Initialize Command widget.
14744  */
14745  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14746  resource_info,&windows->command);
14747  windows->command.data=MagickMenus;
14748  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14749  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14750  resource_info->client_name);
14751  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14752  resource_name,"geometry",(char *) NULL);
14753  (void) CloneString(&windows->command.name,MagickTitle);
14754  windows->command.border_width=0;
14755  windows->command.flags|=PPosition;
14756  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14757  ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14758  OwnerGrabButtonMask | StructureNotifyMask;
14759  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14760  manager_hints->input=MagickTrue;
14761  manager_hints->initial_state=NormalState;
14762  manager_hints->window_group=windows->image.id;
14763  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14764  &windows->command);
14765  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14766  windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14767  HighlightHeight);
14768  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14769  windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14770  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14771  if (windows->command.mapped != MagickFalse)
14772  (void) XMapRaised(display,windows->command.id);
14773  if (resource_info->debug != MagickFalse)
14774  (void) LogMagickEvent(X11Event,GetMagickModule(),
14775  "Window id: 0x%lx (command)",windows->command.id);
14776  /*
14777  Initialize Widget window.
14778  */
14779  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14780  resource_info,&windows->widget);
14781  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14782  resource_info->client_name);
14783  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14784  resource_name,"geometry",(char *) NULL);
14785  windows->widget.border_width=0;
14786  windows->widget.flags|=PPosition;
14787  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14788  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14789  KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14790  StructureNotifyMask;
14791  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14792  manager_hints->input=MagickTrue;
14793  manager_hints->initial_state=NormalState;
14794  manager_hints->window_group=windows->image.id;
14795  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14796  &windows->widget);
14797  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14798  windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14799  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14800  windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14801  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14802  if (resource_info->debug != MagickFalse)
14803  (void) LogMagickEvent(X11Event,GetMagickModule(),
14804  "Window id: 0x%lx (widget)",windows->widget.id);
14805  /*
14806  Initialize popup window.
14807  */
14808  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14809  resource_info,&windows->popup);
14810  windows->popup.border_width=0;
14811  windows->popup.flags|=PPosition;
14812  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14813  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14814  KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14815  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14816  manager_hints->input=MagickTrue;
14817  manager_hints->initial_state=NormalState;
14818  manager_hints->window_group=windows->image.id;
14819  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14820  &windows->popup);
14821  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14822  windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14823  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14824  windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14825  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14826  if (resource_info->debug != MagickFalse)
14827  (void) LogMagickEvent(X11Event,GetMagickModule(),
14828  "Window id: 0x%lx (pop up)",windows->popup.id);
14829  /*
14830  Initialize Magnify window and cursor.
14831  */
14832  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14833  resource_info,&windows->magnify);
14834  if (resource_info->use_shared_memory == MagickFalse)
14835  windows->magnify.shared_memory=MagickFalse;
14836  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14837  resource_info->client_name);
14838  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14839  resource_name,"geometry",(char *) NULL);
14840  (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14841  resource_info->magnify);
14842  if (windows->magnify.cursor != (Cursor) NULL)
14843  (void) XFreeCursor(display,windows->magnify.cursor);
14844  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14845  map_info->colormap,resource_info->background_color,
14846  resource_info->foreground_color);
14847  if (windows->magnify.cursor == (Cursor) NULL)
14848  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14849  display_image->filename);
14850  windows->magnify.width=MagnifySize;
14851  windows->magnify.height=MagnifySize;
14852  windows->magnify.flags|=PPosition;
14853  windows->magnify.min_width=MagnifySize;
14854  windows->magnify.min_height=MagnifySize;
14855  windows->magnify.width_inc=MagnifySize;
14856  windows->magnify.height_inc=MagnifySize;
14857  windows->magnify.data=resource_info->magnify;
14858  windows->magnify.attributes.cursor=windows->magnify.cursor;
14859  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14860  ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14861  StructureNotifyMask;
14862  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14863  manager_hints->input=MagickTrue;
14864  manager_hints->initial_state=NormalState;
14865  manager_hints->window_group=windows->image.id;
14866  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14867  &windows->magnify);
14868  if (resource_info->debug != MagickFalse)
14869  (void) LogMagickEvent(X11Event,GetMagickModule(),
14870  "Window id: 0x%lx (magnify)",windows->magnify.id);
14871  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14872  /*
14873  Initialize panning window.
14874  */
14875  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14876  resource_info,&windows->pan);
14877  (void) CloneString(&windows->pan.name,"Pan Icon");
14878  windows->pan.width=windows->icon.width;
14879  windows->pan.height=windows->icon.height;
14880  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14881  resource_info->client_name);
14882  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14883  resource_name,"geometry",(char *) NULL);
14884  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14885  &windows->pan.width,&windows->pan.height);
14886  windows->pan.flags|=PPosition;
14887  windows->pan.immutable=MagickTrue;
14888  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14889  ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14890  StructureNotifyMask;
14891  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14892  manager_hints->input=MagickFalse;
14893  manager_hints->initial_state=NormalState;
14894  manager_hints->window_group=windows->image.id;
14895  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14896  &windows->pan);
14897  if (resource_info->debug != MagickFalse)
14898  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14899  windows->pan.id);
14900  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14901  if (windows->info.mapped != MagickFalse)
14902  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14903  if ((windows->image.mapped == MagickFalse) ||
14904  (windows->backdrop.id != (Window) NULL))
14905  (void) XMapWindow(display,windows->image.id);
14906  /*
14907  Set our progress monitor and warning handlers.
14908  */
14909  if (warning_handler == (WarningHandler) NULL)
14910  {
14911  warning_handler=resource_info->display_warnings ?
14912  SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14913  warning_handler=resource_info->display_warnings ?
14914  SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14915  }
14916  /*
14917  Initialize Image and Magnify X images.
14918  */
14919  windows->image.x=0;
14920  windows->image.y=0;
14921  windows->magnify.shape=MagickFalse;
14922  width=(unsigned int) display_image->columns;
14923  height=(unsigned int) display_image->rows;
14924  if ((display_image->columns != width) || (display_image->rows != height))
14925  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14926  display_image->filename);
14927  status=XMakeImage(display,resource_info,&windows->image,display_image,
14928  width,height);
14929  if (status == MagickFalse)
14930  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14931  display_image->filename);
14932  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14933  windows->magnify.width,windows->magnify.height);
14934  if (status == MagickFalse)
14935  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14936  display_image->filename);
14937  if (windows->magnify.mapped != MagickFalse)
14938  (void) XMapRaised(display,windows->magnify.id);
14939  if (windows->pan.mapped != MagickFalse)
14940  (void) XMapRaised(display,windows->pan.id);
14941  windows->image.window_changes.width=(int) display_image->columns;
14942  windows->image.window_changes.height=(int) display_image->rows;
14943  (void) XConfigureImage(display,resource_info,windows,display_image);
14944  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14945  (void) XSync(display,MagickFalse);
14946  /*
14947  Respond to events.
14948  */
14949  delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
14950  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
14951  update_time=0;
14952  if (resource_info->update != MagickFalse)
14953  {
14954  MagickBooleanType
14955  status;
14956 
14957  /*
14958  Determine when file data was last modified.
14959  */
14960  status=GetPathAttributes(display_image->filename,&attributes);
14961  if (status != MagickFalse)
14962  update_time=attributes.st_mtime;
14963  }
14964  *state&=(~FormerImageState);
14965  *state&=(~MontageImageState);
14966  *state&=(~NextImageState);
14967  do
14968  {
14969  /*
14970  Handle a window event.
14971  */
14972  if (windows->image.mapped != MagickFalse)
14973  if ((display_image->delay != 0) || (resource_info->update != 0))
14974  {
14975  if (timer < GetMagickTime())
14976  {
14977  if (resource_info->update == MagickFalse)
14978  *state|=NextImageState | ExitState;
14979  else
14980  {
14981  MagickBooleanType
14982  status;
14983 
14984  /*
14985  Determine if image file was modified.
14986  */
14987  status=GetPathAttributes(display_image->filename,&attributes);
14988  if (status != MagickFalse)
14989  if (update_time != attributes.st_mtime)
14990  {
14991  /*
14992  Redisplay image.
14993  */
14994  (void) FormatLocaleString(
14995  resource_info->image_info->filename,MaxTextExtent,
14996  "%s:%s",display_image->magick,
14997  display_image->filename);
14998  nexus=ReadImage(resource_info->image_info,
14999  &display_image->exception);
15000  if (nexus != (Image *) NULL)
15001  *state|=NextImageState | ExitState;
15002  }
15003  delay=display_image->delay/MagickMax(
15004  display_image->ticks_per_second,1L);
15005  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15006  }
15007  }
15008  if (XEventsQueued(display,QueuedAfterFlush) == 0)
15009  {
15010  /*
15011  Do not block if delay > 0.
15012  */
15013  XDelay(display,SuspendTime << 2);
15014  continue;
15015  }
15016  }
15017  timestamp=GetMagickTime();
15018  (void) XNextEvent(display,&event);
15019  if (windows->image.stasis == MagickFalse)
15020  windows->image.stasis=(GetMagickTime()-timestamp) > 0 ?
15021  MagickTrue : MagickFalse;
15022  if (windows->magnify.stasis == MagickFalse)
15023  windows->magnify.stasis=(GetMagickTime()-timestamp) > 0 ?
15024  MagickTrue : MagickFalse;
15025  if (event.xany.window == windows->command.id)
15026  {
15027  /*
15028  Select a command from the Command widget.
15029  */
15030  id=XCommandWidget(display,windows,CommandMenu,&event);
15031  if (id < 0)
15032  continue;
15033  (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15034  display_command=CommandMenus[id];
15035  if (id < MagickMenus)
15036  {
15037  /*
15038  Select a command from a pop-up menu.
15039  */
15040  entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15041  command);
15042  if (entry < 0)
15043  continue;
15044  (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15045  display_command=Commands[id][entry];
15046  }
15047  if (display_command != NullCommand)
15048  nexus=XMagickCommand(display,resource_info,windows,display_command,
15049  &display_image);
15050  continue;
15051  }
15052  switch (event.type)
15053  {
15054  case ButtonPress:
15055  {
15056  if (resource_info->debug != MagickFalse)
15057  (void) LogMagickEvent(X11Event,GetMagickModule(),
15058  "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15059  event.xbutton.button,event.xbutton.x,event.xbutton.y);
15060  if ((event.xbutton.button == Button3) &&
15061  (event.xbutton.state & Mod1Mask))
15062  {
15063  /*
15064  Convert Alt-Button3 to Button2.
15065  */
15066  event.xbutton.button=Button2;
15067  event.xbutton.state&=(~Mod1Mask);
15068  }
15069  if (event.xbutton.window == windows->backdrop.id)
15070  {
15071  (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15072  event.xbutton.time);
15073  break;
15074  }
15075  if (event.xbutton.window == windows->image.id)
15076  {
15077  switch (event.xbutton.button)
15078  {
15079  case Button1:
15080  {
15081  if (resource_info->immutable)
15082  {
15083  /*
15084  Select a command from the Virtual menu.
15085  */
15086  entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15087  command);
15088  if (entry >= 0)
15089  nexus=XMagickCommand(display,resource_info,windows,
15090  VirtualCommands[entry],&display_image);
15091  break;
15092  }
15093  /*
15094  Map/unmap Command widget.
15095  */
15096  if (windows->command.mapped != MagickFalse)
15097  (void) XWithdrawWindow(display,windows->command.id,
15098  windows->command.screen);
15099  else
15100  {
15101  (void) XCommandWidget(display,windows,CommandMenu,
15102  (XEvent *) NULL);
15103  (void) XMapRaised(display,windows->command.id);
15104  }
15105  break;
15106  }
15107  case Button2:
15108  {
15109  /*
15110  User pressed the image magnify button.
15111  */
15112  (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15113  &display_image);
15114  XMagnifyImage(display,windows,&event);
15115  break;
15116  }
15117  case Button3:
15118  {
15119  if (resource_info->immutable)
15120  {
15121  /*
15122  Select a command from the Virtual menu.
15123  */
15124  entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15125  command);
15126  if (entry >= 0)
15127  nexus=XMagickCommand(display,resource_info,windows,
15128  VirtualCommands[entry],&display_image);
15129  break;
15130  }
15131  if (display_image->montage != (char *) NULL)
15132  {
15133  /*
15134  Open or delete a tile from a visual image directory.
15135  */
15136  nexus=XTileImage(display,resource_info,windows,
15137  display_image,&event);
15138  if (nexus != (Image *) NULL)
15139  *state|=MontageImageState | NextImageState | ExitState;
15140  vid_info.x=(short int) windows->image.x;
15141  vid_info.y=(short int) windows->image.y;
15142  break;
15143  }
15144  /*
15145  Select a command from the Short Cuts menu.
15146  */
15147  entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15148  command);
15149  if (entry >= 0)
15150  nexus=XMagickCommand(display,resource_info,windows,
15151  ShortCutsCommands[entry],&display_image);
15152  break;
15153  }
15154  case Button4:
15155  {
15156  /*
15157  Wheel up.
15158  */
15159  XTranslateImage(display,windows,*image,XK_Up);
15160  break;
15161  }
15162  case Button5:
15163  {
15164  /*
15165  Wheel down.
15166  */
15167  XTranslateImage(display,windows,*image,XK_Down);
15168  break;
15169  }
15170  default:
15171  break;
15172  }
15173  break;
15174  }
15175  if (event.xbutton.window == windows->magnify.id)
15176  {
15177  const char
15178  *const MagnifyMenu[] =
15179  {
15180  "2",
15181  "4",
15182  "5",
15183  "6",
15184  "7",
15185  "8",
15186  "9",
15187  "3",
15188  (char *) NULL,
15189  };
15190 
15191  int
15192  factor;
15193 
15194  static KeySym
15195  MagnifyCommands[] =
15196  {
15197  XK_2,
15198  XK_4,
15199  XK_5,
15200  XK_6,
15201  XK_7,
15202  XK_8,
15203  XK_9,
15204  XK_3
15205  };
15206 
15207  /*
15208  Select a magnify factor from the pop-up menu.
15209  */
15210  factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15211  if (factor >= 0)
15212  XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15213  break;
15214  }
15215  if (event.xbutton.window == windows->pan.id)
15216  {
15217  switch (event.xbutton.button)
15218  {
15219  case Button4:
15220  {
15221  /*
15222  Wheel up.
15223  */
15224  XTranslateImage(display,windows,*image,XK_Up);
15225  break;
15226  }
15227  case Button5:
15228  {
15229  /*
15230  Wheel down.
15231  */
15232  XTranslateImage(display,windows,*image,XK_Down);
15233  break;
15234  }
15235  default:
15236  {
15237  XPanImage(display,windows,&event);
15238  break;
15239  }
15240  }
15241  break;
15242  }
15243  delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15244  1L);
15245  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15246  break;
15247  }
15248  case ButtonRelease:
15249  {
15250  if (resource_info->debug != MagickFalse)
15251  (void) LogMagickEvent(X11Event,GetMagickModule(),
15252  "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15253  event.xbutton.button,event.xbutton.x,event.xbutton.y);
15254  break;
15255  }
15256  case ClientMessage:
15257  {
15258  if (resource_info->debug != MagickFalse)
15259  (void) LogMagickEvent(X11Event,GetMagickModule(),
15260  "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15261  event.xclient.message_type,event.xclient.format,(unsigned long)
15262  event.xclient.data.l[0]);
15263  if (event.xclient.message_type == windows->im_protocols)
15264  {
15265  if (*event.xclient.data.l == (long) windows->im_update_widget)
15266  {
15267  (void) CloneString(&windows->command.name,MagickTitle);
15268  windows->command.data=MagickMenus;
15269  (void) XCommandWidget(display,windows,CommandMenu,
15270  (XEvent *) NULL);
15271  break;
15272  }
15273  if (*event.xclient.data.l == (long) windows->im_update_colormap)
15274  {
15275  /*
15276  Update graphic context and window colormap.
15277  */
15278  for (i=0; i < (int) number_windows; i++)
15279  {
15280  if (magick_windows[i]->id == windows->icon.id)
15281  continue;
15282  context_values.background=pixel->background_color.pixel;
15283  context_values.foreground=pixel->foreground_color.pixel;
15284  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15285  context_mask,&context_values);
15286  (void) XChangeGC(display,magick_windows[i]->widget_context,
15287  context_mask,&context_values);
15288  context_values.background=pixel->foreground_color.pixel;
15289  context_values.foreground=pixel->background_color.pixel;
15290  context_values.plane_mask=context_values.background ^
15291  context_values.foreground;
15292  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15293  (size_t) (context_mask | GCPlaneMask),
15294  &context_values);
15295  magick_windows[i]->attributes.background_pixel=
15296  pixel->background_color.pixel;
15297  magick_windows[i]->attributes.border_pixel=
15298  pixel->border_color.pixel;
15299  magick_windows[i]->attributes.colormap=map_info->colormap;
15300  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15301  (unsigned long) magick_windows[i]->mask,
15302  &magick_windows[i]->attributes);
15303  }
15304  if (windows->pan.mapped != MagickFalse)
15305  {
15306  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15307  windows->pan.pixmap);
15308  (void) XClearWindow(display,windows->pan.id);
15309  XDrawPanRectangle(display,windows);
15310  }
15311  if (windows->backdrop.id != (Window) NULL)
15312  (void) XInstallColormap(display,map_info->colormap);
15313  break;
15314  }
15315  if (*event.xclient.data.l == (long) windows->im_former_image)
15316  {
15317  *state|=FormerImageState | ExitState;
15318  break;
15319  }
15320  if (*event.xclient.data.l == (long) windows->im_next_image)
15321  {
15322  *state|=NextImageState | ExitState;
15323  break;
15324  }
15325  if (*event.xclient.data.l == (long) windows->im_retain_colors)
15326  {
15327  *state|=RetainColorsState;
15328  break;
15329  }
15330  if (*event.xclient.data.l == (long) windows->im_exit)
15331  {
15332  *state|=ExitState;
15333  break;
15334  }
15335  break;
15336  }
15337  if (event.xclient.message_type == windows->dnd_protocols)
15338  {
15339  Atom
15340  selection,
15341  type;
15342 
15343  int
15344  format,
15345  status;
15346 
15347  unsigned char
15348  *data;
15349 
15350  unsigned long
15351  after,
15352  length;
15353 
15354  /*
15355  Display image named by the Drag-and-Drop selection.
15356  */
15357  if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15358  break;
15359  selection=XInternAtom(display,"DndSelection",MagickFalse);
15360  status=XGetWindowProperty(display,root_window,selection,0L,(long)
15361  MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15362  &length,&after,&data);
15363  if ((status != Success) || (length == 0))
15364  break;
15365  if (*event.xclient.data.l == 2)
15366  {
15367  /*
15368  Offix DND.
15369  */
15370  (void) CopyMagickString(resource_info->image_info->filename,
15371  (char *) data,MaxTextExtent);
15372  }
15373  else
15374  {
15375  /*
15376  XDND.
15377  */
15378  if (strncmp((char *) data, "file:", 5) != 0)
15379  {
15380  (void) XFree((void *) data);
15381  break;
15382  }
15383  (void) CopyMagickString(resource_info->image_info->filename,
15384  ((char *) data)+5,MaxTextExtent);
15385  }
15386  nexus=ReadImage(resource_info->image_info,
15387  &display_image->exception);
15388  CatchException(&display_image->exception);
15389  if (nexus != (Image *) NULL)
15390  *state|=NextImageState | ExitState;
15391  (void) XFree((void *) data);
15392  break;
15393  }
15394  /*
15395  If client window delete message, exit.
15396  */
15397  if (event.xclient.message_type != windows->wm_protocols)
15398  break;
15399  if (*event.xclient.data.l != (long) windows->wm_delete_window)
15400  break;
15401  (void) XWithdrawWindow(display,event.xclient.window,
15402  visual_info->screen);
15403  if (event.xclient.window == windows->image.id)
15404  {
15405  *state|=ExitState;
15406  break;
15407  }
15408  if (event.xclient.window == windows->pan.id)
15409  {
15410  /*
15411  Restore original image size when pan window is deleted.
15412  */
15413  windows->image.window_changes.width=windows->image.ximage->width;
15414  windows->image.window_changes.height=windows->image.ximage->height;
15415  (void) XConfigureImage(display,resource_info,windows,
15416  display_image);
15417  }
15418  break;
15419  }
15420  case ConfigureNotify:
15421  {
15422  if (resource_info->debug != MagickFalse)
15423  (void) LogMagickEvent(X11Event,GetMagickModule(),
15424  "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15425  event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15426  event.xconfigure.y,event.xconfigure.send_event);
15427  if (event.xconfigure.window == windows->image.id)
15428  {
15429  /*
15430  Image window has a new configuration.
15431  */
15432  if (event.xconfigure.send_event != 0)
15433  {
15434  XWindowChanges
15435  window_changes;
15436 
15437  /*
15438  Position the transient windows relative of the Image window.
15439  */
15440  if (windows->command.geometry == (char *) NULL)
15441  if (windows->command.mapped == MagickFalse)
15442  {
15443  windows->command.x=event.xconfigure.x-
15444  windows->command.width-25;
15445  windows->command.y=event.xconfigure.y;
15446  XConstrainWindowPosition(display,&windows->command);
15447  window_changes.x=windows->command.x;
15448  window_changes.y=windows->command.y;
15449  (void) XReconfigureWMWindow(display,windows->command.id,
15450  windows->command.screen,(unsigned int) (CWX | CWY),
15451  &window_changes);
15452  }
15453  if (windows->widget.geometry == (char *) NULL)
15454  if (windows->widget.mapped == MagickFalse)
15455  {
15456  windows->widget.x=event.xconfigure.x+
15457  event.xconfigure.width/10;
15458  windows->widget.y=event.xconfigure.y+
15459  event.xconfigure.height/10;
15460  XConstrainWindowPosition(display,&windows->widget);
15461  window_changes.x=windows->widget.x;
15462  window_changes.y=windows->widget.y;
15463  (void) XReconfigureWMWindow(display,windows->widget.id,
15464  windows->widget.screen,(unsigned int) (CWX | CWY),
15465  &window_changes);
15466  }
15467  if (windows->magnify.geometry == (char *) NULL)
15468  if (windows->magnify.mapped == MagickFalse)
15469  {
15470  windows->magnify.x=event.xconfigure.x+
15471  event.xconfigure.width+25;
15472  windows->magnify.y=event.xconfigure.y;
15473  XConstrainWindowPosition(display,&windows->magnify);
15474  window_changes.x=windows->magnify.x;
15475  window_changes.y=windows->magnify.y;
15476  (void) XReconfigureWMWindow(display,windows->magnify.id,
15477  windows->magnify.screen,(unsigned int) (CWX | CWY),
15478  &window_changes);
15479  }
15480  if (windows->pan.geometry == (char *) NULL)
15481  if (windows->pan.mapped == MagickFalse)
15482  {
15483  windows->pan.x=event.xconfigure.x+
15484  event.xconfigure.width+25;
15485  windows->pan.y=event.xconfigure.y+
15486  windows->magnify.height+50;
15487  XConstrainWindowPosition(display,&windows->pan);
15488  window_changes.x=windows->pan.x;
15489  window_changes.y=windows->pan.y;
15490  (void) XReconfigureWMWindow(display,windows->pan.id,
15491  windows->pan.screen,(unsigned int) (CWX | CWY),
15492  &window_changes);
15493  }
15494  }
15495  if ((event.xconfigure.width == (int) windows->image.width) &&
15496  (event.xconfigure.height == (int) windows->image.height))
15497  break;
15498  windows->image.width=(unsigned int) event.xconfigure.width;
15499  windows->image.height=(unsigned int) event.xconfigure.height;
15500  windows->image.x=0;
15501  windows->image.y=0;
15502  if (display_image->montage != (char *) NULL)
15503  {
15504  windows->image.x=vid_info.x;
15505  windows->image.y=vid_info.y;
15506  }
15507  if ((windows->image.mapped != MagickFalse) &&
15508  (windows->image.stasis != MagickFalse))
15509  {
15510  /*
15511  Update image window configuration.
15512  */
15513  windows->image.window_changes.width=event.xconfigure.width;
15514  windows->image.window_changes.height=event.xconfigure.height;
15515  (void) XConfigureImage(display,resource_info,windows,
15516  display_image);
15517  }
15518  /*
15519  Update pan window configuration.
15520  */
15521  if ((event.xconfigure.width < windows->image.ximage->width) ||
15522  (event.xconfigure.height < windows->image.ximage->height))
15523  {
15524  (void) XMapRaised(display,windows->pan.id);
15525  XDrawPanRectangle(display,windows);
15526  }
15527  else
15528  if (windows->pan.mapped != MagickFalse)
15529  (void) XWithdrawWindow(display,windows->pan.id,
15530  windows->pan.screen);
15531  break;
15532  }
15533  if (event.xconfigure.window == windows->magnify.id)
15534  {
15535  unsigned int
15536  magnify;
15537 
15538  /*
15539  Magnify window has a new configuration.
15540  */
15541  windows->magnify.width=(unsigned int) event.xconfigure.width;
15542  windows->magnify.height=(unsigned int) event.xconfigure.height;
15543  if (windows->magnify.mapped == MagickFalse)
15544  break;
15545  magnify=1;
15546  while ((int) magnify <= event.xconfigure.width)
15547  magnify<<=1;
15548  while ((int) magnify <= event.xconfigure.height)
15549  magnify<<=1;
15550  magnify>>=1;
15551  if (((int) magnify != event.xconfigure.width) ||
15552  ((int) magnify != event.xconfigure.height))
15553  {
15554  window_changes.width=(int) magnify;
15555  window_changes.height=(int) magnify;
15556  (void) XReconfigureWMWindow(display,windows->magnify.id,
15557  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15558  &window_changes);
15559  break;
15560  }
15561  if ((windows->magnify.mapped != MagickFalse) &&
15562  (windows->magnify.stasis != MagickFalse))
15563  {
15564  status=XMakeImage(display,resource_info,&windows->magnify,
15565  display_image,windows->magnify.width,windows->magnify.height);
15566  XMakeMagnifyImage(display,windows);
15567  }
15568  break;
15569  }
15570  if ((windows->magnify.mapped != MagickFalse) &&
15571  (event.xconfigure.window == windows->pan.id))
15572  {
15573  /*
15574  Pan icon window has a new configuration.
15575  */
15576  if (event.xconfigure.send_event != 0)
15577  {
15578  windows->pan.x=event.xconfigure.x;
15579  windows->pan.y=event.xconfigure.y;
15580  }
15581  windows->pan.width=(unsigned int) event.xconfigure.width;
15582  windows->pan.height=(unsigned int) event.xconfigure.height;
15583  break;
15584  }
15585  if (event.xconfigure.window == windows->icon.id)
15586  {
15587  /*
15588  Icon window has a new configuration.
15589  */
15590  windows->icon.width=(unsigned int) event.xconfigure.width;
15591  windows->icon.height=(unsigned int) event.xconfigure.height;
15592  break;
15593  }
15594  break;
15595  }
15596  case DestroyNotify:
15597  {
15598  /*
15599  Group leader has exited.
15600  */
15601  if (resource_info->debug != MagickFalse)
15602  (void) LogMagickEvent(X11Event,GetMagickModule(),
15603  "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15604  if (event.xdestroywindow.window == windows->group_leader.id)
15605  {
15606  *state|=ExitState;
15607  break;
15608  }
15609  break;
15610  }
15611  case EnterNotify:
15612  {
15613  /*
15614  Selectively install colormap.
15615  */
15616  if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15617  if (event.xcrossing.mode != NotifyUngrab)
15618  XInstallColormap(display,map_info->colormap);
15619  break;
15620  }
15621  case Expose:
15622  {
15623  if (resource_info->debug != MagickFalse)
15624  (void) LogMagickEvent(X11Event,GetMagickModule(),
15625  "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15626  event.xexpose.width,event.xexpose.height,event.xexpose.x,
15627  event.xexpose.y);
15628  /*
15629  Refresh windows that are now exposed.
15630  */
15631  if ((event.xexpose.window == windows->image.id) &&
15632  (windows->image.mapped != MagickFalse))
15633  {
15634  XRefreshWindow(display,&windows->image,&event);
15635  delay=display_image->delay/MagickMax(
15636  display_image->ticks_per_second,1L);
15637  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15638  break;
15639  }
15640  if ((event.xexpose.window == windows->magnify.id) &&
15641  (windows->magnify.mapped != MagickFalse))
15642  {
15643  XMakeMagnifyImage(display,windows);
15644  break;
15645  }
15646  if (event.xexpose.window == windows->pan.id)
15647  {
15648  XDrawPanRectangle(display,windows);
15649  break;
15650  }
15651  if (event.xexpose.window == windows->icon.id)
15652  {
15653  XRefreshWindow(display,&windows->icon,&event);
15654  break;
15655  }
15656  break;
15657  }
15658  case KeyPress:
15659  {
15660  int
15661  length;
15662 
15663  /*
15664  Respond to a user key press.
15665  */
15666  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15667  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15668  *(command+length)='\0';
15669  if (resource_info->debug != MagickFalse)
15670  (void) LogMagickEvent(X11Event,GetMagickModule(),
15671  "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15672  key_symbol,command);
15673  if (event.xkey.window == windows->image.id)
15674  {
15675  display_command=XImageWindowCommand(display,resource_info,windows,
15676  event.xkey.state,key_symbol,&display_image);
15677  if (display_command != NullCommand)
15678  nexus=XMagickCommand(display,resource_info,windows,display_command,
15679  &display_image);
15680  }
15681  if (event.xkey.window == windows->magnify.id)
15682  XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15683  if (event.xkey.window == windows->pan.id)
15684  {
15685  if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15686  (void) XWithdrawWindow(display,windows->pan.id,
15687  windows->pan.screen);
15688  else
15689  if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15690  XTextViewHelp(display,resource_info,windows,MagickFalse,
15691  "Help Viewer - Image Pan",ImagePanHelp);
15692  else
15693  XTranslateImage(display,windows,*image,key_symbol);
15694  }
15695  delay=display_image->delay/MagickMax(
15696  display_image->ticks_per_second,1L);
15697  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15698  break;
15699  }
15700  case KeyRelease:
15701  {
15702  /*
15703  Respond to a user key release.
15704  */
15705  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15706  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15707  if (resource_info->debug != MagickFalse)
15708  (void) LogMagickEvent(X11Event,GetMagickModule(),
15709  "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15710  break;
15711  }
15712  case LeaveNotify:
15713  {
15714  /*
15715  Selectively uninstall colormap.
15716  */
15717  if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15718  if (event.xcrossing.mode != NotifyUngrab)
15719  XUninstallColormap(display,map_info->colormap);
15720  break;
15721  }
15722  case MapNotify:
15723  {
15724  if (resource_info->debug != MagickFalse)
15725  (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15726  event.xmap.window);
15727  if (event.xmap.window == windows->backdrop.id)
15728  {
15729  (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15730  CurrentTime);
15731  windows->backdrop.mapped=MagickTrue;
15732  break;
15733  }
15734  if (event.xmap.window == windows->image.id)
15735  {
15736  if (windows->backdrop.id != (Window) NULL)
15737  (void) XInstallColormap(display,map_info->colormap);
15738  if (LocaleCompare(display_image->magick,"LOGO") == 0)
15739  {
15740  if (LocaleCompare(display_image->filename,"LOGO") == 0)
15741  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15742  }
15743  if (((int) windows->image.width < windows->image.ximage->width) ||
15744  ((int) windows->image.height < windows->image.ximage->height))
15745  (void) XMapRaised(display,windows->pan.id);
15746  windows->image.mapped=MagickTrue;
15747  break;
15748  }
15749  if (event.xmap.window == windows->magnify.id)
15750  {
15751  XMakeMagnifyImage(display,windows);
15752  windows->magnify.mapped=MagickTrue;
15753  (void) XWithdrawWindow(display,windows->info.id,
15754  windows->info.screen);
15755  break;
15756  }
15757  if (event.xmap.window == windows->pan.id)
15758  {
15759  XMakePanImage(display,resource_info,windows,display_image);
15760  windows->pan.mapped=MagickTrue;
15761  break;
15762  }
15763  if (event.xmap.window == windows->info.id)
15764  {
15765  windows->info.mapped=MagickTrue;
15766  break;
15767  }
15768  if (event.xmap.window == windows->icon.id)
15769  {
15770  MagickBooleanType
15771  taint;
15772 
15773  /*
15774  Create an icon image.
15775  */
15776  taint=display_image->taint;
15777  XMakeStandardColormap(display,icon_visual,icon_resources,
15778  display_image,icon_map,icon_pixel);
15779  (void) XMakeImage(display,icon_resources,&windows->icon,
15780  display_image,windows->icon.width,windows->icon.height);
15781  display_image->taint=taint;
15782  (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15783  windows->icon.pixmap);
15784  (void) XClearWindow(display,windows->icon.id);
15785  (void) XWithdrawWindow(display,windows->info.id,
15786  windows->info.screen);
15787  windows->icon.mapped=MagickTrue;
15788  break;
15789  }
15790  if (event.xmap.window == windows->command.id)
15791  {
15792  windows->command.mapped=MagickTrue;
15793  break;
15794  }
15795  if (event.xmap.window == windows->popup.id)
15796  {
15797  windows->popup.mapped=MagickTrue;
15798  break;
15799  }
15800  if (event.xmap.window == windows->widget.id)
15801  {
15802  windows->widget.mapped=MagickTrue;
15803  break;
15804  }
15805  break;
15806  }
15807  case MappingNotify:
15808  {
15809  (void) XRefreshKeyboardMapping(&event.xmapping);
15810  break;
15811  }
15812  case NoExpose:
15813  break;
15814  case PropertyNotify:
15815  {
15816  Atom
15817  type;
15818 
15819  int
15820  format,
15821  status;
15822 
15823  unsigned char
15824  *data;
15825 
15826  unsigned long
15827  after,
15828  length;
15829 
15830  if (resource_info->debug != MagickFalse)
15831  (void) LogMagickEvent(X11Event,GetMagickModule(),
15832  "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15833  event.xproperty.atom,event.xproperty.state);
15834  if (event.xproperty.atom != windows->im_remote_command)
15835  break;
15836  /*
15837  Display image named by the remote command protocol.
15838  */
15839  status=XGetWindowProperty(display,event.xproperty.window,
15840  event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15841  AnyPropertyType,&type,&format,&length,&after,&data);
15842  if ((status != Success) || (length == 0))
15843  break;
15844  if (LocaleCompare((char *) data,"-quit") == 0)
15845  {
15846  XClientMessage(display,windows->image.id,windows->im_protocols,
15847  windows->im_exit,CurrentTime);
15848  (void) XFree((void *) data);
15849  break;
15850  }
15851  (void) CopyMagickString(resource_info->image_info->filename,
15852  (char *) data,MaxTextExtent);
15853  (void) XFree((void *) data);
15854  nexus=ReadImage(resource_info->image_info,&display_image->exception);
15855  CatchException(&display_image->exception);
15856  if (nexus != (Image *) NULL)
15857  *state|=NextImageState | ExitState;
15858  break;
15859  }
15860  case ReparentNotify:
15861  {
15862  if (resource_info->debug != MagickFalse)
15863  (void) LogMagickEvent(X11Event,GetMagickModule(),
15864  "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15865  event.xreparent.window);
15866  break;
15867  }
15868  case UnmapNotify:
15869  {
15870  if (resource_info->debug != MagickFalse)
15871  (void) LogMagickEvent(X11Event,GetMagickModule(),
15872  "Unmap Notify: 0x%lx",event.xunmap.window);
15873  if (event.xunmap.window == windows->backdrop.id)
15874  {
15875  windows->backdrop.mapped=MagickFalse;
15876  break;
15877  }
15878  if (event.xunmap.window == windows->image.id)
15879  {
15880  windows->image.mapped=MagickFalse;
15881  break;
15882  }
15883  if (event.xunmap.window == windows->magnify.id)
15884  {
15885  windows->magnify.mapped=MagickFalse;
15886  break;
15887  }
15888  if (event.xunmap.window == windows->pan.id)
15889  {
15890  windows->pan.mapped=MagickFalse;
15891  break;
15892  }
15893  if (event.xunmap.window == windows->info.id)
15894  {
15895  windows->info.mapped=MagickFalse;
15896  break;
15897  }
15898  if (event.xunmap.window == windows->icon.id)
15899  {
15900  if (map_info->colormap == icon_map->colormap)
15901  XConfigureImageColormap(display,resource_info,windows,
15902  display_image);
15903  (void) XFreeStandardColormap(display,icon_visual,icon_map,
15904  icon_pixel);
15905  windows->icon.mapped=MagickFalse;
15906  break;
15907  }
15908  if (event.xunmap.window == windows->command.id)
15909  {
15910  windows->command.mapped=MagickFalse;
15911  break;
15912  }
15913  if (event.xunmap.window == windows->popup.id)
15914  {
15915  if (windows->backdrop.id != (Window) NULL)
15916  (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15917  CurrentTime);
15918  windows->popup.mapped=MagickFalse;
15919  break;
15920  }
15921  if (event.xunmap.window == windows->widget.id)
15922  {
15923  if (windows->backdrop.id != (Window) NULL)
15924  (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15925  CurrentTime);
15926  windows->widget.mapped=MagickFalse;
15927  break;
15928  }
15929  break;
15930  }
15931  default:
15932  {
15933  if (resource_info->debug != MagickFalse)
15934  (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
15935  event.type);
15936  break;
15937  }
15938  }
15939  } while (!(*state & ExitState));
15940  if ((*state & ExitState) == 0)
15941  (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
15942  &display_image);
15943  else
15944  if (resource_info->confirm_edit != MagickFalse)
15945  {
15946  /*
15947  Query user if image has changed.
15948  */
15949  if ((resource_info->immutable == MagickFalse) &&
15950  (display_image->taint != MagickFalse))
15951  {
15952  int
15953  status;
15954 
15955  status=XConfirmWidget(display,windows,"Your image changed.",
15956  "Do you want to save it");
15957  if (status == 0)
15958  *state&=(~ExitState);
15959  else
15960  if (status > 0)
15961  (void) XMagickCommand(display,resource_info,windows,SaveCommand,
15962  &display_image);
15963  }
15964  }
15965  if ((windows->visual_info->klass == GrayScale) ||
15966  (windows->visual_info->klass == PseudoColor) ||
15967  (windows->visual_info->klass == DirectColor))
15968  {
15969  /*
15970  Withdraw pan and Magnify window.
15971  */
15972  if (windows->info.mapped != MagickFalse)
15973  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15974  if (windows->magnify.mapped != MagickFalse)
15975  (void) XWithdrawWindow(display,windows->magnify.id,
15976  windows->magnify.screen);
15977  if (windows->command.mapped != MagickFalse)
15978  (void) XWithdrawWindow(display,windows->command.id,
15979  windows->command.screen);
15980  }
15981  if (windows->pan.mapped != MagickFalse)
15982  (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
15983  if (resource_info->backdrop == MagickFalse)
15984  if (windows->backdrop.mapped)
15985  {
15986  (void) XWithdrawWindow(display,windows->backdrop.id,
15987  windows->backdrop.screen);
15988  (void) XDestroyWindow(display,windows->backdrop.id);
15989  windows->backdrop.id=(Window) NULL;
15990  (void) XWithdrawWindow(display,windows->image.id,
15991  windows->image.screen);
15992  (void) XDestroyWindow(display,windows->image.id);
15993  windows->image.id=(Window) NULL;
15994  }
15995  XSetCursorState(display,windows,MagickTrue);
15996  XCheckRefreshWindows(display,windows);
15997  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
15998  *state&=(~ExitState);
15999  if (*state & ExitState)
16000  {
16001  /*
16002  Free Standard Colormap.
16003  */
16004  (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16005  if (resource_info->map_type == (char *) NULL)
16006  (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16007  /*
16008  Free X resources.
16009  */
16010  if (resource_info->copy_image != (Image *) NULL)
16011  resource_info->copy_image=DestroyImage(resource_info->copy_image);
16012  DestroyXResources();
16013  }
16014  (void) XSync(display,MagickFalse);
16015  /*
16016  Restore our progress monitor and warning handlers.
16017  */
16018  (void) SetErrorHandler(warning_handler);
16019  (void) SetWarningHandler(warning_handler);
16020  /*
16021  Change to home directory.
16022  */
16023  directory=getcwd(working_directory,MaxTextExtent);
16024  (void) directory;
16025  {
16026  int
16027  status;
16028 
16029  if (*resource_info->home_directory == '\0')
16030  (void) CopyMagickString(resource_info->home_directory,".",MaxTextExtent);
16031  status=chdir(resource_info->home_directory);
16032  if (status == -1)
16033  (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
16034  FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
16035  }
16036  *image=display_image;
16037  return(nexus);
16038 }
16039 #else
16040 ␌
16041 /*
16042 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16043 % %
16044 % %
16045 % %
16046 + D i s p l a y I m a g e s %
16047 % %
16048 % %
16049 % %
16050 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16051 %
16052 % DisplayImages() displays an image sequence to any X window screen. It
16053 % returns a value other than 0 if successful. Check the exception member
16054 % of image to determine the reason for any failure.
16055 %
16056 % The format of the DisplayImages method is:
16057 %
16058 % MagickBooleanType DisplayImages(const ImageInfo *image_info,
16059 % Image *images)
16060 %
16061 % A description of each parameter follows:
16062 %
16063 % o image_info: the image info.
16064 %
16065 % o image: the image.
16066 %
16067 */
16068 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16069  Image *image)
16070 {
16071  assert(image_info != (const ImageInfo *) NULL);
16072  assert(image_info->signature == MagickCoreSignature);
16073  assert(image != (Image *) NULL);
16074  assert(image->signature == MagickCoreSignature);
16075  if (IsEventLogging() != MagickFalse)
16076  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16077  (void) image_info;
16078  (void) ThrowMagickException(&image->exception,GetMagickModule(),
16079  MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
16080  image->filename);
16081  return(MagickFalse);
16082 }
16083 ␌
16084 /*
16085 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16086 % %
16087 % %
16088 % %
16089 + R e m o t e D i s p l a y C o m m a n d %
16090 % %
16091 % %
16092 % %
16093 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16094 %
16095 % RemoteDisplayCommand() encourages a remote display program to display the
16096 % specified image filename.
16097 %
16098 % The format of the RemoteDisplayCommand method is:
16099 %
16100 % MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16101 % const char *window,const char *filename,ExceptionInfo *exception)
16102 %
16103 % A description of each parameter follows:
16104 %
16105 % o image_info: the image info.
16106 %
16107 % o window: Specifies the name or id of an X window.
16108 %
16109 % o filename: the name of the image filename to display.
16110 %
16111 % o exception: return any errors or warnings in this structure.
16112 %
16113 */
16114 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16115  const char *window,const char *filename,ExceptionInfo *exception)
16116 {
16117  assert(image_info != (const ImageInfo *) NULL);
16118  assert(image_info->signature == MagickCoreSignature);
16119  assert(filename != (char *) NULL);
16120  if (IsEventLogging() != MagickFalse)
16121  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16122  (void) window;
16123  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16124  "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16125  return(MagickFalse);
16126 }
16127 #endif
Definition: image.h:134