MagickCore  6.9.13-17
Convert, Edit, Or Compose Bitmap Images
display.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % DDDD IIIII SSSSS PPPP L AAA Y Y %
7 % D D I SS P P L A A Y Y %
8 % D D I SSS PPPP L AAAAA Y %
9 % D D I SS P L A A Y %
10 % DDDD IIIII SSSSS P LLLLL A A Y %
11 % %
12 % %
13 % MagickCore Methods to Interactively Display and Edit an Image %
14 % %
15 % Software Design %
16 % Cristy %
17 % July 1992 %
18 % %
19 % %
20 % Copyright 1999 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
22 % %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
25 % %
26 % https://imagemagick.org/script/license.php %
27 % %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
33 % %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 ␌
39 /*
40  Include declarations.
41 */
42 #include "magick/studio.h"
43 #include "magick/artifact.h"
44 #include "magick/attribute.h"
45 #include "magick/blob.h"
46 #include "magick/cache.h"
47 #include "magick/channel.h"
48 #include "magick/client.h"
49 #include "magick/color.h"
50 #include "magick/colorspace.h"
51 #include "magick/composite.h"
52 #include "magick/constitute.h"
53 #include "magick/decorate.h"
54 #include "magick/delegate.h"
55 #include "magick/display.h"
56 #include "magick/display-private.h"
57 #include "magick/distort.h"
58 #include "magick/draw.h"
59 #include "magick/effect.h"
60 #include "magick/enhance.h"
61 #include "magick/exception.h"
62 #include "magick/exception-private.h"
63 #include "magick/fx.h"
64 #include "magick/geometry.h"
65 #include "magick/image.h"
66 #include "magick/image-private.h"
67 #include "magick/list.h"
68 #include "magick/locale-private.h"
69 #include "magick/log.h"
70 #include "magick/magick.h"
71 #include "magick/memory_.h"
72 #include "magick/monitor.h"
73 #include "magick/monitor-private.h"
74 #include "magick/montage.h"
75 #include "magick/nt-base-private.h"
76 #include "magick/option.h"
77 #include "magick/paint.h"
78 #include "magick/pixel.h"
79 #include "magick/pixel-private.h"
80 #include "magick/property.h"
81 #include "magick/quantum.h"
82 #include "magick/resize.h"
83 #include "magick/resource_.h"
84 #include "magick/shear.h"
85 #include "magick/segment.h"
86 #include "magick/statistic.h"
87 #include "magick/string_.h"
88 #include "magick/string-private.h"
89 #include "magick/timer-private.h"
90 #include "magick/transform.h"
91 #include "magick/threshold.h"
92 #include "magick/utility.h"
93 #include "magick/utility-private.h"
94 #include "magick/version.h"
95 #include "magick/visual-effects.h"
96 #include "magick/widget.h"
97 #include "magick/xwindow-private.h"
98 ␌
99 #if defined(MAGICKCORE_X11_DELEGATE)
100 /*
101  Define declarations.
102 */
103 #define MaxColors MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
104 ␌
105 /*
106  Constant declarations.
107 */
108 static const unsigned char
109  HighlightBitmap[8] =
110  {
111  0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
112  },
113  OpaqueBitmap[8] =
114  {
115  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
116  },
117  ShadowBitmap[8] =
118  {
119  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
120  };
121 ␌
122 /*
123  Help widget declarations.
124 */
125 static const char
126  ImageAnnotateHelp[] =
127  {
128  "In annotate mode, the Command widget has these options:\n"
129  "\n"
130  " Font Name\n"
131  " fixed\n"
132  " variable\n"
133  " 5x8\n"
134  " 6x10\n"
135  " 7x13bold\n"
136  " 8x13bold\n"
137  " 9x15bold\n"
138  " 10x20\n"
139  " 12x24\n"
140  " Browser...\n"
141  " Font Color\n"
142  " black\n"
143  " blue\n"
144  " cyan\n"
145  " green\n"
146  " gray\n"
147  " red\n"
148  " magenta\n"
149  " yellow\n"
150  " white\n"
151  " transparent\n"
152  " Browser...\n"
153  " Font Color\n"
154  " black\n"
155  " blue\n"
156  " cyan\n"
157  " green\n"
158  " gray\n"
159  " red\n"
160  " magenta\n"
161  " yellow\n"
162  " white\n"
163  " transparent\n"
164  " Browser...\n"
165  " Rotate Text\n"
166  " -90\n"
167  " -45\n"
168  " -30\n"
169  " 0\n"
170  " 30\n"
171  " 45\n"
172  " 90\n"
173  " 180\n"
174  " Dialog...\n"
175  " Help\n"
176  " Dismiss\n"
177  "\n"
178  "Choose a font name from the Font Name sub-menu. Additional\n"
179  "font names can be specified with the font browser. You can\n"
180  "change the menu names by setting the X resources font1\n"
181  "through font9.\n"
182  "\n"
183  "Choose a font color from the Font Color sub-menu.\n"
184  "Additional font colors can be specified with the color\n"
185  "browser. You can change the menu colors by setting the X\n"
186  "resources pen1 through pen9.\n"
187  "\n"
188  "If you select the color browser and press Grab, you can\n"
189  "choose the font color by moving the pointer to the desired\n"
190  "color on the screen and press any button.\n"
191  "\n"
192  "If you choose to rotate the text, choose Rotate Text from the\n"
193  "menu and select an angle. Typically you will only want to\n"
194  "rotate one line of text at a time. Depending on the angle you\n"
195  "choose, subsequent lines may end up overwriting each other.\n"
196  "\n"
197  "Choosing a font and its color is optional. The default font\n"
198  "is fixed and the default color is black. However, you must\n"
199  "choose a location to begin entering text and press button 1.\n"
200  "An underscore character will appear at the location of the\n"
201  "pointer. The cursor changes to a pencil to indicate you are\n"
202  "in text mode. To exit immediately, press Dismiss.\n"
203  "\n"
204  "In text mode, any key presses will display the character at\n"
205  "the location of the underscore and advance the underscore\n"
206  "cursor. Enter your text and once completed press Apply to\n"
207  "finish your image annotation. To correct errors press BACK\n"
208  "SPACE. To delete an entire line of text, press DELETE. Any\n"
209  "text that exceeds the boundaries of the image window is\n"
210  "automagically continued onto the next line.\n"
211  "\n"
212  "The actual color you request for the font is saved in the\n"
213  "image. However, the color that appears in your image window\n"
214  "may be different. For example, on a monochrome screen the\n"
215  "text will appear black or white even if you choose the color\n"
216  "red as the font color. However, the image saved to a file\n"
217  "with -write is written with red lettering. To assure the\n"
218  "correct color text in the final image, any PseudoClass image\n"
219  "is promoted to DirectClass (see miff(5)). To force a\n"
220  "PseudoClass image to remain PseudoClass, use -colors.\n"
221  },
222  ImageChopHelp[] =
223  {
224  "In chop mode, the Command widget has these options:\n"
225  "\n"
226  " Direction\n"
227  " horizontal\n"
228  " vertical\n"
229  " Help\n"
230  " Dismiss\n"
231  "\n"
232  "If the you choose the horizontal direction (this the\n"
233  "default), the area of the image between the two horizontal\n"
234  "endpoints of the chop line is removed. Otherwise, the area\n"
235  "of the image between the two vertical endpoints of the chop\n"
236  "line is removed.\n"
237  "\n"
238  "Select a location within the image window to begin your chop,\n"
239  "press and hold any button. Next, move the pointer to\n"
240  "another location in the image. As you move a line will\n"
241  "connect the initial location and the pointer. When you\n"
242  "release the button, the area within the image to chop is\n"
243  "determined by which direction you choose from the Command\n"
244  "widget.\n"
245  "\n"
246  "To cancel the image chopping, move the pointer back to the\n"
247  "starting point of the line and release the button.\n"
248  },
249  ImageColorEditHelp[] =
250  {
251  "In color edit mode, the Command widget has these options:\n"
252  "\n"
253  " Method\n"
254  " point\n"
255  " replace\n"
256  " floodfill\n"
257  " filltoborder\n"
258  " reset\n"
259  " Pixel Color\n"
260  " black\n"
261  " blue\n"
262  " cyan\n"
263  " green\n"
264  " gray\n"
265  " red\n"
266  " magenta\n"
267  " yellow\n"
268  " white\n"
269  " Browser...\n"
270  " Border Color\n"
271  " black\n"
272  " blue\n"
273  " cyan\n"
274  " green\n"
275  " gray\n"
276  " red\n"
277  " magenta\n"
278  " yellow\n"
279  " white\n"
280  " Browser...\n"
281  " Fuzz\n"
282  " 0%\n"
283  " 2%\n"
284  " 5%\n"
285  " 10%\n"
286  " 15%\n"
287  " Dialog...\n"
288  " Undo\n"
289  " Help\n"
290  " Dismiss\n"
291  "\n"
292  "Choose a color editing method from the Method sub-menu\n"
293  "of the Command widget. The point method recolors any pixel\n"
294  "selected with the pointer until the button is released. The\n"
295  "replace method recolors any pixel that matches the color of\n"
296  "the pixel you select with a button press. Floodfill recolors\n"
297  "any pixel that matches the color of the pixel you select with\n"
298  "a button press and is a neighbor. Whereas filltoborder recolors\n"
299  "any neighbor pixel that is not the border color. Finally reset\n"
300  "changes the entire image to the designated color.\n"
301  "\n"
302  "Next, choose a pixel color from the Pixel Color sub-menu.\n"
303  "Additional pixel colors can be specified with the color\n"
304  "browser. You can change the menu colors by setting the X\n"
305  "resources pen1 through pen9.\n"
306  "\n"
307  "Now press button 1 to select a pixel within the image window\n"
308  "to change its color. Additional pixels may be recolored as\n"
309  "prescribed by the method you choose.\n"
310  "\n"
311  "If the Magnify widget is mapped, it can be helpful in positioning\n"
312  "your pointer within the image (refer to button 2).\n"
313  "\n"
314  "The actual color you request for the pixels is saved in the\n"
315  "image. However, the color that appears in your image window\n"
316  "may be different. For example, on a monochrome screen the\n"
317  "pixel will appear black or white even if you choose the\n"
318  "color red as the pixel color. However, the image saved to a\n"
319  "file with -write is written with red pixels. To assure the\n"
320  "correct color text in the final image, any PseudoClass image\n"
321  "is promoted to DirectClass (see miff(5)). To force a\n"
322  "PseudoClass image to remain PseudoClass, use -colors.\n"
323  },
324  ImageCompositeHelp[] =
325  {
326  "First a widget window is displayed requesting you to enter an\n"
327  "image name. Press Composite, Grab or type a file name.\n"
328  "Press Cancel if you choose not to create a composite image.\n"
329  "When you choose Grab, move the pointer to the desired window\n"
330  "and press any button.\n"
331  "\n"
332  "If the Composite image does not have any matte information,\n"
333  "you are informed and the file browser is displayed again.\n"
334  "Enter the name of a mask image. The image is typically\n"
335  "grayscale and the same size as the composite image. If the\n"
336  "image is not grayscale, it is converted to grayscale and the\n"
337  "resulting intensities are used as matte information.\n"
338  "\n"
339  "A small window appears showing the location of the cursor in\n"
340  "the image window. You are now in composite mode. To exit\n"
341  "immediately, press Dismiss. In composite mode, the Command\n"
342  "widget has these options:\n"
343  "\n"
344  " Operators\n"
345  " Over\n"
346  " In\n"
347  " Out\n"
348  " Atop\n"
349  " Xor\n"
350  " Plus\n"
351  " Minus\n"
352  " Add\n"
353  " Subtract\n"
354  " Difference\n"
355  " Multiply\n"
356  " Bumpmap\n"
357  " Copy\n"
358  " CopyRed\n"
359  " CopyGreen\n"
360  " CopyBlue\n"
361  " CopyOpacity\n"
362  " Clear\n"
363  " Dissolve\n"
364  " Displace\n"
365  " Help\n"
366  " Dismiss\n"
367  "\n"
368  "Choose a composite operation from the Operators sub-menu of\n"
369  "the Command widget. How each operator behaves is described\n"
370  "below. Image window is the image currently displayed on\n"
371  "your X server and image is the image obtained with the File\n"
372  "Browser widget.\n"
373  "\n"
374  "Over The result is the union of the two image shapes,\n"
375  " with image obscuring image window in the region of\n"
376  " overlap.\n"
377  "\n"
378  "In The result is simply image cut by the shape of\n"
379  " image window. None of the image data of image\n"
380  " window is in the result.\n"
381  "\n"
382  "Out The resulting image is image with the shape of\n"
383  " image window cut out.\n"
384  "\n"
385  "Atop The result is the same shape as the image window,\n"
386  " with image obscuring image window where the image\n"
387  " shapes overlap. Note this differs from over\n"
388  " because the portion of image outside image window's\n"
389  " shape does not appear in the result.\n"
390  "\n"
391  "Xor The result is the image data from both image and\n"
392  " image window that is outside the overlap region.\n"
393  " The overlap region is blank.\n"
394  "\n"
395  "Plus The result is just the sum of the image data.\n"
396  " Output values are cropped to QuantumRange (no overflow).\n"
397  "\n"
398  "Minus The result of image - image window, with underflow\n"
399  " cropped to zero.\n"
400  "\n"
401  "Add The result of image + image window, with overflow\n"
402  " wrapping around (mod 256).\n"
403  "\n"
404  "Subtract The result of image - image window, with underflow\n"
405  " wrapping around (mod 256). The add and subtract\n"
406  " operators can be used to perform reversible\n"
407  " transformations.\n"
408  "\n"
409  "Difference\n"
410  " The result of abs(image - image window). This\n"
411  " useful for comparing two very similar images.\n"
412  "\n"
413  "Multiply\n"
414  " The result of image * image window. This\n"
415  " useful for the creation of drop-shadows.\n"
416  "\n"
417  "Bumpmap The result of surface normals from image * image\n"
418  " window.\n"
419  "\n"
420  "Copy The resulting image is image window replaced with\n"
421  " image. Here the matte information is ignored.\n"
422  "\n"
423  "CopyRed The red layer of the image window is replace with\n"
424  " the red layer of the image. The other layers are\n"
425  " untouched.\n"
426  "\n"
427  "CopyGreen\n"
428  " The green layer of the image window is replace with\n"
429  " the green layer of the image. The other layers are\n"
430  " untouched.\n"
431  "\n"
432  "CopyBlue The blue layer of the image window is replace with\n"
433  " the blue layer of the image. The other layers are\n"
434  " untouched.\n"
435  "\n"
436  "CopyOpacity\n"
437  " The matte layer of the image window is replace with\n"
438  " the matte layer of the image. The other layers are\n"
439  " untouched.\n"
440  "\n"
441  "The image compositor requires a matte, or alpha channel in\n"
442  "the image for some operations. This extra channel usually\n"
443  "defines a mask which represents a sort of a cookie-cutter\n"
444  "for the image. This the case when matte is opaque (full\n"
445  "coverage) for pixels inside the shape, zero outside, and\n"
446  "between 0 and QuantumRange on the boundary. If image does not\n"
447  "have a matte channel, it is initialized with 0 for any pixel\n"
448  "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
449  "\n"
450  "If you choose Dissolve, the composite operator becomes Over. The\n"
451  "image matte channel percent transparency is initialized to factor.\n"
452  "The image window is initialized to (100-factor). Where factor is the\n"
453  "value you specify in the Dialog widget.\n"
454  "\n"
455  "Displace shifts the image pixels as defined by a displacement\n"
456  "map. With this option, image is used as a displacement map.\n"
457  "Black, within the displacement map, is a maximum positive\n"
458  "displacement. White is a maximum negative displacement and\n"
459  "middle gray is neutral. The displacement is scaled to determine\n"
460  "the pixel shift. By default, the displacement applies in both the\n"
461  "horizontal and vertical directions. However, if you specify a mask,\n"
462  "image is the horizontal X displacement and mask the vertical Y\n"
463  "displacement.\n"
464  "\n"
465  "Note that matte information for image window is not retained\n"
466  "for colormapped X server visuals (e.g. StaticColor,\n"
467  "StaticColor, GrayScale, PseudoColor). Correct compositing\n"
468  "behavior may require a TrueColor or DirectColor visual or a\n"
469  "Standard Colormap.\n"
470  "\n"
471  "Choosing a composite operator is optional. The default\n"
472  "operator is replace. However, you must choose a location to\n"
473  "composite your image and press button 1. Press and hold the\n"
474  "button before releasing and an outline of the image will\n"
475  "appear to help you identify your location.\n"
476  "\n"
477  "The actual colors of the composite image is saved. However,\n"
478  "the color that appears in image window may be different.\n"
479  "For example, on a monochrome screen image window will appear\n"
480  "black or white even though your composited image may have\n"
481  "many colors. If the image is saved to a file it is written\n"
482  "with the correct colors. To assure the correct colors are\n"
483  "saved in the final image, any PseudoClass image is promoted\n"
484  "to DirectClass (see miff(5)). To force a PseudoClass image\n"
485  "to remain PseudoClass, use -colors.\n"
486  },
487  ImageCutHelp[] =
488  {
489  "In cut mode, the Command widget has these options:\n"
490  "\n"
491  " Help\n"
492  " Dismiss\n"
493  "\n"
494  "To define a cut region, press button 1 and drag. The\n"
495  "cut region is defined by a highlighted rectangle that\n"
496  "expands or contracts as it follows the pointer. Once you\n"
497  "are satisfied with the cut region, release the button.\n"
498  "You are now in rectify mode. In rectify mode, the Command\n"
499  "widget has these options:\n"
500  "\n"
501  " Cut\n"
502  " Help\n"
503  " Dismiss\n"
504  "\n"
505  "You can make adjustments by moving the pointer to one of the\n"
506  "cut rectangle corners, pressing a button, and dragging.\n"
507  "Finally, press Cut to commit your copy region. To\n"
508  "exit without cutting the image, press Dismiss.\n"
509  },
510  ImageCopyHelp[] =
511  {
512  "In copy mode, the Command widget has these options:\n"
513  "\n"
514  " Help\n"
515  " Dismiss\n"
516  "\n"
517  "To define a copy region, press button 1 and drag. The\n"
518  "copy region is defined by a highlighted rectangle that\n"
519  "expands or contracts as it follows the pointer. Once you\n"
520  "are satisfied with the copy region, release the button.\n"
521  "You are now in rectify mode. In rectify mode, the Command\n"
522  "widget has these options:\n"
523  "\n"
524  " Copy\n"
525  " Help\n"
526  " Dismiss\n"
527  "\n"
528  "You can make adjustments by moving the pointer to one of the\n"
529  "copy rectangle corners, pressing a button, and dragging.\n"
530  "Finally, press Copy to commit your copy region. To\n"
531  "exit without copying the image, press Dismiss.\n"
532  },
533  ImageCropHelp[] =
534  {
535  "In crop mode, the Command widget has these options:\n"
536  "\n"
537  " Help\n"
538  " Dismiss\n"
539  "\n"
540  "To define a cropping region, press button 1 and drag. The\n"
541  "cropping region is defined by a highlighted rectangle that\n"
542  "expands or contracts as it follows the pointer. Once you\n"
543  "are satisfied with the cropping region, release the button.\n"
544  "You are now in rectify mode. In rectify mode, the Command\n"
545  "widget has these options:\n"
546  "\n"
547  " Crop\n"
548  " Help\n"
549  " Dismiss\n"
550  "\n"
551  "You can make adjustments by moving the pointer to one of the\n"
552  "cropping rectangle corners, pressing a button, and dragging.\n"
553  "Finally, press Crop to commit your cropping region. To\n"
554  "exit without cropping the image, press Dismiss.\n"
555  },
556  ImageDrawHelp[] =
557  {
558  "The cursor changes to a crosshair to indicate you are in\n"
559  "draw mode. To exit immediately, press Dismiss. In draw mode,\n"
560  "the Command widget has these options:\n"
561  "\n"
562  " Element\n"
563  " point\n"
564  " line\n"
565  " rectangle\n"
566  " fill rectangle\n"
567  " circle\n"
568  " fill circle\n"
569  " ellipse\n"
570  " fill ellipse\n"
571  " polygon\n"
572  " fill polygon\n"
573  " Color\n"
574  " black\n"
575  " blue\n"
576  " cyan\n"
577  " green\n"
578  " gray\n"
579  " red\n"
580  " magenta\n"
581  " yellow\n"
582  " white\n"
583  " transparent\n"
584  " Browser...\n"
585  " Stipple\n"
586  " Brick\n"
587  " Diagonal\n"
588  " Scales\n"
589  " Vertical\n"
590  " Wavy\n"
591  " Translucent\n"
592  " Opaque\n"
593  " Open...\n"
594  " Width\n"
595  " 1\n"
596  " 2\n"
597  " 4\n"
598  " 8\n"
599  " 16\n"
600  " Dialog...\n"
601  " Undo\n"
602  " Help\n"
603  " Dismiss\n"
604  "\n"
605  "Choose a drawing primitive from the Element sub-menu.\n"
606  "\n"
607  "Choose a color from the Color sub-menu. Additional\n"
608  "colors can be specified with the color browser.\n"
609  "\n"
610  "If you choose the color browser and press Grab, you can\n"
611  "select the color by moving the pointer to the desired\n"
612  "color on the screen and press any button. The transparent\n"
613  "color updates the image matte channel and is useful for\n"
614  "image compositing.\n"
615  "\n"
616  "Choose a stipple, if appropriate, from the Stipple sub-menu.\n"
617  "Additional stipples can be specified with the file browser.\n"
618  "Stipples obtained from the file browser must be on disk in the\n"
619  "X11 bitmap format.\n"
620  "\n"
621  "Choose a width, if appropriate, from the Width sub-menu. To\n"
622  "choose a specific width select the Dialog widget.\n"
623  "\n"
624  "Choose a point in the Image window and press button 1 and\n"
625  "hold. Next, move the pointer to another location in the\n"
626  "image. As you move, a line connects the initial location and\n"
627  "the pointer. When you release the button, the image is\n"
628  "updated with the primitive you just drew. For polygons, the\n"
629  "image is updated when you press and release the button without\n"
630  "moving the pointer.\n"
631  "\n"
632  "To cancel image drawing, move the pointer back to the\n"
633  "starting point of the line and release the button.\n"
634  },
635  DisplayHelp[] =
636  {
637  "BUTTONS\n"
638  " The effects of each button press is described below. Three\n"
639  " buttons are required. If you have a two button mouse,\n"
640  " button 1 and 3 are returned. Press ALT and button 3 to\n"
641  " simulate button 2.\n"
642  "\n"
643  " 1 Press this button to map or unmap the Command widget.\n"
644  "\n"
645  " 2 Press and drag to define a region of the image to\n"
646  " magnify.\n"
647  "\n"
648  " 3 Press and drag to choose from a select set of commands.\n"
649  " This button behaves differently if the image being\n"
650  " displayed is a visual image directory. Here, choose a\n"
651  " particular tile of the directory and press this button and\n"
652  " drag to select a command from a pop-up menu. Choose from\n"
653  " these menu items:\n"
654  "\n"
655  " Open\n"
656  " Next\n"
657  " Former\n"
658  " Delete\n"
659  " Update\n"
660  "\n"
661  " If you choose Open, the image represented by the tile is\n"
662  " displayed. To return to the visual image directory, choose\n"
663  " Next from the Command widget. Next and Former moves to the\n"
664  " next or former image respectively. Choose Delete to delete\n"
665  " a particular image tile. Finally, choose Update to\n"
666  " synchronize all the image tiles with their respective\n"
667  " images.\n"
668  "\n"
669  "COMMAND WIDGET\n"
670  " The Command widget lists a number of sub-menus and commands.\n"
671  " They are\n"
672  "\n"
673  " File\n"
674  " Open...\n"
675  " Next\n"
676  " Former\n"
677  " Select...\n"
678  " Save...\n"
679  " Print...\n"
680  " Delete...\n"
681  " New...\n"
682  " Visual Directory...\n"
683  " Quit\n"
684  " Edit\n"
685  " Undo\n"
686  " Redo\n"
687  " Cut\n"
688  " Copy\n"
689  " Paste\n"
690  " View\n"
691  " Half Size\n"
692  " Original Size\n"
693  " Double Size\n"
694  " Resize...\n"
695  " Apply\n"
696  " Refresh\n"
697  " Restore\n"
698  " Transform\n"
699  " Crop\n"
700  " Chop\n"
701  " Flop\n"
702  " Flip\n"
703  " Rotate Right\n"
704  " Rotate Left\n"
705  " Rotate...\n"
706  " Shear...\n"
707  " Roll...\n"
708  " Trim Edges\n"
709  " Enhance\n"
710  " Brightness...\n"
711  " Saturation...\n"
712  " Hue...\n"
713  " Gamma...\n"
714  " Sharpen...\n"
715  " Dull\n"
716  " Contrast Stretch...\n"
717  " Sigmoidal Contrast...\n"
718  " Normalize\n"
719  " Equalize\n"
720  " Negate\n"
721  " Grayscale\n"
722  " Map...\n"
723  " Quantize...\n"
724  " Effects\n"
725  " Despeckle\n"
726  " Emboss\n"
727  " Reduce Noise\n"
728  " Add Noise\n"
729  " Sharpen...\n"
730  " Blur...\n"
731  " Threshold...\n"
732  " Edge Detect...\n"
733  " Spread...\n"
734  " Shade...\n"
735  " Painting...\n"
736  " Segment...\n"
737  " F/X\n"
738  " Solarize...\n"
739  " Sepia Tone...\n"
740  " Swirl...\n"
741  " Implode...\n"
742  " Vignette...\n"
743  " Wave...\n"
744  " Oil Painting...\n"
745  " Charcoal Drawing...\n"
746  " Image Edit\n"
747  " Annotate...\n"
748  " Draw...\n"
749  " Color...\n"
750  " Matte...\n"
751  " Composite...\n"
752  " Add Border...\n"
753  " Add Frame...\n"
754  " Comment...\n"
755  " Launch...\n"
756  " Region of Interest...\n"
757  " Miscellany\n"
758  " Image Info\n"
759  " Zoom Image\n"
760  " Show Preview...\n"
761  " Show Histogram\n"
762  " Show Matte\n"
763  " Background...\n"
764  " Slide Show\n"
765  " Preferences...\n"
766  " Help\n"
767  " Overview\n"
768  " Browse Documentation\n"
769  " About Display\n"
770  "\n"
771  " Menu items with a indented triangle have a sub-menu. They\n"
772  " are represented above as the indented items. To access a\n"
773  " sub-menu item, move the pointer to the appropriate menu and\n"
774  " press a button and drag. When you find the desired sub-menu\n"
775  " item, release the button and the command is executed. Move\n"
776  " the pointer away from the sub-menu if you decide not to\n"
777  " execute a particular command.\n"
778  "\n"
779  "KEYBOARD ACCELERATORS\n"
780  " Accelerators are one or two key presses that effect a\n"
781  " particular command. The keyboard accelerators that\n"
782  " display(1) understands is:\n"
783  "\n"
784  " Ctl+O Press to open an image from a file.\n"
785  "\n"
786  " space Press to display the next image.\n"
787  "\n"
788  " If the image is a multi-paged document such as a Postscript\n"
789  " document, you can skip ahead several pages by preceding\n"
790  " this command with a number. For example to display the\n"
791  " third page beyond the current page, press 3<space>.\n"
792  "\n"
793  " backspace Press to display the former image.\n"
794  "\n"
795  " If the image is a multi-paged document such as a Postscript\n"
796  " document, you can skip behind several pages by preceding\n"
797  " this command with a number. For example to display the\n"
798  " third page preceding the current page, press 3<backspace>.\n"
799  "\n"
800  " Ctl+S Press to write the image to a file.\n"
801  "\n"
802  " Ctl+P Press to print the image to a Postscript printer.\n"
803  "\n"
804  " Ctl+D Press to delete an image file.\n"
805  "\n"
806  " Ctl+N Press to create a blank canvas.\n"
807  "\n"
808  " Ctl+Q Press to discard all images and exit program.\n"
809  "\n"
810  " Ctl+Z Press to undo last image transformation.\n"
811  "\n"
812  " Ctl+R Press to redo last image transformation.\n"
813  "\n"
814  " Ctl+X Press to cut a region of the image.\n"
815  "\n"
816  " Ctl+C Press to copy a region of the image.\n"
817  "\n"
818  " Ctl+V Press to paste a region to the image.\n"
819  "\n"
820  " < Press to half the image size.\n"
821  "\n"
822  " - Press to return to the original image size.\n"
823  "\n"
824  " > Press to double the image size.\n"
825  "\n"
826  " % Press to resize the image to a width and height you\n"
827  " specify.\n"
828  "\n"
829  "Cmd-A Press to make any image transformations permanent."
830  "\n"
831  " By default, any image size transformations are applied\n"
832  " to the original image to create the image displayed on\n"
833  " the X server. However, the transformations are not\n"
834  " permanent (i.e. the original image does not change\n"
835  " size only the X image does). For example, if you\n"
836  " press > the X image will appear to double in size,\n"
837  " but the original image will in fact remain the same size.\n"
838  " To force the original image to double in size, press >\n"
839  " followed by Cmd-A.\n"
840  "\n"
841  " @ Press to refresh the image window.\n"
842  "\n"
843  " C Press to cut out a rectangular region of the image.\n"
844  "\n"
845  " [ Press to chop the image.\n"
846  "\n"
847  " H Press to flop image in the horizontal direction.\n"
848  "\n"
849  " V Press to flip image in the vertical direction.\n"
850  "\n"
851  " / Press to rotate the image 90 degrees clockwise.\n"
852  "\n"
853  " \\ Press to rotate the image 90 degrees counter-clockwise.\n"
854  "\n"
855  " * Press to rotate the image the number of degrees you\n"
856  " specify.\n"
857  "\n"
858  " S Press to shear the image the number of degrees you\n"
859  " specify.\n"
860  "\n"
861  " R Press to roll the image.\n"
862  "\n"
863  " T Press to trim the image edges.\n"
864  "\n"
865  " Shft-H Press to vary the image hue.\n"
866  "\n"
867  " Shft-S Press to vary the color saturation.\n"
868  "\n"
869  " Shft-L Press to vary the color brightness.\n"
870  "\n"
871  " Shft-G Press to gamma correct the image.\n"
872  "\n"
873  " Shft-C Press to sharpen the image contrast.\n"
874  "\n"
875  " Shft-Z Press to dull the image contrast.\n"
876  "\n"
877  " = Press to perform histogram equalization on the image.\n"
878  "\n"
879  " Shft-N Press to perform histogram normalization on the image.\n"
880  "\n"
881  " Shft-~ Press to negate the colors of the image.\n"
882  "\n"
883  " . Press to convert the image colors to gray.\n"
884  "\n"
885  " Shft-# Press to set the maximum number of unique colors in the\n"
886  " image.\n"
887  "\n"
888  " F2 Press to reduce the speckles in an image.\n"
889  "\n"
890  " F3 Press to eliminate peak noise from an image.\n"
891  "\n"
892  " F4 Press to add noise to an image.\n"
893  "\n"
894  " F5 Press to sharpen an image.\n"
895  "\n"
896  " F6 Press to delete an image file.\n"
897  "\n"
898  " F7 Press to threshold the image.\n"
899  "\n"
900  " F8 Press to detect edges within an image.\n"
901  "\n"
902  " F9 Press to emboss an image.\n"
903  "\n"
904  " F10 Press to displace pixels by a random amount.\n"
905  "\n"
906  " F11 Press to negate all pixels above the threshold level.\n"
907  "\n"
908  " F12 Press to shade the image using a distant light source.\n"
909  "\n"
910  " F13 Press to lighten or darken image edges to create a 3-D effect.\n"
911  "\n"
912  " F14 Press to segment the image by color.\n"
913  "\n"
914  " Meta-S Press to swirl image pixels about the center.\n"
915  "\n"
916  " Meta-I Press to implode image pixels about the center.\n"
917  "\n"
918  " Meta-W Press to alter an image along a sine wave.\n"
919  "\n"
920  " Meta-P Press to simulate an oil painting.\n"
921  "\n"
922  " Meta-C Press to simulate a charcoal drawing.\n"
923  "\n"
924  " Alt-A Press to annotate the image with text.\n"
925  "\n"
926  " Alt-D Press to draw on an image.\n"
927  "\n"
928  " Alt-P Press to edit an image pixel color.\n"
929  "\n"
930  " Alt-M Press to edit the image matte information.\n"
931  "\n"
932  " Alt-V Press to composite the image with another.\n"
933  "\n"
934  " Alt-B Press to add a border to the image.\n"
935  "\n"
936  " Alt-F Press to add an ornamental border to the image.\n"
937  "\n"
938  " Alt-Shft-!\n"
939  " Press to add an image comment.\n"
940  "\n"
941  " Ctl-A Press to apply image processing techniques to a region\n"
942  " of interest.\n"
943  "\n"
944  " Shft-? Press to display information about the image.\n"
945  "\n"
946  " Shft-+ Press to map the zoom image window.\n"
947  "\n"
948  " Shft-P Press to preview an image enhancement, effect, or f/x.\n"
949  "\n"
950  " F1 Press to display helpful information about display(1).\n"
951  "\n"
952  " Find Press to browse documentation about ImageMagick.\n"
953  "\n"
954  " 1-9 Press to change the level of magnification.\n"
955  "\n"
956  " Use the arrow keys to move the image one pixel up, down,\n"
957  " left, or right within the magnify window. Be sure to first\n"
958  " map the magnify window by pressing button 2.\n"
959  "\n"
960  " Press ALT and one of the arrow keys to trim off one pixel\n"
961  " from any side of the image.\n"
962  },
963  ImageMatteEditHelp[] =
964  {
965  "Matte information within an image is useful for some\n"
966  "operations such as image compositing (See IMAGE\n"
967  "COMPOSITING). This extra channel usually defines a mask\n"
968  "which represents a sort of a cookie-cutter for the image.\n"
969  "This the case when matte is opaque (full coverage) for\n"
970  "pixels inside the shape, zero outside, and between 0 and\n"
971  "QuantumRange on the boundary.\n"
972  "\n"
973  "A small window appears showing the location of the cursor in\n"
974  "the image window. You are now in matte edit mode. To exit\n"
975  "immediately, press Dismiss. In matte edit mode, the Command\n"
976  "widget has these options:\n"
977  "\n"
978  " Method\n"
979  " point\n"
980  " replace\n"
981  " floodfill\n"
982  " filltoborder\n"
983  " reset\n"
984  " Border Color\n"
985  " black\n"
986  " blue\n"
987  " cyan\n"
988  " green\n"
989  " gray\n"
990  " red\n"
991  " magenta\n"
992  " yellow\n"
993  " white\n"
994  " Browser...\n"
995  " Fuzz\n"
996  " 0%\n"
997  " 2%\n"
998  " 5%\n"
999  " 10%\n"
1000  " 15%\n"
1001  " Dialog...\n"
1002  " Matte\n"
1003  " Opaque\n"
1004  " Transparent\n"
1005  " Dialog...\n"
1006  " Undo\n"
1007  " Help\n"
1008  " Dismiss\n"
1009  "\n"
1010  "Choose a matte editing method from the Method sub-menu of\n"
1011  "the Command widget. The point method changes the matte value\n"
1012  "of any pixel selected with the pointer until the button is\n"
1013  "is released. The replace method changes the matte value of\n"
1014  "any pixel that matches the color of the pixel you select with\n"
1015  "a button press. Floodfill changes the matte value of any pixel\n"
1016  "that matches the color of the pixel you select with a button\n"
1017  "press and is a neighbor. Whereas filltoborder changes the matte\n"
1018  "value any neighbor pixel that is not the border color. Finally\n"
1019  "reset changes the entire image to the designated matte value.\n"
1020  "\n"
1021  "Choose Matte Value and pick Opaque or Transparent. For other values\n"
1022  "select the Dialog entry. Here a dialog appears requesting a matte\n"
1023  "value. The value you select is assigned as the opacity value of the\n"
1024  "selected pixel or pixels.\n"
1025  "\n"
1026  "Now, press any button to select a pixel within the image\n"
1027  "window to change its matte value.\n"
1028  "\n"
1029  "If the Magnify widget is mapped, it can be helpful in positioning\n"
1030  "your pointer within the image (refer to button 2).\n"
1031  "\n"
1032  "Matte information is only valid in a DirectClass image.\n"
1033  "Therefore, any PseudoClass image is promoted to DirectClass\n"
1034  "(see miff(5)). Note that matte information for PseudoClass\n"
1035  "is not retained for colormapped X server visuals (e.g.\n"
1036  "StaticColor, StaticColor, GrayScale, PseudoColor) unless you\n"
1037  "immediately save your image to a file (refer to Write).\n"
1038  "Correct matte editing behavior may require a TrueColor or\n"
1039  "DirectColor visual or a Standard Colormap.\n"
1040  },
1041  ImagePanHelp[] =
1042  {
1043  "When an image exceeds the width or height of the X server\n"
1044  "screen, display maps a small panning icon. The rectangle\n"
1045  "within the panning icon shows the area that is currently\n"
1046  "displayed in the image window. To pan about the image,\n"
1047  "press any button and drag the pointer within the panning\n"
1048  "icon. The pan rectangle moves with the pointer and the\n"
1049  "image window is updated to reflect the location of the\n"
1050  "rectangle within the panning icon. When you have selected\n"
1051  "the area of the image you wish to view, release the button.\n"
1052  "\n"
1053  "Use the arrow keys to pan the image one pixel up, down,\n"
1054  "left, or right within the image window.\n"
1055  "\n"
1056  "The panning icon is withdrawn if the image becomes smaller\n"
1057  "than the dimensions of the X server screen.\n"
1058  },
1059  ImagePasteHelp[] =
1060  {
1061  "A small window appears showing the location of the cursor in\n"
1062  "the image window. You are now in paste mode. To exit\n"
1063  "immediately, press Dismiss. In paste mode, the Command\n"
1064  "widget has these options:\n"
1065  "\n"
1066  " Operators\n"
1067  " over\n"
1068  " in\n"
1069  " out\n"
1070  " atop\n"
1071  " xor\n"
1072  " plus\n"
1073  " minus\n"
1074  " add\n"
1075  " subtract\n"
1076  " difference\n"
1077  " replace\n"
1078  " Help\n"
1079  " Dismiss\n"
1080  "\n"
1081  "Choose a composite operation from the Operators sub-menu of\n"
1082  "the Command widget. How each operator behaves is described\n"
1083  "below. Image window is the image currently displayed on\n"
1084  "your X server and image is the image obtained with the File\n"
1085  "Browser widget.\n"
1086  "\n"
1087  "Over The result is the union of the two image shapes,\n"
1088  " with image obscuring image window in the region of\n"
1089  " overlap.\n"
1090  "\n"
1091  "In The result is simply image cut by the shape of\n"
1092  " image window. None of the image data of image\n"
1093  " window is in the result.\n"
1094  "\n"
1095  "Out The resulting image is image with the shape of\n"
1096  " image window cut out.\n"
1097  "\n"
1098  "Atop The result is the same shape as the image window,\n"
1099  " with image obscuring image window where the image\n"
1100  " shapes overlap. Note this differs from over\n"
1101  " because the portion of image outside image window's\n"
1102  " shape does not appear in the result.\n"
1103  "\n"
1104  "Xor The result is the image data from both image and\n"
1105  " image window that is outside the overlap region.\n"
1106  " The overlap region is blank.\n"
1107  "\n"
1108  "Plus The result is just the sum of the image data.\n"
1109  " Output values are cropped to QuantumRange (no overflow).\n"
1110  " This operation is independent of the matte\n"
1111  " channels.\n"
1112  "\n"
1113  "Minus The result of image - image window, with underflow\n"
1114  " cropped to zero.\n"
1115  "\n"
1116  "Add The result of image + image window, with overflow\n"
1117  " wrapping around (mod 256).\n"
1118  "\n"
1119  "Subtract The result of image - image window, with underflow\n"
1120  " wrapping around (mod 256). The add and subtract\n"
1121  " operators can be used to perform reversible\n"
1122  " transformations.\n"
1123  "\n"
1124  "Difference\n"
1125  " The result of abs(image - image window). This\n"
1126  " useful for comparing two very similar images.\n"
1127  "\n"
1128  "Copy The resulting image is image window replaced with\n"
1129  " image. Here the matte information is ignored.\n"
1130  "\n"
1131  "CopyRed The red layer of the image window is replace with\n"
1132  " the red layer of the image. The other layers are\n"
1133  " untouched.\n"
1134  "\n"
1135  "CopyGreen\n"
1136  " The green layer of the image window is replace with\n"
1137  " the green layer of the image. The other layers are\n"
1138  " untouched.\n"
1139  "\n"
1140  "CopyBlue The blue layer of the image window is replace with\n"
1141  " the blue layer of the image. The other layers are\n"
1142  " untouched.\n"
1143  "\n"
1144  "CopyOpacity\n"
1145  " The matte layer of the image window is replace with\n"
1146  " the matte layer of the image. The other layers are\n"
1147  " untouched.\n"
1148  "\n"
1149  "The image compositor requires a matte, or alpha channel in\n"
1150  "the image for some operations. This extra channel usually\n"
1151  "defines a mask which represents a sort of a cookie-cutter\n"
1152  "for the image. This the case when matte is opaque (full\n"
1153  "coverage) for pixels inside the shape, zero outside, and\n"
1154  "between 0 and QuantumRange on the boundary. If image does not\n"
1155  "have a matte channel, it is initialized with 0 for any pixel\n"
1156  "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
1157  "\n"
1158  "Note that matte information for image window is not retained\n"
1159  "for colormapped X server visuals (e.g. StaticColor,\n"
1160  "StaticColor, GrayScale, PseudoColor). Correct compositing\n"
1161  "behavior may require a TrueColor or DirectColor visual or a\n"
1162  "Standard Colormap.\n"
1163  "\n"
1164  "Choosing a composite operator is optional. The default\n"
1165  "operator is replace. However, you must choose a location to\n"
1166  "paste your image and press button 1. Press and hold the\n"
1167  "button before releasing and an outline of the image will\n"
1168  "appear to help you identify your location.\n"
1169  "\n"
1170  "The actual colors of the pasted image is saved. However,\n"
1171  "the color that appears in image window may be different.\n"
1172  "For example, on a monochrome screen image window will appear\n"
1173  "black or white even though your pasted image may have\n"
1174  "many colors. If the image is saved to a file it is written\n"
1175  "with the correct colors. To assure the correct colors are\n"
1176  "saved in the final image, any PseudoClass image is promoted\n"
1177  "to DirectClass (see miff(5)). To force a PseudoClass image\n"
1178  "to remain PseudoClass, use -colors.\n"
1179  },
1180  ImageROIHelp[] =
1181  {
1182  "In region of interest mode, the Command widget has these\n"
1183  "options:\n"
1184  "\n"
1185  " Help\n"
1186  " Dismiss\n"
1187  "\n"
1188  "To define a region of interest, press button 1 and drag.\n"
1189  "The region of interest is defined by a highlighted rectangle\n"
1190  "that expands or contracts as it follows the pointer. Once\n"
1191  "you are satisfied with the region of interest, release the\n"
1192  "button. You are now in apply mode. In apply mode the\n"
1193  "Command widget has these options:\n"
1194  "\n"
1195  " File\n"
1196  " Save...\n"
1197  " Print...\n"
1198  " Edit\n"
1199  " Undo\n"
1200  " Redo\n"
1201  " Transform\n"
1202  " Flop\n"
1203  " Flip\n"
1204  " Rotate Right\n"
1205  " Rotate Left\n"
1206  " Enhance\n"
1207  " Hue...\n"
1208  " Saturation...\n"
1209  " Brightness...\n"
1210  " Gamma...\n"
1211  " Spiff\n"
1212  " Dull\n"
1213  " Contrast Stretch\n"
1214  " Sigmoidal Contrast...\n"
1215  " Normalize\n"
1216  " Equalize\n"
1217  " Negate\n"
1218  " Grayscale\n"
1219  " Map...\n"
1220  " Quantize...\n"
1221  " Effects\n"
1222  " Despeckle\n"
1223  " Emboss\n"
1224  " Reduce Noise\n"
1225  " Sharpen...\n"
1226  " Blur...\n"
1227  " Threshold...\n"
1228  " Edge Detect...\n"
1229  " Spread...\n"
1230  " Shade...\n"
1231  " Raise...\n"
1232  " Segment...\n"
1233  " F/X\n"
1234  " Solarize...\n"
1235  " Sepia Tone...\n"
1236  " Swirl...\n"
1237  " Implode...\n"
1238  " Vignette...\n"
1239  " Wave...\n"
1240  " Oil Painting...\n"
1241  " Charcoal Drawing...\n"
1242  " Miscellany\n"
1243  " Image Info\n"
1244  " Zoom Image\n"
1245  " Show Preview...\n"
1246  " Show Histogram\n"
1247  " Show Matte\n"
1248  " Help\n"
1249  " Dismiss\n"
1250  "\n"
1251  "You can make adjustments to the region of interest by moving\n"
1252  "the pointer to one of the rectangle corners, pressing a\n"
1253  "button, and dragging. Finally, choose an image processing\n"
1254  "technique from the Command widget. You can choose more than\n"
1255  "one image processing technique to apply to an area.\n"
1256  "Alternatively, you can move the region of interest before\n"
1257  "applying another image processing technique. To exit, press\n"
1258  "Dismiss.\n"
1259  },
1260  ImageRotateHelp[] =
1261  {
1262  "In rotate mode, the Command widget has these options:\n"
1263  "\n"
1264  " Pixel Color\n"
1265  " black\n"
1266  " blue\n"
1267  " cyan\n"
1268  " green\n"
1269  " gray\n"
1270  " red\n"
1271  " magenta\n"
1272  " yellow\n"
1273  " white\n"
1274  " Browser...\n"
1275  " Direction\n"
1276  " horizontal\n"
1277  " vertical\n"
1278  " Help\n"
1279  " Dismiss\n"
1280  "\n"
1281  "Choose a background color from the Pixel Color sub-menu.\n"
1282  "Additional background colors can be specified with the color\n"
1283  "browser. You can change the menu colors by setting the X\n"
1284  "resources pen1 through pen9.\n"
1285  "\n"
1286  "If you choose the color browser and press Grab, you can\n"
1287  "select the background color by moving the pointer to the\n"
1288  "desired color on the screen and press any button.\n"
1289  "\n"
1290  "Choose a point in the image window and press this button and\n"
1291  "hold. Next, move the pointer to another location in the\n"
1292  "image. As you move a line connects the initial location and\n"
1293  "the pointer. When you release the button, the degree of\n"
1294  "image rotation is determined by the slope of the line you\n"
1295  "just drew. The slope is relative to the direction you\n"
1296  "choose from the Direction sub-menu of the Command widget.\n"
1297  "\n"
1298  "To cancel the image rotation, move the pointer back to the\n"
1299  "starting point of the line and release the button.\n"
1300  };
1301 ␌
1302 /*
1303  Enumeration declarations.
1304 */
1305 typedef enum
1306 {
1307  CopyMode,
1308  CropMode,
1309  CutMode
1310 } ClipboardMode;
1311 
1312 typedef enum
1313 {
1314  OpenCommand,
1315  NextCommand,
1316  FormerCommand,
1317  SelectCommand,
1318  SaveCommand,
1319  PrintCommand,
1320  DeleteCommand,
1321  NewCommand,
1322  VisualDirectoryCommand,
1323  QuitCommand,
1324  UndoCommand,
1325  RedoCommand,
1326  CutCommand,
1327  CopyCommand,
1328  PasteCommand,
1329  HalfSizeCommand,
1330  OriginalSizeCommand,
1331  DoubleSizeCommand,
1332  ResizeCommand,
1333  ApplyCommand,
1334  RefreshCommand,
1335  RestoreCommand,
1336  CropCommand,
1337  ChopCommand,
1338  FlopCommand,
1339  FlipCommand,
1340  RotateRightCommand,
1341  RotateLeftCommand,
1342  RotateCommand,
1343  ShearCommand,
1344  RollCommand,
1345  TrimCommand,
1346  HueCommand,
1347  SaturationCommand,
1348  BrightnessCommand,
1349  GammaCommand,
1350  SpiffCommand,
1351  DullCommand,
1352  ContrastStretchCommand,
1353  SigmoidalContrastCommand,
1354  NormalizeCommand,
1355  EqualizeCommand,
1356  NegateCommand,
1357  GrayscaleCommand,
1358  MapCommand,
1359  QuantizeCommand,
1360  DespeckleCommand,
1361  EmbossCommand,
1362  ReduceNoiseCommand,
1363  AddNoiseCommand,
1364  SharpenCommand,
1365  BlurCommand,
1366  ThresholdCommand,
1367  EdgeDetectCommand,
1368  SpreadCommand,
1369  ShadeCommand,
1370  RaiseCommand,
1371  SegmentCommand,
1372  SolarizeCommand,
1373  SepiaToneCommand,
1374  SwirlCommand,
1375  ImplodeCommand,
1376  VignetteCommand,
1377  WaveCommand,
1378  OilPaintCommand,
1379  CharcoalDrawCommand,
1380  AnnotateCommand,
1381  DrawCommand,
1382  ColorCommand,
1383  MatteCommand,
1384  CompositeCommand,
1385  AddBorderCommand,
1386  AddFrameCommand,
1387  CommentCommand,
1388  LaunchCommand,
1389  RegionOfInterestCommand,
1390  ROIHelpCommand,
1391  ROIDismissCommand,
1392  InfoCommand,
1393  ZoomCommand,
1394  ShowPreviewCommand,
1395  ShowHistogramCommand,
1396  ShowMatteCommand,
1397  BackgroundCommand,
1398  SlideShowCommand,
1399  PreferencesCommand,
1400  HelpCommand,
1401  BrowseDocumentationCommand,
1402  VersionCommand,
1403  SaveToUndoBufferCommand,
1404  FreeBuffersCommand,
1405  NullCommand
1406 } DisplayCommand;
1407 
1408 typedef enum
1409 {
1410  AnnotateNameCommand,
1411  AnnotateFontColorCommand,
1412  AnnotateBackgroundColorCommand,
1413  AnnotateRotateCommand,
1414  AnnotateHelpCommand,
1415  AnnotateDismissCommand,
1416  TextHelpCommand,
1417  TextApplyCommand,
1418  ChopDirectionCommand,
1419  ChopHelpCommand,
1420  ChopDismissCommand,
1421  HorizontalChopCommand,
1422  VerticalChopCommand,
1423  ColorEditMethodCommand,
1424  ColorEditColorCommand,
1425  ColorEditBorderCommand,
1426  ColorEditFuzzCommand,
1427  ColorEditUndoCommand,
1428  ColorEditHelpCommand,
1429  ColorEditDismissCommand,
1430  CompositeOperatorsCommand,
1431  CompositeDissolveCommand,
1432  CompositeDisplaceCommand,
1433  CompositeHelpCommand,
1434  CompositeDismissCommand,
1435  CropHelpCommand,
1436  CropDismissCommand,
1437  RectifyCopyCommand,
1438  RectifyHelpCommand,
1439  RectifyDismissCommand,
1440  DrawElementCommand,
1441  DrawColorCommand,
1442  DrawStippleCommand,
1443  DrawWidthCommand,
1444  DrawUndoCommand,
1445  DrawHelpCommand,
1446  DrawDismissCommand,
1447  MatteEditMethod,
1448  MatteEditBorderCommand,
1449  MatteEditFuzzCommand,
1450  MatteEditValueCommand,
1451  MatteEditUndoCommand,
1452  MatteEditHelpCommand,
1453  MatteEditDismissCommand,
1454  PasteOperatorsCommand,
1455  PasteHelpCommand,
1456  PasteDismissCommand,
1457  RotateColorCommand,
1458  RotateDirectionCommand,
1459  RotateCropCommand,
1460  RotateSharpenCommand,
1461  RotateHelpCommand,
1462  RotateDismissCommand,
1463  HorizontalRotateCommand,
1464  VerticalRotateCommand,
1465  TileLoadCommand,
1466  TileNextCommand,
1467  TileFormerCommand,
1468  TileDeleteCommand,
1469  TileUpdateCommand
1470 } ModeType;
1471 ␌
1472 /*
1473  Stipples.
1474 */
1475 #define BricksWidth 20
1476 #define BricksHeight 20
1477 #define DiagonalWidth 16
1478 #define DiagonalHeight 16
1479 #define HighlightWidth 8
1480 #define HighlightHeight 8
1481 #define OpaqueWidth 8
1482 #define OpaqueHeight 8
1483 #define ScalesWidth 16
1484 #define ScalesHeight 16
1485 #define ShadowWidth 8
1486 #define ShadowHeight 8
1487 #define VerticalWidth 16
1488 #define VerticalHeight 16
1489 #define WavyWidth 16
1490 #define WavyHeight 16
1491 ␌
1492 /*
1493  Constant declaration.
1494 */
1495 static const int
1496  RoiDelta = 8;
1497 
1498 static const unsigned char
1499  BricksBitmap[] =
1500  {
1501  0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1502  0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1503  0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1504  0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1505  0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1506  },
1507  DiagonalBitmap[] =
1508  {
1509  0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1510  0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1511  0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1512  },
1513  ScalesBitmap[] =
1514  {
1515  0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1516  0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1517  0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1518  },
1519  VerticalBitmap[] =
1520  {
1521  0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1522  0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1523  0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1524  },
1525  WavyBitmap[] =
1526  {
1527  0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1528  0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1529  0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1530  };
1531 ␌
1532 /*
1533  Function prototypes.
1534 */
1535 static DisplayCommand
1536  XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1537  const MagickStatusType,KeySym,Image **);
1538 
1539 static Image
1540  *XMagickCommand(Display *,XResourceInfo *,XWindows *,const DisplayCommand,
1541  Image **),
1542  *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1543  *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *),
1544  *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *);
1545 
1546 static MagickBooleanType
1547  XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *),
1548  XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1549  XChopImage(Display *,XResourceInfo *,XWindows *,Image **),
1550  XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode),
1551  XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **),
1552  XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1553  XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *),
1554  XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *),
1555  XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1556  XPasteImage(Display *,XResourceInfo *,XWindows *,Image *),
1557  XPrintImage(Display *,XResourceInfo *,XWindows *,Image *),
1558  XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **),
1559  XROIImage(Display *,XResourceInfo *,XWindows *,Image **),
1560  XSaveImage(Display *,XResourceInfo *,XWindows *,Image *),
1561  XTrimImage(Display *,XResourceInfo *,XWindows *,Image *);
1562 
1563 static void
1564  XDrawPanRectangle(Display *,XWindows *),
1565  XImageCache(Display *,XResourceInfo *,XWindows *,const DisplayCommand,Image **),
1566  XMagnifyImage(Display *,XWindows *,XEvent *),
1567  XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *),
1568  XPanImage(Display *,XWindows *,XEvent *),
1569  XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1570  const KeySym),
1571  XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1572  XScreenEvent(Display *,XWindows *,XEvent *),
1573  XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1574 ␌
1575 /*
1576 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1577 % %
1578 % %
1579 % %
1580 % D i s p l a y I m a g e s %
1581 % %
1582 % %
1583 % %
1584 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1585 %
1586 % DisplayImages() displays an image sequence to any X window screen. It
1587 % returns a value other than 0 if successful. Check the exception member
1588 % of image to determine the reason for any failure.
1589 %
1590 % The format of the DisplayImages method is:
1591 %
1592 % MagickBooleanType DisplayImages(const ImageInfo *image_info,
1593 % Image *images)
1594 %
1595 % A description of each parameter follows:
1596 %
1597 % o image_info: the image info.
1598 %
1599 % o image: the image.
1600 %
1601 */
1602 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1603  Image *images)
1604 {
1605  char
1606  *argv[1];
1607 
1608  Display
1609  *display;
1610 
1611  Image
1612  *image;
1613 
1614  size_t
1615  state;
1616 
1617  ssize_t
1618  i;
1619 
1620  XrmDatabase
1621  resource_database;
1622 
1623  XResourceInfo
1624  resource_info;
1625 
1626  assert(image_info != (const ImageInfo *) NULL);
1627  assert(image_info->signature == MagickCoreSignature);
1628  assert(images != (Image *) NULL);
1629  assert(images->signature == MagickCoreSignature);
1630  if (IsEventLogging() != MagickFalse)
1631  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1632  display=XOpenDisplay(image_info->server_name);
1633  if (display == (Display *) NULL)
1634  {
1635  (void) ThrowMagickException(&images->exception,GetMagickModule(),
1636  XServerError,"UnableToOpenXServer","`%s'",XDisplayName(
1637  image_info->server_name));
1638  return(MagickFalse);
1639  }
1640  if (images->exception.severity != UndefinedException)
1641  CatchException(&images->exception);
1642  (void) XSetErrorHandler(XError);
1643  resource_database=XGetResourceDatabase(display,GetClientName());
1644  (void) memset(&resource_info,0,sizeof(resource_info));
1645  XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1646  if (image_info->page != (char *) NULL)
1647  resource_info.image_geometry=AcquireString(image_info->page);
1648  resource_info.immutable=MagickTrue;
1649  argv[0]=AcquireString(GetClientName());
1650  state=DefaultState;
1651  for (i=0; (state & ExitState) == 0; i++)
1652  {
1653  if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1654  break;
1655  image=GetImageFromList(images,i % GetImageListLength(images));
1656  (void) XDisplayImage(display,&resource_info,argv,1,&image,&state);
1657  }
1658  (void) SetErrorHandler((ErrorHandler) NULL);
1659  (void) SetWarningHandler((WarningHandler) NULL);
1660  argv[0]=DestroyString(argv[0]);
1661  XDestroyResourceInfo(&resource_info);
1662  if (images->exception.severity != UndefinedException)
1663  return(MagickFalse);
1664  return(MagickTrue);
1665 }
1666 ␌
1667 /*
1668 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1669 % %
1670 % %
1671 % %
1672 % R e m o t e D i s p l a y C o m m a n d %
1673 % %
1674 % %
1675 % %
1676 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1677 %
1678 % RemoteDisplayCommand() encourages a remote display program to display the
1679 % specified image filename.
1680 %
1681 % The format of the RemoteDisplayCommand method is:
1682 %
1683 % MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1684 % const char *window,const char *filename,ExceptionInfo *exception)
1685 %
1686 % A description of each parameter follows:
1687 %
1688 % o image_info: the image info.
1689 %
1690 % o window: Specifies the name or id of an X window.
1691 %
1692 % o filename: the name of the image filename to display.
1693 %
1694 % o exception: return any errors or warnings in this structure.
1695 %
1696 */
1697 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1698  const char *window,const char *filename,ExceptionInfo *exception)
1699 {
1700  Display
1701  *display;
1702 
1703  MagickStatusType
1704  status;
1705 
1706  assert(image_info != (const ImageInfo *) NULL);
1707  assert(image_info->signature == MagickCoreSignature);
1708  assert(filename != (char *) NULL);
1709  if (IsEventLogging() != MagickFalse)
1710  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1711  display=XOpenDisplay(image_info->server_name);
1712  if (display == (Display *) NULL)
1713  {
1714  (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1715  "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1716  return(MagickFalse);
1717  }
1718  (void) XSetErrorHandler(XError);
1719  status=XRemoteCommand(display,window,filename);
1720  (void) XCloseDisplay(display);
1721  return(status != 0 ? MagickTrue : MagickFalse);
1722 }
1723 ␌
1724 /*
1725 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1726 % %
1727 % %
1728 % %
1729 + X A n n o t a t e E d i t I m a g e %
1730 % %
1731 % %
1732 % %
1733 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1734 %
1735 % XAnnotateEditImage() annotates the image with text.
1736 %
1737 % The format of the XAnnotateEditImage method is:
1738 %
1739 % MagickBooleanType XAnnotateEditImage(Display *display,
1740 % XResourceInfo *resource_info,XWindows *windows,Image *image)
1741 %
1742 % A description of each parameter follows:
1743 %
1744 % o display: Specifies a connection to an X server; returned from
1745 % XOpenDisplay.
1746 %
1747 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1748 %
1749 % o windows: Specifies a pointer to a XWindows structure.
1750 %
1751 % o image: the image; returned from ReadImage.
1752 %
1753 */
1754 
1755 static MagickBooleanType XAnnotateEditImage(Display *display,
1756  XResourceInfo *resource_info,XWindows *windows,Image *image)
1757 {
1758  const char
1759  *const AnnotateMenu[] =
1760  {
1761  "Font Name",
1762  "Font Color",
1763  "Box Color",
1764  "Rotate Text",
1765  "Help",
1766  "Dismiss",
1767  (char *) NULL
1768  },
1769  *const TextMenu[] =
1770  {
1771  "Help",
1772  "Apply",
1773  (char *) NULL
1774  };
1775 
1776  static const ModeType
1777  AnnotateCommands[] =
1778  {
1779  AnnotateNameCommand,
1780  AnnotateFontColorCommand,
1781  AnnotateBackgroundColorCommand,
1782  AnnotateRotateCommand,
1783  AnnotateHelpCommand,
1784  AnnotateDismissCommand
1785  },
1786  TextCommands[] =
1787  {
1788  TextHelpCommand,
1789  TextApplyCommand
1790  };
1791 
1792  static MagickBooleanType
1793  transparent_box = MagickTrue,
1794  transparent_pen = MagickFalse;
1795 
1796  static MagickRealType
1797  degrees = 0.0;
1798 
1799  static unsigned int
1800  box_id = MaxNumberPens-2,
1801  font_id = 0,
1802  pen_id = 0;
1803 
1804  char
1805  command[MaxTextExtent],
1806  *p,
1807  text[MaxTextExtent];
1808 
1809  const char
1810  *ColorMenu[MaxNumberPens+1];
1811 
1812  Cursor
1813  cursor;
1814 
1815  GC
1816  annotate_context;
1817 
1818  int
1819  id,
1820  pen_number,
1821  status,
1822  x,
1823  y;
1824 
1825  KeySym
1826  key_symbol;
1827 
1828  size_t
1829  state;
1830 
1831  ssize_t
1832  i;
1833 
1834  unsigned int
1835  height,
1836  width;
1837 
1838  XAnnotateInfo
1839  *annotate_info,
1840  *previous_info;
1841 
1842  XColor
1843  color;
1844 
1845  XFontStruct
1846  *font_info;
1847 
1848  XEvent
1849  event,
1850  text_event;
1851 
1852  /*
1853  Map Command widget.
1854  */
1855  (void) CloneString(&windows->command.name,"Annotate");
1856  windows->command.data=4;
1857  (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1858  (void) XMapRaised(display,windows->command.id);
1859  XClientMessage(display,windows->image.id,windows->im_protocols,
1860  windows->im_update_widget,CurrentTime);
1861  /*
1862  Track pointer until button 1 is pressed.
1863  */
1864  XQueryPosition(display,windows->image.id,&x,&y);
1865  (void) XSelectInput(display,windows->image.id,
1866  windows->image.attributes.event_mask | PointerMotionMask);
1867  cursor=XCreateFontCursor(display,XC_left_side);
1868  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1869  state=DefaultState;
1870  do
1871  {
1872  if (windows->info.mapped != MagickFalse)
1873  {
1874  /*
1875  Display pointer position.
1876  */
1877  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1878  x+windows->image.x,y+windows->image.y);
1879  XInfoWidget(display,windows,text);
1880  }
1881  /*
1882  Wait for next event.
1883  */
1884  XScreenEvent(display,windows,&event);
1885  if (event.xany.window == windows->command.id)
1886  {
1887  /*
1888  Select a command from the Command widget.
1889  */
1890  id=XCommandWidget(display,windows,AnnotateMenu,&event);
1891  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1892  if (id < 0)
1893  continue;
1894  switch (AnnotateCommands[id])
1895  {
1896  case AnnotateNameCommand:
1897  {
1898  const char
1899  *FontMenu[MaxNumberFonts];
1900 
1901  int
1902  font_number;
1903 
1904  /*
1905  Initialize menu selections.
1906  */
1907  for (i=0; i < MaxNumberFonts; i++)
1908  FontMenu[i]=resource_info->font_name[i];
1909  FontMenu[MaxNumberFonts-2]="Browser...";
1910  FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1911  /*
1912  Select a font name from the pop-up menu.
1913  */
1914  font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1915  (const char **) FontMenu,command);
1916  if (font_number < 0)
1917  break;
1918  if (font_number == (MaxNumberFonts-2))
1919  {
1920  static char
1921  font_name[MaxTextExtent] = "fixed";
1922 
1923  /*
1924  Select a font name from a browser.
1925  */
1926  resource_info->font_name[font_number]=font_name;
1927  XFontBrowserWidget(display,windows,"Select",font_name);
1928  if (*font_name == '\0')
1929  break;
1930  }
1931  /*
1932  Initialize font info.
1933  */
1934  font_info=XLoadQueryFont(display,resource_info->font_name[
1935  font_number]);
1936  if (font_info == (XFontStruct *) NULL)
1937  {
1938  XNoticeWidget(display,windows,"Unable to load font:",
1939  resource_info->font_name[font_number]);
1940  break;
1941  }
1942  font_id=(unsigned int) font_number;
1943  (void) XFreeFont(display,font_info);
1944  break;
1945  }
1946  case AnnotateFontColorCommand:
1947  {
1948  /*
1949  Initialize menu selections.
1950  */
1951  for (i=0; i < (int) (MaxNumberPens-2); i++)
1952  ColorMenu[i]=resource_info->pen_colors[i];
1953  ColorMenu[MaxNumberPens-2]="transparent";
1954  ColorMenu[MaxNumberPens-1]="Browser...";
1955  ColorMenu[MaxNumberPens]=(const char *) NULL;
1956  /*
1957  Select a pen color from the pop-up menu.
1958  */
1959  pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
1960  (const char **) ColorMenu,command);
1961  if (pen_number < 0)
1962  break;
1963  transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
1964  MagickFalse;
1965  if (transparent_pen != MagickFalse)
1966  break;
1967  if (pen_number == (MaxNumberPens-1))
1968  {
1969  static char
1970  color_name[MaxTextExtent] = "gray";
1971 
1972  /*
1973  Select a pen color from a dialog.
1974  */
1975  resource_info->pen_colors[pen_number]=color_name;
1976  XColorBrowserWidget(display,windows,"Select",color_name);
1977  if (*color_name == '\0')
1978  break;
1979  }
1980  /*
1981  Set pen color.
1982  */
1983  (void) XParseColor(display,windows->map_info->colormap,
1984  resource_info->pen_colors[pen_number],&color);
1985  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
1986  (unsigned int) MaxColors,&color);
1987  windows->pixel_info->pen_colors[pen_number]=color;
1988  pen_id=(unsigned int) pen_number;
1989  break;
1990  }
1991  case AnnotateBackgroundColorCommand:
1992  {
1993  /*
1994  Initialize menu selections.
1995  */
1996  for (i=0; i < (int) (MaxNumberPens-2); i++)
1997  ColorMenu[i]=resource_info->pen_colors[i];
1998  ColorMenu[MaxNumberPens-2]="transparent";
1999  ColorMenu[MaxNumberPens-1]="Browser...";
2000  ColorMenu[MaxNumberPens]=(const char *) NULL;
2001  /*
2002  Select a pen color from the pop-up menu.
2003  */
2004  pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2005  (const char **) ColorMenu,command);
2006  if (pen_number < 0)
2007  break;
2008  transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2009  MagickFalse;
2010  if (transparent_box != MagickFalse)
2011  break;
2012  if (pen_number == (MaxNumberPens-1))
2013  {
2014  static char
2015  color_name[MaxTextExtent] = "gray";
2016 
2017  /*
2018  Select a pen color from a dialog.
2019  */
2020  resource_info->pen_colors[pen_number]=color_name;
2021  XColorBrowserWidget(display,windows,"Select",color_name);
2022  if (*color_name == '\0')
2023  break;
2024  }
2025  /*
2026  Set pen color.
2027  */
2028  (void) XParseColor(display,windows->map_info->colormap,
2029  resource_info->pen_colors[pen_number],&color);
2030  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2031  (unsigned int) MaxColors,&color);
2032  windows->pixel_info->pen_colors[pen_number]=color;
2033  box_id=(unsigned int) pen_number;
2034  break;
2035  }
2036  case AnnotateRotateCommand:
2037  {
2038  int
2039  entry;
2040 
2041  const char
2042  *const RotateMenu[] =
2043  {
2044  "-90",
2045  "-45",
2046  "-30",
2047  "0",
2048  "30",
2049  "45",
2050  "90",
2051  "180",
2052  "Dialog...",
2053  (char *) NULL,
2054  };
2055 
2056  static char
2057  angle[MaxTextExtent] = "30.0";
2058 
2059  /*
2060  Select a command from the pop-up menu.
2061  */
2062  entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2063  command);
2064  if (entry < 0)
2065  break;
2066  if (entry != 8)
2067  {
2068  degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2069  break;
2070  }
2071  (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2072  angle);
2073  if (*angle == '\0')
2074  break;
2075  degrees=StringToDouble(angle,(char **) NULL);
2076  break;
2077  }
2078  case AnnotateHelpCommand:
2079  {
2080  XTextViewHelp(display,resource_info,windows,MagickFalse,
2081  "Help Viewer - Image Annotation",ImageAnnotateHelp);
2082  break;
2083  }
2084  case AnnotateDismissCommand:
2085  {
2086  /*
2087  Prematurely exit.
2088  */
2089  state|=EscapeState;
2090  state|=ExitState;
2091  break;
2092  }
2093  default:
2094  break;
2095  }
2096  continue;
2097  }
2098  switch (event.type)
2099  {
2100  case ButtonPress:
2101  {
2102  if (event.xbutton.button != Button1)
2103  break;
2104  if (event.xbutton.window != windows->image.id)
2105  break;
2106  /*
2107  Change to text entering mode.
2108  */
2109  x=event.xbutton.x;
2110  y=event.xbutton.y;
2111  state|=ExitState;
2112  break;
2113  }
2114  case ButtonRelease:
2115  break;
2116  case Expose:
2117  break;
2118  case KeyPress:
2119  {
2120  if (event.xkey.window != windows->image.id)
2121  break;
2122  /*
2123  Respond to a user key press.
2124  */
2125  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2126  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2127  switch ((int) key_symbol)
2128  {
2129  case XK_Escape:
2130  case XK_F20:
2131  {
2132  /*
2133  Prematurely exit.
2134  */
2135  state|=EscapeState;
2136  state|=ExitState;
2137  break;
2138  }
2139  case XK_F1:
2140  case XK_Help:
2141  {
2142  XTextViewHelp(display,resource_info,windows,MagickFalse,
2143  "Help Viewer - Image Annotation",ImageAnnotateHelp);
2144  break;
2145  }
2146  default:
2147  {
2148  (void) XBell(display,0);
2149  break;
2150  }
2151  }
2152  break;
2153  }
2154  case MotionNotify:
2155  {
2156  /*
2157  Map and unmap Info widget as cursor crosses its boundaries.
2158  */
2159  x=event.xmotion.x;
2160  y=event.xmotion.y;
2161  if (windows->info.mapped != MagickFalse)
2162  {
2163  if ((x < (int) (windows->info.x+windows->info.width)) &&
2164  (y < (int) (windows->info.y+windows->info.height)))
2165  (void) XWithdrawWindow(display,windows->info.id,
2166  windows->info.screen);
2167  }
2168  else
2169  if ((x > (int) (windows->info.x+windows->info.width)) ||
2170  (y > (int) (windows->info.y+windows->info.height)))
2171  (void) XMapWindow(display,windows->info.id);
2172  break;
2173  }
2174  default:
2175  break;
2176  }
2177  } while ((state & ExitState) == 0);
2178  (void) XSelectInput(display,windows->image.id,
2179  windows->image.attributes.event_mask);
2180  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2181  if ((state & EscapeState) != 0)
2182  return(MagickTrue);
2183  /*
2184  Set font info and check boundary conditions.
2185  */
2186  font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2187  if (font_info == (XFontStruct *) NULL)
2188  {
2189  XNoticeWidget(display,windows,"Unable to load font:",
2190  resource_info->font_name[font_id]);
2191  font_info=windows->font_info;
2192  }
2193  if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2194  x=(int) windows->image.width-font_info->max_bounds.width;
2195  if (y < (int) (font_info->ascent+font_info->descent))
2196  y=(int) font_info->ascent+font_info->descent;
2197  if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2198  ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2199  return(MagickFalse);
2200  /*
2201  Initialize annotate structure.
2202  */
2203  annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2204  if (annotate_info == (XAnnotateInfo *) NULL)
2205  return(MagickFalse);
2206  XGetAnnotateInfo(annotate_info);
2207  annotate_info->x=x;
2208  annotate_info->y=y;
2209  if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2210  annotate_info->stencil=OpaqueStencil;
2211  else
2212  if (transparent_box == MagickFalse)
2213  annotate_info->stencil=BackgroundStencil;
2214  else
2215  annotate_info->stencil=ForegroundStencil;
2216  annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2217  annotate_info->degrees=degrees;
2218  annotate_info->font_info=font_info;
2219  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2220  windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2221  sizeof(*annotate_info->text));
2222  if (annotate_info->text == (char *) NULL)
2223  return(MagickFalse);
2224  /*
2225  Create cursor and set graphic context.
2226  */
2227  cursor=XCreateFontCursor(display,XC_pencil);
2228  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2229  annotate_context=windows->image.annotate_context;
2230  (void) XSetFont(display,annotate_context,font_info->fid);
2231  (void) XSetBackground(display,annotate_context,
2232  windows->pixel_info->pen_colors[box_id].pixel);
2233  (void) XSetForeground(display,annotate_context,
2234  windows->pixel_info->pen_colors[pen_id].pixel);
2235  /*
2236  Begin annotating the image with text.
2237  */
2238  (void) CloneString(&windows->command.name,"Text");
2239  windows->command.data=0;
2240  (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2241  state=DefaultState;
2242  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2243  text_event.xexpose.width=(int) font_info->max_bounds.width;
2244  text_event.xexpose.height=font_info->max_bounds.ascent+
2245  font_info->max_bounds.descent;
2246  p=annotate_info->text;
2247  do
2248  {
2249  /*
2250  Display text cursor.
2251  */
2252  *p='\0';
2253  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2254  /*
2255  Wait for next event.
2256  */
2257  XScreenEvent(display,windows,&event);
2258  if (event.xany.window == windows->command.id)
2259  {
2260  /*
2261  Select a command from the Command widget.
2262  */
2263  (void) XSetBackground(display,annotate_context,
2264  windows->pixel_info->background_color.pixel);
2265  (void) XSetForeground(display,annotate_context,
2266  windows->pixel_info->foreground_color.pixel);
2267  id=XCommandWidget(display,windows,AnnotateMenu,&event);
2268  (void) XSetBackground(display,annotate_context,
2269  windows->pixel_info->pen_colors[box_id].pixel);
2270  (void) XSetForeground(display,annotate_context,
2271  windows->pixel_info->pen_colors[pen_id].pixel);
2272  if (id < 0)
2273  continue;
2274  switch (TextCommands[id])
2275  {
2276  case TextHelpCommand:
2277  {
2278  XTextViewHelp(display,resource_info,windows,MagickFalse,
2279  "Help Viewer - Image Annotation",ImageAnnotateHelp);
2280  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2281  break;
2282  }
2283  case TextApplyCommand:
2284  {
2285  /*
2286  Finished annotating.
2287  */
2288  annotate_info->width=(unsigned int) XTextWidth(font_info,
2289  annotate_info->text,(int) strlen(annotate_info->text));
2290  XRefreshWindow(display,&windows->image,&text_event);
2291  state|=ExitState;
2292  break;
2293  }
2294  default:
2295  break;
2296  }
2297  continue;
2298  }
2299  /*
2300  Erase text cursor.
2301  */
2302  text_event.xexpose.x=x;
2303  text_event.xexpose.y=y-font_info->max_bounds.ascent;
2304  (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2305  (unsigned int) text_event.xexpose.width,(unsigned int)
2306  text_event.xexpose.height,MagickFalse);
2307  XRefreshWindow(display,&windows->image,&text_event);
2308  switch (event.type)
2309  {
2310  case ButtonPress:
2311  {
2312  if (event.xbutton.window != windows->image.id)
2313  break;
2314  if (event.xbutton.button == Button2)
2315  {
2316  /*
2317  Request primary selection.
2318  */
2319  (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2320  windows->image.id,CurrentTime);
2321  break;
2322  }
2323  break;
2324  }
2325  case Expose:
2326  {
2327  if (event.xexpose.count == 0)
2328  {
2329  XAnnotateInfo
2330  *text_info;
2331 
2332  /*
2333  Refresh Image window.
2334  */
2335  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2336  text_info=annotate_info;
2337  while (text_info != (XAnnotateInfo *) NULL)
2338  {
2339  if (annotate_info->stencil == ForegroundStencil)
2340  (void) XDrawString(display,windows->image.id,annotate_context,
2341  text_info->x,text_info->y,text_info->text,
2342  (int) strlen(text_info->text));
2343  else
2344  (void) XDrawImageString(display,windows->image.id,
2345  annotate_context,text_info->x,text_info->y,text_info->text,
2346  (int) strlen(text_info->text));
2347  text_info=text_info->previous;
2348  }
2349  (void) XDrawString(display,windows->image.id,annotate_context,
2350  x,y,"_",1);
2351  }
2352  break;
2353  }
2354  case KeyPress:
2355  {
2356  int
2357  length;
2358 
2359  if (event.xkey.window != windows->image.id)
2360  break;
2361  /*
2362  Respond to a user key press.
2363  */
2364  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2365  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2366  *(command+length)='\0';
2367  if (((event.xkey.state & ControlMask) != 0) ||
2368  ((event.xkey.state & Mod1Mask) != 0))
2369  state|=ModifierState;
2370  if ((state & ModifierState) != 0)
2371  switch ((int) key_symbol)
2372  {
2373  case XK_u:
2374  case XK_U:
2375  {
2376  key_symbol=DeleteCommand;
2377  break;
2378  }
2379  default:
2380  break;
2381  }
2382  switch ((int) key_symbol)
2383  {
2384  case XK_BackSpace:
2385  {
2386  /*
2387  Erase one character.
2388  */
2389  if (p == annotate_info->text)
2390  {
2391  if (annotate_info->previous == (XAnnotateInfo *) NULL)
2392  break;
2393  else
2394  {
2395  /*
2396  Go to end of the previous line of text.
2397  */
2398  annotate_info=annotate_info->previous;
2399  p=annotate_info->text;
2400  x=annotate_info->x+annotate_info->width;
2401  y=annotate_info->y;
2402  if (annotate_info->width != 0)
2403  p+=strlen(annotate_info->text);
2404  break;
2405  }
2406  }
2407  p--;
2408  x-=XTextWidth(font_info,p,1);
2409  text_event.xexpose.x=x;
2410  text_event.xexpose.y=y-font_info->max_bounds.ascent;
2411  XRefreshWindow(display,&windows->image,&text_event);
2412  break;
2413  }
2414  case XK_bracketleft:
2415  {
2416  key_symbol=XK_Escape;
2417  break;
2418  }
2419  case DeleteCommand:
2420  {
2421  /*
2422  Erase the entire line of text.
2423  */
2424  while (p != annotate_info->text)
2425  {
2426  p--;
2427  x-=XTextWidth(font_info,p,1);
2428  text_event.xexpose.x=x;
2429  XRefreshWindow(display,&windows->image,&text_event);
2430  }
2431  break;
2432  }
2433  case XK_Escape:
2434  case XK_F20:
2435  {
2436  /*
2437  Finished annotating.
2438  */
2439  annotate_info->width=(unsigned int) XTextWidth(font_info,
2440  annotate_info->text,(int) strlen(annotate_info->text));
2441  XRefreshWindow(display,&windows->image,&text_event);
2442  state|=ExitState;
2443  break;
2444  }
2445  default:
2446  {
2447  /*
2448  Draw a single character on the Image window.
2449  */
2450  if ((state & ModifierState) != 0)
2451  break;
2452  if (*command == '\0')
2453  break;
2454  *p=(*command);
2455  if (annotate_info->stencil == ForegroundStencil)
2456  (void) XDrawString(display,windows->image.id,annotate_context,
2457  x,y,p,1);
2458  else
2459  (void) XDrawImageString(display,windows->image.id,
2460  annotate_context,x,y,p,1);
2461  x+=XTextWidth(font_info,p,1);
2462  p++;
2463  if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2464  break;
2465  magick_fallthrough;
2466  }
2467  case XK_Return:
2468  case XK_KP_Enter:
2469  {
2470  /*
2471  Advance to the next line of text.
2472  */
2473  *p='\0';
2474  annotate_info->width=(unsigned int) XTextWidth(font_info,
2475  annotate_info->text,(int) strlen(annotate_info->text));
2476  if (annotate_info->next != (XAnnotateInfo *) NULL)
2477  {
2478  /*
2479  Line of text already exists.
2480  */
2481  annotate_info=annotate_info->next;
2482  x=annotate_info->x;
2483  y=annotate_info->y;
2484  p=annotate_info->text;
2485  break;
2486  }
2487  annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2488  sizeof(*annotate_info->next));
2489  if (annotate_info->next == (XAnnotateInfo *) NULL)
2490  return(MagickFalse);
2491  *annotate_info->next=(*annotate_info);
2492  annotate_info->next->previous=annotate_info;
2493  annotate_info=annotate_info->next;
2494  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2495  windows->image.width/MagickMax((ssize_t)
2496  font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2497  if (annotate_info->text == (char *) NULL)
2498  return(MagickFalse);
2499  annotate_info->y+=annotate_info->height;
2500  if (annotate_info->y > (int) windows->image.height)
2501  annotate_info->y=(int) annotate_info->height;
2502  annotate_info->next=(XAnnotateInfo *) NULL;
2503  x=annotate_info->x;
2504  y=annotate_info->y;
2505  p=annotate_info->text;
2506  break;
2507  }
2508  }
2509  break;
2510  }
2511  case KeyRelease:
2512  {
2513  /*
2514  Respond to a user key release.
2515  */
2516  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2517  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2518  state&=(~ModifierState);
2519  break;
2520  }
2521  case SelectionNotify:
2522  {
2523  Atom
2524  type;
2525 
2526  int
2527  format;
2528 
2529  unsigned char
2530  *data;
2531 
2532  unsigned long
2533  after,
2534  length;
2535 
2536  /*
2537  Obtain response from primary selection.
2538  */
2539  if (event.xselection.property == (Atom) None)
2540  break;
2541  status=XGetWindowProperty(display,event.xselection.requestor,
2542  event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2543  &type,&format,&length,&after,&data);
2544  if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2545  (length == 0))
2546  break;
2547  /*
2548  Annotate Image window with primary selection.
2549  */
2550  for (i=0; i < (ssize_t) length; i++)
2551  {
2552  if ((char) data[i] != '\n')
2553  {
2554  /*
2555  Draw a single character on the Image window.
2556  */
2557  *p=(char) data[i];
2558  (void) XDrawString(display,windows->image.id,annotate_context,
2559  x,y,p,1);
2560  x+=XTextWidth(font_info,p,1);
2561  p++;
2562  if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2563  continue;
2564  }
2565  /*
2566  Advance to the next line of text.
2567  */
2568  *p='\0';
2569  annotate_info->width=(unsigned int) XTextWidth(font_info,
2570  annotate_info->text,(int) strlen(annotate_info->text));
2571  if (annotate_info->next != (XAnnotateInfo *) NULL)
2572  {
2573  /*
2574  Line of text already exists.
2575  */
2576  annotate_info=annotate_info->next;
2577  x=annotate_info->x;
2578  y=annotate_info->y;
2579  p=annotate_info->text;
2580  continue;
2581  }
2582  annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2583  sizeof(*annotate_info->next));
2584  if (annotate_info->next == (XAnnotateInfo *) NULL)
2585  return(MagickFalse);
2586  *annotate_info->next=(*annotate_info);
2587  annotate_info->next->previous=annotate_info;
2588  annotate_info=annotate_info->next;
2589  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2590  windows->image.width/MagickMax((ssize_t)
2591  font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2592  if (annotate_info->text == (char *) NULL)
2593  return(MagickFalse);
2594  annotate_info->y+=annotate_info->height;
2595  if (annotate_info->y > (int) windows->image.height)
2596  annotate_info->y=(int) annotate_info->height;
2597  annotate_info->next=(XAnnotateInfo *) NULL;
2598  x=annotate_info->x;
2599  y=annotate_info->y;
2600  p=annotate_info->text;
2601  }
2602  (void) XFree((void *) data);
2603  break;
2604  }
2605  default:
2606  break;
2607  }
2608  } while ((state & ExitState) == 0);
2609  (void) XFreeCursor(display,cursor);
2610  /*
2611  Annotation is relative to image configuration.
2612  */
2613  width=(unsigned int) image->columns;
2614  height=(unsigned int) image->rows;
2615  x=0;
2616  y=0;
2617  if (windows->image.crop_geometry != (char *) NULL)
2618  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2619  /*
2620  Initialize annotated image.
2621  */
2622  XSetCursorState(display,windows,MagickTrue);
2623  XCheckRefreshWindows(display,windows);
2624  while (annotate_info != (XAnnotateInfo *) NULL)
2625  {
2626  if (annotate_info->width == 0)
2627  {
2628  /*
2629  No text on this line-- go to the next line of text.
2630  */
2631  previous_info=annotate_info->previous;
2632  annotate_info->text=(char *)
2633  RelinquishMagickMemory(annotate_info->text);
2634  annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2635  annotate_info=previous_info;
2636  continue;
2637  }
2638  /*
2639  Determine pixel index for box and pen color.
2640  */
2641  windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2642  if (windows->pixel_info->colors != 0)
2643  for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2644  if (windows->pixel_info->pixels[i] ==
2645  windows->pixel_info->pen_colors[box_id].pixel)
2646  {
2647  windows->pixel_info->box_index=(unsigned short) i;
2648  break;
2649  }
2650  windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2651  if (windows->pixel_info->colors != 0)
2652  for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2653  if (windows->pixel_info->pixels[i] ==
2654  windows->pixel_info->pen_colors[pen_id].pixel)
2655  {
2656  windows->pixel_info->pen_index=(unsigned short) i;
2657  break;
2658  }
2659  /*
2660  Define the annotate geometry string.
2661  */
2662  annotate_info->x=(int)
2663  width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2664  annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2665  windows->image.y)/windows->image.ximage->height;
2666  (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2667  "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2668  height*annotate_info->height/windows->image.ximage->height,
2669  annotate_info->x+x,annotate_info->y+y);
2670  /*
2671  Annotate image with text.
2672  */
2673  status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
2674  if (status == 0)
2675  return(MagickFalse);
2676  /*
2677  Free up memory.
2678  */
2679  previous_info=annotate_info->previous;
2680  annotate_info->text=DestroyString(annotate_info->text);
2681  annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2682  annotate_info=previous_info;
2683  }
2684  (void) XSetForeground(display,annotate_context,
2685  windows->pixel_info->foreground_color.pixel);
2686  (void) XSetBackground(display,annotate_context,
2687  windows->pixel_info->background_color.pixel);
2688  (void) XSetFont(display,annotate_context,windows->font_info->fid);
2689  XSetCursorState(display,windows,MagickFalse);
2690  (void) XFreeFont(display,font_info);
2691  /*
2692  Update image configuration.
2693  */
2694  XConfigureImageColormap(display,resource_info,windows,image);
2695  (void) XConfigureImage(display,resource_info,windows,image);
2696  return(MagickTrue);
2697 }
2698 ␌
2699 /*
2700 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2701 % %
2702 % %
2703 % %
2704 + X B a c k g r o u n d I m a g e %
2705 % %
2706 % %
2707 % %
2708 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2709 %
2710 % XBackgroundImage() displays the image in the background of a window.
2711 %
2712 % The format of the XBackgroundImage method is:
2713 %
2714 % MagickBooleanType XBackgroundImage(Display *display,
2715 % XResourceInfo *resource_info,XWindows *windows,Image **image)
2716 %
2717 % A description of each parameter follows:
2718 %
2719 % o display: Specifies a connection to an X server; returned from
2720 % XOpenDisplay.
2721 %
2722 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2723 %
2724 % o windows: Specifies a pointer to a XWindows structure.
2725 %
2726 % o image: the image.
2727 %
2728 */
2729 static MagickBooleanType XBackgroundImage(Display *display,
2730  XResourceInfo *resource_info,XWindows *windows,Image **image)
2731 {
2732 #define BackgroundImageTag "Background/Image"
2733 
2734  int
2735  status;
2736 
2737  static char
2738  window_id[MaxTextExtent] = "root";
2739 
2740  XResourceInfo
2741  background_resources;
2742 
2743  /*
2744  Put image in background.
2745  */
2746  status=XDialogWidget(display,windows,"Background",
2747  "Enter window id (id 0x00 selects window with pointer):",window_id);
2748  if (*window_id == '\0')
2749  return(MagickFalse);
2750  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
2751  XInfoWidget(display,windows,BackgroundImageTag);
2752  XSetCursorState(display,windows,MagickTrue);
2753  XCheckRefreshWindows(display,windows);
2754  background_resources=(*resource_info);
2755  background_resources.window_id=window_id;
2756  background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2757  status=XDisplayBackgroundImage(display,&background_resources,*image);
2758  if (status != MagickFalse)
2759  XClientMessage(display,windows->image.id,windows->im_protocols,
2760  windows->im_retain_colors,CurrentTime);
2761  XSetCursorState(display,windows,MagickFalse);
2762  (void) XMagickCommand(display,resource_info,windows,UndoCommand,image);
2763  return(MagickTrue);
2764 }
2765 ␌
2766 /*
2767 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2768 % %
2769 % %
2770 % %
2771 + X C h o p I m a g e %
2772 % %
2773 % %
2774 % %
2775 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2776 %
2777 % XChopImage() chops the X image.
2778 %
2779 % The format of the XChopImage method is:
2780 %
2781 % MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2782 % XWindows *windows,Image **image)
2783 %
2784 % A description of each parameter follows:
2785 %
2786 % o display: Specifies a connection to an X server; returned from
2787 % XOpenDisplay.
2788 %
2789 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2790 %
2791 % o windows: Specifies a pointer to a XWindows structure.
2792 %
2793 % o image: the image.
2794 %
2795 */
2796 static MagickBooleanType XChopImage(Display *display,
2797  XResourceInfo *resource_info,XWindows *windows,Image **image)
2798 {
2799  const char
2800  *const ChopMenu[] =
2801  {
2802  "Direction",
2803  "Help",
2804  "Dismiss",
2805  (char *) NULL
2806  };
2807 
2808  static ModeType
2809  direction = HorizontalChopCommand;
2810 
2811  static const ModeType
2812  ChopCommands[] =
2813  {
2814  ChopDirectionCommand,
2815  ChopHelpCommand,
2816  ChopDismissCommand
2817  },
2818  DirectionCommands[] =
2819  {
2820  HorizontalChopCommand,
2821  VerticalChopCommand
2822  };
2823 
2824  char
2825  text[MaxTextExtent];
2826 
2827  Image
2828  *chop_image;
2829 
2830  int
2831  id,
2832  x,
2833  y;
2834 
2835  MagickRealType
2836  scale_factor;
2837 
2839  chop_info;
2840 
2841  unsigned int
2842  distance,
2843  height,
2844  width;
2845 
2846  size_t
2847  state;
2848 
2849  XEvent
2850  event;
2851 
2852  XSegment
2853  segment_info;
2854 
2855  /*
2856  Map Command widget.
2857  */
2858  (void) CloneString(&windows->command.name,"Chop");
2859  windows->command.data=1;
2860  (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2861  (void) XMapRaised(display,windows->command.id);
2862  XClientMessage(display,windows->image.id,windows->im_protocols,
2863  windows->im_update_widget,CurrentTime);
2864  /*
2865  Track pointer until button 1 is pressed.
2866  */
2867  XQueryPosition(display,windows->image.id,&x,&y);
2868  (void) XSelectInput(display,windows->image.id,
2869  windows->image.attributes.event_mask | PointerMotionMask);
2870  state=DefaultState;
2871  (void) memset(&segment_info,0,sizeof(segment_info));
2872  do
2873  {
2874  if (windows->info.mapped != MagickFalse)
2875  {
2876  /*
2877  Display pointer position.
2878  */
2879  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2880  x+windows->image.x,y+windows->image.y);
2881  XInfoWidget(display,windows,text);
2882  }
2883  /*
2884  Wait for next event.
2885  */
2886  XScreenEvent(display,windows,&event);
2887  if (event.xany.window == windows->command.id)
2888  {
2889  /*
2890  Select a command from the Command widget.
2891  */
2892  id=XCommandWidget(display,windows,ChopMenu,&event);
2893  if (id < 0)
2894  continue;
2895  switch (ChopCommands[id])
2896  {
2897  case ChopDirectionCommand:
2898  {
2899  char
2900  command[MaxTextExtent];
2901 
2902  const char
2903  *const Directions[] =
2904  {
2905  "horizontal",
2906  "vertical",
2907  (char *) NULL,
2908  };
2909 
2910  /*
2911  Select a command from the pop-up menu.
2912  */
2913  id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2914  if (id >= 0)
2915  direction=DirectionCommands[id];
2916  break;
2917  }
2918  case ChopHelpCommand:
2919  {
2920  XTextViewHelp(display,resource_info,windows,MagickFalse,
2921  "Help Viewer - Image Chop",ImageChopHelp);
2922  break;
2923  }
2924  case ChopDismissCommand:
2925  {
2926  /*
2927  Prematurely exit.
2928  */
2929  state|=EscapeState;
2930  state|=ExitState;
2931  break;
2932  }
2933  default:
2934  break;
2935  }
2936  continue;
2937  }
2938  switch (event.type)
2939  {
2940  case ButtonPress:
2941  {
2942  if (event.xbutton.button != Button1)
2943  break;
2944  if (event.xbutton.window != windows->image.id)
2945  break;
2946  /*
2947  User has committed to start point of chopping line.
2948  */
2949  segment_info.x1=(short int) event.xbutton.x;
2950  segment_info.x2=(short int) event.xbutton.x;
2951  segment_info.y1=(short int) event.xbutton.y;
2952  segment_info.y2=(short int) event.xbutton.y;
2953  state|=ExitState;
2954  break;
2955  }
2956  case ButtonRelease:
2957  break;
2958  case Expose:
2959  break;
2960  case KeyPress:
2961  {
2962  char
2963  command[MaxTextExtent];
2964 
2965  KeySym
2966  key_symbol;
2967 
2968  if (event.xkey.window != windows->image.id)
2969  break;
2970  /*
2971  Respond to a user key press.
2972  */
2973  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2974  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2975  switch ((int) key_symbol)
2976  {
2977  case XK_Escape:
2978  case XK_F20:
2979  {
2980  /*
2981  Prematurely exit.
2982  */
2983  state|=EscapeState;
2984  state|=ExitState;
2985  break;
2986  }
2987  case XK_F1:
2988  case XK_Help:
2989  {
2990  (void) XSetFunction(display,windows->image.highlight_context,
2991  GXcopy);
2992  XTextViewHelp(display,resource_info,windows,MagickFalse,
2993  "Help Viewer - Image Chop",ImageChopHelp);
2994  (void) XSetFunction(display,windows->image.highlight_context,
2995  GXinvert);
2996  break;
2997  }
2998  default:
2999  {
3000  (void) XBell(display,0);
3001  break;
3002  }
3003  }
3004  break;
3005  }
3006  case MotionNotify:
3007  {
3008  /*
3009  Map and unmap Info widget as text cursor crosses its boundaries.
3010  */
3011  x=event.xmotion.x;
3012  y=event.xmotion.y;
3013  if (windows->info.mapped != MagickFalse)
3014  {
3015  if ((x < (int) (windows->info.x+windows->info.width)) &&
3016  (y < (int) (windows->info.y+windows->info.height)))
3017  (void) XWithdrawWindow(display,windows->info.id,
3018  windows->info.screen);
3019  }
3020  else
3021  if ((x > (int) (windows->info.x+windows->info.width)) ||
3022  (y > (int) (windows->info.y+windows->info.height)))
3023  (void) XMapWindow(display,windows->info.id);
3024  }
3025  }
3026  } while ((state & ExitState) == 0);
3027  (void) XSelectInput(display,windows->image.id,
3028  windows->image.attributes.event_mask);
3029  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3030  if ((state & EscapeState) != 0)
3031  return(MagickTrue);
3032  /*
3033  Draw line as pointer moves until the mouse button is released.
3034  */
3035  chop_info.width=0;
3036  chop_info.height=0;
3037  chop_info.x=0;
3038  chop_info.y=0;
3039  distance=0;
3040  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3041  state=DefaultState;
3042  do
3043  {
3044  if (distance > 9)
3045  {
3046  /*
3047  Display info and draw chopping line.
3048  */
3049  if (windows->info.mapped == MagickFalse)
3050  (void) XMapWindow(display,windows->info.id);
3051  (void) FormatLocaleString(text,MaxTextExtent,
3052  " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3053  chop_info.height,(double) chop_info.x,(double) chop_info.y);
3054  XInfoWidget(display,windows,text);
3055  XHighlightLine(display,windows->image.id,
3056  windows->image.highlight_context,&segment_info);
3057  }
3058  else
3059  if (windows->info.mapped != MagickFalse)
3060  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3061  /*
3062  Wait for next event.
3063  */
3064  XScreenEvent(display,windows,&event);
3065  if (distance > 9)
3066  XHighlightLine(display,windows->image.id,
3067  windows->image.highlight_context,&segment_info);
3068  switch (event.type)
3069  {
3070  case ButtonPress:
3071  {
3072  segment_info.x2=(short int) event.xmotion.x;
3073  segment_info.y2=(short int) event.xmotion.y;
3074  break;
3075  }
3076  case ButtonRelease:
3077  {
3078  /*
3079  User has committed to chopping line.
3080  */
3081  segment_info.x2=(short int) event.xbutton.x;
3082  segment_info.y2=(short int) event.xbutton.y;
3083  state|=ExitState;
3084  break;
3085  }
3086  case Expose:
3087  break;
3088  case MotionNotify:
3089  {
3090  segment_info.x2=(short int) event.xmotion.x;
3091  segment_info.y2=(short int) event.xmotion.y;
3092  }
3093  default:
3094  break;
3095  }
3096  /*
3097  Check boundary conditions.
3098  */
3099  if (segment_info.x2 < 0)
3100  segment_info.x2=0;
3101  else
3102  if (segment_info.x2 > windows->image.ximage->width)
3103  segment_info.x2=windows->image.ximage->width;
3104  if (segment_info.y2 < 0)
3105  segment_info.y2=0;
3106  else
3107  if (segment_info.y2 > windows->image.ximage->height)
3108  segment_info.y2=windows->image.ximage->height;
3109  distance=(unsigned int)
3110  (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3111  ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3112  /*
3113  Compute chopping geometry.
3114  */
3115  if (direction == HorizontalChopCommand)
3116  {
3117  chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3118  chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3119  chop_info.height=0;
3120  chop_info.y=0;
3121  if (segment_info.x1 > (int) segment_info.x2)
3122  {
3123  chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3124  chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3125  }
3126  }
3127  else
3128  {
3129  chop_info.width=0;
3130  chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3131  chop_info.x=0;
3132  chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3133  if (segment_info.y1 > segment_info.y2)
3134  {
3135  chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3136  chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3137  }
3138  }
3139  } while ((state & ExitState) == 0);
3140  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3141  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3142  if (distance <= 9)
3143  return(MagickTrue);
3144  /*
3145  Image chopping is relative to image configuration.
3146  */
3147  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
3148  XSetCursorState(display,windows,MagickTrue);
3149  XCheckRefreshWindows(display,windows);
3150  windows->image.window_changes.width=windows->image.ximage->width-
3151  (unsigned int) chop_info.width;
3152  windows->image.window_changes.height=windows->image.ximage->height-
3153  (unsigned int) chop_info.height;
3154  width=(unsigned int) (*image)->columns;
3155  height=(unsigned int) (*image)->rows;
3156  x=0;
3157  y=0;
3158  if (windows->image.crop_geometry != (char *) NULL)
3159  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3160  scale_factor=(MagickRealType) width/windows->image.ximage->width;
3161  chop_info.x+=x;
3162  chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3163  chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3164  scale_factor=(MagickRealType) height/windows->image.ximage->height;
3165  chop_info.y+=y;
3166  chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3167  chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3168  /*
3169  Chop image.
3170  */
3171  chop_image=ChopImage(*image,&chop_info,&(*image)->exception);
3172  XSetCursorState(display,windows,MagickFalse);
3173  if (chop_image == (Image *) NULL)
3174  return(MagickFalse);
3175  *image=DestroyImage(*image);
3176  *image=chop_image;
3177  /*
3178  Update image configuration.
3179  */
3180  XConfigureImageColormap(display,resource_info,windows,*image);
3181  (void) XConfigureImage(display,resource_info,windows,*image);
3182  return(MagickTrue);
3183 }
3184 ␌
3185 /*
3186 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3187 % %
3188 % %
3189 % %
3190 + X C o l o r E d i t I m a g e %
3191 % %
3192 % %
3193 % %
3194 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3195 %
3196 % XColorEditImage() allows the user to interactively change the color of one
3197 % pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3198 %
3199 % The format of the XColorEditImage method is:
3200 %
3201 % MagickBooleanType XColorEditImage(Display *display,
3202 % XResourceInfo *resource_info,XWindows *windows,Image **image)
3203 %
3204 % A description of each parameter follows:
3205 %
3206 % o display: Specifies a connection to an X server; returned from
3207 % XOpenDisplay.
3208 %
3209 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3210 %
3211 % o windows: Specifies a pointer to a XWindows structure.
3212 %
3213 % o image: the image; returned from ReadImage.
3214 %
3215 */
3216 
3217 
3218 static MagickBooleanType XColorEditImage(Display *display,
3219  XResourceInfo *resource_info,XWindows *windows,Image **image)
3220 {
3221  const char
3222  *const ColorEditMenu[] =
3223  {
3224  "Method",
3225  "Pixel Color",
3226  "Border Color",
3227  "Fuzz",
3228  "Undo",
3229  "Help",
3230  "Dismiss",
3231  (char *) NULL
3232  };
3233 
3234  static const ModeType
3235  ColorEditCommands[] =
3236  {
3237  ColorEditMethodCommand,
3238  ColorEditColorCommand,
3239  ColorEditBorderCommand,
3240  ColorEditFuzzCommand,
3241  ColorEditUndoCommand,
3242  ColorEditHelpCommand,
3243  ColorEditDismissCommand
3244  };
3245 
3246  static PaintMethod
3247  method = PointMethod;
3248 
3249  static unsigned int
3250  pen_id = 0;
3251 
3252  static XColor
3253  border_color = { 0, 0, 0, 0, 0, 0 };
3254 
3255  char
3256  command[MaxTextExtent] = "",
3257  text[MaxTextExtent] = "";
3258 
3259  Cursor
3260  cursor;
3261 
3263  *exception;
3264 
3265  int
3266  entry,
3267  id,
3268  x,
3269  x_offset,
3270  y,
3271  y_offset;
3272 
3273  PixelPacket
3274  *q;
3275 
3276  size_t
3277  state;
3278 
3279  ssize_t
3280  i;
3281 
3282  unsigned int
3283  height,
3284  width;
3285 
3286  XColor
3287  color;
3288 
3289  XEvent
3290  event;
3291 
3292  /*
3293  Map Command widget.
3294  */
3295  (void) CloneString(&windows->command.name,"Color Edit");
3296  windows->command.data=4;
3297  (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3298  (void) XMapRaised(display,windows->command.id);
3299  XClientMessage(display,windows->image.id,windows->im_protocols,
3300  windows->im_update_widget,CurrentTime);
3301  /*
3302  Make cursor.
3303  */
3304  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3305  resource_info->background_color,resource_info->foreground_color);
3306  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3307  /*
3308  Track pointer until button 1 is pressed.
3309  */
3310  XQueryPosition(display,windows->image.id,&x,&y);
3311  (void) XSelectInput(display,windows->image.id,
3312  windows->image.attributes.event_mask | PointerMotionMask);
3313  state=DefaultState;
3314  do
3315  {
3316  if (windows->info.mapped != MagickFalse)
3317  {
3318  /*
3319  Display pointer position.
3320  */
3321  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3322  x+windows->image.x,y+windows->image.y);
3323  XInfoWidget(display,windows,text);
3324  }
3325  /*
3326  Wait for next event.
3327  */
3328  XScreenEvent(display,windows,&event);
3329  if (event.xany.window == windows->command.id)
3330  {
3331  /*
3332  Select a command from the Command widget.
3333  */
3334  id=XCommandWidget(display,windows,ColorEditMenu,&event);
3335  if (id < 0)
3336  {
3337  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3338  continue;
3339  }
3340  switch (ColorEditCommands[id])
3341  {
3342  case ColorEditMethodCommand:
3343  {
3344  char
3345  **methods;
3346 
3347  /*
3348  Select a method from the pop-up menu.
3349  */
3350  methods=(char **) GetCommandOptions(MagickMethodOptions);
3351  if (methods == (char **) NULL)
3352  break;
3353  entry=XMenuWidget(display,windows,ColorEditMenu[id],
3354  (const char **) methods,command);
3355  if (entry >= 0)
3356  method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3357  MagickFalse,methods[entry]);
3358  methods=DestroyStringList(methods);
3359  break;
3360  }
3361  case ColorEditColorCommand:
3362  {
3363  const char
3364  *ColorMenu[MaxNumberPens];
3365 
3366  int
3367  pen_number;
3368 
3369  /*
3370  Initialize menu selections.
3371  */
3372  for (i=0; i < (int) (MaxNumberPens-2); i++)
3373  ColorMenu[i]=resource_info->pen_colors[i];
3374  ColorMenu[MaxNumberPens-2]="Browser...";
3375  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3376  /*
3377  Select a pen color from the pop-up menu.
3378  */
3379  pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3380  (const char **) ColorMenu,command);
3381  if (pen_number < 0)
3382  break;
3383  if (pen_number == (MaxNumberPens-2))
3384  {
3385  static char
3386  color_name[MaxTextExtent] = "gray";
3387 
3388  /*
3389  Select a pen color from a dialog.
3390  */
3391  resource_info->pen_colors[pen_number]=color_name;
3392  XColorBrowserWidget(display,windows,"Select",color_name);
3393  if (*color_name == '\0')
3394  break;
3395  }
3396  /*
3397  Set pen color.
3398  */
3399  (void) XParseColor(display,windows->map_info->colormap,
3400  resource_info->pen_colors[pen_number],&color);
3401  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3402  (unsigned int) MaxColors,&color);
3403  windows->pixel_info->pen_colors[pen_number]=color;
3404  pen_id=(unsigned int) pen_number;
3405  break;
3406  }
3407  case ColorEditBorderCommand:
3408  {
3409  const char
3410  *ColorMenu[MaxNumberPens];
3411 
3412  int
3413  pen_number;
3414 
3415  /*
3416  Initialize menu selections.
3417  */
3418  for (i=0; i < (int) (MaxNumberPens-2); i++)
3419  ColorMenu[i]=resource_info->pen_colors[i];
3420  ColorMenu[MaxNumberPens-2]="Browser...";
3421  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3422  /*
3423  Select a pen color from the pop-up menu.
3424  */
3425  pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3426  (const char **) ColorMenu,command);
3427  if (pen_number < 0)
3428  break;
3429  if (pen_number == (MaxNumberPens-2))
3430  {
3431  static char
3432  color_name[MaxTextExtent] = "gray";
3433 
3434  /*
3435  Select a pen color from a dialog.
3436  */
3437  resource_info->pen_colors[pen_number]=color_name;
3438  XColorBrowserWidget(display,windows,"Select",color_name);
3439  if (*color_name == '\0')
3440  break;
3441  }
3442  /*
3443  Set border color.
3444  */
3445  (void) XParseColor(display,windows->map_info->colormap,
3446  resource_info->pen_colors[pen_number],&border_color);
3447  break;
3448  }
3449  case ColorEditFuzzCommand:
3450  {
3451  static char
3452  fuzz[MaxTextExtent];
3453 
3454  static const char
3455  *FuzzMenu[] =
3456  {
3457  "0%",
3458  "2%",
3459  "5%",
3460  "10%",
3461  "15%",
3462  "Dialog...",
3463  (char *) NULL,
3464  };
3465 
3466  /*
3467  Select a command from the pop-up menu.
3468  */
3469  entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3470  command);
3471  if (entry < 0)
3472  break;
3473  if (entry != 5)
3474  {
3475  (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3476  QuantumRange+1.0);
3477  break;
3478  }
3479  (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3480  (void) XDialogWidget(display,windows,"Ok",
3481  "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3482  if (*fuzz == '\0')
3483  break;
3484  (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3485  (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3486  1.0);
3487  break;
3488  }
3489  case ColorEditUndoCommand:
3490  {
3491  (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3492  image);
3493  break;
3494  }
3495  case ColorEditHelpCommand:
3496  default:
3497  {
3498  XTextViewHelp(display,resource_info,windows,MagickFalse,
3499  "Help Viewer - Image Annotation",ImageColorEditHelp);
3500  break;
3501  }
3502  case ColorEditDismissCommand:
3503  {
3504  /*
3505  Prematurely exit.
3506  */
3507  state|=EscapeState;
3508  state|=ExitState;
3509  break;
3510  }
3511  }
3512  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3513  continue;
3514  }
3515  switch (event.type)
3516  {
3517  case ButtonPress:
3518  {
3519  if (event.xbutton.button != Button1)
3520  break;
3521  if ((event.xbutton.window != windows->image.id) &&
3522  (event.xbutton.window != windows->magnify.id))
3523  break;
3524  /*
3525  exit loop.
3526  */
3527  x=event.xbutton.x;
3528  y=event.xbutton.y;
3529  (void) XMagickCommand(display,resource_info,windows,
3530  SaveToUndoBufferCommand,image);
3531  state|=UpdateConfigurationState;
3532  break;
3533  }
3534  case ButtonRelease:
3535  {
3536  if (event.xbutton.button != Button1)
3537  break;
3538  if ((event.xbutton.window != windows->image.id) &&
3539  (event.xbutton.window != windows->magnify.id))
3540  break;
3541  /*
3542  Update colormap information.
3543  */
3544  x=event.xbutton.x;
3545  y=event.xbutton.y;
3546  XConfigureImageColormap(display,resource_info,windows,*image);
3547  (void) XConfigureImage(display,resource_info,windows,*image);
3548  XInfoWidget(display,windows,text);
3549  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3550  state&=(~UpdateConfigurationState);
3551  break;
3552  }
3553  case Expose:
3554  break;
3555  case KeyPress:
3556  {
3557  KeySym
3558  key_symbol;
3559 
3560  if (event.xkey.window == windows->magnify.id)
3561  {
3562  Window
3563  window;
3564 
3565  window=windows->magnify.id;
3566  while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3567  }
3568  if (event.xkey.window != windows->image.id)
3569  break;
3570  /*
3571  Respond to a user key press.
3572  */
3573  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3574  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3575  switch ((int) key_symbol)
3576  {
3577  case XK_Escape:
3578  case XK_F20:
3579  {
3580  /*
3581  Prematurely exit.
3582  */
3583  state|=ExitState;
3584  break;
3585  }
3586  case XK_F1:
3587  case XK_Help:
3588  {
3589  XTextViewHelp(display,resource_info,windows,MagickFalse,
3590  "Help Viewer - Image Annotation",ImageColorEditHelp);
3591  break;
3592  }
3593  default:
3594  {
3595  (void) XBell(display,0);
3596  break;
3597  }
3598  }
3599  break;
3600  }
3601  case MotionNotify:
3602  {
3603  /*
3604  Map and unmap Info widget as cursor crosses its boundaries.
3605  */
3606  x=event.xmotion.x;
3607  y=event.xmotion.y;
3608  if (windows->info.mapped != MagickFalse)
3609  {
3610  if ((x < (int) (windows->info.x+windows->info.width)) &&
3611  (y < (int) (windows->info.y+windows->info.height)))
3612  (void) XWithdrawWindow(display,windows->info.id,
3613  windows->info.screen);
3614  }
3615  else
3616  if ((x > (int) (windows->info.x+windows->info.width)) ||
3617  (y > (int) (windows->info.y+windows->info.height)))
3618  (void) XMapWindow(display,windows->info.id);
3619  break;
3620  }
3621  default:
3622  break;
3623  }
3624  if (event.xany.window == windows->magnify.id)
3625  {
3626  x=windows->magnify.x-windows->image.x;
3627  y=windows->magnify.y-windows->image.y;
3628  }
3629  x_offset=x;
3630  y_offset=y;
3631  if ((state & UpdateConfigurationState) != 0)
3632  {
3633  CacheView
3634  *image_view;
3635 
3636  int
3637  x,
3638  y;
3639 
3640  /*
3641  Pixel edit is relative to image configuration.
3642  */
3643  (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3644  MagickTrue);
3645  color=windows->pixel_info->pen_colors[pen_id];
3646  XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3647  width=(unsigned int) (*image)->columns;
3648  height=(unsigned int) (*image)->rows;
3649  x=0;
3650  y=0;
3651  if (windows->image.crop_geometry != (char *) NULL)
3652  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3653  &width,&height);
3654  x_offset=(int)
3655  (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3656  y_offset=(int)
3657  (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3658  if ((x_offset < 0) || (y_offset < 0))
3659  continue;
3660  if ((x_offset >= (int) (*image)->columns) ||
3661  (y_offset >= (int) (*image)->rows))
3662  continue;
3663  exception=(&(*image)->exception);
3664  image_view=AcquireAuthenticCacheView(*image,exception);
3665  switch (method)
3666  {
3667  case PointMethod:
3668  default:
3669  {
3670  /*
3671  Update color information using point algorithm.
3672  */
3673  if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3674  return(MagickFalse);
3675  q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3676  (ssize_t)y_offset,1,1,exception);
3677  if (q == (PixelPacket *) NULL)
3678  break;
3679  q->red=ScaleShortToQuantum(color.red);
3680  q->green=ScaleShortToQuantum(color.green);
3681  q->blue=ScaleShortToQuantum(color.blue);
3682  (void) SyncCacheViewAuthenticPixels(image_view,
3683  &(*image)->exception);
3684  break;
3685  }
3686  case ReplaceMethod:
3687  {
3688  PixelPacket
3689  target;
3690 
3691  /*
3692  Update color information using replace algorithm.
3693  */
3694  (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3695  (ssize_t) y_offset,&target,&(*image)->exception);
3696  if ((*image)->storage_class == DirectClass)
3697  {
3698  for (y=0; y < (int) (*image)->rows; y++)
3699  {
3700  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3701  (*image)->columns,1,exception);
3702  if (q == (PixelPacket *) NULL)
3703  break;
3704  for (x=0; x < (int) (*image)->columns; x++)
3705  {
3706  if (IsColorSimilar(*image,q,&target) != MagickFalse)
3707  {
3708  q->red=ScaleShortToQuantum(color.red);
3709  q->green=ScaleShortToQuantum(color.green);
3710  q->blue=ScaleShortToQuantum(color.blue);
3711  }
3712  q++;
3713  }
3714  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3715  break;
3716  }
3717  }
3718  else
3719  {
3720  for (i=0; i < (ssize_t) (*image)->colors; i++)
3721  if (IsColorSimilar(*image,(*image)->colormap+i,&target) != MagickFalse)
3722  {
3723  (*image)->colormap[i].red=ScaleShortToQuantum(color.red);
3724  (*image)->colormap[i].green=ScaleShortToQuantum(
3725  color.green);
3726  (*image)->colormap[i].blue=ScaleShortToQuantum(
3727  color.blue);
3728  }
3729  (void) SyncImage(*image);
3730  }
3731  break;
3732  }
3733  case FloodfillMethod:
3734  case FillToBorderMethod:
3735  {
3736  DrawInfo
3737  *draw_info;
3738 
3740  target;
3741 
3742  /*
3743  Update color information using floodfill algorithm.
3744  */
3745  (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
3746  (ssize_t) y_offset,&target,exception);
3747  if (method == FillToBorderMethod)
3748  {
3749  target.red=(MagickRealType)
3750  ScaleShortToQuantum(border_color.red);
3751  target.green=(MagickRealType)
3752  ScaleShortToQuantum(border_color.green);
3753  target.blue=(MagickRealType)
3754  ScaleShortToQuantum(border_color.blue);
3755  }
3756  draw_info=CloneDrawInfo(resource_info->image_info,
3757  (DrawInfo *) NULL);
3758  (void) QueryColorDatabase(resource_info->pen_colors[pen_id],
3759  &draw_info->fill,exception);
3760  (void) FloodfillPaintImage(*image,DefaultChannels,draw_info,&target,
3761  (ssize_t) x_offset,(ssize_t) y_offset,
3762  method == FloodfillMethod ? MagickFalse : MagickTrue);
3763  draw_info=DestroyDrawInfo(draw_info);
3764  break;
3765  }
3766  case ResetMethod:
3767  {
3768  /*
3769  Update color information using reset algorithm.
3770  */
3771  if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3772  return(MagickFalse);
3773  for (y=0; y < (int) (*image)->rows; y++)
3774  {
3775  q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3776  (*image)->columns,1,exception);
3777  if (q == (PixelPacket *) NULL)
3778  break;
3779  for (x=0; x < (int) (*image)->columns; x++)
3780  {
3781  q->red=ScaleShortToQuantum(color.red);
3782  q->green=ScaleShortToQuantum(color.green);
3783  q->blue=ScaleShortToQuantum(color.blue);
3784  q++;
3785  }
3786  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3787  break;
3788  }
3789  break;
3790  }
3791  }
3792  image_view=DestroyCacheView(image_view);
3793  state&=(~UpdateConfigurationState);
3794  }
3795  } while ((state & ExitState) == 0);
3796  (void) XSelectInput(display,windows->image.id,
3797  windows->image.attributes.event_mask);
3798  XSetCursorState(display,windows,MagickFalse);
3799  (void) XFreeCursor(display,cursor);
3800  return(MagickTrue);
3801 }
3802 ␌
3803 /*
3804 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3805 % %
3806 % %
3807 % %
3808 + X C o m p o s i t e I m a g e %
3809 % %
3810 % %
3811 % %
3812 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3813 %
3814 % XCompositeImage() requests an image name from the user, reads the image and
3815 % composites it with the X window image at a location the user chooses with
3816 % the pointer.
3817 %
3818 % The format of the XCompositeImage method is:
3819 %
3820 % MagickBooleanType XCompositeImage(Display *display,
3821 % XResourceInfo *resource_info,XWindows *windows,Image *image)
3822 %
3823 % A description of each parameter follows:
3824 %
3825 % o display: Specifies a connection to an X server; returned from
3826 % XOpenDisplay.
3827 %
3828 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3829 %
3830 % o windows: Specifies a pointer to a XWindows structure.
3831 %
3832 % o image: the image; returned from ReadImage.
3833 %
3834 */
3835 static MagickBooleanType XCompositeImage(Display *display,
3836  XResourceInfo *resource_info,XWindows *windows,Image *image)
3837 {
3838  const char
3839  *const CompositeMenu[] =
3840  {
3841  "Operators",
3842  "Dissolve",
3843  "Displace",
3844  "Help",
3845  "Dismiss",
3846  (char *) NULL
3847  };
3848 
3849  static char
3850  displacement_geometry[MaxTextExtent] = "30x30",
3851  filename[MaxTextExtent] = "\0";
3852 
3853  static CompositeOperator
3854  compose = CopyCompositeOp;
3855 
3856  static const ModeType
3857  CompositeCommands[] =
3858  {
3859  CompositeOperatorsCommand,
3860  CompositeDissolveCommand,
3861  CompositeDisplaceCommand,
3862  CompositeHelpCommand,
3863  CompositeDismissCommand
3864  };
3865 
3866  char
3867  text[MaxTextExtent];
3868 
3869  Cursor
3870  cursor;
3871 
3872  Image
3873  *composite_image;
3874 
3875  int
3876  entry,
3877  id,
3878  x,
3879  y;
3880 
3881  MagickRealType
3882  blend,
3883  scale_factor;
3884 
3886  highlight_info,
3887  composite_info;
3888 
3889  unsigned int
3890  height,
3891  width;
3892 
3893  size_t
3894  state;
3895 
3896  XEvent
3897  event;
3898 
3899  /*
3900  Request image file name from user.
3901  */
3902  XFileBrowserWidget(display,windows,"Composite",filename);
3903  if (*filename == '\0')
3904  return(MagickTrue);
3905  /*
3906  Read image.
3907  */
3908  XSetCursorState(display,windows,MagickTrue);
3909  XCheckRefreshWindows(display,windows);
3910  (void) CopyMagickString(resource_info->image_info->filename,filename,
3911  MaxTextExtent);
3912  composite_image=ReadImage(resource_info->image_info,&image->exception);
3913  CatchException(&image->exception);
3914  XSetCursorState(display,windows,MagickFalse);
3915  if (composite_image == (Image *) NULL)
3916  return(MagickFalse);
3917  /*
3918  Map Command widget.
3919  */
3920  (void) CloneString(&windows->command.name,"Composite");
3921  windows->command.data=1;
3922  (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3923  (void) XMapRaised(display,windows->command.id);
3924  XClientMessage(display,windows->image.id,windows->im_protocols,
3925  windows->im_update_widget,CurrentTime);
3926  /*
3927  Track pointer until button 1 is pressed.
3928  */
3929  XQueryPosition(display,windows->image.id,&x,&y);
3930  (void) XSelectInput(display,windows->image.id,
3931  windows->image.attributes.event_mask | PointerMotionMask);
3932  composite_info.x=(ssize_t) windows->image.x+x;
3933  composite_info.y=(ssize_t) windows->image.y+y;
3934  composite_info.width=0;
3935  composite_info.height=0;
3936  cursor=XCreateFontCursor(display,XC_ul_angle);
3937  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3938  blend=0.0;
3939  state=DefaultState;
3940  do
3941  {
3942  if (windows->info.mapped != MagickFalse)
3943  {
3944  /*
3945  Display pointer position.
3946  */
3947  (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
3948  (long) composite_info.x,(long) composite_info.y);
3949  XInfoWidget(display,windows,text);
3950  }
3951  highlight_info=composite_info;
3952  highlight_info.x=composite_info.x-windows->image.x;
3953  highlight_info.y=composite_info.y-windows->image.y;
3954  XHighlightRectangle(display,windows->image.id,
3955  windows->image.highlight_context,&highlight_info);
3956  /*
3957  Wait for next event.
3958  */
3959  XScreenEvent(display,windows,&event);
3960  XHighlightRectangle(display,windows->image.id,
3961  windows->image.highlight_context,&highlight_info);
3962  if (event.xany.window == windows->command.id)
3963  {
3964  /*
3965  Select a command from the Command widget.
3966  */
3967  id=XCommandWidget(display,windows,CompositeMenu,&event);
3968  if (id < 0)
3969  continue;
3970  switch (CompositeCommands[id])
3971  {
3972  case CompositeOperatorsCommand:
3973  {
3974  char
3975  command[MaxTextExtent],
3976  **operators;
3977 
3978  /*
3979  Select a command from the pop-up menu.
3980  */
3981  operators=GetCommandOptions(MagickComposeOptions);
3982  if (operators == (char **) NULL)
3983  break;
3984  entry=XMenuWidget(display,windows,CompositeMenu[id],
3985  (const char **) operators,command);
3986  if (entry >= 0)
3987  compose=(CompositeOperator) ParseCommandOption(
3988  MagickComposeOptions,MagickFalse,operators[entry]);
3989  operators=DestroyStringList(operators);
3990  break;
3991  }
3992  case CompositeDissolveCommand:
3993  {
3994  static char
3995  factor[MaxTextExtent] = "20.0";
3996 
3997  /*
3998  Dissolve the two images a given percent.
3999  */
4000  (void) XSetFunction(display,windows->image.highlight_context,
4001  GXcopy);
4002  (void) XDialogWidget(display,windows,"Dissolve",
4003  "Enter the blend factor (0.0 - 99.9%):",factor);
4004  (void) XSetFunction(display,windows->image.highlight_context,
4005  GXinvert);
4006  if (*factor == '\0')
4007  break;
4008  blend=StringToDouble(factor,(char **) NULL);
4009  compose=DissolveCompositeOp;
4010  break;
4011  }
4012  case CompositeDisplaceCommand:
4013  {
4014  /*
4015  Get horizontal and vertical scale displacement geometry.
4016  */
4017  (void) XSetFunction(display,windows->image.highlight_context,
4018  GXcopy);
4019  (void) XDialogWidget(display,windows,"Displace",
4020  "Enter the horizontal and vertical scale:",displacement_geometry);
4021  (void) XSetFunction(display,windows->image.highlight_context,
4022  GXinvert);
4023  if (*displacement_geometry == '\0')
4024  break;
4025  compose=DisplaceCompositeOp;
4026  break;
4027  }
4028  case CompositeHelpCommand:
4029  {
4030  (void) XSetFunction(display,windows->image.highlight_context,
4031  GXcopy);
4032  XTextViewHelp(display,resource_info,windows,MagickFalse,
4033  "Help Viewer - Image Composite",ImageCompositeHelp);
4034  (void) XSetFunction(display,windows->image.highlight_context,
4035  GXinvert);
4036  break;
4037  }
4038  case CompositeDismissCommand:
4039  {
4040  /*
4041  Prematurely exit.
4042  */
4043  state|=EscapeState;
4044  state|=ExitState;
4045  break;
4046  }
4047  default:
4048  break;
4049  }
4050  continue;
4051  }
4052  switch (event.type)
4053  {
4054  case ButtonPress:
4055  {
4056  if (resource_info->debug != MagickFalse)
4057  (void) LogMagickEvent(X11Event,GetMagickModule(),
4058  "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4059  event.xbutton.button,event.xbutton.x,event.xbutton.y);
4060  if (event.xbutton.button != Button1)
4061  break;
4062  if (event.xbutton.window != windows->image.id)
4063  break;
4064  /*
4065  Change cursor.
4066  */
4067  composite_info.width=composite_image->columns;
4068  composite_info.height=composite_image->rows;
4069  (void) XCheckDefineCursor(display,windows->image.id,cursor);
4070  composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4071  composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4072  break;
4073  }
4074  case ButtonRelease:
4075  {
4076  if (resource_info->debug != MagickFalse)
4077  (void) LogMagickEvent(X11Event,GetMagickModule(),
4078  "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4079  event.xbutton.button,event.xbutton.x,event.xbutton.y);
4080  if (event.xbutton.button != Button1)
4081  break;
4082  if (event.xbutton.window != windows->image.id)
4083  break;
4084  if ((composite_info.width != 0) && (composite_info.height != 0))
4085  {
4086  /*
4087  User has selected the location of the composite image.
4088  */
4089  composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4090  composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4091  state|=ExitState;
4092  }
4093  break;
4094  }
4095  case Expose:
4096  break;
4097  case KeyPress:
4098  {
4099  char
4100  command[MaxTextExtent];
4101 
4102  KeySym
4103  key_symbol;
4104 
4105  int
4106  length;
4107 
4108  if (event.xkey.window != windows->image.id)
4109  break;
4110  /*
4111  Respond to a user key press.
4112  */
4113  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4114  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4115  *(command+length)='\0';
4116  if (resource_info->debug != MagickFalse)
4117  (void) LogMagickEvent(X11Event,GetMagickModule(),
4118  "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4119  switch ((int) key_symbol)
4120  {
4121  case XK_Escape:
4122  case XK_F20:
4123  {
4124  /*
4125  Prematurely exit.
4126  */
4127  composite_image=DestroyImage(composite_image);
4128  state|=EscapeState;
4129  state|=ExitState;
4130  break;
4131  }
4132  case XK_F1:
4133  case XK_Help:
4134  {
4135  (void) XSetFunction(display,windows->image.highlight_context,
4136  GXcopy);
4137  XTextViewHelp(display,resource_info,windows,MagickFalse,
4138  "Help Viewer - Image Composite",ImageCompositeHelp);
4139  (void) XSetFunction(display,windows->image.highlight_context,
4140  GXinvert);
4141  break;
4142  }
4143  default:
4144  {
4145  (void) XBell(display,0);
4146  break;
4147  }
4148  }
4149  break;
4150  }
4151  case MotionNotify:
4152  {
4153  /*
4154  Map and unmap Info widget as text cursor crosses its boundaries.
4155  */
4156  x=event.xmotion.x;
4157  y=event.xmotion.y;
4158  if (windows->info.mapped != MagickFalse)
4159  {
4160  if ((x < (int) (windows->info.x+windows->info.width)) &&
4161  (y < (int) (windows->info.y+windows->info.height)))
4162  (void) XWithdrawWindow(display,windows->info.id,
4163  windows->info.screen);
4164  }
4165  else
4166  if ((x > (int) (windows->info.x+windows->info.width)) ||
4167  (y > (int) (windows->info.y+windows->info.height)))
4168  (void) XMapWindow(display,windows->info.id);
4169  composite_info.x=(ssize_t) windows->image.x+x;
4170  composite_info.y=(ssize_t) windows->image.y+y;
4171  break;
4172  }
4173  default:
4174  {
4175  if (resource_info->debug != MagickFalse)
4176  (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4177  event.type);
4178  break;
4179  }
4180  }
4181  } while ((state & ExitState) == 0);
4182  (void) XSelectInput(display,windows->image.id,
4183  windows->image.attributes.event_mask);
4184  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4185  XSetCursorState(display,windows,MagickFalse);
4186  (void) XFreeCursor(display,cursor);
4187  if ((state & EscapeState) != 0)
4188  return(MagickTrue);
4189  /*
4190  Image compositing is relative to image configuration.
4191  */
4192  XSetCursorState(display,windows,MagickTrue);
4193  XCheckRefreshWindows(display,windows);
4194  width=(unsigned int) image->columns;
4195  height=(unsigned int) image->rows;
4196  x=0;
4197  y=0;
4198  if (windows->image.crop_geometry != (char *) NULL)
4199  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4200  scale_factor=(MagickRealType) width/windows->image.ximage->width;
4201  composite_info.x+=x;
4202  composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4203  composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4204  scale_factor=(MagickRealType) height/windows->image.ximage->height;
4205  composite_info.y+=y;
4206  composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4207  composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4208  if ((composite_info.width != composite_image->columns) ||
4209  (composite_info.height != composite_image->rows))
4210  {
4211  Image
4212  *resize_image;
4213 
4214  /*
4215  Scale composite image.
4216  */
4217  resize_image=ResizeImage(composite_image,composite_info.width,
4218  composite_info.height,composite_image->filter,composite_image->blur,
4219  &image->exception);
4220  composite_image=DestroyImage(composite_image);
4221  if (resize_image == (Image *) NULL)
4222  {
4223  XSetCursorState(display,windows,MagickFalse);
4224  return(MagickFalse);
4225  }
4226  composite_image=resize_image;
4227  }
4228  if (compose == DisplaceCompositeOp)
4229  (void) SetImageArtifact(composite_image,"compose:args",
4230  displacement_geometry);
4231  if (blend != 0.0)
4232  {
4233  CacheView
4234  *image_view;
4235 
4237  *exception;
4238 
4239  int
4240  y;
4241 
4242  Quantum
4243  opacity;
4244 
4245  int
4246  x;
4247 
4248  PixelPacket
4249  *q;
4250 
4251  /*
4252  Create mattes for blending.
4253  */
4254  (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel);
4255  opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4256  ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4257  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
4258  return(MagickFalse);
4259  image->matte=MagickTrue;
4260  exception=(&image->exception);
4261  image_view=AcquireAuthenticCacheView(image,exception);
4262  for (y=0; y < (int) image->rows; y++)
4263  {
4264  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4265  exception);
4266  if (q == (PixelPacket *) NULL)
4267  break;
4268  for (x=0; x < (int) image->columns; x++)
4269  {
4270  q->opacity=opacity;
4271  q++;
4272  }
4273  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4274  break;
4275  }
4276  image_view=DestroyCacheView(image_view);
4277  }
4278  /*
4279  Composite image with X Image window.
4280  */
4281  (void) CompositeImage(image,compose,composite_image,composite_info.x,
4282  composite_info.y);
4283  composite_image=DestroyImage(composite_image);
4284  XSetCursorState(display,windows,MagickFalse);
4285  /*
4286  Update image configuration.
4287  */
4288  XConfigureImageColormap(display,resource_info,windows,image);
4289  (void) XConfigureImage(display,resource_info,windows,image);
4290  return(MagickTrue);
4291 }
4292 ␌
4293 /*
4294 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4295 % %
4296 % %
4297 % %
4298 + X C o n f i g u r e I m a g e %
4299 % %
4300 % %
4301 % %
4302 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4303 %
4304 % XConfigureImage() creates a new X image. It also notifies the window
4305 % manager of the new image size and configures the transient widows.
4306 %
4307 % The format of the XConfigureImage method is:
4308 %
4309 % MagickBooleanType XConfigureImage(Display *display,
4310 % XResourceInfo *resource_info,XWindows *windows,Image *image)
4311 %
4312 % A description of each parameter follows:
4313 %
4314 % o display: Specifies a connection to an X server; returned from
4315 % XOpenDisplay.
4316 %
4317 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4318 %
4319 % o windows: Specifies a pointer to a XWindows structure.
4320 %
4321 % o image: the image.
4322 %
4323 %
4324 */
4325 static MagickBooleanType XConfigureImage(Display *display,
4326  XResourceInfo *resource_info,XWindows *windows,Image *image)
4327 {
4328  char
4329  geometry[MaxTextExtent];
4330 
4331  MagickStatusType
4332  status;
4333 
4334  size_t
4335  mask,
4336  height,
4337  width;
4338 
4339  ssize_t
4340  x,
4341  y;
4342 
4343  XSizeHints
4344  *size_hints;
4345 
4346  XWindowChanges
4347  window_changes;
4348 
4349  /*
4350  Dismiss if window dimensions are zero.
4351  */
4352  width=(unsigned int) windows->image.window_changes.width;
4353  height=(unsigned int) windows->image.window_changes.height;
4354  if (resource_info->debug != MagickFalse)
4355  (void) LogMagickEvent(X11Event,GetMagickModule(),
4356  "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4357  windows->image.ximage->height,(double) width,(double) height);
4358  if ((width*height) == 0)
4359  return(MagickTrue);
4360  x=0;
4361  y=0;
4362  /*
4363  Resize image to fit Image window dimensions.
4364  */
4365  XSetCursorState(display,windows,MagickTrue);
4366  (void) XFlush(display);
4367  if (((int) width != windows->image.ximage->width) ||
4368  ((int) height != windows->image.ximage->height))
4369  image->taint=MagickTrue;
4370  windows->magnify.x=(int)
4371  width*windows->magnify.x/windows->image.ximage->width;
4372  windows->magnify.y=(int)
4373  height*windows->magnify.y/windows->image.ximage->height;
4374  windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4375  windows->image.y=(int)
4376  (height*windows->image.y/windows->image.ximage->height);
4377  status=XMakeImage(display,resource_info,&windows->image,image,
4378  (unsigned int) width,(unsigned int) height);
4379  if (status == MagickFalse)
4380  XNoticeWidget(display,windows,"Unable to configure X image:",
4381  windows->image.name);
4382  /*
4383  Notify window manager of the new configuration.
4384  */
4385  if (resource_info->image_geometry != (char *) NULL)
4386  (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4387  resource_info->image_geometry);
4388  else
4389  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4390  XDisplayWidth(display,windows->image.screen),
4391  XDisplayHeight(display,windows->image.screen));
4392  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4393  window_changes.width=(int) width;
4394  if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4395  window_changes.width=XDisplayWidth(display,windows->image.screen);
4396  window_changes.height=(int) height;
4397  if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4398  window_changes.height=XDisplayHeight(display,windows->image.screen);
4399  mask=(size_t) (CWWidth | CWHeight);
4400  if (resource_info->backdrop)
4401  {
4402  mask|=CWX | CWY;
4403  window_changes.x=(int)
4404  ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4405  window_changes.y=(int)
4406  ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4407  }
4408  (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4409  (unsigned int) mask,&window_changes);
4410  (void) XClearWindow(display,windows->image.id);
4411  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4412  /*
4413  Update Magnify window configuration.
4414  */
4415  if (windows->magnify.mapped != MagickFalse)
4416  XMakeMagnifyImage(display,windows);
4417  windows->pan.crop_geometry=windows->image.crop_geometry;
4418  XBestIconSize(display,&windows->pan,image);
4419  while (((windows->pan.width << 1) < MaxIconSize) &&
4420  ((windows->pan.height << 1) < MaxIconSize))
4421  {
4422  windows->pan.width<<=1;
4423  windows->pan.height<<=1;
4424  }
4425  if (windows->pan.geometry != (char *) NULL)
4426  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4427  &windows->pan.width,&windows->pan.height);
4428  window_changes.width=(int) windows->pan.width;
4429  window_changes.height=(int) windows->pan.height;
4430  size_hints=XAllocSizeHints();
4431  if (size_hints != (XSizeHints *) NULL)
4432  {
4433  /*
4434  Set new size hints.
4435  */
4436  size_hints->flags=PSize | PMinSize | PMaxSize;
4437  size_hints->width=window_changes.width;
4438  size_hints->height=window_changes.height;
4439  size_hints->min_width=size_hints->width;
4440  size_hints->min_height=size_hints->height;
4441  size_hints->max_width=size_hints->width;
4442  size_hints->max_height=size_hints->height;
4443  (void) XSetNormalHints(display,windows->pan.id,size_hints);
4444  (void) XFree((void *) size_hints);
4445  }
4446  (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4447  (unsigned int) (CWWidth | CWHeight),&window_changes);
4448  /*
4449  Update icon window configuration.
4450  */
4451  windows->icon.crop_geometry=windows->image.crop_geometry;
4452  XBestIconSize(display,&windows->icon,image);
4453  window_changes.width=(int) windows->icon.width;
4454  window_changes.height=(int) windows->icon.height;
4455  (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4456  (unsigned int) (CWWidth | CWHeight),&window_changes);
4457  XSetCursorState(display,windows,MagickFalse);
4458  return(status != 0 ? MagickTrue : MagickFalse);
4459 }
4460 ␌
4461 /*
4462 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4463 % %
4464 % %
4465 % %
4466 + X C r o p I m a g e %
4467 % %
4468 % %
4469 % %
4470 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4471 %
4472 % XCropImage() allows the user to select a region of the image and crop, copy,
4473 % or cut it. For copy or cut, the image can subsequently be composited onto
4474 % the image with XPasteImage.
4475 %
4476 % The format of the XCropImage method is:
4477 %
4478 % MagickBooleanType XCropImage(Display *display,
4479 % XResourceInfo *resource_info,XWindows *windows,Image *image,
4480 % const ClipboardMode mode)
4481 %
4482 % A description of each parameter follows:
4483 %
4484 % o display: Specifies a connection to an X server; returned from
4485 % XOpenDisplay.
4486 %
4487 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4488 %
4489 % o windows: Specifies a pointer to a XWindows structure.
4490 %
4491 % o image: the image; returned from ReadImage.
4492 %
4493 % o mode: This unsigned value specified whether the image should be
4494 % cropped, copied, or cut.
4495 %
4496 */
4497 static MagickBooleanType XCropImage(Display *display,
4498  XResourceInfo *resource_info,XWindows *windows,Image *image,
4499  const ClipboardMode mode)
4500 {
4501  static const char
4502  *CropModeMenu[] =
4503  {
4504  "Help",
4505  "Dismiss",
4506  (char *) NULL
4507  },
4508  *RectifyModeMenu[] =
4509  {
4510  "Crop",
4511  "Help",
4512  "Dismiss",
4513  (char *) NULL
4514  };
4515 
4516  static const ModeType
4517  CropCommands[] =
4518  {
4519  CropHelpCommand,
4520  CropDismissCommand
4521  },
4522  RectifyCommands[] =
4523  {
4524  RectifyCopyCommand,
4525  RectifyHelpCommand,
4526  RectifyDismissCommand
4527  };
4528 
4529  CacheView
4530  *image_view;
4531 
4532  char
4533  command[MaxTextExtent],
4534  text[MaxTextExtent];
4535 
4536  Cursor
4537  cursor;
4538 
4540  *exception;
4541 
4542  int
4543  id,
4544  x,
4545  y;
4546 
4547  KeySym
4548  key_symbol;
4549 
4550  Image
4551  *crop_image;
4552 
4553  MagickRealType
4554  scale_factor;
4555 
4557  crop_info,
4558  highlight_info;
4559 
4560  PixelPacket
4561  *q;
4562 
4563  unsigned int
4564  height,
4565  width;
4566 
4567  size_t
4568  state;
4569 
4570  XEvent
4571  event;
4572 
4573  /*
4574  Map Command widget.
4575  */
4576  switch (mode)
4577  {
4578  case CopyMode:
4579  {
4580  (void) CloneString(&windows->command.name,"Copy");
4581  break;
4582  }
4583  case CropMode:
4584  {
4585  (void) CloneString(&windows->command.name,"Crop");
4586  break;
4587  }
4588  case CutMode:
4589  {
4590  (void) CloneString(&windows->command.name,"Cut");
4591  break;
4592  }
4593  }
4594  RectifyModeMenu[0]=windows->command.name;
4595  windows->command.data=0;
4596  (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4597  (void) XMapRaised(display,windows->command.id);
4598  XClientMessage(display,windows->image.id,windows->im_protocols,
4599  windows->im_update_widget,CurrentTime);
4600  /*
4601  Track pointer until button 1 is pressed.
4602  */
4603  XQueryPosition(display,windows->image.id,&x,&y);
4604  (void) XSelectInput(display,windows->image.id,
4605  windows->image.attributes.event_mask | PointerMotionMask);
4606  crop_info.x=(ssize_t) windows->image.x+x;
4607  crop_info.y=(ssize_t) windows->image.y+y;
4608  crop_info.width=0;
4609  crop_info.height=0;
4610  cursor=XCreateFontCursor(display,XC_fleur);
4611  state=DefaultState;
4612  do
4613  {
4614  if (windows->info.mapped != MagickFalse)
4615  {
4616  /*
4617  Display pointer position.
4618  */
4619  (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4620  (long) crop_info.x,(long) crop_info.y);
4621  XInfoWidget(display,windows,text);
4622  }
4623  /*
4624  Wait for next event.
4625  */
4626  XScreenEvent(display,windows,&event);
4627  if (event.xany.window == windows->command.id)
4628  {
4629  /*
4630  Select a command from the Command widget.
4631  */
4632  id=XCommandWidget(display,windows,CropModeMenu,&event);
4633  if (id < 0)
4634  continue;
4635  switch (CropCommands[id])
4636  {
4637  case CropHelpCommand:
4638  {
4639  switch (mode)
4640  {
4641  case CopyMode:
4642  {
4643  XTextViewHelp(display,resource_info,windows,MagickFalse,
4644  "Help Viewer - Image Copy",ImageCopyHelp);
4645  break;
4646  }
4647  case CropMode:
4648  {
4649  XTextViewHelp(display,resource_info,windows,MagickFalse,
4650  "Help Viewer - Image Crop",ImageCropHelp);
4651  break;
4652  }
4653  case CutMode:
4654  {
4655  XTextViewHelp(display,resource_info,windows,MagickFalse,
4656  "Help Viewer - Image Cut",ImageCutHelp);
4657  break;
4658  }
4659  }
4660  break;
4661  }
4662  case CropDismissCommand:
4663  {
4664  /*
4665  Prematurely exit.
4666  */
4667  state|=EscapeState;
4668  state|=ExitState;
4669  break;
4670  }
4671  default:
4672  break;
4673  }
4674  continue;
4675  }
4676  switch (event.type)
4677  {
4678  case ButtonPress:
4679  {
4680  if (event.xbutton.button != Button1)
4681  break;
4682  if (event.xbutton.window != windows->image.id)
4683  break;
4684  /*
4685  Note first corner of cropping rectangle-- exit loop.
4686  */
4687  (void) XCheckDefineCursor(display,windows->image.id,cursor);
4688  crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4689  crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4690  state|=ExitState;
4691  break;
4692  }
4693  case ButtonRelease:
4694  break;
4695  case Expose:
4696  break;
4697  case KeyPress:
4698  {
4699  if (event.xkey.window != windows->image.id)
4700  break;
4701  /*
4702  Respond to a user key press.
4703  */
4704  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4705  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4706  switch ((int) key_symbol)
4707  {
4708  case XK_Escape:
4709  case XK_F20:
4710  {
4711  /*
4712  Prematurely exit.
4713  */
4714  state|=EscapeState;
4715  state|=ExitState;
4716  break;
4717  }
4718  case XK_F1:
4719  case XK_Help:
4720  {
4721  switch (mode)
4722  {
4723  case CopyMode:
4724  {
4725  XTextViewHelp(display,resource_info,windows,MagickFalse,
4726  "Help Viewer - Image Copy",ImageCopyHelp);
4727  break;
4728  }
4729  case CropMode:
4730  {
4731  XTextViewHelp(display,resource_info,windows,MagickFalse,
4732  "Help Viewer - Image Crop",ImageCropHelp);
4733  break;
4734  }
4735  case CutMode:
4736  {
4737  XTextViewHelp(display,resource_info,windows,MagickFalse,
4738  "Help Viewer - Image Cut",ImageCutHelp);
4739  break;
4740  }
4741  }
4742  break;
4743  }
4744  default:
4745  {
4746  (void) XBell(display,0);
4747  break;
4748  }
4749  }
4750  break;
4751  }
4752  case MotionNotify:
4753  {
4754  if (event.xmotion.window != windows->image.id)
4755  break;
4756  /*
4757  Map and unmap Info widget as text cursor crosses its boundaries.
4758  */
4759  x=event.xmotion.x;
4760  y=event.xmotion.y;
4761  if (windows->info.mapped != MagickFalse)
4762  {
4763  if ((x < (int) (windows->info.x+windows->info.width)) &&
4764  (y < (int) (windows->info.y+windows->info.height)))
4765  (void) XWithdrawWindow(display,windows->info.id,
4766  windows->info.screen);
4767  }
4768  else
4769  if ((x > (int) (windows->info.x+windows->info.width)) ||
4770  (y > (int) (windows->info.y+windows->info.height)))
4771  (void) XMapWindow(display,windows->info.id);
4772  crop_info.x=(ssize_t) windows->image.x+x;
4773  crop_info.y=(ssize_t) windows->image.y+y;
4774  break;
4775  }
4776  default:
4777  break;
4778  }
4779  } while ((state & ExitState) == 0);
4780  (void) XSelectInput(display,windows->image.id,
4781  windows->image.attributes.event_mask);
4782  if ((state & EscapeState) != 0)
4783  {
4784  /*
4785  User want to exit without cropping.
4786  */
4787  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4788  (void) XFreeCursor(display,cursor);
4789  return(MagickTrue);
4790  }
4791  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4792  do
4793  {
4794  /*
4795  Size rectangle as pointer moves until the mouse button is released.
4796  */
4797  x=(int) crop_info.x;
4798  y=(int) crop_info.y;
4799  crop_info.width=0;
4800  crop_info.height=0;
4801  state=DefaultState;
4802  do
4803  {
4804  highlight_info=crop_info;
4805  highlight_info.x=crop_info.x-windows->image.x;
4806  highlight_info.y=crop_info.y-windows->image.y;
4807  if ((highlight_info.width > 3) && (highlight_info.height > 3))
4808  {
4809  /*
4810  Display info and draw cropping rectangle.
4811  */
4812  if (windows->info.mapped == MagickFalse)
4813  (void) XMapWindow(display,windows->info.id);
4814  (void) FormatLocaleString(text,MaxTextExtent,
4815  " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4816  crop_info.height,(double) crop_info.x,(double) crop_info.y);
4817  XInfoWidget(display,windows,text);
4818  XHighlightRectangle(display,windows->image.id,
4819  windows->image.highlight_context,&highlight_info);
4820  }
4821  else
4822  if (windows->info.mapped != MagickFalse)
4823  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4824  /*
4825  Wait for next event.
4826  */
4827  XScreenEvent(display,windows,&event);
4828  if ((highlight_info.width > 3) && (highlight_info.height > 3))
4829  XHighlightRectangle(display,windows->image.id,
4830  windows->image.highlight_context,&highlight_info);
4831  switch (event.type)
4832  {
4833  case ButtonPress:
4834  {
4835  crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4836  crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4837  break;
4838  }
4839  case ButtonRelease:
4840  {
4841  /*
4842  User has committed to cropping rectangle.
4843  */
4844  crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4845  crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4846  XSetCursorState(display,windows,MagickFalse);
4847  state|=ExitState;
4848  windows->command.data=0;
4849  (void) XCommandWidget(display,windows,RectifyModeMenu,
4850  (XEvent *) NULL);
4851  break;
4852  }
4853  case Expose:
4854  break;
4855  case MotionNotify:
4856  {
4857  crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4858  crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4859  }
4860  default:
4861  break;
4862  }
4863  if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4864  ((state & ExitState) != 0))
4865  {
4866  /*
4867  Check boundary conditions.
4868  */
4869  if (crop_info.x < 0)
4870  crop_info.x=0;
4871  else
4872  if (crop_info.x > (ssize_t) windows->image.ximage->width)
4873  crop_info.x=(ssize_t) windows->image.ximage->width;
4874  if ((int) crop_info.x < x)
4875  crop_info.width=(unsigned int) (x-crop_info.x);
4876  else
4877  {
4878  crop_info.width=(unsigned int) (crop_info.x-x);
4879  crop_info.x=(ssize_t) x;
4880  }
4881  if (crop_info.y < 0)
4882  crop_info.y=0;
4883  else
4884  if (crop_info.y > (ssize_t) windows->image.ximage->height)
4885  crop_info.y=(ssize_t) windows->image.ximage->height;
4886  if ((int) crop_info.y < y)
4887  crop_info.height=(unsigned int) (y-crop_info.y);
4888  else
4889  {
4890  crop_info.height=(unsigned int) (crop_info.y-y);
4891  crop_info.y=(ssize_t) y;
4892  }
4893  }
4894  } while ((state & ExitState) == 0);
4895  /*
4896  Wait for user to grab a corner of the rectangle or press return.
4897  */
4898  state=DefaultState;
4899  (void) XMapWindow(display,windows->info.id);
4900  do
4901  {
4902  if (windows->info.mapped != MagickFalse)
4903  {
4904  /*
4905  Display pointer position.
4906  */
4907  (void) FormatLocaleString(text,MaxTextExtent,
4908  " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4909  crop_info.height,(double) crop_info.x,(double) crop_info.y);
4910  XInfoWidget(display,windows,text);
4911  }
4912  highlight_info=crop_info;
4913  highlight_info.x=crop_info.x-windows->image.x;
4914  highlight_info.y=crop_info.y-windows->image.y;
4915  if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4916  {
4917  state|=EscapeState;
4918  state|=ExitState;
4919  break;
4920  }
4921  XHighlightRectangle(display,windows->image.id,
4922  windows->image.highlight_context,&highlight_info);
4923  XScreenEvent(display,windows,&event);
4924  if (event.xany.window == windows->command.id)
4925  {
4926  /*
4927  Select a command from the Command widget.
4928  */
4929  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4930  id=XCommandWidget(display,windows,RectifyModeMenu,&event);
4931  (void) XSetFunction(display,windows->image.highlight_context,
4932  GXinvert);
4933  XHighlightRectangle(display,windows->image.id,
4934  windows->image.highlight_context,&highlight_info);
4935  if (id >= 0)
4936  switch (RectifyCommands[id])
4937  {
4938  case RectifyCopyCommand:
4939  {
4940  state|=ExitState;
4941  break;
4942  }
4943  case RectifyHelpCommand:
4944  {
4945  (void) XSetFunction(display,windows->image.highlight_context,
4946  GXcopy);
4947  switch (mode)
4948  {
4949  case CopyMode:
4950  {
4951  XTextViewHelp(display,resource_info,windows,MagickFalse,
4952  "Help Viewer - Image Copy",ImageCopyHelp);
4953  break;
4954  }
4955  case CropMode:
4956  {
4957  XTextViewHelp(display,resource_info,windows,MagickFalse,
4958  "Help Viewer - Image Crop",ImageCropHelp);
4959  break;
4960  }
4961  case CutMode:
4962  {
4963  XTextViewHelp(display,resource_info,windows,MagickFalse,
4964  "Help Viewer - Image Cut",ImageCutHelp);
4965  break;
4966  }
4967  }
4968  (void) XSetFunction(display,windows->image.highlight_context,
4969  GXinvert);
4970  break;
4971  }
4972  case RectifyDismissCommand:
4973  {
4974  /*
4975  Prematurely exit.
4976  */
4977  state|=EscapeState;
4978  state|=ExitState;
4979  break;
4980  }
4981  default:
4982  break;
4983  }
4984  continue;
4985  }
4986  XHighlightRectangle(display,windows->image.id,
4987  windows->image.highlight_context,&highlight_info);
4988  switch (event.type)
4989  {
4990  case ButtonPress:
4991  {
4992  if (event.xbutton.button != Button1)
4993  break;
4994  if (event.xbutton.window != windows->image.id)
4995  break;
4996  x=windows->image.x+event.xbutton.x;
4997  y=windows->image.y+event.xbutton.y;
4998  if ((x < (int) (crop_info.x+RoiDelta)) &&
4999  (x > (int) (crop_info.x-RoiDelta)) &&
5000  (y < (int) (crop_info.y+RoiDelta)) &&
5001  (y > (int) (crop_info.y-RoiDelta)))
5002  {
5003  crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5004  crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5005  state|=UpdateConfigurationState;
5006  break;
5007  }
5008  if ((x < (int) (crop_info.x+RoiDelta)) &&
5009  (x > (int) (crop_info.x-RoiDelta)) &&
5010  (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5011  (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5012  {
5013  crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5014  state|=UpdateConfigurationState;
5015  break;
5016  }
5017  if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5018  (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5019  (y < (int) (crop_info.y+RoiDelta)) &&
5020  (y > (int) (crop_info.y-RoiDelta)))
5021  {
5022  crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5023  state|=UpdateConfigurationState;
5024  break;
5025  }
5026  if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5027  (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5028  (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5029  (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5030  {
5031  state|=UpdateConfigurationState;
5032  break;
5033  }
5034  magick_fallthrough;
5035  }
5036  case ButtonRelease:
5037  {
5038  if (event.xbutton.window == windows->pan.id)
5039  if ((highlight_info.x != crop_info.x-windows->image.x) ||
5040  (highlight_info.y != crop_info.y-windows->image.y))
5041  XHighlightRectangle(display,windows->image.id,
5042  windows->image.highlight_context,&highlight_info);
5043  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5044  event.xbutton.time);
5045  break;
5046  }
5047  case Expose:
5048  {
5049  if (event.xexpose.window == windows->image.id)
5050  if (event.xexpose.count == 0)
5051  {
5052  event.xexpose.x=(int) highlight_info.x;
5053  event.xexpose.y=(int) highlight_info.y;
5054  event.xexpose.width=(int) highlight_info.width;
5055  event.xexpose.height=(int) highlight_info.height;
5056  XRefreshWindow(display,&windows->image,&event);
5057  }
5058  if (event.xexpose.window == windows->info.id)
5059  if (event.xexpose.count == 0)
5060  XInfoWidget(display,windows,text);
5061  break;
5062  }
5063  case KeyPress:
5064  {
5065  if (event.xkey.window != windows->image.id)
5066  break;
5067  /*
5068  Respond to a user key press.
5069  */
5070  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5071  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5072  switch ((int) key_symbol)
5073  {
5074  case XK_Escape:
5075  case XK_F20:
5076  {
5077  state|=EscapeState;
5078  magick_fallthrough;
5079  }
5080  case XK_Return:
5081  {
5082  state|=ExitState;
5083  break;
5084  }
5085  case XK_Home:
5086  case XK_KP_Home:
5087  {
5088  crop_info.x=(ssize_t) (windows->image.width/2L-crop_info.width/
5089  2L);
5090  crop_info.y=(ssize_t) (windows->image.height/2L-crop_info.height/
5091  2L);
5092  break;
5093  }
5094  case XK_Left:
5095  case XK_KP_Left:
5096  {
5097  crop_info.x--;
5098  break;
5099  }
5100  case XK_Up:
5101  case XK_KP_Up:
5102  case XK_Next:
5103  {
5104  crop_info.y--;
5105  break;
5106  }
5107  case XK_Right:
5108  case XK_KP_Right:
5109  {
5110  crop_info.x++;
5111  break;
5112  }
5113  case XK_Prior:
5114  case XK_Down:
5115  case XK_KP_Down:
5116  {
5117  crop_info.y++;
5118  break;
5119  }
5120  case XK_F1:
5121  case XK_Help:
5122  {
5123  (void) XSetFunction(display,windows->image.highlight_context,
5124  GXcopy);
5125  switch (mode)
5126  {
5127  case CopyMode:
5128  {
5129  XTextViewHelp(display,resource_info,windows,MagickFalse,
5130  "Help Viewer - Image Copy",ImageCopyHelp);
5131  break;
5132  }
5133  case CropMode:
5134  {
5135  XTextViewHelp(display,resource_info,windows,MagickFalse,
5136  "Help Viewer - Image Cropg",ImageCropHelp);
5137  break;
5138  }
5139  case CutMode:
5140  {
5141  XTextViewHelp(display,resource_info,windows,MagickFalse,
5142  "Help Viewer - Image Cutg",ImageCutHelp);
5143  break;
5144  }
5145  }
5146  (void) XSetFunction(display,windows->image.highlight_context,
5147  GXinvert);
5148  break;
5149  }
5150  default:
5151  {
5152  (void) XBell(display,0);
5153  break;
5154  }
5155  }
5156  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5157  event.xkey.time);
5158  break;
5159  }
5160  case KeyRelease:
5161  break;
5162  case MotionNotify:
5163  {
5164  if (event.xmotion.window != windows->image.id)
5165  break;
5166  /*
5167  Map and unmap Info widget as text cursor crosses its boundaries.
5168  */
5169  x=event.xmotion.x;
5170  y=event.xmotion.y;
5171  if (windows->info.mapped != MagickFalse)
5172  {
5173  if ((x < (int) (windows->info.x+windows->info.width)) &&
5174  (y < (int) (windows->info.y+windows->info.height)))
5175  (void) XWithdrawWindow(display,windows->info.id,
5176  windows->info.screen);
5177  }
5178  else
5179  if ((x > (int) (windows->info.x+windows->info.width)) ||
5180  (y > (int) (windows->info.y+windows->info.height)))
5181  (void) XMapWindow(display,windows->info.id);
5182  crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5183  crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5184  break;
5185  }
5186  case SelectionRequest:
5187  {
5188  XSelectionEvent
5189  notify;
5190 
5191  XSelectionRequestEvent
5192  *request;
5193 
5194  /*
5195  Set primary selection.
5196  */
5197  (void) FormatLocaleString(text,MaxTextExtent,
5198  "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5199  crop_info.height,(double) crop_info.x,(double) crop_info.y);
5200  request=(&(event.xselectionrequest));
5201  (void) XChangeProperty(request->display,request->requestor,
5202  request->property,request->target,8,PropModeReplace,
5203  (unsigned char *) text,(int) strlen(text));
5204  notify.type=SelectionNotify;
5205  notify.display=request->display;
5206  notify.requestor=request->requestor;
5207  notify.selection=request->selection;
5208  notify.target=request->target;
5209  notify.time=request->time;
5210  if (request->property == None)
5211  notify.property=request->target;
5212  else
5213  notify.property=request->property;
5214  (void) XSendEvent(request->display,request->requestor,False,0,
5215  (XEvent *) &notify);
5216  }
5217  default:
5218  break;
5219  }
5220  if ((state & UpdateConfigurationState) != 0)
5221  {
5222  (void) XPutBackEvent(display,&event);
5223  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5224  break;
5225  }
5226  } while ((state & ExitState) == 0);
5227  } while ((state & ExitState) == 0);
5228  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5229  XSetCursorState(display,windows,MagickFalse);
5230  if ((state & EscapeState) != 0)
5231  return(MagickTrue);
5232  if (mode == CropMode)
5233  if (((int) crop_info.width != windows->image.ximage->width) ||
5234  ((int) crop_info.height != windows->image.ximage->height))
5235  {
5236  /*
5237  Reconfigure Image window as defined by cropping rectangle.
5238  */
5239  XSetCropGeometry(display,windows,&crop_info,image);
5240  windows->image.window_changes.width=(int) crop_info.width;
5241  windows->image.window_changes.height=(int) crop_info.height;
5242  (void) XConfigureImage(display,resource_info,windows,image);
5243  return(MagickTrue);
5244  }
5245  /*
5246  Copy image before applying image transforms.
5247  */
5248  XSetCursorState(display,windows,MagickTrue);
5249  XCheckRefreshWindows(display,windows);
5250  width=(unsigned int) image->columns;
5251  height=(unsigned int) image->rows;
5252  x=0;
5253  y=0;
5254  if (windows->image.crop_geometry != (char *) NULL)
5255  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5256  scale_factor=(MagickRealType) width/windows->image.ximage->width;
5257  crop_info.x+=x;
5258  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5259  crop_info.x+=image->page.x;
5260  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5261  scale_factor=(MagickRealType) height/windows->image.ximage->height;
5262  crop_info.y+=y;
5263  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5264  crop_info.y+=image->page.y;
5265  crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5266  crop_image=CropImage(image,&crop_info,&image->exception);
5267  XSetCursorState(display,windows,MagickFalse);
5268  if (crop_image == (Image *) NULL)
5269  return(MagickFalse);
5270  if (resource_info->copy_image != (Image *) NULL)
5271  resource_info->copy_image=DestroyImage(resource_info->copy_image);
5272  resource_info->copy_image=crop_image;
5273  if (mode == CopyMode)
5274  {
5275  (void) XConfigureImage(display,resource_info,windows,image);
5276  return(MagickTrue);
5277  }
5278  /*
5279  Cut image.
5280  */
5281  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
5282  return(MagickFalse);
5283  image->matte=MagickTrue;
5284  exception=(&image->exception);
5285  image_view=AcquireAuthenticCacheView(image,exception);
5286  for (y=0; y < (int) crop_info.height; y++)
5287  {
5288  q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5289  crop_info.width,1,exception);
5290  if (q == (PixelPacket *) NULL)
5291  break;
5292  for (x=0; x < (int) crop_info.width; x++)
5293  {
5294  q->opacity=(Quantum) TransparentOpacity;
5295  q++;
5296  }
5297  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5298  break;
5299  }
5300  image_view=DestroyCacheView(image_view);
5301  /*
5302  Update image configuration.
5303  */
5304  XConfigureImageColormap(display,resource_info,windows,image);
5305  (void) XConfigureImage(display,resource_info,windows,image);
5306  return(MagickTrue);
5307 }
5308 ␌
5309 /*
5310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5311 % %
5312 % %
5313 % %
5314 + X D r a w I m a g e %
5315 % %
5316 % %
5317 % %
5318 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5319 %
5320 % XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5321 % the image.
5322 %
5323 % The format of the XDrawEditImage method is:
5324 %
5325 % MagickBooleanType XDrawEditImage(Display *display,
5326 % XResourceInfo *resource_info,XWindows *windows,Image **image)
5327 %
5328 % A description of each parameter follows:
5329 %
5330 % o display: Specifies a connection to an X server; returned from
5331 % XOpenDisplay.
5332 %
5333 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5334 %
5335 % o windows: Specifies a pointer to a XWindows structure.
5336 %
5337 % o image: the image.
5338 %
5339 */
5340 static MagickBooleanType XDrawEditImage(Display *display,
5341  XResourceInfo *resource_info,XWindows *windows,Image **image)
5342 {
5343  const char
5344  *const DrawMenu[] =
5345  {
5346  "Element",
5347  "Color",
5348  "Stipple",
5349  "Width",
5350  "Undo",
5351  "Help",
5352  "Dismiss",
5353  (char *) NULL
5354  };
5355 
5356  static ElementType
5357  element = PointElement;
5358 
5359  static const ModeType
5360  DrawCommands[] =
5361  {
5362  DrawElementCommand,
5363  DrawColorCommand,
5364  DrawStippleCommand,
5365  DrawWidthCommand,
5366  DrawUndoCommand,
5367  DrawHelpCommand,
5368  DrawDismissCommand
5369  };
5370 
5371  static Pixmap
5372  stipple = (Pixmap) NULL;
5373 
5374  static unsigned int
5375  pen_id = 0,
5376  line_width = 1;
5377 
5378  char
5379  command[MaxTextExtent],
5380  text[MaxTextExtent];
5381 
5382  Cursor
5383  cursor;
5384 
5385  int
5386  entry,
5387  id,
5388  number_coordinates,
5389  x,
5390  y;
5391 
5392  MagickRealType
5393  degrees;
5394 
5395  MagickStatusType
5396  status;
5397 
5399  rectangle_info;
5400 
5401  int
5402  i;
5403 
5404  unsigned int
5405  distance,
5406  height,
5407  max_coordinates,
5408  width;
5409 
5410  size_t
5411  state;
5412 
5413  Window
5414  root_window;
5415 
5416  XDrawInfo
5417  draw_info;
5418 
5419  XEvent
5420  event;
5421 
5422  XPoint
5423  *coordinate_info;
5424 
5425  XSegment
5426  line_info;
5427 
5428  /*
5429  Allocate polygon info.
5430  */
5431  max_coordinates=2048;
5432  coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5433  sizeof(*coordinate_info));
5434  if (coordinate_info == (XPoint *) NULL)
5435  {
5436  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
5437  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5438  return(MagickFalse);
5439  }
5440  /*
5441  Map Command widget.
5442  */
5443  (void) CloneString(&windows->command.name,"Draw");
5444  windows->command.data=4;
5445  (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5446  (void) XMapRaised(display,windows->command.id);
5447  XClientMessage(display,windows->image.id,windows->im_protocols,
5448  windows->im_update_widget,CurrentTime);
5449  /*
5450  Wait for first button press.
5451  */
5452  root_window=XRootWindow(display,XDefaultScreen(display));
5453  draw_info.stencil=OpaqueStencil;
5454  status=MagickTrue;
5455  cursor=XCreateFontCursor(display,XC_tcross);
5456  for ( ; ; )
5457  {
5458  XQueryPosition(display,windows->image.id,&x,&y);
5459  (void) XSelectInput(display,windows->image.id,
5460  windows->image.attributes.event_mask | PointerMotionMask);
5461  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5462  state=DefaultState;
5463  do
5464  {
5465  if (windows->info.mapped != MagickFalse)
5466  {
5467  /*
5468  Display pointer position.
5469  */
5470  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5471  x+windows->image.x,y+windows->image.y);
5472  XInfoWidget(display,windows,text);
5473  }
5474  /*
5475  Wait for next event.
5476  */
5477  XScreenEvent(display,windows,&event);
5478  if (event.xany.window == windows->command.id)
5479  {
5480  /*
5481  Select a command from the Command widget.
5482  */
5483  id=XCommandWidget(display,windows,DrawMenu,&event);
5484  if (id < 0)
5485  continue;
5486  switch (DrawCommands[id])
5487  {
5488  case DrawElementCommand:
5489  {
5490  const char
5491  *const Elements[] =
5492  {
5493  "point",
5494  "line",
5495  "rectangle",
5496  "fill rectangle",
5497  "circle",
5498  "fill circle",
5499  "ellipse",
5500  "fill ellipse",
5501  "polygon",
5502  "fill polygon",
5503  (char *) NULL,
5504  };
5505 
5506  /*
5507  Select a command from the pop-up menu.
5508  */
5509  element=(ElementType) (XMenuWidget(display,windows,
5510  DrawMenu[id],Elements,command)+1);
5511  break;
5512  }
5513  case DrawColorCommand:
5514  {
5515  const char
5516  *ColorMenu[MaxNumberPens+1];
5517 
5518  int
5519  pen_number;
5520 
5521  MagickBooleanType
5522  transparent;
5523 
5524  XColor
5525  color;
5526 
5527  /*
5528  Initialize menu selections.
5529  */
5530  for (i=0; i < (int) (MaxNumberPens-2); i++)
5531  ColorMenu[i]=resource_info->pen_colors[i];
5532  ColorMenu[MaxNumberPens-2]="transparent";
5533  ColorMenu[MaxNumberPens-1]="Browser...";
5534  ColorMenu[MaxNumberPens]=(char *) NULL;
5535  /*
5536  Select a pen color from the pop-up menu.
5537  */
5538  pen_number=XMenuWidget(display,windows,DrawMenu[id],
5539  (const char **) ColorMenu,command);
5540  if (pen_number < 0)
5541  break;
5542  transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5543  MagickFalse;
5544  if (transparent != MagickFalse)
5545  {
5546  draw_info.stencil=TransparentStencil;
5547  break;
5548  }
5549  if (pen_number == (MaxNumberPens-1))
5550  {
5551  static char
5552  color_name[MaxTextExtent] = "gray";
5553 
5554  /*
5555  Select a pen color from a dialog.
5556  */
5557  resource_info->pen_colors[pen_number]=color_name;
5558  XColorBrowserWidget(display,windows,"Select",color_name);
5559  if (*color_name == '\0')
5560  break;
5561  }
5562  /*
5563  Set pen color.
5564  */
5565  (void) XParseColor(display,windows->map_info->colormap,
5566  resource_info->pen_colors[pen_number],&color);
5567  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5568  (unsigned int) MaxColors,&color);
5569  windows->pixel_info->pen_colors[pen_number]=color;
5570  pen_id=(unsigned int) pen_number;
5571  draw_info.stencil=OpaqueStencil;
5572  break;
5573  }
5574  case DrawStippleCommand:
5575  {
5576  const char
5577  *StipplesMenu[] =
5578  {
5579  "Brick",
5580  "Diagonal",
5581  "Scales",
5582  "Vertical",
5583  "Wavy",
5584  "Translucent",
5585  "Opaque",
5586  (char *) NULL,
5587  (char *) NULL,
5588  };
5589 
5590  Image
5591  *stipple_image;
5592 
5593  ImageInfo
5594  *image_info;
5595 
5596  int
5597  status;
5598 
5599  static char
5600  filename[MaxTextExtent] = "\0";
5601 
5602  /*
5603  Select a command from the pop-up menu.
5604  */
5605  StipplesMenu[7]="Open...";
5606  entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5607  command);
5608  if (entry < 0)
5609  break;
5610  if (stipple != (Pixmap) NULL)
5611  (void) XFreePixmap(display,stipple);
5612  stipple=(Pixmap) NULL;
5613  if (entry != 7)
5614  {
5615  switch (entry)
5616  {
5617  case 0:
5618  {
5619  stipple=XCreateBitmapFromData(display,root_window,
5620  (char *) BricksBitmap,BricksWidth,BricksHeight);
5621  break;
5622  }
5623  case 1:
5624  {
5625  stipple=XCreateBitmapFromData(display,root_window,
5626  (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5627  break;
5628  }
5629  case 2:
5630  {
5631  stipple=XCreateBitmapFromData(display,root_window,
5632  (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5633  break;
5634  }
5635  case 3:
5636  {
5637  stipple=XCreateBitmapFromData(display,root_window,
5638  (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5639  break;
5640  }
5641  case 4:
5642  {
5643  stipple=XCreateBitmapFromData(display,root_window,
5644  (char *) WavyBitmap,WavyWidth,WavyHeight);
5645  break;
5646  }
5647  case 5:
5648  {
5649  stipple=XCreateBitmapFromData(display,root_window,
5650  (char *) HighlightBitmap,HighlightWidth,
5651  HighlightHeight);
5652  break;
5653  }
5654  case 6:
5655  default:
5656  {
5657  stipple=XCreateBitmapFromData(display,root_window,
5658  (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5659  break;
5660  }
5661  }
5662  break;
5663  }
5664  XFileBrowserWidget(display,windows,"Stipple",filename);
5665  if (*filename == '\0')
5666  break;
5667  /*
5668  Read image.
5669  */
5670  XSetCursorState(display,windows,MagickTrue);
5671  XCheckRefreshWindows(display,windows);
5672  image_info=AcquireImageInfo();
5673  (void) CopyMagickString(image_info->filename,filename,
5674  MaxTextExtent);
5675  stipple_image=ReadImage(image_info,&(*image)->exception);
5676  CatchException(&(*image)->exception);
5677  XSetCursorState(display,windows,MagickFalse);
5678  if (stipple_image == (Image *) NULL)
5679  break;
5680  (void) AcquireUniqueFileResource(filename);
5681  (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5682  "xbm:%s",filename);
5683  (void) WriteImage(image_info,stipple_image);
5684  stipple_image=DestroyImage(stipple_image);
5685  image_info=DestroyImageInfo(image_info);
5686  status=XReadBitmapFile(display,root_window,filename,&width,
5687  &height,&stipple,&x,&y);
5688  (void) RelinquishUniqueFileResource(filename);
5689  if ((status != BitmapSuccess) != 0)
5690  XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5691  filename);
5692  break;
5693  }
5694  case DrawWidthCommand:
5695  {
5696  const char
5697  *const WidthsMenu[] =
5698  {
5699  "1",
5700  "2",
5701  "4",
5702  "8",
5703  "16",
5704  "Dialog...",
5705  (char *) NULL,
5706  };
5707 
5708  static char
5709  width[MaxTextExtent] = "0";
5710 
5711  /*
5712  Select a command from the pop-up menu.
5713  */
5714  entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5715  command);
5716  if (entry < 0)
5717  break;
5718  if (entry != 5)
5719  {
5720  line_width=(unsigned int) StringToUnsignedLong(
5721  WidthsMenu[entry]);
5722  break;
5723  }
5724  (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5725  width);
5726  if (*width == '\0')
5727  break;
5728  line_width=(unsigned int) StringToUnsignedLong(width);
5729  break;
5730  }
5731  case DrawUndoCommand:
5732  {
5733  (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5734  image);
5735  break;
5736  }
5737  case DrawHelpCommand:
5738  {
5739  XTextViewHelp(display,resource_info,windows,MagickFalse,
5740  "Help Viewer - Image Rotation",ImageDrawHelp);
5741  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5742  break;
5743  }
5744  case DrawDismissCommand:
5745  {
5746  /*
5747  Prematurely exit.
5748  */
5749  state|=EscapeState;
5750  state|=ExitState;
5751  break;
5752  }
5753  default:
5754  break;
5755  }
5756  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5757  continue;
5758  }
5759  switch (event.type)
5760  {
5761  case ButtonPress:
5762  {
5763  if (event.xbutton.button != Button1)
5764  break;
5765  if (event.xbutton.window != windows->image.id)
5766  break;
5767  /*
5768  exit loop.
5769  */
5770  x=event.xbutton.x;
5771  y=event.xbutton.y;
5772  state|=ExitState;
5773  break;
5774  }
5775  case ButtonRelease:
5776  break;
5777  case Expose:
5778  break;
5779  case KeyPress:
5780  {
5781  KeySym
5782  key_symbol;
5783 
5784  if (event.xkey.window != windows->image.id)
5785  break;
5786  /*
5787  Respond to a user key press.
5788  */
5789  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5790  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5791  switch ((int) key_symbol)
5792  {
5793  case XK_Escape:
5794  case XK_F20:
5795  {
5796  /*
5797  Prematurely exit.
5798  */
5799  state|=EscapeState;
5800  state|=ExitState;
5801  break;
5802  }
5803  case XK_F1:
5804  case XK_Help:
5805  {
5806  XTextViewHelp(display,resource_info,windows,MagickFalse,
5807  "Help Viewer - Image Rotation",ImageDrawHelp);
5808  break;
5809  }
5810  default:
5811  {
5812  (void) XBell(display,0);
5813  break;
5814  }
5815  }
5816  break;
5817  }
5818  case MotionNotify:
5819  {
5820  /*
5821  Map and unmap Info widget as text cursor crosses its boundaries.
5822  */
5823  x=event.xmotion.x;
5824  y=event.xmotion.y;
5825  if (windows->info.mapped != MagickFalse)
5826  {
5827  if ((x < (int) (windows->info.x+windows->info.width)) &&
5828  (y < (int) (windows->info.y+windows->info.height)))
5829  (void) XWithdrawWindow(display,windows->info.id,
5830  windows->info.screen);
5831  }
5832  else
5833  if ((x > (int) (windows->info.x+windows->info.width)) ||
5834  (y > (int) (windows->info.y+windows->info.height)))
5835  (void) XMapWindow(display,windows->info.id);
5836  break;
5837  }
5838  }
5839  } while ((state & ExitState) == 0);
5840  (void) XSelectInput(display,windows->image.id,
5841  windows->image.attributes.event_mask);
5842  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5843  if ((state & EscapeState) != 0)
5844  break;
5845  /*
5846  Draw element as pointer moves until the button is released.
5847  */
5848  distance=0;
5849  degrees=0.0;
5850  line_info.x1=x;
5851  line_info.y1=y;
5852  line_info.x2=x;
5853  line_info.y2=y;
5854  rectangle_info.x=(ssize_t) x;
5855  rectangle_info.y=(ssize_t) y;
5856  rectangle_info.width=0;
5857  rectangle_info.height=0;
5858  number_coordinates=1;
5859  coordinate_info->x=x;
5860  coordinate_info->y=y;
5861  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5862  state=DefaultState;
5863  do
5864  {
5865  switch (element)
5866  {
5867  case PointElement:
5868  default:
5869  {
5870  if (number_coordinates > 1)
5871  {
5872  (void) XDrawLines(display,windows->image.id,
5873  windows->image.highlight_context,coordinate_info,
5874  number_coordinates,CoordModeOrigin);
5875  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5876  coordinate_info[number_coordinates-1].x,
5877  coordinate_info[number_coordinates-1].y);
5878  XInfoWidget(display,windows,text);
5879  }
5880  break;
5881  }
5882  case LineElement:
5883  {
5884  if (distance > 9)
5885  {
5886  /*
5887  Display angle of the line.
5888  */
5889  degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5890  line_info.y1),(double) (line_info.x2-line_info.x1)));
5891  (void) FormatLocaleString(text,MaxTextExtent," %g",
5892  (double) degrees);
5893  XInfoWidget(display,windows,text);
5894  XHighlightLine(display,windows->image.id,
5895  windows->image.highlight_context,&line_info);
5896  }
5897  else
5898  if (windows->info.mapped != MagickFalse)
5899  (void) XWithdrawWindow(display,windows->info.id,
5900  windows->info.screen);
5901  break;
5902  }
5903  case RectangleElement:
5904  case FillRectangleElement:
5905  {
5906  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5907  {
5908  /*
5909  Display info and draw drawing rectangle.
5910  */
5911  (void) FormatLocaleString(text,MaxTextExtent,
5912  " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5913  (double) rectangle_info.height,(double) rectangle_info.x,
5914  (double) rectangle_info.y);
5915  XInfoWidget(display,windows,text);
5916  XHighlightRectangle(display,windows->image.id,
5917  windows->image.highlight_context,&rectangle_info);
5918  }
5919  else
5920  if (windows->info.mapped != MagickFalse)
5921  (void) XWithdrawWindow(display,windows->info.id,
5922  windows->info.screen);
5923  break;
5924  }
5925  case CircleElement:
5926  case FillCircleElement:
5927  case EllipseElement:
5928  case FillEllipseElement:
5929  {
5930  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5931  {
5932  /*
5933  Display info and draw drawing rectangle.
5934  */
5935  (void) FormatLocaleString(text,MaxTextExtent,
5936  " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5937  (double) rectangle_info.height,(double) rectangle_info.x,
5938  (double) rectangle_info.y);
5939  XInfoWidget(display,windows,text);
5940  XHighlightEllipse(display,windows->image.id,
5941  windows->image.highlight_context,&rectangle_info);
5942  }
5943  else
5944  if (windows->info.mapped != MagickFalse)
5945  (void) XWithdrawWindow(display,windows->info.id,
5946  windows->info.screen);
5947  break;
5948  }
5949  case PolygonElement:
5950  case FillPolygonElement:
5951  {
5952  if (number_coordinates > 1)
5953  (void) XDrawLines(display,windows->image.id,
5954  windows->image.highlight_context,coordinate_info,
5955  number_coordinates,CoordModeOrigin);
5956  if (distance > 9)
5957  {
5958  /*
5959  Display angle of the line.
5960  */
5961  degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5962  line_info.y1),(double) (line_info.x2-line_info.x1)));
5963  (void) FormatLocaleString(text,MaxTextExtent," %g",
5964  (double) degrees);
5965  XInfoWidget(display,windows,text);
5966  XHighlightLine(display,windows->image.id,
5967  windows->image.highlight_context,&line_info);
5968  }
5969  else
5970  if (windows->info.mapped != MagickFalse)
5971  (void) XWithdrawWindow(display,windows->info.id,
5972  windows->info.screen);
5973  break;
5974  }
5975  }
5976  /*
5977  Wait for next event.
5978  */
5979  XScreenEvent(display,windows,&event);
5980  switch (element)
5981  {
5982  case PointElement:
5983  default:
5984  {
5985  if (number_coordinates > 1)
5986  (void) XDrawLines(display,windows->image.id,
5987  windows->image.highlight_context,coordinate_info,
5988  number_coordinates,CoordModeOrigin);
5989  break;
5990  }
5991  case LineElement:
5992  {
5993  if (distance > 9)
5994  XHighlightLine(display,windows->image.id,
5995  windows->image.highlight_context,&line_info);
5996  break;
5997  }
5998  case RectangleElement:
5999  case FillRectangleElement:
6000  {
6001  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6002  XHighlightRectangle(display,windows->image.id,
6003  windows->image.highlight_context,&rectangle_info);
6004  break;
6005  }
6006  case CircleElement:
6007  case FillCircleElement:
6008  case EllipseElement:
6009  case FillEllipseElement:
6010  {
6011  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6012  XHighlightEllipse(display,windows->image.id,
6013  windows->image.highlight_context,&rectangle_info);
6014  break;
6015  }
6016  case PolygonElement:
6017  case FillPolygonElement:
6018  {
6019  if (number_coordinates > 1)
6020  (void) XDrawLines(display,windows->image.id,
6021  windows->image.highlight_context,coordinate_info,
6022  number_coordinates,CoordModeOrigin);
6023  if (distance > 9)
6024  XHighlightLine(display,windows->image.id,
6025  windows->image.highlight_context,&line_info);
6026  break;
6027  }
6028  }
6029  switch (event.type)
6030  {
6031  case ButtonPress:
6032  break;
6033  case ButtonRelease:
6034  {
6035  /*
6036  User has committed to element.
6037  */
6038  line_info.x2=event.xbutton.x;
6039  line_info.y2=event.xbutton.y;
6040  rectangle_info.x=(ssize_t) event.xbutton.x;
6041  rectangle_info.y=(ssize_t) event.xbutton.y;
6042  coordinate_info[number_coordinates].x=event.xbutton.x;
6043  coordinate_info[number_coordinates].y=event.xbutton.y;
6044  if (((element != PolygonElement) &&
6045  (element != FillPolygonElement)) || (distance <= 9))
6046  {
6047  state|=ExitState;
6048  break;
6049  }
6050  number_coordinates++;
6051  if (number_coordinates < (int) max_coordinates)
6052  {
6053  line_info.x1=event.xbutton.x;
6054  line_info.y1=event.xbutton.y;
6055  break;
6056  }
6057  max_coordinates<<=1;
6058  coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6059  max_coordinates,sizeof(*coordinate_info));
6060  if (coordinate_info == (XPoint *) NULL)
6061  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6062  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6063  break;
6064  }
6065  case Expose:
6066  break;
6067  case MotionNotify:
6068  {
6069  if (event.xmotion.window != windows->image.id)
6070  break;
6071  if (element != PointElement)
6072  {
6073  line_info.x2=event.xmotion.x;
6074  line_info.y2=event.xmotion.y;
6075  rectangle_info.x=(ssize_t) event.xmotion.x;
6076  rectangle_info.y=(ssize_t) event.xmotion.y;
6077  break;
6078  }
6079  coordinate_info[number_coordinates].x=event.xbutton.x;
6080  coordinate_info[number_coordinates].y=event.xbutton.y;
6081  number_coordinates++;
6082  if (number_coordinates < (int) max_coordinates)
6083  break;
6084  max_coordinates<<=1;
6085  coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6086  max_coordinates,sizeof(*coordinate_info));
6087  if (coordinate_info == (XPoint *) NULL)
6088  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6089  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6090  break;
6091  }
6092  default:
6093  break;
6094  }
6095  /*
6096  Check boundary conditions.
6097  */
6098  if (line_info.x2 < 0)
6099  line_info.x2=0;
6100  else
6101  if (line_info.x2 > (int) windows->image.width)
6102  line_info.x2=(short) windows->image.width;
6103  if (line_info.y2 < 0)
6104  line_info.y2=0;
6105  else
6106  if (line_info.y2 > (int) windows->image.height)
6107  line_info.y2=(short) windows->image.height;
6108  distance=(unsigned int)
6109  (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6110  ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6111  if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6112  ((state & ExitState) != 0))
6113  {
6114  if (rectangle_info.x < 0)
6115  rectangle_info.x=0;
6116  else
6117  if (rectangle_info.x > (ssize_t) windows->image.width)
6118  rectangle_info.x=(ssize_t) windows->image.width;
6119  if ((int) rectangle_info.x < x)
6120  rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6121  else
6122  {
6123  rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6124  rectangle_info.x=(ssize_t) x;
6125  }
6126  if (rectangle_info.y < 0)
6127  rectangle_info.y=0;
6128  else
6129  if (rectangle_info.y > (ssize_t) windows->image.height)
6130  rectangle_info.y=(ssize_t) windows->image.height;
6131  if ((int) rectangle_info.y < y)
6132  rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6133  else
6134  {
6135  rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6136  rectangle_info.y=(ssize_t) y;
6137  }
6138  }
6139  } while ((state & ExitState) == 0);
6140  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6141  if ((element == PointElement) || (element == PolygonElement) ||
6142  (element == FillPolygonElement))
6143  {
6144  /*
6145  Determine polygon bounding box.
6146  */
6147  rectangle_info.x=(ssize_t) coordinate_info->x;
6148  rectangle_info.y=(ssize_t) coordinate_info->y;
6149  x=coordinate_info->x;
6150  y=coordinate_info->y;
6151  for (i=1; i < number_coordinates; i++)
6152  {
6153  if (coordinate_info[i].x > x)
6154  x=coordinate_info[i].x;
6155  if (coordinate_info[i].y > y)
6156  y=coordinate_info[i].y;
6157  if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6158  rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6159  if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6160  rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6161  }
6162  rectangle_info.width=(size_t) (x-rectangle_info.x);
6163  rectangle_info.height=(size_t) (y-rectangle_info.y);
6164  for (i=0; i < number_coordinates; i++)
6165  {
6166  coordinate_info[i].x-=rectangle_info.x;
6167  coordinate_info[i].y-=rectangle_info.y;
6168  }
6169  }
6170  else
6171  if (distance <= 9)
6172  continue;
6173  else
6174  if ((element == RectangleElement) ||
6175  (element == CircleElement) || (element == EllipseElement))
6176  {
6177  rectangle_info.width--;
6178  rectangle_info.height--;
6179  }
6180  /*
6181  Drawing is relative to image configuration.
6182  */
6183  draw_info.x=(int) rectangle_info.x;
6184  draw_info.y=(int) rectangle_info.y;
6185  (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6186  image);
6187  width=(unsigned int) (*image)->columns;
6188  height=(unsigned int) (*image)->rows;
6189  x=0;
6190  y=0;
6191  if (windows->image.crop_geometry != (char *) NULL)
6192  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6193  draw_info.x+=windows->image.x-(line_width/2);
6194  if (draw_info.x < 0)
6195  draw_info.x=0;
6196  draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6197  draw_info.y+=windows->image.y-(line_width/2);
6198  if (draw_info.y < 0)
6199  draw_info.y=0;
6200  draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6201  draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6202  if (draw_info.width > (unsigned int) (*image)->columns)
6203  draw_info.width=(unsigned int) (*image)->columns;
6204  draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6205  if (draw_info.height > (unsigned int) (*image)->rows)
6206  draw_info.height=(unsigned int) (*image)->rows;
6207  (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6208  width*draw_info.width/windows->image.ximage->width,
6209  height*draw_info.height/windows->image.ximage->height,
6210  draw_info.x+x,draw_info.y+y);
6211  /*
6212  Initialize drawing attributes.
6213  */
6214  draw_info.degrees=0.0;
6215  draw_info.element=element;
6216  draw_info.stipple=stipple;
6217  draw_info.line_width=line_width;
6218  draw_info.line_info=line_info;
6219  if (line_info.x1 > (int) (line_width/2))
6220  draw_info.line_info.x1=(short) line_width/2;
6221  if (line_info.y1 > (int) (line_width/2))
6222  draw_info.line_info.y1=(short) line_width/2;
6223  draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6224  draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6225  if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6226  {
6227  draw_info.line_info.x2=(-draw_info.line_info.x2);
6228  draw_info.line_info.y2=(-draw_info.line_info.y2);
6229  }
6230  if (draw_info.line_info.x2 < 0)
6231  {
6232  draw_info.line_info.x2=(-draw_info.line_info.x2);
6233  Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6234  }
6235  if (draw_info.line_info.y2 < 0)
6236  {
6237  draw_info.line_info.y2=(-draw_info.line_info.y2);
6238  Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6239  }
6240  draw_info.rectangle_info=rectangle_info;
6241  if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6242  draw_info.rectangle_info.x=(ssize_t) line_width/2;
6243  if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6244  draw_info.rectangle_info.y=(ssize_t) line_width/2;
6245  draw_info.number_coordinates=(unsigned int) number_coordinates;
6246  draw_info.coordinate_info=coordinate_info;
6247  windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6248  /*
6249  Draw element on image.
6250  */
6251  XSetCursorState(display,windows,MagickTrue);
6252  XCheckRefreshWindows(display,windows);
6253  status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6254  XSetCursorState(display,windows,MagickFalse);
6255  /*
6256  Update image colormap and return to image drawing.
6257  */
6258  XConfigureImageColormap(display,resource_info,windows,*image);
6259  (void) XConfigureImage(display,resource_info,windows,*image);
6260  }
6261  XSetCursorState(display,windows,MagickFalse);
6262  coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6263  return(status != 0 ? MagickTrue : MagickFalse);
6264 }
6265 ␌
6266 /*
6267 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6268 % %
6269 % %
6270 % %
6271 + X D r a w P a n R e c t a n g l e %
6272 % %
6273 % %
6274 % %
6275 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6276 %
6277 % XDrawPanRectangle() draws a rectangle in the pan window. The pan window
6278 % displays a zoom image and the rectangle shows which portion of the image is
6279 % displayed in the Image window.
6280 %
6281 % The format of the XDrawPanRectangle method is:
6282 %
6283 % XDrawPanRectangle(Display *display,XWindows *windows)
6284 %
6285 % A description of each parameter follows:
6286 %
6287 % o display: Specifies a connection to an X server; returned from
6288 % XOpenDisplay.
6289 %
6290 % o windows: Specifies a pointer to a XWindows structure.
6291 %
6292 */
6293 static void XDrawPanRectangle(Display *display,XWindows *windows)
6294 {
6295  MagickRealType
6296  scale_factor;
6297 
6299  highlight_info;
6300 
6301  /*
6302  Determine dimensions of the panning rectangle.
6303  */
6304  scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6305  highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6306  highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6307  scale_factor=(MagickRealType)
6308  windows->pan.height/windows->image.ximage->height;
6309  highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6310  highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6311  /*
6312  Display the panning rectangle.
6313  */
6314  (void) XClearWindow(display,windows->pan.id);
6315  XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6316  &highlight_info);
6317 }
6318 ␌
6319 /*
6320 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6321 % %
6322 % %
6323 % %
6324 + X I m a g e C a c h e %
6325 % %
6326 % %
6327 % %
6328 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6329 %
6330 % XImageCache() handles the creation, manipulation, and destruction of the
6331 % image cache (undo and redo buffers).
6332 %
6333 % The format of the XImageCache method is:
6334 %
6335 % void XImageCache(Display *display,XResourceInfo *resource_info,
6336 % XWindows *windows,const DisplayCommand command,Image **image)
6337 %
6338 % A description of each parameter follows:
6339 %
6340 % o display: Specifies a connection to an X server; returned from
6341 % XOpenDisplay.
6342 %
6343 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6344 %
6345 % o windows: Specifies a pointer to a XWindows structure.
6346 %
6347 % o command: Specifies a command to perform.
6348 %
6349 % o image: the image; XImageCache may transform the image and return a new
6350 % image pointer.
6351 %
6352 */
6353 static void XImageCache(Display *display,XResourceInfo *resource_info,
6354  XWindows *windows,const DisplayCommand command,Image **image)
6355 {
6356  Image
6357  *cache_image;
6358 
6359  static Image
6360  *redo_image = (Image *) NULL,
6361  *undo_image = (Image *) NULL;
6362 
6363  switch (command)
6364  {
6365  case FreeBuffersCommand:
6366  {
6367  /*
6368  Free memory from the undo and redo cache.
6369  */
6370  while (undo_image != (Image *) NULL)
6371  {
6372  cache_image=undo_image;
6373  undo_image=GetPreviousImageInList(undo_image);
6374  cache_image->list=DestroyImage(cache_image->list);
6375  cache_image=DestroyImage(cache_image);
6376  }
6377  undo_image=NewImageList();
6378  if (redo_image != (Image *) NULL)
6379  redo_image=DestroyImage(redo_image);
6380  redo_image=NewImageList();
6381  return;
6382  }
6383  case UndoCommand:
6384  {
6385  char
6386  image_geometry[MaxTextExtent];
6387 
6388  /*
6389  Undo the last image transformation.
6390  */
6391  if (undo_image == (Image *) NULL)
6392  {
6393  (void) XBell(display,0);
6394  return;
6395  }
6396  cache_image=undo_image;
6397  undo_image=GetPreviousImageInList(undo_image);
6398  windows->image.window_changes.width=(int) cache_image->columns;
6399  windows->image.window_changes.height=(int) cache_image->rows;
6400  (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6401  windows->image.ximage->width,windows->image.ximage->height);
6402  (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
6403  if (windows->image.crop_geometry != (char *) NULL)
6404  windows->image.crop_geometry=(char *)
6405  RelinquishMagickMemory(windows->image.crop_geometry);
6406  windows->image.crop_geometry=cache_image->geometry;
6407  if (redo_image != (Image *) NULL)
6408  redo_image=DestroyImage(redo_image);
6409  redo_image=(*image);
6410  *image=cache_image->list;
6411  cache_image=DestroyImage(cache_image);
6412  if (windows->image.orphan != MagickFalse)
6413  return;
6414  XConfigureImageColormap(display,resource_info,windows,*image);
6415  (void) XConfigureImage(display,resource_info,windows,*image);
6416  return;
6417  }
6418  case CutCommand:
6419  case PasteCommand:
6420  case ApplyCommand:
6421  case HalfSizeCommand:
6422  case OriginalSizeCommand:
6423  case DoubleSizeCommand:
6424  case ResizeCommand:
6425  case TrimCommand:
6426  case CropCommand:
6427  case ChopCommand:
6428  case FlipCommand:
6429  case FlopCommand:
6430  case RotateRightCommand:
6431  case RotateLeftCommand:
6432  case RotateCommand:
6433  case ShearCommand:
6434  case RollCommand:
6435  case NegateCommand:
6436  case ContrastStretchCommand:
6437  case SigmoidalContrastCommand:
6438  case NormalizeCommand:
6439  case EqualizeCommand:
6440  case HueCommand:
6441  case SaturationCommand:
6442  case BrightnessCommand:
6443  case GammaCommand:
6444  case SpiffCommand:
6445  case DullCommand:
6446  case GrayscaleCommand:
6447  case MapCommand:
6448  case QuantizeCommand:
6449  case DespeckleCommand:
6450  case EmbossCommand:
6451  case ReduceNoiseCommand:
6452  case AddNoiseCommand:
6453  case SharpenCommand:
6454  case BlurCommand:
6455  case ThresholdCommand:
6456  case EdgeDetectCommand:
6457  case SpreadCommand:
6458  case ShadeCommand:
6459  case RaiseCommand:
6460  case SegmentCommand:
6461  case SolarizeCommand:
6462  case SepiaToneCommand:
6463  case SwirlCommand:
6464  case ImplodeCommand:
6465  case VignetteCommand:
6466  case WaveCommand:
6467  case OilPaintCommand:
6468  case CharcoalDrawCommand:
6469  case AnnotateCommand:
6470  case AddBorderCommand:
6471  case AddFrameCommand:
6472  case CompositeCommand:
6473  case CommentCommand:
6474  case LaunchCommand:
6475  case RegionOfInterestCommand:
6476  case SaveToUndoBufferCommand:
6477  case RedoCommand:
6478  {
6479  Image
6480  *previous_image;
6481 
6482  ssize_t
6483  bytes;
6484 
6485  bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
6486  if (undo_image != (Image *) NULL)
6487  {
6488  /*
6489  Ensure the undo cache has enough memory available.
6490  */
6491  previous_image=undo_image;
6492  while (previous_image != (Image *) NULL)
6493  {
6494  bytes+=previous_image->list->columns*previous_image->list->rows*
6495  sizeof(PixelPacket);
6496  if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6497  {
6498  previous_image=GetPreviousImageInList(previous_image);
6499  continue;
6500  }
6501  bytes-=previous_image->list->columns*previous_image->list->rows*
6502  sizeof(PixelPacket);
6503  if (previous_image == undo_image)
6504  undo_image=NewImageList();
6505  else
6506  previous_image->next->previous=NewImageList();
6507  break;
6508  }
6509  while (previous_image != (Image *) NULL)
6510  {
6511  /*
6512  Delete any excess memory from undo cache.
6513  */
6514  cache_image=previous_image;
6515  previous_image=GetPreviousImageInList(previous_image);
6516  cache_image->list=DestroyImage(cache_image->list);
6517  cache_image=DestroyImage(cache_image);
6518  }
6519  }
6520  if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6521  break;
6522  /*
6523  Save image before transformations are applied.
6524  */
6525  cache_image=AcquireImage((ImageInfo *) NULL);
6526  if (cache_image == (Image *) NULL)
6527  break;
6528  XSetCursorState(display,windows,MagickTrue);
6529  XCheckRefreshWindows(display,windows);
6530  cache_image->list=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
6531  XSetCursorState(display,windows,MagickFalse);
6532  if (cache_image->list == (Image *) NULL)
6533  {
6534  cache_image=DestroyImage(cache_image);
6535  break;
6536  }
6537  cache_image->columns=(size_t) windows->image.ximage->width;
6538  cache_image->rows=(size_t) windows->image.ximage->height;
6539  cache_image->geometry=windows->image.crop_geometry;
6540  if (windows->image.crop_geometry != (char *) NULL)
6541  {
6542  cache_image->geometry=AcquireString((char *) NULL);
6543  (void) CopyMagickString(cache_image->geometry,
6544  windows->image.crop_geometry,MaxTextExtent);
6545  }
6546  if (undo_image == (Image *) NULL)
6547  {
6548  undo_image=cache_image;
6549  break;
6550  }
6551  undo_image->next=cache_image;
6552  undo_image->next->previous=undo_image;
6553  undo_image=undo_image->next;
6554  break;
6555  }
6556  default:
6557  break;
6558  }
6559  if (command == RedoCommand)
6560  {
6561  /*
6562  Redo the last image transformation.
6563  */
6564  if (redo_image == (Image *) NULL)
6565  {
6566  (void) XBell(display,0);
6567  return;
6568  }
6569  windows->image.window_changes.width=(int) redo_image->columns;
6570  windows->image.window_changes.height=(int) redo_image->rows;
6571  if (windows->image.crop_geometry != (char *) NULL)
6572  windows->image.crop_geometry=(char *)
6573  RelinquishMagickMemory(windows->image.crop_geometry);
6574  windows->image.crop_geometry=redo_image->geometry;
6575  *image=DestroyImage(*image);
6576  *image=redo_image;
6577  redo_image=NewImageList();
6578  if (windows->image.orphan != MagickFalse)
6579  return;
6580  XConfigureImageColormap(display,resource_info,windows,*image);
6581  (void) XConfigureImage(display,resource_info,windows,*image);
6582  return;
6583  }
6584  if (command != InfoCommand)
6585  return;
6586  /*
6587  Display image info.
6588  */
6589  XSetCursorState(display,windows,MagickTrue);
6590  XCheckRefreshWindows(display,windows);
6591  XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6592  XSetCursorState(display,windows,MagickFalse);
6593 }
6594 ␌
6595 /*
6596 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6597 % %
6598 % %
6599 % %
6600 + X I m a g e W i n d o w C o m m a n d %
6601 % %
6602 % %
6603 % %
6604 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6605 %
6606 % XImageWindowCommand() makes a transform to the image or Image window as
6607 % specified by a user menu button or keyboard command.
6608 %
6609 % The format of the XMagickCommand method is:
6610 %
6611 % DisplayCommand XImageWindowCommand(Display *display,
6612 % XResourceInfo *resource_info,XWindows *windows,
6613 % const MagickStatusType state,KeySym key_symbol,Image **image)
6614 %
6615 % A description of each parameter follows:
6616 %
6617 % o nexus: Method XImageWindowCommand returns an image when the
6618 % user chooses 'Open Image' from the command menu. Otherwise a null
6619 % image is returned.
6620 %
6621 % o display: Specifies a connection to an X server; returned from
6622 % XOpenDisplay.
6623 %
6624 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6625 %
6626 % o windows: Specifies a pointer to a XWindows structure.
6627 %
6628 % o state: key mask.
6629 %
6630 % o key_symbol: Specifies a command to perform.
6631 %
6632 % o image: the image; XImageWIndowCommand
6633 % may transform the image and return a new image pointer.
6634 %
6635 */
6636 static DisplayCommand XImageWindowCommand(Display *display,
6637  XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6638  KeySym key_symbol,Image **image)
6639 {
6640  static char
6641  delta[MaxTextExtent] = "";
6642 
6643  static const char
6644  Digits[] = "01234567890";
6645 
6646  static KeySym
6647  last_symbol = XK_0;
6648 
6649  if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6650  {
6651  if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6652  {
6653  *delta='\0';
6654  resource_info->quantum=1;
6655  }
6656  last_symbol=key_symbol;
6657  delta[strlen(delta)+1]='\0';
6658  delta[strlen(delta)]=Digits[key_symbol-XK_0];
6659  resource_info->quantum=StringToLong(delta);
6660  return(NullCommand);
6661  }
6662  last_symbol=key_symbol;
6663  if (resource_info->immutable)
6664  {
6665  /*
6666  Virtual image window has a restricted command set.
6667  */
6668  switch (key_symbol)
6669  {
6670  case XK_question:
6671  return(InfoCommand);
6672  case XK_p:
6673  case XK_Print:
6674  return(PrintCommand);
6675  case XK_space:
6676  return(NextCommand);
6677  case XK_q:
6678  case XK_Escape:
6679  return(QuitCommand);
6680  default:
6681  break;
6682  }
6683  return(NullCommand);
6684  }
6685  switch ((int) key_symbol)
6686  {
6687  case XK_o:
6688  {
6689  if ((state & ControlMask) == 0)
6690  break;
6691  return(OpenCommand);
6692  }
6693  case XK_space:
6694  return(NextCommand);
6695  case XK_BackSpace:
6696  return(FormerCommand);
6697  case XK_s:
6698  {
6699  if ((state & Mod1Mask) != 0)
6700  return(SwirlCommand);
6701  if ((state & ControlMask) == 0)
6702  return(ShearCommand);
6703  return(SaveCommand);
6704  }
6705  case XK_p:
6706  case XK_Print:
6707  {
6708  if ((state & Mod1Mask) != 0)
6709  return(OilPaintCommand);
6710  if ((state & Mod4Mask) != 0)
6711  return(ColorCommand);
6712  if ((state & ControlMask) == 0)
6713  return(NullCommand);
6714  return(PrintCommand);
6715  }
6716  case XK_d:
6717  {
6718  if ((state & Mod4Mask) != 0)
6719  return(DrawCommand);
6720  if ((state & ControlMask) == 0)
6721  return(NullCommand);
6722  return(DeleteCommand);
6723  }
6724  case XK_Select:
6725  {
6726  if ((state & ControlMask) == 0)
6727  return(NullCommand);
6728  return(SelectCommand);
6729  }
6730  case XK_n:
6731  {
6732  if ((state & ControlMask) == 0)
6733  return(NullCommand);
6734  return(NewCommand);
6735  }
6736  case XK_q:
6737  case XK_Escape:
6738  return(QuitCommand);
6739  case XK_z:
6740  case XK_Undo:
6741  {
6742  if ((state & ControlMask) == 0)
6743  return(NullCommand);
6744  return(UndoCommand);
6745  }
6746  case XK_r:
6747  case XK_Redo:
6748  {
6749  if ((state & ControlMask) == 0)
6750  return(RollCommand);
6751  return(RedoCommand);
6752  }
6753  case XK_x:
6754  {
6755  if ((state & ControlMask) == 0)
6756  return(NullCommand);
6757  return(CutCommand);
6758  }
6759  case XK_c:
6760  {
6761  if ((state & Mod1Mask) != 0)
6762  return(CharcoalDrawCommand);
6763  if ((state & ControlMask) == 0)
6764  return(CropCommand);
6765  return(CopyCommand);
6766  }
6767  case XK_v:
6768  case XK_Insert:
6769  {
6770  if ((state & Mod4Mask) != 0)
6771  return(CompositeCommand);
6772  if ((state & ControlMask) == 0)
6773  return(FlipCommand);
6774  return(PasteCommand);
6775  }
6776  case XK_less:
6777  return(HalfSizeCommand);
6778  case XK_minus:
6779  return(OriginalSizeCommand);
6780  case XK_greater:
6781  return(DoubleSizeCommand);
6782  case XK_percent:
6783  return(ResizeCommand);
6784  case XK_at:
6785  return(RefreshCommand);
6786  case XK_bracketleft:
6787  return(ChopCommand);
6788  case XK_h:
6789  return(FlopCommand);
6790  case XK_slash:
6791  return(RotateRightCommand);
6792  case XK_backslash:
6793  return(RotateLeftCommand);
6794  case XK_asterisk:
6795  return(RotateCommand);
6796  case XK_t:
6797  return(TrimCommand);
6798  case XK_H:
6799  return(HueCommand);
6800  case XK_S:
6801  return(SaturationCommand);
6802  case XK_L:
6803  return(BrightnessCommand);
6804  case XK_G:
6805  return(GammaCommand);
6806  case XK_C:
6807  return(SpiffCommand);
6808  case XK_Z:
6809  return(DullCommand);
6810  case XK_N:
6811  return(NormalizeCommand);
6812  case XK_equal:
6813  return(EqualizeCommand);
6814  case XK_asciitilde:
6815  return(NegateCommand);
6816  case XK_period:
6817  return(GrayscaleCommand);
6818  case XK_numbersign:
6819  return(QuantizeCommand);
6820  case XK_F2:
6821  return(DespeckleCommand);
6822  case XK_F3:
6823  return(EmbossCommand);
6824  case XK_F4:
6825  return(ReduceNoiseCommand);
6826  case XK_F5:
6827  return(AddNoiseCommand);
6828  case XK_F6:
6829  return(SharpenCommand);
6830  case XK_F7:
6831  return(BlurCommand);
6832  case XK_F8:
6833  return(ThresholdCommand);
6834  case XK_F9:
6835  return(EdgeDetectCommand);
6836  case XK_F10:
6837  return(SpreadCommand);
6838  case XK_F11:
6839  return(ShadeCommand);
6840  case XK_F12:
6841  return(RaiseCommand);
6842  case XK_F13:
6843  return(SegmentCommand);
6844  case XK_i:
6845  {
6846  if ((state & Mod1Mask) == 0)
6847  return(NullCommand);
6848  return(ImplodeCommand);
6849  }
6850  case XK_w:
6851  {
6852  if ((state & Mod1Mask) == 0)
6853  return(NullCommand);
6854  return(WaveCommand);
6855  }
6856  case XK_m:
6857  {
6858  if ((state & Mod4Mask) == 0)
6859  return(NullCommand);
6860  return(MatteCommand);
6861  }
6862  case XK_b:
6863  {
6864  if ((state & Mod4Mask) == 0)
6865  return(NullCommand);
6866  return(AddBorderCommand);
6867  }
6868  case XK_f:
6869  {
6870  if ((state & Mod4Mask) == 0)
6871  return(NullCommand);
6872  return(AddFrameCommand);
6873  }
6874  case XK_exclam:
6875  {
6876  if ((state & Mod4Mask) == 0)
6877  return(NullCommand);
6878  return(CommentCommand);
6879  }
6880  case XK_a:
6881  {
6882  if ((state & Mod1Mask) != 0)
6883  return(ApplyCommand);
6884  if ((state & Mod4Mask) != 0)
6885  return(AnnotateCommand);
6886  if ((state & ControlMask) == 0)
6887  return(NullCommand);
6888  return(RegionOfInterestCommand);
6889  }
6890  case XK_question:
6891  return(InfoCommand);
6892  case XK_plus:
6893  return(ZoomCommand);
6894  case XK_P:
6895  {
6896  if ((state & ShiftMask) == 0)
6897  return(NullCommand);
6898  return(ShowPreviewCommand);
6899  }
6900  case XK_Execute:
6901  return(LaunchCommand);
6902  case XK_F1:
6903  return(HelpCommand);
6904  case XK_Find:
6905  return(BrowseDocumentationCommand);
6906  case XK_Menu:
6907  {
6908  (void) XMapRaised(display,windows->command.id);
6909  return(NullCommand);
6910  }
6911  case XK_Next:
6912  case XK_Prior:
6913  case XK_Home:
6914  case XK_KP_Home:
6915  {
6916  XTranslateImage(display,windows,*image,key_symbol);
6917  return(NullCommand);
6918  }
6919  case XK_Up:
6920  case XK_KP_Up:
6921  case XK_Down:
6922  case XK_KP_Down:
6923  case XK_Left:
6924  case XK_KP_Left:
6925  case XK_Right:
6926  case XK_KP_Right:
6927  {
6928  if ((state & Mod1Mask) != 0)
6929  {
6931  crop_info;
6932 
6933  /*
6934  Trim one pixel from edge of image.
6935  */
6936  crop_info.x=0;
6937  crop_info.y=0;
6938  crop_info.width=(size_t) windows->image.ximage->width;
6939  crop_info.height=(size_t) windows->image.ximage->height;
6940  if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
6941  {
6942  if (resource_info->quantum >= (int) crop_info.height)
6943  resource_info->quantum=(int) crop_info.height-1;
6944  crop_info.height-=resource_info->quantum;
6945  }
6946  if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
6947  {
6948  if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
6949  resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
6950  crop_info.y+=resource_info->quantum;
6951  crop_info.height-=resource_info->quantum;
6952  }
6953  if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
6954  {
6955  if (resource_info->quantum >= (int) crop_info.width)
6956  resource_info->quantum=(int) crop_info.width-1;
6957  crop_info.width-=resource_info->quantum;
6958  }
6959  if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
6960  {
6961  if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
6962  resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
6963  crop_info.x+=resource_info->quantum;
6964  crop_info.width-=resource_info->quantum;
6965  }
6966  if ((int) (windows->image.x+windows->image.width) >
6967  (int) crop_info.width)
6968  windows->image.x=(int) (crop_info.width-windows->image.width);
6969  if ((int) (windows->image.y+windows->image.height) >
6970  (int) crop_info.height)
6971  windows->image.y=(int) (crop_info.height-windows->image.height);
6972  XSetCropGeometry(display,windows,&crop_info,*image);
6973  windows->image.window_changes.width=(int) crop_info.width;
6974  windows->image.window_changes.height=(int) crop_info.height;
6975  (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
6976  (void) XConfigureImage(display,resource_info,windows,*image);
6977  return(NullCommand);
6978  }
6979  XTranslateImage(display,windows,*image,key_symbol);
6980  return(NullCommand);
6981  }
6982  default:
6983  return(NullCommand);
6984  }
6985  return(NullCommand);
6986 }
6987 ␌
6988 /*
6989 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6990 % %
6991 % %
6992 % %
6993 + X M a g i c k C o m m a n d %
6994 % %
6995 % %
6996 % %
6997 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6998 %
6999 % XMagickCommand() makes a transform to the image or Image window as
7000 % specified by a user menu button or keyboard command.
7001 %
7002 % The format of the XMagickCommand method is:
7003 %
7004 % Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7005 % XWindows *windows,const DisplayCommand command,Image **image)
7006 %
7007 % A description of each parameter follows:
7008 %
7009 % o nexus: Method XMagickCommand returns an image when the
7010 % user chooses 'Load Image' from the command menu. Otherwise a null
7011 % image is returned.
7012 %
7013 % o display: Specifies a connection to an X server; returned from
7014 % XOpenDisplay.
7015 %
7016 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7017 %
7018 % o windows: Specifies a pointer to a XWindows structure.
7019 %
7020 % o command: Specifies a command to perform.
7021 %
7022 % o image: the image; XMagickCommand
7023 % may transform the image and return a new image pointer.
7024 %
7025 */
7026 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7027  XWindows *windows,const DisplayCommand command,Image **image)
7028 {
7029  char
7030  filename[MaxTextExtent],
7031  geometry[MaxTextExtent],
7032  modulate_factors[MaxTextExtent];
7033 
7034  GeometryInfo
7035  geometry_info;
7036 
7037  Image
7038  *nexus;
7039 
7040  ImageInfo
7041  *image_info;
7042 
7043  int
7044  x,
7045  y;
7046 
7047  MagickStatusType
7048  flags,
7049  status;
7050 
7051  QuantizeInfo
7052  quantize_info;
7053 
7055  page_geometry;
7056 
7057  int
7058  i;
7059 
7060  static char
7061  color[MaxTextExtent] = "gray";
7062 
7063  unsigned int
7064  height,
7065  width;
7066 
7067  /*
7068  Process user command.
7069  */
7070  XCheckRefreshWindows(display,windows);
7071  XImageCache(display,resource_info,windows,command,image);
7072  nexus=NewImageList();
7073  windows->image.window_changes.width=windows->image.ximage->width;
7074  windows->image.window_changes.height=windows->image.ximage->height;
7075  image_info=CloneImageInfo(resource_info->image_info);
7076  SetGeometryInfo(&geometry_info);
7077  GetQuantizeInfo(&quantize_info);
7078  switch (command)
7079  {
7080  case OpenCommand:
7081  {
7082  /*
7083  Load image.
7084  */
7085  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7086  break;
7087  }
7088  case NextCommand:
7089  {
7090  /*
7091  Display next image.
7092  */
7093  for (i=0; i < resource_info->quantum; i++)
7094  XClientMessage(display,windows->image.id,windows->im_protocols,
7095  windows->im_next_image,CurrentTime);
7096  break;
7097  }
7098  case FormerCommand:
7099  {
7100  /*
7101  Display former image.
7102  */
7103  for (i=0; i < resource_info->quantum; i++)
7104  XClientMessage(display,windows->image.id,windows->im_protocols,
7105  windows->im_former_image,CurrentTime);
7106  break;
7107  }
7108  case SelectCommand:
7109  {
7110  int
7111  status;
7112 
7113  /*
7114  Select image.
7115  */
7116  if (*resource_info->home_directory == '\0')
7117  (void) CopyMagickString(resource_info->home_directory,".",
7118  MaxTextExtent);
7119  status=chdir(resource_info->home_directory);
7120  if (status == -1)
7121  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
7122  FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
7123  nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7124  break;
7125  }
7126  case SaveCommand:
7127  {
7128  /*
7129  Save image.
7130  */
7131  status=XSaveImage(display,resource_info,windows,*image);
7132  if (status == MagickFalse)
7133  {
7134  char
7135  message[MaxTextExtent];
7136 
7137  (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7138  (*image)->exception.reason != (char *) NULL ?
7139  (*image)->exception.reason : "",
7140  (*image)->exception.description != (char *) NULL ?
7141  (*image)->exception.description : "");
7142  XNoticeWidget(display,windows,"Unable to save file:",message);
7143  break;
7144  }
7145  break;
7146  }
7147  case PrintCommand:
7148  {
7149  /*
7150  Print image.
7151  */
7152  status=XPrintImage(display,resource_info,windows,*image);
7153  if (status == MagickFalse)
7154  {
7155  char
7156  message[MaxTextExtent];
7157 
7158  (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7159  (*image)->exception.reason != (char *) NULL ?
7160  (*image)->exception.reason : "",
7161  (*image)->exception.description != (char *) NULL ?
7162  (*image)->exception.description : "");
7163  XNoticeWidget(display,windows,"Unable to print file:",message);
7164  break;
7165  }
7166  break;
7167  }
7168  case DeleteCommand:
7169  {
7170  static char
7171  filename[MaxTextExtent] = "\0";
7172 
7173  /*
7174  Delete image file.
7175  */
7176  XFileBrowserWidget(display,windows,"Delete",filename);
7177  if (*filename == '\0')
7178  break;
7179  status=ShredFile(filename);
7180  status|=remove_utf8(filename);
7181  if (status != MagickFalse)
7182  XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7183  break;
7184  }
7185  case NewCommand:
7186  {
7187  int
7188  status;
7189 
7190  static char
7191  color[MaxTextExtent] = "gray",
7192  geometry[MaxTextExtent] = "640x480";
7193 
7194  static const char
7195  *format = "gradient";
7196 
7197  /*
7198  Query user for canvas geometry.
7199  */
7200  status=XDialogWidget(display,windows,"New","Enter image geometry:",
7201  geometry);
7202  if (*geometry == '\0')
7203  break;
7204  if (status == 0)
7205  format="xc";
7206  XColorBrowserWidget(display,windows,"Select",color);
7207  if (*color == '\0')
7208  break;
7209  /*
7210  Create canvas.
7211  */
7212  (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7213  "%s:%s",format,color);
7214  (void) CloneString(&image_info->size,geometry);
7215  nexus=ReadImage(image_info,&(*image)->exception);
7216  CatchException(&(*image)->exception);
7217  XClientMessage(display,windows->image.id,windows->im_protocols,
7218  windows->im_next_image,CurrentTime);
7219  break;
7220  }
7221  case VisualDirectoryCommand:
7222  {
7223  /*
7224  Visual Image directory.
7225  */
7226  nexus=XVisualDirectoryImage(display,resource_info,windows);
7227  break;
7228  }
7229  case QuitCommand:
7230  {
7231  /*
7232  exit program.
7233  */
7234  if (resource_info->confirm_exit == MagickFalse)
7235  XClientMessage(display,windows->image.id,windows->im_protocols,
7236  windows->im_exit,CurrentTime);
7237  else
7238  {
7239  int
7240  status;
7241 
7242  /*
7243  Confirm program exit.
7244  */
7245  status=XConfirmWidget(display,windows,"Do you really want to exit",
7246  resource_info->client_name);
7247  if (status > 0)
7248  XClientMessage(display,windows->image.id,windows->im_protocols,
7249  windows->im_exit,CurrentTime);
7250  }
7251  break;
7252  }
7253  case CutCommand:
7254  {
7255  /*
7256  Cut image.
7257  */
7258  (void) XCropImage(display,resource_info,windows,*image,CutMode);
7259  break;
7260  }
7261  case CopyCommand:
7262  {
7263  /*
7264  Copy image.
7265  */
7266  (void) XCropImage(display,resource_info,windows,*image,CopyMode);
7267  break;
7268  }
7269  case PasteCommand:
7270  {
7271  /*
7272  Paste image.
7273  */
7274  status=XPasteImage(display,resource_info,windows,*image);
7275  if (status == MagickFalse)
7276  {
7277  XNoticeWidget(display,windows,"Unable to paste X image",
7278  (*image)->filename);
7279  break;
7280  }
7281  break;
7282  }
7283  case HalfSizeCommand:
7284  {
7285  /*
7286  Half image size.
7287  */
7288  windows->image.window_changes.width=windows->image.ximage->width/2;
7289  windows->image.window_changes.height=windows->image.ximage->height/2;
7290  (void) XConfigureImage(display,resource_info,windows,*image);
7291  break;
7292  }
7293  case OriginalSizeCommand:
7294  {
7295  /*
7296  Original image size.
7297  */
7298  windows->image.window_changes.width=(int) (*image)->columns;
7299  windows->image.window_changes.height=(int) (*image)->rows;
7300  (void) XConfigureImage(display,resource_info,windows,*image);
7301  break;
7302  }
7303  case DoubleSizeCommand:
7304  {
7305  /*
7306  Double the image size.
7307  */
7308  windows->image.window_changes.width=windows->image.ximage->width << 1;
7309  windows->image.window_changes.height=windows->image.ximage->height << 1;
7310  (void) XConfigureImage(display,resource_info,windows,*image);
7311  break;
7312  }
7313  case ResizeCommand:
7314  {
7315  int
7316  status;
7317 
7318  size_t
7319  height,
7320  width;
7321 
7322  ssize_t
7323  x,
7324  y;
7325 
7326  /*
7327  Resize image.
7328  */
7329  width=(size_t) windows->image.ximage->width;
7330  height=(size_t) windows->image.ximage->height;
7331  x=0;
7332  y=0;
7333  (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7334  (double) width,(double) height);
7335  status=XDialogWidget(display,windows,"Resize",
7336  "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7337  if (*geometry == '\0')
7338  break;
7339  if (status == 0)
7340  (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7341  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7342  windows->image.window_changes.width=(int) width;
7343  windows->image.window_changes.height=(int) height;
7344  (void) XConfigureImage(display,resource_info,windows,*image);
7345  break;
7346  }
7347  case ApplyCommand:
7348  {
7349  char
7350  image_geometry[MaxTextExtent];
7351 
7352  if ((windows->image.crop_geometry == (char *) NULL) &&
7353  ((int) (*image)->columns == windows->image.ximage->width) &&
7354  ((int) (*image)->rows == windows->image.ximage->height))
7355  break;
7356  /*
7357  Apply size transforms to image.
7358  */
7359  XSetCursorState(display,windows,MagickTrue);
7360  XCheckRefreshWindows(display,windows);
7361  /*
7362  Crop and/or scale displayed image.
7363  */
7364  (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7365  windows->image.ximage->width,windows->image.ximage->height);
7366  (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7367  if (windows->image.crop_geometry != (char *) NULL)
7368  windows->image.crop_geometry=(char *)
7369  RelinquishMagickMemory(windows->image.crop_geometry);
7370  windows->image.x=0;
7371  windows->image.y=0;
7372  XConfigureImageColormap(display,resource_info,windows,*image);
7373  (void) XConfigureImage(display,resource_info,windows,*image);
7374  break;
7375  }
7376  case RefreshCommand:
7377  {
7378  (void) XConfigureImage(display,resource_info,windows,*image);
7379  break;
7380  }
7381  case RestoreCommand:
7382  {
7383  /*
7384  Restore Image window to its original size.
7385  */
7386  if ((windows->image.width == (unsigned int) (*image)->columns) &&
7387  (windows->image.height == (unsigned int) (*image)->rows) &&
7388  (windows->image.crop_geometry == (char *) NULL))
7389  {
7390  (void) XBell(display,0);
7391  break;
7392  }
7393  windows->image.window_changes.width=(int) (*image)->columns;
7394  windows->image.window_changes.height=(int) (*image)->rows;
7395  if (windows->image.crop_geometry != (char *) NULL)
7396  {
7397  windows->image.crop_geometry=(char *)
7398  RelinquishMagickMemory(windows->image.crop_geometry);
7399  windows->image.crop_geometry=(char *) NULL;
7400  windows->image.x=0;
7401  windows->image.y=0;
7402  }
7403  XConfigureImageColormap(display,resource_info,windows,*image);
7404  (void) XConfigureImage(display,resource_info,windows,*image);
7405  break;
7406  }
7407  case CropCommand:
7408  {
7409  /*
7410  Crop image.
7411  */
7412  (void) XCropImage(display,resource_info,windows,*image,CropMode);
7413  break;
7414  }
7415  case ChopCommand:
7416  {
7417  /*
7418  Chop image.
7419  */
7420  status=XChopImage(display,resource_info,windows,image);
7421  if (status == MagickFalse)
7422  {
7423  XNoticeWidget(display,windows,"Unable to cut X image",
7424  (*image)->filename);
7425  break;
7426  }
7427  break;
7428  }
7429  case FlopCommand:
7430  {
7431  Image
7432  *flop_image;
7433 
7434  /*
7435  Flop image scanlines.
7436  */
7437  XSetCursorState(display,windows,MagickTrue);
7438  XCheckRefreshWindows(display,windows);
7439  flop_image=FlopImage(*image,&(*image)->exception);
7440  if (flop_image != (Image *) NULL)
7441  {
7442  *image=DestroyImage(*image);
7443  *image=flop_image;
7444  }
7445  CatchException(&(*image)->exception);
7446  XSetCursorState(display,windows,MagickFalse);
7447  if (windows->image.crop_geometry != (char *) NULL)
7448  {
7449  /*
7450  Flop crop geometry.
7451  */
7452  width=(unsigned int) (*image)->columns;
7453  height=(unsigned int) (*image)->rows;
7454  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7455  &width,&height);
7456  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7457  "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7458  }
7459  if (windows->image.orphan != MagickFalse)
7460  break;
7461  (void) XConfigureImage(display,resource_info,windows,*image);
7462  break;
7463  }
7464  case FlipCommand:
7465  {
7466  Image
7467  *flip_image;
7468 
7469  /*
7470  Flip image scanlines.
7471  */
7472  XSetCursorState(display,windows,MagickTrue);
7473  XCheckRefreshWindows(display,windows);
7474  flip_image=FlipImage(*image,&(*image)->exception);
7475  if (flip_image != (Image *) NULL)
7476  {
7477  *image=DestroyImage(*image);
7478  *image=flip_image;
7479  }
7480  CatchException(&(*image)->exception);
7481  XSetCursorState(display,windows,MagickFalse);
7482  if (windows->image.crop_geometry != (char *) NULL)
7483  {
7484  /*
7485  Flip crop geometry.
7486  */
7487  width=(unsigned int) (*image)->columns;
7488  height=(unsigned int) (*image)->rows;
7489  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7490  &width,&height);
7491  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7492  "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7493  }
7494  if (windows->image.orphan != MagickFalse)
7495  break;
7496  (void) XConfigureImage(display,resource_info,windows,*image);
7497  break;
7498  }
7499  case RotateRightCommand:
7500  {
7501  /*
7502  Rotate image 90 degrees clockwise.
7503  */
7504  status=XRotateImage(display,resource_info,windows,90.0,image);
7505  if (status == MagickFalse)
7506  {
7507  XNoticeWidget(display,windows,"Unable to rotate X image",
7508  (*image)->filename);
7509  break;
7510  }
7511  break;
7512  }
7513  case RotateLeftCommand:
7514  {
7515  /*
7516  Rotate image 90 degrees counter-clockwise.
7517  */
7518  status=XRotateImage(display,resource_info,windows,-90.0,image);
7519  if (status == MagickFalse)
7520  {
7521  XNoticeWidget(display,windows,"Unable to rotate X image",
7522  (*image)->filename);
7523  break;
7524  }
7525  break;
7526  }
7527  case RotateCommand:
7528  {
7529  /*
7530  Rotate image.
7531  */
7532  status=XRotateImage(display,resource_info,windows,0.0,image);
7533  if (status == MagickFalse)
7534  {
7535  XNoticeWidget(display,windows,"Unable to rotate X image",
7536  (*image)->filename);
7537  break;
7538  }
7539  break;
7540  }
7541  case ShearCommand:
7542  {
7543  Image
7544  *shear_image;
7545 
7546  static char
7547  geometry[MaxTextExtent] = "45.0x45.0";
7548 
7549  /*
7550  Query user for shear color and geometry.
7551  */
7552  XColorBrowserWidget(display,windows,"Select",color);
7553  if (*color == '\0')
7554  break;
7555  (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7556  geometry);
7557  if (*geometry == '\0')
7558  break;
7559  /*
7560  Shear image.
7561  */
7562  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7563  XSetCursorState(display,windows,MagickTrue);
7564  XCheckRefreshWindows(display,windows);
7565  (void) QueryColorDatabase(color,&(*image)->background_color,
7566  &(*image)->exception);
7567  flags=ParseGeometry(geometry,&geometry_info);
7568  if ((flags & SigmaValue) == 0)
7569  geometry_info.sigma=geometry_info.rho;
7570  shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7571  &(*image)->exception);
7572  if (shear_image != (Image *) NULL)
7573  {
7574  *image=DestroyImage(*image);
7575  *image=shear_image;
7576  }
7577  CatchException(&(*image)->exception);
7578  XSetCursorState(display,windows,MagickFalse);
7579  if (windows->image.orphan != MagickFalse)
7580  break;
7581  windows->image.window_changes.width=(int) (*image)->columns;
7582  windows->image.window_changes.height=(int) (*image)->rows;
7583  XConfigureImageColormap(display,resource_info,windows,*image);
7584  (void) XConfigureImage(display,resource_info,windows,*image);
7585  break;
7586  }
7587  case RollCommand:
7588  {
7589  Image
7590  *roll_image;
7591 
7592  static char
7593  geometry[MaxTextExtent] = "+2+2";
7594 
7595  /*
7596  Query user for the roll geometry.
7597  */
7598  (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7599  geometry);
7600  if (*geometry == '\0')
7601  break;
7602  /*
7603  Roll image.
7604  */
7605  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7606  XSetCursorState(display,windows,MagickTrue);
7607  XCheckRefreshWindows(display,windows);
7608  (void) ParsePageGeometry(*image,geometry,&page_geometry,
7609  &(*image)->exception);
7610  roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7611  &(*image)->exception);
7612  if (roll_image != (Image *) NULL)
7613  {
7614  *image=DestroyImage(*image);
7615  *image=roll_image;
7616  }
7617  CatchException(&(*image)->exception);
7618  XSetCursorState(display,windows,MagickFalse);
7619  if (windows->image.orphan != MagickFalse)
7620  break;
7621  windows->image.window_changes.width=(int) (*image)->columns;
7622  windows->image.window_changes.height=(int) (*image)->rows;
7623  XConfigureImageColormap(display,resource_info,windows,*image);
7624  (void) XConfigureImage(display,resource_info,windows,*image);
7625  break;
7626  }
7627  case TrimCommand:
7628  {
7629  static char
7630  fuzz[MaxTextExtent];
7631 
7632  /*
7633  Query user for the fuzz factor.
7634  */
7635  (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7636  (*image)->fuzz/((double) QuantumRange+1.0));
7637  (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7638  if (*fuzz == '\0')
7639  break;
7640  (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7641  /*
7642  Trim image.
7643  */
7644  status=XTrimImage(display,resource_info,windows,*image);
7645  if (status == MagickFalse)
7646  {
7647  XNoticeWidget(display,windows,"Unable to trim X image",
7648  (*image)->filename);
7649  break;
7650  }
7651  break;
7652  }
7653  case HueCommand:
7654  {
7655  static char
7656  hue_percent[MaxTextExtent] = "110";
7657 
7658  /*
7659  Query user for percent hue change.
7660  */
7661  (void) XDialogWidget(display,windows,"Apply",
7662  "Enter percent change in image hue (0-200):",hue_percent);
7663  if (*hue_percent == '\0')
7664  break;
7665  /*
7666  Vary the image hue.
7667  */
7668  XSetCursorState(display,windows,MagickTrue);
7669  XCheckRefreshWindows(display,windows);
7670  (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7671  (void) ConcatenateMagickString(modulate_factors,hue_percent,
7672  MaxTextExtent);
7673  (void) ModulateImage(*image,modulate_factors);
7674  XSetCursorState(display,windows,MagickFalse);
7675  if (windows->image.orphan != MagickFalse)
7676  break;
7677  XConfigureImageColormap(display,resource_info,windows,*image);
7678  (void) XConfigureImage(display,resource_info,windows,*image);
7679  break;
7680  }
7681  case SaturationCommand:
7682  {
7683  static char
7684  saturation_percent[MaxTextExtent] = "110";
7685 
7686  /*
7687  Query user for percent saturation change.
7688  */
7689  (void) XDialogWidget(display,windows,"Apply",
7690  "Enter percent change in color saturation (0-200):",saturation_percent);
7691  if (*saturation_percent == '\0')
7692  break;
7693  /*
7694  Vary color saturation.
7695  */
7696  XSetCursorState(display,windows,MagickTrue);
7697  XCheckRefreshWindows(display,windows);
7698  (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7699  (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7700  MaxTextExtent);
7701  (void) ModulateImage(*image,modulate_factors);
7702  XSetCursorState(display,windows,MagickFalse);
7703  if (windows->image.orphan != MagickFalse)
7704  break;
7705  XConfigureImageColormap(display,resource_info,windows,*image);
7706  (void) XConfigureImage(display,resource_info,windows,*image);
7707  break;
7708  }
7709  case BrightnessCommand:
7710  {
7711  static char
7712  brightness_percent[MaxTextExtent] = "110";
7713 
7714  /*
7715  Query user for percent brightness change.
7716  */
7717  (void) XDialogWidget(display,windows,"Apply",
7718  "Enter percent change in color brightness (0-200):",brightness_percent);
7719  if (*brightness_percent == '\0')
7720  break;
7721  /*
7722  Vary the color brightness.
7723  */
7724  XSetCursorState(display,windows,MagickTrue);
7725  XCheckRefreshWindows(display,windows);
7726  (void) CopyMagickString(modulate_factors,brightness_percent,
7727  MaxTextExtent);
7728  (void) ModulateImage(*image,modulate_factors);
7729  XSetCursorState(display,windows,MagickFalse);
7730  if (windows->image.orphan != MagickFalse)
7731  break;
7732  XConfigureImageColormap(display,resource_info,windows,*image);
7733  (void) XConfigureImage(display,resource_info,windows,*image);
7734  break;
7735  }
7736  case GammaCommand:
7737  {
7738  static char
7739  factor[MaxTextExtent] = "1.6";
7740 
7741  /*
7742  Query user for gamma value.
7743  */
7744  (void) XDialogWidget(display,windows,"Gamma",
7745  "Enter gamma value (e.g. 1.0,1.0,1.6):",factor);
7746  if (*factor == '\0')
7747  break;
7748  /*
7749  Gamma correct image.
7750  */
7751  XSetCursorState(display,windows,MagickTrue);
7752  XCheckRefreshWindows(display,windows);
7753  (void) GammaImage(*image,factor);
7754  XSetCursorState(display,windows,MagickFalse);
7755  if (windows->image.orphan != MagickFalse)
7756  break;
7757  XConfigureImageColormap(display,resource_info,windows,*image);
7758  (void) XConfigureImage(display,resource_info,windows,*image);
7759  break;
7760  }
7761  case SpiffCommand:
7762  {
7763  /*
7764  Sharpen the image contrast.
7765  */
7766  XSetCursorState(display,windows,MagickTrue);
7767  XCheckRefreshWindows(display,windows);
7768  (void) ContrastImage(*image,MagickTrue);
7769  XSetCursorState(display,windows,MagickFalse);
7770  if (windows->image.orphan != MagickFalse)
7771  break;
7772  XConfigureImageColormap(display,resource_info,windows,*image);
7773  (void) XConfigureImage(display,resource_info,windows,*image);
7774  break;
7775  }
7776  case DullCommand:
7777  {
7778  /*
7779  Dull the image contrast.
7780  */
7781  XSetCursorState(display,windows,MagickTrue);
7782  XCheckRefreshWindows(display,windows);
7783  (void) ContrastImage(*image,MagickFalse);
7784  XSetCursorState(display,windows,MagickFalse);
7785  if (windows->image.orphan != MagickFalse)
7786  break;
7787  XConfigureImageColormap(display,resource_info,windows,*image);
7788  (void) XConfigureImage(display,resource_info,windows,*image);
7789  break;
7790  }
7791  case ContrastStretchCommand:
7792  {
7793  double
7794  black_point,
7795  white_point;
7796 
7797  static char
7798  levels[MaxTextExtent] = "1%";
7799 
7800  /*
7801  Query user for gamma value.
7802  */
7803  (void) XDialogWidget(display,windows,"Contrast Stretch",
7804  "Enter black and white points:",levels);
7805  if (*levels == '\0')
7806  break;
7807  /*
7808  Contrast stretch image.
7809  */
7810  XSetCursorState(display,windows,MagickTrue);
7811  XCheckRefreshWindows(display,windows);
7812  flags=ParseGeometry(levels,&geometry_info);
7813  black_point=geometry_info.rho;
7814  white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7815  if ((flags & PercentValue) != 0)
7816  {
7817  black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7818  white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7819  }
7820  white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7821  (void) ContrastStretchImageChannel(*image,DefaultChannels,black_point,
7822  white_point);
7823  XSetCursorState(display,windows,MagickFalse);
7824  if (windows->image.orphan != MagickFalse)
7825  break;
7826  XConfigureImageColormap(display,resource_info,windows,*image);
7827  (void) XConfigureImage(display,resource_info,windows,*image);
7828  break;
7829  }
7830  case SigmoidalContrastCommand:
7831  {
7832  static char
7833  levels[MaxTextExtent] = "3x50%";
7834 
7835  /*
7836  Query user for gamma value.
7837  */
7838  (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7839  "Enter contrast and midpoint:",levels);
7840  if (*levels == '\0')
7841  break;
7842  /*
7843  Contrast stretch image.
7844  */
7845  XSetCursorState(display,windows,MagickTrue);
7846  XCheckRefreshWindows(display,windows);
7847  (void) SigmoidalContrastImage(*image,MagickTrue,levels);
7848  XSetCursorState(display,windows,MagickFalse);
7849  if (windows->image.orphan != MagickFalse)
7850  break;
7851  XConfigureImageColormap(display,resource_info,windows,*image);
7852  (void) XConfigureImage(display,resource_info,windows,*image);
7853  break;
7854  }
7855  case NormalizeCommand:
7856  {
7857  /*
7858  Perform histogram normalization on the image.
7859  */
7860  XSetCursorState(display,windows,MagickTrue);
7861  XCheckRefreshWindows(display,windows);
7862  (void) NormalizeImage(*image);
7863  XSetCursorState(display,windows,MagickFalse);
7864  if (windows->image.orphan != MagickFalse)
7865  break;
7866  XConfigureImageColormap(display,resource_info,windows,*image);
7867  (void) XConfigureImage(display,resource_info,windows,*image);
7868  break;
7869  }
7870  case EqualizeCommand:
7871  {
7872  /*
7873  Perform histogram equalization on the image.
7874  */
7875  XSetCursorState(display,windows,MagickTrue);
7876  XCheckRefreshWindows(display,windows);
7877  (void) EqualizeImage(*image);
7878  XSetCursorState(display,windows,MagickFalse);
7879  if (windows->image.orphan != MagickFalse)
7880  break;
7881  XConfigureImageColormap(display,resource_info,windows,*image);
7882  (void) XConfigureImage(display,resource_info,windows,*image);
7883  break;
7884  }
7885  case NegateCommand:
7886  {
7887  /*
7888  Negate colors in image.
7889  */
7890  XSetCursorState(display,windows,MagickTrue);
7891  XCheckRefreshWindows(display,windows);
7892  (void) NegateImage(*image,MagickFalse);
7893  XSetCursorState(display,windows,MagickFalse);
7894  if (windows->image.orphan != MagickFalse)
7895  break;
7896  XConfigureImageColormap(display,resource_info,windows,*image);
7897  (void) XConfigureImage(display,resource_info,windows,*image);
7898  break;
7899  }
7900  case GrayscaleCommand:
7901  {
7902  /*
7903  Convert image to grayscale.
7904  */
7905  XSetCursorState(display,windows,MagickTrue);
7906  XCheckRefreshWindows(display,windows);
7907  (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7908  GrayscaleType : GrayscaleMatteType);
7909  XSetCursorState(display,windows,MagickFalse);
7910  if (windows->image.orphan != MagickFalse)
7911  break;
7912  XConfigureImageColormap(display,resource_info,windows,*image);
7913  (void) XConfigureImage(display,resource_info,windows,*image);
7914  break;
7915  }
7916  case MapCommand:
7917  {
7918  Image
7919  *affinity_image;
7920 
7921  static char
7922  filename[MaxTextExtent] = "\0";
7923 
7924  /*
7925  Request image file name from user.
7926  */
7927  XFileBrowserWidget(display,windows,"Map",filename);
7928  if (*filename == '\0')
7929  break;
7930  /*
7931  Map image.
7932  */
7933  XSetCursorState(display,windows,MagickTrue);
7934  XCheckRefreshWindows(display,windows);
7935  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
7936  affinity_image=ReadImage(image_info,&(*image)->exception);
7937  if (affinity_image != (Image *) NULL)
7938  {
7939  (void) RemapImage(&quantize_info,*image,affinity_image);
7940  affinity_image=DestroyImage(affinity_image);
7941  }
7942  CatchException(&(*image)->exception);
7943  XSetCursorState(display,windows,MagickFalse);
7944  if (windows->image.orphan != MagickFalse)
7945  break;
7946  XConfigureImageColormap(display,resource_info,windows,*image);
7947  (void) XConfigureImage(display,resource_info,windows,*image);
7948  break;
7949  }
7950  case QuantizeCommand:
7951  {
7952  int
7953  status;
7954 
7955  static char
7956  colors[MaxTextExtent] = "256";
7957 
7958  /*
7959  Query user for maximum number of colors.
7960  */
7961  status=XDialogWidget(display,windows,"Quantize",
7962  "Maximum number of colors:",colors);
7963  if (*colors == '\0')
7964  break;
7965  /*
7966  Color reduce the image.
7967  */
7968  XSetCursorState(display,windows,MagickTrue);
7969  XCheckRefreshWindows(display,windows);
7970  quantize_info.number_colors=StringToUnsignedLong(colors);
7971  quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
7972  (void) QuantizeImage(&quantize_info,*image);
7973  XSetCursorState(display,windows,MagickFalse);
7974  if (windows->image.orphan != MagickFalse)
7975  break;
7976  XConfigureImageColormap(display,resource_info,windows,*image);
7977  (void) XConfigureImage(display,resource_info,windows,*image);
7978  break;
7979  }
7980  case DespeckleCommand:
7981  {
7982  Image
7983  *despeckle_image;
7984 
7985  /*
7986  Despeckle image.
7987  */
7988  XSetCursorState(display,windows,MagickTrue);
7989  XCheckRefreshWindows(display,windows);
7990  despeckle_image=DespeckleImage(*image,&(*image)->exception);
7991  if (despeckle_image != (Image *) NULL)
7992  {
7993  *image=DestroyImage(*image);
7994  *image=despeckle_image;
7995  }
7996  CatchException(&(*image)->exception);
7997  XSetCursorState(display,windows,MagickFalse);
7998  if (windows->image.orphan != MagickFalse)
7999  break;
8000  XConfigureImageColormap(display,resource_info,windows,*image);
8001  (void) XConfigureImage(display,resource_info,windows,*image);
8002  break;
8003  }
8004  case EmbossCommand:
8005  {
8006  Image
8007  *emboss_image;
8008 
8009  static char
8010  radius[MaxTextExtent] = "0.0x1.0";
8011 
8012  /*
8013  Query user for emboss radius.
8014  */
8015  (void) XDialogWidget(display,windows,"Emboss",
8016  "Enter the emboss radius and standard deviation:",radius);
8017  if (*radius == '\0')
8018  break;
8019  /*
8020  Reduce noise in the image.
8021  */
8022  XSetCursorState(display,windows,MagickTrue);
8023  XCheckRefreshWindows(display,windows);
8024  flags=ParseGeometry(radius,&geometry_info);
8025  if ((flags & SigmaValue) == 0)
8026  geometry_info.sigma=1.0;
8027  emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8028  &(*image)->exception);
8029  if (emboss_image != (Image *) NULL)
8030  {
8031  *image=DestroyImage(*image);
8032  *image=emboss_image;
8033  }
8034  CatchException(&(*image)->exception);
8035  XSetCursorState(display,windows,MagickFalse);
8036  if (windows->image.orphan != MagickFalse)
8037  break;
8038  XConfigureImageColormap(display,resource_info,windows,*image);
8039  (void) XConfigureImage(display,resource_info,windows,*image);
8040  break;
8041  }
8042  case ReduceNoiseCommand:
8043  {
8044  Image
8045  *noise_image;
8046 
8047  static char
8048  radius[MaxTextExtent] = "0";
8049 
8050  /*
8051  Query user for noise radius.
8052  */
8053  (void) XDialogWidget(display,windows,"Reduce Noise",
8054  "Enter the noise radius:",radius);
8055  if (*radius == '\0')
8056  break;
8057  /*
8058  Reduce noise in the image.
8059  */
8060  XSetCursorState(display,windows,MagickTrue);
8061  XCheckRefreshWindows(display,windows);
8062  flags=ParseGeometry(radius,&geometry_info);
8063  noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8064  geometry_info.rho,(size_t) geometry_info.rho,&(*image)->exception);
8065  if (noise_image != (Image *) NULL)
8066  {
8067  *image=DestroyImage(*image);
8068  *image=noise_image;
8069  }
8070  CatchException(&(*image)->exception);
8071  XSetCursorState(display,windows,MagickFalse);
8072  if (windows->image.orphan != MagickFalse)
8073  break;
8074  XConfigureImageColormap(display,resource_info,windows,*image);
8075  (void) XConfigureImage(display,resource_info,windows,*image);
8076  break;
8077  }
8078  case AddNoiseCommand:
8079  {
8080  char
8081  **noises;
8082 
8083  Image
8084  *noise_image;
8085 
8086  static char
8087  noise_type[MaxTextExtent] = "Gaussian";
8088 
8089  /*
8090  Add noise to the image.
8091  */
8092  noises=GetCommandOptions(MagickNoiseOptions);
8093  if (noises == (char **) NULL)
8094  break;
8095  XListBrowserWidget(display,windows,&windows->widget,
8096  (const char **) noises,"Add Noise",
8097  "Select a type of noise to add to your image:",noise_type);
8098  noises=DestroyStringList(noises);
8099  if (*noise_type == '\0')
8100  break;
8101  XSetCursorState(display,windows,MagickTrue);
8102  XCheckRefreshWindows(display,windows);
8103  noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8104  MagickNoiseOptions,MagickFalse,noise_type),&(*image)->exception);
8105  if (noise_image != (Image *) NULL)
8106  {
8107  *image=DestroyImage(*image);
8108  *image=noise_image;
8109  }
8110  CatchException(&(*image)->exception);
8111  XSetCursorState(display,windows,MagickFalse);
8112  if (windows->image.orphan != MagickFalse)
8113  break;
8114  XConfigureImageColormap(display,resource_info,windows,*image);
8115  (void) XConfigureImage(display,resource_info,windows,*image);
8116  break;
8117  }
8118  case SharpenCommand:
8119  {
8120  Image
8121  *sharp_image;
8122 
8123  static char
8124  radius[MaxTextExtent] = "0.0x1.0";
8125 
8126  /*
8127  Query user for sharpen radius.
8128  */
8129  (void) XDialogWidget(display,windows,"Sharpen",
8130  "Enter the sharpen radius and standard deviation:",radius);
8131  if (*radius == '\0')
8132  break;
8133  /*
8134  Sharpen image scanlines.
8135  */
8136  XSetCursorState(display,windows,MagickTrue);
8137  XCheckRefreshWindows(display,windows);
8138  flags=ParseGeometry(radius,&geometry_info);
8139  sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8140  &(*image)->exception);
8141  if (sharp_image != (Image *) NULL)
8142  {
8143  *image=DestroyImage(*image);
8144  *image=sharp_image;
8145  }
8146  CatchException(&(*image)->exception);
8147  XSetCursorState(display,windows,MagickFalse);
8148  if (windows->image.orphan != MagickFalse)
8149  break;
8150  XConfigureImageColormap(display,resource_info,windows,*image);
8151  (void) XConfigureImage(display,resource_info,windows,*image);
8152  break;
8153  }
8154  case BlurCommand:
8155  {
8156  Image
8157  *blur_image;
8158 
8159  static char
8160  radius[MaxTextExtent] = "0.0x1.0";
8161 
8162  /*
8163  Query user for blur radius.
8164  */
8165  (void) XDialogWidget(display,windows,"Blur",
8166  "Enter the blur radius and standard deviation:",radius);
8167  if (*radius == '\0')
8168  break;
8169  /*
8170  Blur an image.
8171  */
8172  XSetCursorState(display,windows,MagickTrue);
8173  XCheckRefreshWindows(display,windows);
8174  flags=ParseGeometry(radius,&geometry_info);
8175  blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8176  &(*image)->exception);
8177  if (blur_image != (Image *) NULL)
8178  {
8179  *image=DestroyImage(*image);
8180  *image=blur_image;
8181  }
8182  CatchException(&(*image)->exception);
8183  XSetCursorState(display,windows,MagickFalse);
8184  if (windows->image.orphan != MagickFalse)
8185  break;
8186  XConfigureImageColormap(display,resource_info,windows,*image);
8187  (void) XConfigureImage(display,resource_info,windows,*image);
8188  break;
8189  }
8190  case ThresholdCommand:
8191  {
8192  double
8193  threshold;
8194 
8195  static char
8196  factor[MaxTextExtent] = "128";
8197 
8198  /*
8199  Query user for threshold value.
8200  */
8201  (void) XDialogWidget(display,windows,"Threshold",
8202  "Enter threshold value:",factor);
8203  if (*factor == '\0')
8204  break;
8205  /*
8206  Gamma correct image.
8207  */
8208  XSetCursorState(display,windows,MagickTrue);
8209  XCheckRefreshWindows(display,windows);
8210  threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8211  (void) BilevelImage(*image,threshold);
8212  XSetCursorState(display,windows,MagickFalse);
8213  if (windows->image.orphan != MagickFalse)
8214  break;
8215  XConfigureImageColormap(display,resource_info,windows,*image);
8216  (void) XConfigureImage(display,resource_info,windows,*image);
8217  break;
8218  }
8219  case EdgeDetectCommand:
8220  {
8221  Image
8222  *edge_image;
8223 
8224  static char
8225  radius[MaxTextExtent] = "0";
8226 
8227  /*
8228  Query user for edge factor.
8229  */
8230  (void) XDialogWidget(display,windows,"Detect Edges",
8231  "Enter the edge detect radius:",radius);
8232  if (*radius == '\0')
8233  break;
8234  /*
8235  Detect edge in image.
8236  */
8237  XSetCursorState(display,windows,MagickTrue);
8238  XCheckRefreshWindows(display,windows);
8239  flags=ParseGeometry(radius,&geometry_info);
8240  edge_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8241  if (edge_image != (Image *) NULL)
8242  {
8243  *image=DestroyImage(*image);
8244  *image=edge_image;
8245  }
8246  CatchException(&(*image)->exception);
8247  XSetCursorState(display,windows,MagickFalse);
8248  if (windows->image.orphan != MagickFalse)
8249  break;
8250  XConfigureImageColormap(display,resource_info,windows,*image);
8251  (void) XConfigureImage(display,resource_info,windows,*image);
8252  break;
8253  }
8254  case SpreadCommand:
8255  {
8256  Image
8257  *spread_image;
8258 
8259  static char
8260  amount[MaxTextExtent] = "2";
8261 
8262  /*
8263  Query user for spread amount.
8264  */
8265  (void) XDialogWidget(display,windows,"Spread",
8266  "Enter the displacement amount:",amount);
8267  if (*amount == '\0')
8268  break;
8269  /*
8270  Displace image pixels by a random amount.
8271  */
8272  XSetCursorState(display,windows,MagickTrue);
8273  XCheckRefreshWindows(display,windows);
8274  flags=ParseGeometry(amount,&geometry_info);
8275  spread_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8276  if (spread_image != (Image *) NULL)
8277  {
8278  *image=DestroyImage(*image);
8279  *image=spread_image;
8280  }
8281  CatchException(&(*image)->exception);
8282  XSetCursorState(display,windows,MagickFalse);
8283  if (windows->image.orphan != MagickFalse)
8284  break;
8285  XConfigureImageColormap(display,resource_info,windows,*image);
8286  (void) XConfigureImage(display,resource_info,windows,*image);
8287  break;
8288  }
8289  case ShadeCommand:
8290  {
8291  Image
8292  *shade_image;
8293 
8294  int
8295  status;
8296 
8297  static char
8298  geometry[MaxTextExtent] = "30x30";
8299 
8300  /*
8301  Query user for the shade geometry.
8302  */
8303  status=XDialogWidget(display,windows,"Shade",
8304  "Enter the azimuth and elevation of the light source:",geometry);
8305  if (*geometry == '\0')
8306  break;
8307  /*
8308  Shade image pixels.
8309  */
8310  XSetCursorState(display,windows,MagickTrue);
8311  XCheckRefreshWindows(display,windows);
8312  flags=ParseGeometry(geometry,&geometry_info);
8313  if ((flags & SigmaValue) == 0)
8314  geometry_info.sigma=1.0;
8315  shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8316  geometry_info.rho,geometry_info.sigma,&(*image)->exception);
8317  if (shade_image != (Image *) NULL)
8318  {
8319  *image=DestroyImage(*image);
8320  *image=shade_image;
8321  }
8322  CatchException(&(*image)->exception);
8323  XSetCursorState(display,windows,MagickFalse);
8324  if (windows->image.orphan != MagickFalse)
8325  break;
8326  XConfigureImageColormap(display,resource_info,windows,*image);
8327  (void) XConfigureImage(display,resource_info,windows,*image);
8328  break;
8329  }
8330  case RaiseCommand:
8331  {
8332  static char
8333  bevel_width[MaxTextExtent] = "10";
8334 
8335  /*
8336  Query user for bevel width.
8337  */
8338  (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8339  if (*bevel_width == '\0')
8340  break;
8341  /*
8342  Raise an image.
8343  */
8344  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8345  XSetCursorState(display,windows,MagickTrue);
8346  XCheckRefreshWindows(display,windows);
8347  (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8348  &(*image)->exception);
8349  (void) RaiseImage(*image,&page_geometry,MagickTrue);
8350  XSetCursorState(display,windows,MagickFalse);
8351  if (windows->image.orphan != MagickFalse)
8352  break;
8353  XConfigureImageColormap(display,resource_info,windows,*image);
8354  (void) XConfigureImage(display,resource_info,windows,*image);
8355  break;
8356  }
8357  case SegmentCommand:
8358  {
8359  static char
8360  threshold[MaxTextExtent] = "1.0x1.5";
8361 
8362  /*
8363  Query user for smoothing threshold.
8364  */
8365  (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8366  threshold);
8367  if (*threshold == '\0')
8368  break;
8369  /*
8370  Segment an image.
8371  */
8372  XSetCursorState(display,windows,MagickTrue);
8373  XCheckRefreshWindows(display,windows);
8374  flags=ParseGeometry(threshold,&geometry_info);
8375  if ((flags & SigmaValue) == 0)
8376  geometry_info.sigma=1.0;
8377  (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8378  geometry_info.sigma);
8379  XSetCursorState(display,windows,MagickFalse);
8380  if (windows->image.orphan != MagickFalse)
8381  break;
8382  XConfigureImageColormap(display,resource_info,windows,*image);
8383  (void) XConfigureImage(display,resource_info,windows,*image);
8384  break;
8385  }
8386  case SepiaToneCommand:
8387  {
8388  double
8389  threshold;
8390 
8391  Image
8392  *sepia_image;
8393 
8394  static char
8395  factor[MaxTextExtent] = "80%";
8396 
8397  /*
8398  Query user for sepia-tone factor.
8399  */
8400  (void) XDialogWidget(display,windows,"Sepia Tone",
8401  "Enter the sepia tone factor (0 - 99.9%):",factor);
8402  if (*factor == '\0')
8403  break;
8404  /*
8405  Sepia tone image pixels.
8406  */
8407  XSetCursorState(display,windows,MagickTrue);
8408  XCheckRefreshWindows(display,windows);
8409  threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8410  sepia_image=SepiaToneImage(*image,threshold,&(*image)->exception);
8411  if (sepia_image != (Image *) NULL)
8412  {
8413  *image=DestroyImage(*image);
8414  *image=sepia_image;
8415  }
8416  CatchException(&(*image)->exception);
8417  XSetCursorState(display,windows,MagickFalse);
8418  if (windows->image.orphan != MagickFalse)
8419  break;
8420  XConfigureImageColormap(display,resource_info,windows,*image);
8421  (void) XConfigureImage(display,resource_info,windows,*image);
8422  break;
8423  }
8424  case SolarizeCommand:
8425  {
8426  double
8427  threshold;
8428 
8429  static char
8430  factor[MaxTextExtent] = "60%";
8431 
8432  /*
8433  Query user for solarize factor.
8434  */
8435  (void) XDialogWidget(display,windows,"Solarize",
8436  "Enter the solarize factor (0 - 99.9%):",factor);
8437  if (*factor == '\0')
8438  break;
8439  /*
8440  Solarize image pixels.
8441  */
8442  XSetCursorState(display,windows,MagickTrue);
8443  XCheckRefreshWindows(display,windows);
8444  threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8445  (void) SolarizeImage(*image,threshold);
8446  XSetCursorState(display,windows,MagickFalse);
8447  if (windows->image.orphan != MagickFalse)
8448  break;
8449  XConfigureImageColormap(display,resource_info,windows,*image);
8450  (void) XConfigureImage(display,resource_info,windows,*image);
8451  break;
8452  }
8453  case SwirlCommand:
8454  {
8455  Image
8456  *swirl_image;
8457 
8458  static char
8459  degrees[MaxTextExtent] = "60";
8460 
8461  /*
8462  Query user for swirl angle.
8463  */
8464  (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8465  degrees);
8466  if (*degrees == '\0')
8467  break;
8468  /*
8469  Swirl image pixels about the center.
8470  */
8471  XSetCursorState(display,windows,MagickTrue);
8472  XCheckRefreshWindows(display,windows);
8473  flags=ParseGeometry(degrees,&geometry_info);
8474  swirl_image=SwirlImage(*image,geometry_info.rho,&(*image)->exception);
8475  if (swirl_image != (Image *) NULL)
8476  {
8477  *image=DestroyImage(*image);
8478  *image=swirl_image;
8479  }
8480  CatchException(&(*image)->exception);
8481  XSetCursorState(display,windows,MagickFalse);
8482  if (windows->image.orphan != MagickFalse)
8483  break;
8484  XConfigureImageColormap(display,resource_info,windows,*image);
8485  (void) XConfigureImage(display,resource_info,windows,*image);
8486  break;
8487  }
8488  case ImplodeCommand:
8489  {
8490  Image
8491  *implode_image;
8492 
8493  static char
8494  factor[MaxTextExtent] = "0.3";
8495 
8496  /*
8497  Query user for implode factor.
8498  */
8499  (void) XDialogWidget(display,windows,"Implode",
8500  "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8501  if (*factor == '\0')
8502  break;
8503  /*
8504  Implode image pixels about the center.
8505  */
8506  XSetCursorState(display,windows,MagickTrue);
8507  XCheckRefreshWindows(display,windows);
8508  flags=ParseGeometry(factor,&geometry_info);
8509  implode_image=ImplodeImage(*image,geometry_info.rho,&(*image)->exception);
8510  if (implode_image != (Image *) NULL)
8511  {
8512  *image=DestroyImage(*image);
8513  *image=implode_image;
8514  }
8515  CatchException(&(*image)->exception);
8516  XSetCursorState(display,windows,MagickFalse);
8517  if (windows->image.orphan != MagickFalse)
8518  break;
8519  XConfigureImageColormap(display,resource_info,windows,*image);
8520  (void) XConfigureImage(display,resource_info,windows,*image);
8521  break;
8522  }
8523  case VignetteCommand:
8524  {
8525  Image
8526  *vignette_image;
8527 
8528  static char
8529  geometry[MaxTextExtent] = "0x20";
8530 
8531  /*
8532  Query user for the vignette geometry.
8533  */
8534  (void) XDialogWidget(display,windows,"Vignette",
8535  "Enter the radius, sigma, and x and y offsets:",geometry);
8536  if (*geometry == '\0')
8537  break;
8538  /*
8539  Soften the edges of the image in vignette style
8540  */
8541  XSetCursorState(display,windows,MagickTrue);
8542  XCheckRefreshWindows(display,windows);
8543  flags=ParseGeometry(geometry,&geometry_info);
8544  if ((flags & SigmaValue) == 0)
8545  geometry_info.sigma=1.0;
8546  if ((flags & XiValue) == 0)
8547  geometry_info.xi=0.1*(*image)->columns;
8548  if ((flags & PsiValue) == 0)
8549  geometry_info.psi=0.1*(*image)->rows;
8550  vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8551  (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8552  0.5),&(*image)->exception);
8553  if (vignette_image != (Image *) NULL)
8554  {
8555  *image=DestroyImage(*image);
8556  *image=vignette_image;
8557  }
8558  CatchException(&(*image)->exception);
8559  XSetCursorState(display,windows,MagickFalse);
8560  if (windows->image.orphan != MagickFalse)
8561  break;
8562  XConfigureImageColormap(display,resource_info,windows,*image);
8563  (void) XConfigureImage(display,resource_info,windows,*image);
8564  break;
8565  }
8566  case WaveCommand:
8567  {
8568  Image
8569  *wave_image;
8570 
8571  static char
8572  geometry[MaxTextExtent] = "25x150";
8573 
8574  /*
8575  Query user for the wave geometry.
8576  */
8577  (void) XDialogWidget(display,windows,"Wave",
8578  "Enter the amplitude and length of the wave:",geometry);
8579  if (*geometry == '\0')
8580  break;
8581  /*
8582  Alter an image along a sine wave.
8583  */
8584  XSetCursorState(display,windows,MagickTrue);
8585  XCheckRefreshWindows(display,windows);
8586  flags=ParseGeometry(geometry,&geometry_info);
8587  if ((flags & SigmaValue) == 0)
8588  geometry_info.sigma=1.0;
8589  wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8590  &(*image)->exception);
8591  if (wave_image != (Image *) NULL)
8592  {
8593  *image=DestroyImage(*image);
8594  *image=wave_image;
8595  }
8596  CatchException(&(*image)->exception);
8597  XSetCursorState(display,windows,MagickFalse);
8598  if (windows->image.orphan != MagickFalse)
8599  break;
8600  XConfigureImageColormap(display,resource_info,windows,*image);
8601  (void) XConfigureImage(display,resource_info,windows,*image);
8602  break;
8603  }
8604  case OilPaintCommand:
8605  {
8606  Image
8607  *paint_image;
8608 
8609  static char
8610  radius[MaxTextExtent] = "0";
8611 
8612  /*
8613  Query user for circular neighborhood radius.
8614  */
8615  (void) XDialogWidget(display,windows,"Oil Paint",
8616  "Enter the mask radius:",radius);
8617  if (*radius == '\0')
8618  break;
8619  /*
8620  OilPaint image scanlines.
8621  */
8622  XSetCursorState(display,windows,MagickTrue);
8623  XCheckRefreshWindows(display,windows);
8624  flags=ParseGeometry(radius,&geometry_info);
8625  paint_image=OilPaintImage(*image,geometry_info.rho,&(*image)->exception);
8626  if (paint_image != (Image *) NULL)
8627  {
8628  *image=DestroyImage(*image);
8629  *image=paint_image;
8630  }
8631  CatchException(&(*image)->exception);
8632  XSetCursorState(display,windows,MagickFalse);
8633  if (windows->image.orphan != MagickFalse)
8634  break;
8635  XConfigureImageColormap(display,resource_info,windows,*image);
8636  (void) XConfigureImage(display,resource_info,windows,*image);
8637  break;
8638  }
8639  case CharcoalDrawCommand:
8640  {
8641  Image
8642  *charcoal_image;
8643 
8644  static char
8645  radius[MaxTextExtent] = "0x1";
8646 
8647  /*
8648  Query user for charcoal radius.
8649  */
8650  (void) XDialogWidget(display,windows,"Charcoal Draw",
8651  "Enter the charcoal radius and sigma:",radius);
8652  if (*radius == '\0')
8653  break;
8654  /*
8655  Charcoal the image.
8656  */
8657  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8658  XSetCursorState(display,windows,MagickTrue);
8659  XCheckRefreshWindows(display,windows);
8660  flags=ParseGeometry(radius,&geometry_info);
8661  if ((flags & SigmaValue) == 0)
8662  geometry_info.sigma=geometry_info.rho;
8663  charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8664  &(*image)->exception);
8665  if (charcoal_image != (Image *) NULL)
8666  {
8667  *image=DestroyImage(*image);
8668  *image=charcoal_image;
8669  }
8670  CatchException(&(*image)->exception);
8671  XSetCursorState(display,windows,MagickFalse);
8672  if (windows->image.orphan != MagickFalse)
8673  break;
8674  XConfigureImageColormap(display,resource_info,windows,*image);
8675  (void) XConfigureImage(display,resource_info,windows,*image);
8676  break;
8677  }
8678  case AnnotateCommand:
8679  {
8680  /*
8681  Annotate the image with text.
8682  */
8683  status=XAnnotateEditImage(display,resource_info,windows,*image);
8684  if (status == MagickFalse)
8685  {
8686  XNoticeWidget(display,windows,"Unable to annotate X image",
8687  (*image)->filename);
8688  break;
8689  }
8690  break;
8691  }
8692  case DrawCommand:
8693  {
8694  /*
8695  Draw image.
8696  */
8697  status=XDrawEditImage(display,resource_info,windows,image);
8698  if (status == MagickFalse)
8699  {
8700  XNoticeWidget(display,windows,"Unable to draw on the X image",
8701  (*image)->filename);
8702  break;
8703  }
8704  break;
8705  }
8706  case ColorCommand:
8707  {
8708  /*
8709  Color edit.
8710  */
8711  status=XColorEditImage(display,resource_info,windows,image);
8712  if (status == MagickFalse)
8713  {
8714  XNoticeWidget(display,windows,"Unable to pixel edit X image",
8715  (*image)->filename);
8716  break;
8717  }
8718  break;
8719  }
8720  case MatteCommand:
8721  {
8722  /*
8723  Matte edit.
8724  */
8725  status=XMatteEditImage(display,resource_info,windows,image);
8726  if (status == MagickFalse)
8727  {
8728  XNoticeWidget(display,windows,"Unable to matte edit X image",
8729  (*image)->filename);
8730  break;
8731  }
8732  break;
8733  }
8734  case CompositeCommand:
8735  {
8736  /*
8737  Composite image.
8738  */
8739  status=XCompositeImage(display,resource_info,windows,*image);
8740  if (status == MagickFalse)
8741  {
8742  XNoticeWidget(display,windows,"Unable to composite X image",
8743  (*image)->filename);
8744  break;
8745  }
8746  break;
8747  }
8748  case AddBorderCommand:
8749  {
8750  Image
8751  *border_image;
8752 
8753  static char
8754  geometry[MaxTextExtent] = "6x6";
8755 
8756  /*
8757  Query user for border color and geometry.
8758  */
8759  XColorBrowserWidget(display,windows,"Select",color);
8760  if (*color == '\0')
8761  break;
8762  (void) XDialogWidget(display,windows,"Add Border",
8763  "Enter border geometry:",geometry);
8764  if (*geometry == '\0')
8765  break;
8766  /*
8767  Add a border to the image.
8768  */
8769  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8770  XSetCursorState(display,windows,MagickTrue);
8771  XCheckRefreshWindows(display,windows);
8772  (void) QueryColorDatabase(color,&(*image)->border_color,
8773  &(*image)->exception);
8774  (void) ParsePageGeometry(*image,geometry,&page_geometry,
8775  &(*image)->exception);
8776  border_image=BorderImage(*image,&page_geometry,&(*image)->exception);
8777  if (border_image != (Image *) NULL)
8778  {
8779  *image=DestroyImage(*image);
8780  *image=border_image;
8781  }
8782  CatchException(&(*image)->exception);
8783  XSetCursorState(display,windows,MagickFalse);
8784  if (windows->image.orphan != MagickFalse)
8785  break;
8786  windows->image.window_changes.width=(int) (*image)->columns;
8787  windows->image.window_changes.height=(int) (*image)->rows;
8788  XConfigureImageColormap(display,resource_info,windows,*image);
8789  (void) XConfigureImage(display,resource_info,windows,*image);
8790  break;
8791  }
8792  case AddFrameCommand:
8793  {
8794  FrameInfo
8795  frame_info;
8796 
8797  Image
8798  *frame_image;
8799 
8800  static char
8801  geometry[MaxTextExtent] = "6x6";
8802 
8803  /*
8804  Query user for frame color and geometry.
8805  */
8806  XColorBrowserWidget(display,windows,"Select",color);
8807  if (*color == '\0')
8808  break;
8809  (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8810  geometry);
8811  if (*geometry == '\0')
8812  break;
8813  /*
8814  Surround image with an ornamental border.
8815  */
8816  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8817  XSetCursorState(display,windows,MagickTrue);
8818  XCheckRefreshWindows(display,windows);
8819  (void) QueryColorDatabase(color,&(*image)->matte_color,
8820  &(*image)->exception);
8821  (void) ParsePageGeometry(*image,geometry,&page_geometry,
8822  &(*image)->exception);
8823  frame_info.width=page_geometry.width;
8824  frame_info.height=page_geometry.height;
8825  frame_info.outer_bevel=page_geometry.x;
8826  frame_info.inner_bevel=page_geometry.y;
8827  frame_info.x=(ssize_t) frame_info.width;
8828  frame_info.y=(ssize_t) frame_info.height;
8829  frame_info.width=(*image)->columns+2*frame_info.width;
8830  frame_info.height=(*image)->rows+2*frame_info.height;
8831  frame_image=FrameImage(*image,&frame_info,&(*image)->exception);
8832  if (frame_image != (Image *) NULL)
8833  {
8834  *image=DestroyImage(*image);
8835  *image=frame_image;
8836  }
8837  CatchException(&(*image)->exception);
8838  XSetCursorState(display,windows,MagickFalse);
8839  if (windows->image.orphan != MagickFalse)
8840  break;
8841  windows->image.window_changes.width=(int) (*image)->columns;
8842  windows->image.window_changes.height=(int) (*image)->rows;
8843  XConfigureImageColormap(display,resource_info,windows,*image);
8844  (void) XConfigureImage(display,resource_info,windows,*image);
8845  break;
8846  }
8847  case CommentCommand:
8848  {
8849  const char
8850  *value;
8851 
8852  FILE
8853  *file;
8854 
8855  int
8856  unique_file;
8857 
8858  /*
8859  Edit image comment.
8860  */
8861  unique_file=AcquireUniqueFileResource(image_info->filename);
8862  if (unique_file == -1)
8863  {
8864  XNoticeWidget(display,windows,"Unable to edit image comment",
8865  image_info->filename);
8866  break;
8867  }
8868  value=GetImageProperty(*image,"comment");
8869  if (value == (char *) NULL)
8870  unique_file=close(unique_file)-1;
8871  else
8872  {
8873  const char
8874  *p;
8875 
8876  file=fdopen(unique_file,"w");
8877  if (file == (FILE *) NULL)
8878  {
8879  XNoticeWidget(display,windows,"Unable to edit image comment",
8880  image_info->filename);
8881  break;
8882  }
8883  for (p=value; *p != '\0'; p++)
8884  (void) fputc((int) *p,file);
8885  (void) fputc('\n',file);
8886  (void) fclose(file);
8887  }
8888  XSetCursorState(display,windows,MagickTrue);
8889  XCheckRefreshWindows(display,windows);
8890  status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8891  &(*image)->exception);
8892  if (status == MagickFalse)
8893  XNoticeWidget(display,windows,"Unable to edit image comment",
8894  (char *) NULL);
8895  else
8896  {
8897  char
8898  *comment;
8899 
8900  comment=FileToString(image_info->filename,~0UL,&(*image)->exception);
8901  if (comment != (char *) NULL)
8902  {
8903  (void) SetImageProperty(*image,"comment",comment);
8904  (*image)->taint=MagickTrue;
8905  }
8906  }
8907  (void) RelinquishUniqueFileResource(image_info->filename);
8908  XSetCursorState(display,windows,MagickFalse);
8909  break;
8910  }
8911  case LaunchCommand:
8912  {
8913  /*
8914  Launch program.
8915  */
8916  XSetCursorState(display,windows,MagickTrue);
8917  XCheckRefreshWindows(display,windows);
8918  (void) AcquireUniqueFilename(filename);
8919  (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
8920  filename);
8921  status=WriteImage(image_info,*image);
8922  if (status == MagickFalse)
8923  XNoticeWidget(display,windows,"Unable to launch image editor",
8924  (char *) NULL);
8925  else
8926  {
8927  nexus=ReadImage(resource_info->image_info,&(*image)->exception);
8928  CatchException(&(*image)->exception);
8929  XClientMessage(display,windows->image.id,windows->im_protocols,
8930  windows->im_next_image,CurrentTime);
8931  }
8932  (void) RelinquishUniqueFileResource(filename);
8933  XSetCursorState(display,windows,MagickFalse);
8934  break;
8935  }
8936  case RegionOfInterestCommand:
8937  {
8938  /*
8939  Apply an image processing technique to a region of interest.
8940  */
8941  (void) XROIImage(display,resource_info,windows,image);
8942  break;
8943  }
8944  case InfoCommand:
8945  break;
8946  case ZoomCommand:
8947  {
8948  /*
8949  Zoom image.
8950  */
8951  if (windows->magnify.mapped != MagickFalse)
8952  (void) XRaiseWindow(display,windows->magnify.id);
8953  else
8954  {
8955  /*
8956  Make magnify image.
8957  */
8958  XSetCursorState(display,windows,MagickTrue);
8959  (void) XMapRaised(display,windows->magnify.id);
8960  XSetCursorState(display,windows,MagickFalse);
8961  }
8962  break;
8963  }
8964  case ShowPreviewCommand:
8965  {
8966  char
8967  **previews;
8968 
8969  Image
8970  *preview_image;
8971 
8972  static char
8973  preview_type[MaxTextExtent] = "Gamma";
8974 
8975  /*
8976  Select preview type from menu.
8977  */
8978  previews=GetCommandOptions(MagickPreviewOptions);
8979  if (previews == (char **) NULL)
8980  break;
8981  XListBrowserWidget(display,windows,&windows->widget,
8982  (const char **) previews,"Preview",
8983  "Select an enhancement, effect, or F/X:",preview_type);
8984  previews=DestroyStringList(previews);
8985  if (*preview_type == '\0')
8986  break;
8987  /*
8988  Show image preview.
8989  */
8990  XSetCursorState(display,windows,MagickTrue);
8991  XCheckRefreshWindows(display,windows);
8992  image_info->preview_type=(PreviewType)
8993  ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
8994  image_info->group=(ssize_t) windows->image.id;
8995  (void) DeleteImageProperty(*image,"label");
8996  (void) SetImageProperty(*image,"label","Preview");
8997  (void) AcquireUniqueFilename(filename);
8998  (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
8999  filename);
9000  status=WriteImage(image_info,*image);
9001  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9002  preview_image=ReadImage(image_info,&(*image)->exception);
9003  (void) RelinquishUniqueFileResource(filename);
9004  if (preview_image == (Image *) NULL)
9005  break;
9006  (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9007  filename);
9008  status=WriteImage(image_info,preview_image);
9009  preview_image=DestroyImage(preview_image);
9010  if (status == MagickFalse)
9011  XNoticeWidget(display,windows,"Unable to show image preview",
9012  (*image)->filename);
9013  XDelay(display,1500);
9014  XSetCursorState(display,windows,MagickFalse);
9015  break;
9016  }
9017  case ShowHistogramCommand:
9018  {
9019  Image
9020  *histogram_image;
9021 
9022  /*
9023  Show image histogram.
9024  */
9025  XSetCursorState(display,windows,MagickTrue);
9026  XCheckRefreshWindows(display,windows);
9027  image_info->group=(ssize_t) windows->image.id;
9028  (void) DeleteImageProperty(*image,"label");
9029  (void) SetImageProperty(*image,"label","Histogram");
9030  (void) AcquireUniqueFilename(filename);
9031  (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9032  filename);
9033  status=WriteImage(image_info,*image);
9034  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9035  histogram_image=ReadImage(image_info,&(*image)->exception);
9036  (void) RelinquishUniqueFileResource(filename);
9037  if (histogram_image == (Image *) NULL)
9038  break;
9039  (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9040  "show:%s",filename);
9041  status=WriteImage(image_info,histogram_image);
9042  histogram_image=DestroyImage(histogram_image);
9043  if (status == MagickFalse)
9044  XNoticeWidget(display,windows,"Unable to show histogram",
9045  (*image)->filename);
9046  XDelay(display,1500);
9047  XSetCursorState(display,windows,MagickFalse);
9048  break;
9049  }
9050  case ShowMatteCommand:
9051  {
9052  Image
9053  *matte_image;
9054 
9055  if ((*image)->matte == MagickFalse)
9056  {
9057  XNoticeWidget(display,windows,
9058  "Image does not have any matte information",(*image)->filename);
9059  break;
9060  }
9061  /*
9062  Show image matte.
9063  */
9064  XSetCursorState(display,windows,MagickTrue);
9065  XCheckRefreshWindows(display,windows);
9066  image_info->group=(ssize_t) windows->image.id;
9067  (void) DeleteImageProperty(*image,"label");
9068  (void) SetImageProperty(*image,"label","Matte");
9069  (void) AcquireUniqueFilename(filename);
9070  (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9071  filename);
9072  status=WriteImage(image_info,*image);
9073  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9074  matte_image=ReadImage(image_info,&(*image)->exception);
9075  (void) RelinquishUniqueFileResource(filename);
9076  if (matte_image == (Image *) NULL)
9077  break;
9078  (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9079  filename);
9080  status=WriteImage(image_info,matte_image);
9081  matte_image=DestroyImage(matte_image);
9082  if (status == MagickFalse)
9083  XNoticeWidget(display,windows,"Unable to show matte",
9084  (*image)->filename);
9085  XDelay(display,1500);
9086  XSetCursorState(display,windows,MagickFalse);
9087  break;
9088  }
9089  case BackgroundCommand:
9090  {
9091  /*
9092  Background image.
9093  */
9094  status=XBackgroundImage(display,resource_info,windows,image);
9095  if (status == MagickFalse)
9096  break;
9097  nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9098  if (nexus != (Image *) NULL)
9099  XClientMessage(display,windows->image.id,windows->im_protocols,
9100  windows->im_next_image,CurrentTime);
9101  break;
9102  }
9103  case SlideShowCommand:
9104  {
9105  static char
9106  delay[MaxTextExtent] = "5";
9107 
9108  /*
9109  Display next image after pausing.
9110  */
9111  (void) XDialogWidget(display,windows,"Slide Show",
9112  "Pause how many 1/100ths of a second between images:",delay);
9113  if (*delay == '\0')
9114  break;
9115  resource_info->delay=StringToUnsignedLong(delay);
9116  XClientMessage(display,windows->image.id,windows->im_protocols,
9117  windows->im_next_image,CurrentTime);
9118  break;
9119  }
9120  case PreferencesCommand:
9121  {
9122  /*
9123  Set user preferences.
9124  */
9125  status=XPreferencesWidget(display,resource_info,windows);
9126  if (status == MagickFalse)
9127  break;
9128  nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9129  if (nexus != (Image *) NULL)
9130  XClientMessage(display,windows->image.id,windows->im_protocols,
9131  windows->im_next_image,CurrentTime);
9132  break;
9133  }
9134  case HelpCommand:
9135  {
9136  /*
9137  User requested help.
9138  */
9139  XTextViewHelp(display,resource_info,windows,MagickFalse,
9140  "Help Viewer - Display",DisplayHelp);
9141  break;
9142  }
9143  case BrowseDocumentationCommand:
9144  {
9145  Atom
9146  mozilla_atom;
9147 
9148  Window
9149  mozilla_window,
9150  root_window;
9151 
9152  /*
9153  Browse the ImageMagick documentation.
9154  */
9155  root_window=XRootWindow(display,XDefaultScreen(display));
9156  mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9157  mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9158  if (mozilla_window != (Window) NULL)
9159  {
9160  char
9161  command[MaxTextExtent];
9162 
9163  /*
9164  Display documentation using Netscape remote control.
9165  */
9166  (void) FormatLocaleString(command,MaxTextExtent,
9167  "openurl(%s,new-tab)",MagickAuthoritativeURL);
9168  mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9169  (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9170  8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9171  XSetCursorState(display,windows,MagickFalse);
9172  break;
9173  }
9174  XSetCursorState(display,windows,MagickTrue);
9175  XCheckRefreshWindows(display,windows);
9176  status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9177  &(*image)->exception);
9178  if (status == MagickFalse)
9179  XNoticeWidget(display,windows,"Unable to browse documentation",
9180  (char *) NULL);
9181  XDelay(display,1500);
9182  XSetCursorState(display,windows,MagickFalse);
9183  break;
9184  }
9185  case VersionCommand:
9186  {
9187  XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9188  GetMagickCopyright());
9189  break;
9190  }
9191  case SaveToUndoBufferCommand:
9192  break;
9193  default:
9194  {
9195  (void) XBell(display,0);
9196  break;
9197  }
9198  }
9199  image_info=DestroyImageInfo(image_info);
9200  return(nexus);
9201 }
9202 ␌
9203 /*
9204 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9205 % %
9206 % %
9207 % %
9208 + X M a g n i f y I m a g e %
9209 % %
9210 % %
9211 % %
9212 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9213 %
9214 % XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9215 % The magnified portion is displayed in a separate window.
9216 %
9217 % The format of the XMagnifyImage method is:
9218 %
9219 % void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9220 %
9221 % A description of each parameter follows:
9222 %
9223 % o display: Specifies a connection to an X server; returned from
9224 % XOpenDisplay.
9225 %
9226 % o windows: Specifies a pointer to a XWindows structure.
9227 %
9228 % o event: Specifies a pointer to a XEvent structure. If it is NULL,
9229 % the entire image is refreshed.
9230 %
9231 */
9232 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9233 {
9234  char
9235  text[MaxTextExtent];
9236 
9237  int
9238  x,
9239  y;
9240 
9241  size_t
9242  state;
9243 
9244  /*
9245  Update magnified image until the mouse button is released.
9246  */
9247  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9248  state=DefaultState;
9249  x=event->xbutton.x;
9250  y=event->xbutton.y;
9251  windows->magnify.x=(int) windows->image.x+x;
9252  windows->magnify.y=(int) windows->image.y+y;
9253  do
9254  {
9255  /*
9256  Map and unmap Info widget as text cursor crosses its boundaries.
9257  */
9258  if (windows->info.mapped != MagickFalse)
9259  {
9260  if ((x < (int) (windows->info.x+windows->info.width)) &&
9261  (y < (int) (windows->info.y+windows->info.height)))
9262  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9263  }
9264  else
9265  if ((x > (int) (windows->info.x+windows->info.width)) ||
9266  (y > (int) (windows->info.y+windows->info.height)))
9267  (void) XMapWindow(display,windows->info.id);
9268  if (windows->info.mapped != MagickFalse)
9269  {
9270  /*
9271  Display pointer position.
9272  */
9273  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9274  windows->magnify.x,windows->magnify.y);
9275  XInfoWidget(display,windows,text);
9276  }
9277  /*
9278  Wait for next event.
9279  */
9280  XScreenEvent(display,windows,event);
9281  switch (event->type)
9282  {
9283  case ButtonPress:
9284  break;
9285  case ButtonRelease:
9286  {
9287  /*
9288  User has finished magnifying image.
9289  */
9290  x=event->xbutton.x;
9291  y=event->xbutton.y;
9292  state|=ExitState;
9293  break;
9294  }
9295  case Expose:
9296  break;
9297  case MotionNotify:
9298  {
9299  x=event->xmotion.x;
9300  y=event->xmotion.y;
9301  break;
9302  }
9303  default:
9304  break;
9305  }
9306  /*
9307  Check boundary conditions.
9308  */
9309  if (x < 0)
9310  x=0;
9311  else
9312  if (x >= (int) windows->image.width)
9313  x=(int) windows->image.width-1;
9314  if (y < 0)
9315  y=0;
9316  else
9317  if (y >= (int) windows->image.height)
9318  y=(int) windows->image.height-1;
9319  } while ((state & ExitState) == 0);
9320  /*
9321  Display magnified image.
9322  */
9323  XSetCursorState(display,windows,MagickFalse);
9324 }
9325 ␌
9326 /*
9327 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9328 % %
9329 % %
9330 % %
9331 + X M a g n i f y W i n d o w C o m m a n d %
9332 % %
9333 % %
9334 % %
9335 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9336 %
9337 % XMagnifyWindowCommand() moves the image within an Magnify window by one
9338 % pixel as specified by the key symbol.
9339 %
9340 % The format of the XMagnifyWindowCommand method is:
9341 %
9342 % void XMagnifyWindowCommand(Display *display,XWindows *windows,
9343 % const MagickStatusType state,const KeySym key_symbol)
9344 %
9345 % A description of each parameter follows:
9346 %
9347 % o display: Specifies a connection to an X server; returned from
9348 % XOpenDisplay.
9349 %
9350 % o windows: Specifies a pointer to a XWindows structure.
9351 %
9352 % o state: key mask.
9353 %
9354 % o key_symbol: Specifies a KeySym which indicates which side of the image
9355 % to trim.
9356 %
9357 */
9358 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9359  const MagickStatusType state,const KeySym key_symbol)
9360 {
9361  unsigned int
9362  quantum;
9363 
9364  /*
9365  User specified a magnify factor or position.
9366  */
9367  quantum=1;
9368  if ((state & Mod1Mask) != 0)
9369  quantum=10;
9370  switch ((int) key_symbol)
9371  {
9372  case QuitCommand:
9373  {
9374  (void) XWithdrawWindow(display,windows->magnify.id,
9375  windows->magnify.screen);
9376  break;
9377  }
9378  case XK_Home:
9379  case XK_KP_Home:
9380  {
9381  windows->magnify.x=(int) windows->image.width/2;
9382  windows->magnify.y=(int) windows->image.height/2;
9383  break;
9384  }
9385  case XK_Left:
9386  case XK_KP_Left:
9387  {
9388  if (windows->magnify.x > 0)
9389  windows->magnify.x-=quantum;
9390  break;
9391  }
9392  case XK_Up:
9393  case XK_KP_Up:
9394  {
9395  if (windows->magnify.y > 0)
9396  windows->magnify.y-=quantum;
9397  break;
9398  }
9399  case XK_Right:
9400  case XK_KP_Right:
9401  {
9402  if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9403  windows->magnify.x+=quantum;
9404  break;
9405  }
9406  case XK_Down:
9407  case XK_KP_Down:
9408  {
9409  if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9410  windows->magnify.y+=quantum;
9411  break;
9412  }
9413  case XK_0:
9414  case XK_1:
9415  case XK_2:
9416  case XK_3:
9417  case XK_4:
9418  case XK_5:
9419  case XK_6:
9420  case XK_7:
9421  case XK_8:
9422  case XK_9:
9423  {
9424  windows->magnify.data=(key_symbol-XK_0);
9425  break;
9426  }
9427  case XK_KP_0:
9428  case XK_KP_1:
9429  case XK_KP_2:
9430  case XK_KP_3:
9431  case XK_KP_4:
9432  case XK_KP_5:
9433  case XK_KP_6:
9434  case XK_KP_7:
9435  case XK_KP_8:
9436  case XK_KP_9:
9437  {
9438  windows->magnify.data=(key_symbol-XK_KP_0);
9439  break;
9440  }
9441  default:
9442  break;
9443  }
9444  XMakeMagnifyImage(display,windows);
9445 }
9446 ␌
9447 /*
9448 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9449 % %
9450 % %
9451 % %
9452 + X M a k e P a n I m a g e %
9453 % %
9454 % %
9455 % %
9456 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9457 %
9458 % XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9459 % icon window.
9460 %
9461 % The format of the XMakePanImage method is:
9462 %
9463 % void XMakePanImage(Display *display,XResourceInfo *resource_info,
9464 % XWindows *windows,Image *image)
9465 %
9466 % A description of each parameter follows:
9467 %
9468 % o display: Specifies a connection to an X server; returned from
9469 % XOpenDisplay.
9470 %
9471 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9472 %
9473 % o windows: Specifies a pointer to a XWindows structure.
9474 %
9475 % o image: the image.
9476 %
9477 */
9478 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9479  XWindows *windows,Image *image)
9480 {
9481  MagickStatusType
9482  status;
9483 
9484  /*
9485  Create and display image for panning icon.
9486  */
9487  XSetCursorState(display,windows,MagickTrue);
9488  XCheckRefreshWindows(display,windows);
9489  windows->pan.x=(int) windows->image.x;
9490  windows->pan.y=(int) windows->image.y;
9491  status=XMakeImage(display,resource_info,&windows->pan,image,
9492  windows->pan.width,windows->pan.height);
9493  if (status == MagickFalse)
9494  ThrowXWindowFatalException(XServerFatalError,image->exception.reason,
9495  image->exception.description);
9496  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9497  windows->pan.pixmap);
9498  (void) XClearWindow(display,windows->pan.id);
9499  XDrawPanRectangle(display,windows);
9500  XSetCursorState(display,windows,MagickFalse);
9501 }
9502 ␌
9503 /*
9504 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9505 % %
9506 % %
9507 % %
9508 + X M a t t a E d i t I m a g e %
9509 % %
9510 % %
9511 % %
9512 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9513 %
9514 % XMatteEditImage() allows the user to interactively change the Matte channel
9515 % of an image. If the image is PseudoClass it is promoted to DirectClass
9516 % before the matte information is stored.
9517 %
9518 % The format of the XMatteEditImage method is:
9519 %
9520 % MagickBooleanType XMatteEditImage(Display *display,
9521 % XResourceInfo *resource_info,XWindows *windows,Image **image)
9522 %
9523 % A description of each parameter follows:
9524 %
9525 % o display: Specifies a connection to an X server; returned from
9526 % XOpenDisplay.
9527 %
9528 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9529 %
9530 % o windows: Specifies a pointer to a XWindows structure.
9531 %
9532 % o image: the image; returned from ReadImage.
9533 %
9534 */
9535 static MagickBooleanType XMatteEditImage(Display *display,
9536  XResourceInfo *resource_info,XWindows *windows,Image **image)
9537 {
9538  const char
9539  *const MatteEditMenu[] =
9540  {
9541  "Method",
9542  "Border Color",
9543  "Fuzz",
9544  "Matte Value",
9545  "Undo",
9546  "Help",
9547  "Dismiss",
9548  (char *) NULL
9549  };
9550 
9551  static char
9552  matte[MaxTextExtent] = "0";
9553 
9554  static const ModeType
9555  MatteEditCommands[] =
9556  {
9557  MatteEditMethod,
9558  MatteEditBorderCommand,
9559  MatteEditFuzzCommand,
9560  MatteEditValueCommand,
9561  MatteEditUndoCommand,
9562  MatteEditHelpCommand,
9563  MatteEditDismissCommand
9564  };
9565 
9566  static PaintMethod
9567  method = PointMethod;
9568 
9569  static XColor
9570  border_color = { 0, 0, 0, 0, 0, 0 };
9571 
9572  char
9573  command[MaxTextExtent],
9574  text[MaxTextExtent] = "";
9575 
9576  Cursor
9577  cursor;
9578 
9579  int
9580  entry,
9581  id,
9582  x,
9583  x_offset,
9584  y,
9585  y_offset;
9586 
9587  int
9588  i;
9589 
9590  PixelPacket
9591  *q;
9592 
9593  unsigned int
9594  height,
9595  width;
9596 
9597  size_t
9598  state;
9599 
9600  XEvent
9601  event;
9602 
9603  /*
9604  Map Command widget.
9605  */
9606  (void) CloneString(&windows->command.name,"Matte Edit");
9607  windows->command.data=4;
9608  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9609  (void) XMapRaised(display,windows->command.id);
9610  XClientMessage(display,windows->image.id,windows->im_protocols,
9611  windows->im_update_widget,CurrentTime);
9612  /*
9613  Make cursor.
9614  */
9615  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9616  resource_info->background_color,resource_info->foreground_color);
9617  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9618  /*
9619  Track pointer until button 1 is pressed.
9620  */
9621  XQueryPosition(display,windows->image.id,&x,&y);
9622  (void) XSelectInput(display,windows->image.id,
9623  windows->image.attributes.event_mask | PointerMotionMask);
9624  state=DefaultState;
9625  do
9626  {
9627  if (windows->info.mapped != MagickFalse)
9628  {
9629  /*
9630  Display pointer position.
9631  */
9632  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9633  x+windows->image.x,y+windows->image.y);
9634  XInfoWidget(display,windows,text);
9635  }
9636  /*
9637  Wait for next event.
9638  */
9639  XScreenEvent(display,windows,&event);
9640  if (event.xany.window == windows->command.id)
9641  {
9642  /*
9643  Select a command from the Command widget.
9644  */
9645  id=XCommandWidget(display,windows,MatteEditMenu,&event);
9646  if (id < 0)
9647  {
9648  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9649  continue;
9650  }
9651  switch (MatteEditCommands[id])
9652  {
9653  case MatteEditMethod:
9654  {
9655  char
9656  **methods;
9657 
9658  /*
9659  Select a method from the pop-up menu.
9660  */
9661  methods=GetCommandOptions(MagickMethodOptions);
9662  if (methods == (char **) NULL)
9663  break;
9664  entry=XMenuWidget(display,windows,MatteEditMenu[id],
9665  (const char **) methods,command);
9666  if (entry >= 0)
9667  method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9668  MagickFalse,methods[entry]);
9669  methods=DestroyStringList(methods);
9670  break;
9671  }
9672  case MatteEditBorderCommand:
9673  {
9674  const char
9675  *ColorMenu[MaxNumberPens];
9676 
9677  int
9678  pen_number;
9679 
9680  /*
9681  Initialize menu selections.
9682  */
9683  for (i=0; i < (int) (MaxNumberPens-2); i++)
9684  ColorMenu[i]=resource_info->pen_colors[i];
9685  ColorMenu[MaxNumberPens-2]="Browser...";
9686  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9687  /*
9688  Select a pen color from the pop-up menu.
9689  */
9690  pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9691  (const char **) ColorMenu,command);
9692  if (pen_number < 0)
9693  break;
9694  if (pen_number == (MaxNumberPens-2))
9695  {
9696  static char
9697  color_name[MaxTextExtent] = "gray";
9698 
9699  /*
9700  Select a pen color from a dialog.
9701  */
9702  resource_info->pen_colors[pen_number]=color_name;
9703  XColorBrowserWidget(display,windows,"Select",color_name);
9704  if (*color_name == '\0')
9705  break;
9706  }
9707  /*
9708  Set border color.
9709  */
9710  (void) XParseColor(display,windows->map_info->colormap,
9711  resource_info->pen_colors[pen_number],&border_color);
9712  break;
9713  }
9714  case MatteEditFuzzCommand:
9715  {
9716  const char
9717  *const FuzzMenu[] =
9718  {
9719  "0%",
9720  "2%",
9721  "5%",
9722  "10%",
9723  "15%",
9724  "Dialog...",
9725  (char *) NULL,
9726  };
9727 
9728  static char
9729  fuzz[MaxTextExtent];
9730 
9731  /*
9732  Select a command from the pop-up menu.
9733  */
9734  entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9735  command);
9736  if (entry < 0)
9737  break;
9738  if (entry != 5)
9739  {
9740  (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9741  QuantumRange+1.0);
9742  break;
9743  }
9744  (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9745  (void) XDialogWidget(display,windows,"Ok",
9746  "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9747  if (*fuzz == '\0')
9748  break;
9749  (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9750  (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9751  1.0);
9752  break;
9753  }
9754  case MatteEditValueCommand:
9755  {
9756  const char
9757  *const MatteMenu[] =
9758  {
9759  "Opaque",
9760  "Transparent",
9761  "Dialog...",
9762  (char *) NULL,
9763  };
9764 
9765  static char
9766  message[MaxTextExtent];
9767 
9768  /*
9769  Select a command from the pop-up menu.
9770  */
9771  entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9772  command);
9773  if (entry < 0)
9774  break;
9775  if (entry != 2)
9776  {
9777  (void) FormatLocaleString(matte,MaxTextExtent,"%g",
9778  (double) OpaqueOpacity);
9779  if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9780  (void) FormatLocaleString(matte,MaxTextExtent,"%g",
9781  (double) TransparentOpacity);
9782  break;
9783  }
9784  (void) FormatLocaleString(message,MaxTextExtent,
9785  "Enter matte value (0 - " "%g" "):",(double) QuantumRange);
9786  (void) XDialogWidget(display,windows,"Matte",message,matte);
9787  if (*matte == '\0')
9788  break;
9789  break;
9790  }
9791  case MatteEditUndoCommand:
9792  {
9793  (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9794  image);
9795  break;
9796  }
9797  case MatteEditHelpCommand:
9798  {
9799  XTextViewHelp(display,resource_info,windows,MagickFalse,
9800  "Help Viewer - Matte Edit",ImageMatteEditHelp);
9801  break;
9802  }
9803  case MatteEditDismissCommand:
9804  {
9805  /*
9806  Prematurely exit.
9807  */
9808  state|=EscapeState;
9809  state|=ExitState;
9810  break;
9811  }
9812  default:
9813  break;
9814  }
9815  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9816  continue;
9817  }
9818  switch (event.type)
9819  {
9820  case ButtonPress:
9821  {
9822  if (event.xbutton.button != Button1)
9823  break;
9824  if ((event.xbutton.window != windows->image.id) &&
9825  (event.xbutton.window != windows->magnify.id))
9826  break;
9827  /*
9828  Update matte data.
9829  */
9830  x=event.xbutton.x;
9831  y=event.xbutton.y;
9832  (void) XMagickCommand(display,resource_info,windows,
9833  SaveToUndoBufferCommand,image);
9834  state|=UpdateConfigurationState;
9835  break;
9836  }
9837  case ButtonRelease:
9838  {
9839  if (event.xbutton.button != Button1)
9840  break;
9841  if ((event.xbutton.window != windows->image.id) &&
9842  (event.xbutton.window != windows->magnify.id))
9843  break;
9844  /*
9845  Update colormap information.
9846  */
9847  x=event.xbutton.x;
9848  y=event.xbutton.y;
9849  XConfigureImageColormap(display,resource_info,windows,*image);
9850  (void) XConfigureImage(display,resource_info,windows,*image);
9851  XInfoWidget(display,windows,text);
9852  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9853  state&=(~UpdateConfigurationState);
9854  break;
9855  }
9856  case Expose:
9857  break;
9858  case KeyPress:
9859  {
9860  char
9861  command[MaxTextExtent];
9862 
9863  KeySym
9864  key_symbol;
9865 
9866  if (event.xkey.window == windows->magnify.id)
9867  {
9868  Window
9869  window;
9870 
9871  window=windows->magnify.id;
9872  while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9873  }
9874  if (event.xkey.window != windows->image.id)
9875  break;
9876  /*
9877  Respond to a user key press.
9878  */
9879  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9880  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9881  switch ((int) key_symbol)
9882  {
9883  case XK_Escape:
9884  case XK_F20:
9885  {
9886  /*
9887  Prematurely exit.
9888  */
9889  state|=ExitState;
9890  break;
9891  }
9892  case XK_F1:
9893  case XK_Help:
9894  {
9895  XTextViewHelp(display,resource_info,windows,MagickFalse,
9896  "Help Viewer - Matte Edit",ImageMatteEditHelp);
9897  break;
9898  }
9899  default:
9900  {
9901  (void) XBell(display,0);
9902  break;
9903  }
9904  }
9905  break;
9906  }
9907  case MotionNotify:
9908  {
9909  /*
9910  Map and unmap Info widget as cursor crosses its boundaries.
9911  */
9912  x=event.xmotion.x;
9913  y=event.xmotion.y;
9914  if (windows->info.mapped != MagickFalse)
9915  {
9916  if ((x < (int) (windows->info.x+windows->info.width)) &&
9917  (y < (int) (windows->info.y+windows->info.height)))
9918  (void) XWithdrawWindow(display,windows->info.id,
9919  windows->info.screen);
9920  }
9921  else
9922  if ((x > (int) (windows->info.x+windows->info.width)) ||
9923  (y > (int) (windows->info.y+windows->info.height)))
9924  (void) XMapWindow(display,windows->info.id);
9925  break;
9926  }
9927  default:
9928  break;
9929  }
9930  if (event.xany.window == windows->magnify.id)
9931  {
9932  x=windows->magnify.x-windows->image.x;
9933  y=windows->magnify.y-windows->image.y;
9934  }
9935  x_offset=x;
9936  y_offset=y;
9937  if ((state & UpdateConfigurationState) != 0)
9938  {
9939  CacheView
9940  *image_view;
9941 
9943  *exception;
9944 
9945  int
9946  x,
9947  y;
9948 
9949  /*
9950  Matte edit is relative to image configuration.
9951  */
9952  (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
9953  MagickTrue);
9954  XPutPixel(windows->image.ximage,x_offset,y_offset,
9955  windows->pixel_info->background_color.pixel);
9956  width=(unsigned int) (*image)->columns;
9957  height=(unsigned int) (*image)->rows;
9958  x=0;
9959  y=0;
9960  if (windows->image.crop_geometry != (char *) NULL)
9961  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
9962  &width,&height);
9963  x_offset=(int)
9964  (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
9965  y_offset=(int)
9966  (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
9967  if ((x_offset < 0) || (y_offset < 0))
9968  continue;
9969  if ((x_offset >= (int) (*image)->columns) ||
9970  (y_offset >= (int) (*image)->rows))
9971  continue;
9972  if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
9973  return(MagickFalse);
9974  (*image)->matte=MagickTrue;
9975  exception=(&(*image)->exception);
9976  image_view=AcquireAuthenticCacheView(*image,exception);
9977  switch (method)
9978  {
9979  case PointMethod:
9980  default:
9981  {
9982  /*
9983  Update matte information using point algorithm.
9984  */
9985  q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
9986  (ssize_t) y_offset,1,1,exception);
9987  if (q == (PixelPacket *) NULL)
9988  break;
9989  q->opacity=(Quantum) StringToLong(matte);
9990  (void) SyncCacheViewAuthenticPixels(image_view,exception);
9991  break;
9992  }
9993  case ReplaceMethod:
9994  {
9995  PixelPacket
9996  target;
9997 
9998  /*
9999  Update matte information using replace algorithm.
10000  */
10001  (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10002  (ssize_t) y_offset,&target,exception);
10003  for (y=0; y < (int) (*image)->rows; y++)
10004  {
10005  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10006  (*image)->columns,1,&(*image)->exception);
10007  if (q == (PixelPacket *) NULL)
10008  break;
10009  for (x=0; x < (int) (*image)->columns; x++)
10010  {
10011  if (IsColorSimilar(*image,q,&target))
10012  q->opacity=(Quantum) StringToLong(matte);
10013  q++;
10014  }
10015  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10016  break;
10017  }
10018  break;
10019  }
10020  case FloodfillMethod:
10021  case FillToBorderMethod:
10022  {
10023  DrawInfo
10024  *draw_info;
10025 
10027  target;
10028 
10029  /*
10030  Update matte information using floodfill algorithm.
10031  */
10032  (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10033  (ssize_t) y_offset,&target,exception);
10034  if (method == FillToBorderMethod)
10035  {
10036  target.red=(MagickRealType)
10037  ScaleShortToQuantum(border_color.red);
10038  target.green=(MagickRealType)
10039  ScaleShortToQuantum(border_color.green);
10040  target.blue=(MagickRealType)
10041  ScaleShortToQuantum(border_color.blue);
10042  }
10043  draw_info=CloneDrawInfo(resource_info->image_info,
10044  (DrawInfo *) NULL);
10045  draw_info->fill.opacity=ClampToQuantum(StringToDouble(matte,
10046  (char **) NULL));
10047  (void) FloodfillPaintImage(*image,OpacityChannel,draw_info,&target,
10048  (ssize_t) x_offset,(ssize_t) y_offset,
10049  method == FloodfillMethod ? MagickFalse : MagickTrue);
10050  draw_info=DestroyDrawInfo(draw_info);
10051  break;
10052  }
10053  case ResetMethod:
10054  {
10055  /*
10056  Update matte information using reset algorithm.
10057  */
10058  if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
10059  return(MagickFalse);
10060  for (y=0; y < (int) (*image)->rows; y++)
10061  {
10062  q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10063  (*image)->columns,1,exception);
10064  if (q == (PixelPacket *) NULL)
10065  break;
10066  for (x=0; x < (int) (*image)->columns; x++)
10067  {
10068  q->opacity=(Quantum) StringToLong(matte);
10069  q++;
10070  }
10071  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10072  break;
10073  }
10074  if (StringToLong(matte) == OpaqueOpacity)
10075  (*image)->matte=MagickFalse;
10076  break;
10077  }
10078  }
10079  image_view=DestroyCacheView(image_view);
10080  state&=(~UpdateConfigurationState);
10081  }
10082  } while ((state & ExitState) == 0);
10083  (void) XSelectInput(display,windows->image.id,
10084  windows->image.attributes.event_mask);
10085  XSetCursorState(display,windows,MagickFalse);
10086  (void) XFreeCursor(display,cursor);
10087  return(MagickTrue);
10088 }
10089 ␌
10090 /*
10091 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10092 % %
10093 % %
10094 % %
10095 + X O p e n I m a g e %
10096 % %
10097 % %
10098 % %
10099 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10100 %
10101 % XOpenImage() loads an image from a file.
10102 %
10103 % The format of the XOpenImage method is:
10104 %
10105 % Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10106 % XWindows *windows,const unsigned int command)
10107 %
10108 % A description of each parameter follows:
10109 %
10110 % o display: Specifies a connection to an X server; returned from
10111 % XOpenDisplay.
10112 %
10113 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10114 %
10115 % o windows: Specifies a pointer to a XWindows structure.
10116 %
10117 % o command: A value other than zero indicates that the file is selected
10118 % from the command line argument list.
10119 %
10120 */
10121 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10122  XWindows *windows,const MagickBooleanType command)
10123 {
10124  const MagickInfo
10125  *magick_info;
10126 
10128  *exception;
10129 
10130  Image
10131  *nexus;
10132 
10133  ImageInfo
10134  *image_info;
10135 
10136  static char
10137  filename[MaxTextExtent] = "\0";
10138 
10139  /*
10140  Request file name from user.
10141  */
10142  if (command == MagickFalse)
10143  XFileBrowserWidget(display,windows,"Open",filename);
10144  else
10145  {
10146  char
10147  **filelist,
10148  **files;
10149 
10150  int
10151  count,
10152  status;
10153 
10154  int
10155  i,
10156  j;
10157 
10158  /*
10159  Select next image from the command line.
10160  */
10161  status=XGetCommand(display,windows->image.id,&files,&count);
10162  if (status == 0)
10163  ThrowXWindowException(XServerError,"UnableToGetProperty","...");
10164  filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10165  if (filelist == (char **) NULL)
10166  {
10167  (void) XFreeStringList(files);
10168  ThrowXWindowException(ResourceLimitError,
10169  "MemoryAllocationFailed","...");
10170  return((Image *) NULL);
10171  }
10172  j=0;
10173  for (i=1; i < count; i++)
10174  if (*files[i] != '-')
10175  filelist[j++]=files[i];
10176  filelist[j]=(char *) NULL;
10177  XListBrowserWidget(display,windows,&windows->widget,
10178  (const char **) filelist,"Load","Select Image to Load:",filename);
10179  filelist=(char **) RelinquishMagickMemory(filelist);
10180  (void) XFreeStringList(files);
10181  }
10182  if (*filename == '\0')
10183  return((Image *) NULL);
10184  image_info=CloneImageInfo(resource_info->image_info);
10185  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10186  (void *) NULL);
10187  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10188  exception=AcquireExceptionInfo();
10189  (void) SetImageInfo(image_info,0,exception);
10190  if (LocaleCompare(image_info->magick,"X") == 0)
10191  {
10192  char
10193  seconds[MaxTextExtent];
10194 
10195  /*
10196  User may want to delay the X server screen grab.
10197  */
10198  (void) CopyMagickString(seconds,"0",MaxTextExtent);
10199  (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10200  seconds);
10201  if (*seconds == '\0')
10202  return((Image *) NULL);
10203  XDelay(display,(size_t) (1000*StringToLong(seconds)));
10204  }
10205  magick_info=GetMagickInfo(image_info->magick,exception);
10206  if ((magick_info != (const MagickInfo *) NULL) &&
10207  (magick_info->raw != MagickFalse))
10208  {
10209  char
10210  geometry[MaxTextExtent];
10211 
10212  /*
10213  Request image size from the user.
10214  */
10215  (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10216  if (image_info->size != (char *) NULL)
10217  (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10218  (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10219  geometry);
10220  (void) CloneString(&image_info->size,geometry);
10221  }
10222  /*
10223  Load the image.
10224  */
10225  XSetCursorState(display,windows,MagickTrue);
10226  XCheckRefreshWindows(display,windows);
10227  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10228  nexus=ReadImage(image_info,exception);
10229  CatchException(exception);
10230  XSetCursorState(display,windows,MagickFalse);
10231  if (nexus != (Image *) NULL)
10232  XClientMessage(display,windows->image.id,windows->im_protocols,
10233  windows->im_next_image,CurrentTime);
10234  else
10235  {
10236  char
10237  *text,
10238  **textlist;
10239 
10240  /*
10241  Unknown image format.
10242  */
10243  text=FileToString(filename,~0UL,exception);
10244  if (text == (char *) NULL)
10245  return((Image *) NULL);
10246  textlist=StringToList(text);
10247  if (textlist != (char **) NULL)
10248  {
10249  char
10250  title[MaxTextExtent];
10251 
10252  int
10253  i;
10254 
10255  (void) FormatLocaleString(title,MaxTextExtent,
10256  "Unknown format: %s",filename);
10257  XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10258  (const char **) textlist);
10259  for (i=0; textlist[i] != (char *) NULL; i++)
10260  textlist[i]=DestroyString(textlist[i]);
10261  textlist=(char **) RelinquishMagickMemory(textlist);
10262  }
10263  text=DestroyString(text);
10264  }
10265  exception=DestroyExceptionInfo(exception);
10266  image_info=DestroyImageInfo(image_info);
10267  return(nexus);
10268 }
10269 ␌
10270 /*
10271 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10272 % %
10273 % %
10274 % %
10275 + X P a n I m a g e %
10276 % %
10277 % %
10278 % %
10279 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10280 %
10281 % XPanImage() pans the image until the mouse button is released.
10282 %
10283 % The format of the XPanImage method is:
10284 %
10285 % void XPanImage(Display *display,XWindows *windows,XEvent *event)
10286 %
10287 % A description of each parameter follows:
10288 %
10289 % o display: Specifies a connection to an X server; returned from
10290 % XOpenDisplay.
10291 %
10292 % o windows: Specifies a pointer to a XWindows structure.
10293 %
10294 % o event: Specifies a pointer to a XEvent structure. If it is NULL,
10295 % the entire image is refreshed.
10296 %
10297 */
10298 static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10299 {
10300  char
10301  text[MaxTextExtent];
10302 
10303  Cursor
10304  cursor;
10305 
10306  MagickRealType
10307  x_factor,
10308  y_factor;
10309 
10311  pan_info;
10312 
10313  size_t
10314  state;
10315 
10316  /*
10317  Define cursor.
10318  */
10319  if ((windows->image.ximage->width > (int) windows->image.width) &&
10320  (windows->image.ximage->height > (int) windows->image.height))
10321  cursor=XCreateFontCursor(display,XC_fleur);
10322  else
10323  if (windows->image.ximage->width > (int) windows->image.width)
10324  cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10325  else
10326  if (windows->image.ximage->height > (int) windows->image.height)
10327  cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10328  else
10329  cursor=XCreateFontCursor(display,XC_arrow);
10330  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10331  /*
10332  Pan image as pointer moves until the mouse button is released.
10333  */
10334  x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10335  y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10336  pan_info.width=windows->pan.width*windows->image.width/
10337  windows->image.ximage->width;
10338  pan_info.height=windows->pan.height*windows->image.height/
10339  windows->image.ximage->height;
10340  pan_info.x=0;
10341  pan_info.y=0;
10342  state=UpdateConfigurationState;
10343  do
10344  {
10345  switch (event->type)
10346  {
10347  case ButtonPress:
10348  {
10349  /*
10350  User choose an initial pan location.
10351  */
10352  pan_info.x=(ssize_t) event->xbutton.x;
10353  pan_info.y=(ssize_t) event->xbutton.y;
10354  state|=UpdateConfigurationState;
10355  break;
10356  }
10357  case ButtonRelease:
10358  {
10359  /*
10360  User has finished panning the image.
10361  */
10362  pan_info.x=(ssize_t) event->xbutton.x;
10363  pan_info.y=(ssize_t) event->xbutton.y;
10364  state|=UpdateConfigurationState | ExitState;
10365  break;
10366  }
10367  case MotionNotify:
10368  {
10369  pan_info.x=(ssize_t) event->xmotion.x;
10370  pan_info.y=(ssize_t) event->xmotion.y;
10371  state|=UpdateConfigurationState;
10372  }
10373  default:
10374  break;
10375  }
10376  if ((state & UpdateConfigurationState) != 0)
10377  {
10378  /*
10379  Check boundary conditions.
10380  */
10381  if (pan_info.x < (ssize_t) (pan_info.width/2))
10382  pan_info.x=0;
10383  else
10384  pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10385  if (pan_info.x < 0)
10386  pan_info.x=0;
10387  else
10388  if ((int) (pan_info.x+windows->image.width) >
10389  windows->image.ximage->width)
10390  pan_info.x=(ssize_t)
10391  (windows->image.ximage->width-windows->image.width);
10392  if (pan_info.y < (ssize_t) (pan_info.height/2))
10393  pan_info.y=0;
10394  else
10395  pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10396  if (pan_info.y < 0)
10397  pan_info.y=0;
10398  else
10399  if ((int) (pan_info.y+windows->image.height) >
10400  windows->image.ximage->height)
10401  pan_info.y=(ssize_t)
10402  (windows->image.ximage->height-windows->image.height);
10403  if ((windows->image.x != (int) pan_info.x) ||
10404  (windows->image.y != (int) pan_info.y))
10405  {
10406  /*
10407  Display image pan offset.
10408  */
10409  windows->image.x=(int) pan_info.x;
10410  windows->image.y=(int) pan_info.y;
10411  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10412  windows->image.width,windows->image.height,windows->image.x,
10413  windows->image.y);
10414  XInfoWidget(display,windows,text);
10415  /*
10416  Refresh Image window.
10417  */
10418  XDrawPanRectangle(display,windows);
10419  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10420  }
10421  state&=(~UpdateConfigurationState);
10422  }
10423  /*
10424  Wait for next event.
10425  */
10426  if ((state & ExitState) == 0)
10427  XScreenEvent(display,windows,event);
10428  } while ((state & ExitState) == 0);
10429  /*
10430  Restore cursor.
10431  */
10432  (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10433  (void) XFreeCursor(display,cursor);
10434  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10435 }
10436 ␌
10437 /*
10438 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10439 % %
10440 % %
10441 % %
10442 + X P a s t e I m a g e %
10443 % %
10444 % %
10445 % %
10446 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10447 %
10448 % XPasteImage() pastes an image previously saved with XCropImage in the X
10449 % window image at a location the user chooses with the pointer.
10450 %
10451 % The format of the XPasteImage method is:
10452 %
10453 % MagickBooleanType XPasteImage(Display *display,
10454 % XResourceInfo *resource_info,XWindows *windows,Image *image)
10455 %
10456 % A description of each parameter follows:
10457 %
10458 % o display: Specifies a connection to an X server; returned from
10459 % XOpenDisplay.
10460 %
10461 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10462 %
10463 % o windows: Specifies a pointer to a XWindows structure.
10464 %
10465 % o image: the image; returned from ReadImage.
10466 %
10467 */
10468 static MagickBooleanType XPasteImage(Display *display,
10469  XResourceInfo *resource_info,XWindows *windows,Image *image)
10470 {
10471  const char
10472  *const PasteMenu[] =
10473  {
10474  "Operator",
10475  "Help",
10476  "Dismiss",
10477  (char *) NULL
10478  };
10479 
10480  static const ModeType
10481  PasteCommands[] =
10482  {
10483  PasteOperatorsCommand,
10484  PasteHelpCommand,
10485  PasteDismissCommand
10486  };
10487 
10488  static CompositeOperator
10489  compose = CopyCompositeOp;
10490 
10491  char
10492  text[MaxTextExtent];
10493 
10494  Cursor
10495  cursor;
10496 
10497  Image
10498  *paste_image;
10499 
10500  int
10501  entry,
10502  id,
10503  x,
10504  y;
10505 
10506  MagickRealType
10507  scale_factor;
10508 
10510  highlight_info,
10511  paste_info;
10512 
10513  unsigned int
10514  height,
10515  width;
10516 
10517  size_t
10518  state;
10519 
10520  XEvent
10521  event;
10522 
10523  /*
10524  Copy image.
10525  */
10526  if (resource_info->copy_image == (Image *) NULL)
10527  return(MagickFalse);
10528  paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,
10529  &image->exception);
10530  if (paste_image == (Image *) NULL)
10531  return(MagickFalse);
10532  /*
10533  Map Command widget.
10534  */
10535  (void) CloneString(&windows->command.name,"Paste");
10536  windows->command.data=1;
10537  (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10538  (void) XMapRaised(display,windows->command.id);
10539  XClientMessage(display,windows->image.id,windows->im_protocols,
10540  windows->im_update_widget,CurrentTime);
10541  /*
10542  Track pointer until button 1 is pressed.
10543  */
10544  XSetCursorState(display,windows,MagickFalse);
10545  XQueryPosition(display,windows->image.id,&x,&y);
10546  (void) XSelectInput(display,windows->image.id,
10547  windows->image.attributes.event_mask | PointerMotionMask);
10548  paste_info.x=(ssize_t) windows->image.x+x;
10549  paste_info.y=(ssize_t) windows->image.y+y;
10550  paste_info.width=0;
10551  paste_info.height=0;
10552  cursor=XCreateFontCursor(display,XC_ul_angle);
10553  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10554  state=DefaultState;
10555  do
10556  {
10557  if (windows->info.mapped != MagickFalse)
10558  {
10559  /*
10560  Display pointer position.
10561  */
10562  (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10563  (long) paste_info.x,(long) paste_info.y);
10564  XInfoWidget(display,windows,text);
10565  }
10566  highlight_info=paste_info;
10567  highlight_info.x=paste_info.x-windows->image.x;
10568  highlight_info.y=paste_info.y-windows->image.y;
10569  XHighlightRectangle(display,windows->image.id,
10570  windows->image.highlight_context,&highlight_info);
10571  /*
10572  Wait for next event.
10573  */
10574  XScreenEvent(display,windows,&event);
10575  XHighlightRectangle(display,windows->image.id,
10576  windows->image.highlight_context,&highlight_info);
10577  if (event.xany.window == windows->command.id)
10578  {
10579  /*
10580  Select a command from the Command widget.
10581  */
10582  id=XCommandWidget(display,windows,PasteMenu,&event);
10583  if (id < 0)
10584  continue;
10585  switch (PasteCommands[id])
10586  {
10587  case PasteOperatorsCommand:
10588  {
10589  char
10590  command[MaxTextExtent],
10591  **operators;
10592 
10593  /*
10594  Select a command from the pop-up menu.
10595  */
10596  operators=GetCommandOptions(MagickComposeOptions);
10597  if (operators == (char **) NULL)
10598  break;
10599  entry=XMenuWidget(display,windows,PasteMenu[id],
10600  (const char **) operators,command);
10601  if (entry >= 0)
10602  compose=(CompositeOperator) ParseCommandOption(
10603  MagickComposeOptions,MagickFalse,operators[entry]);
10604  operators=DestroyStringList(operators);
10605  break;
10606  }
10607  case PasteHelpCommand:
10608  {
10609  XTextViewHelp(display,resource_info,windows,MagickFalse,
10610  "Help Viewer - Image Composite",ImagePasteHelp);
10611  break;
10612  }
10613  case PasteDismissCommand:
10614  {
10615  /*
10616  Prematurely exit.
10617  */
10618  state|=EscapeState;
10619  state|=ExitState;
10620  break;
10621  }
10622  default:
10623  break;
10624  }
10625  continue;
10626  }
10627  switch (event.type)
10628  {
10629  case ButtonPress:
10630  {
10631  if (resource_info->debug != MagickFalse)
10632  (void) LogMagickEvent(X11Event,GetMagickModule(),
10633  "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10634  event.xbutton.button,event.xbutton.x,event.xbutton.y);
10635  if (event.xbutton.button != Button1)
10636  break;
10637  if (event.xbutton.window != windows->image.id)
10638  break;
10639  /*
10640  Paste rectangle is relative to image configuration.
10641  */
10642  width=(unsigned int) image->columns;
10643  height=(unsigned int) image->rows;
10644  x=0;
10645  y=0;
10646  if (windows->image.crop_geometry != (char *) NULL)
10647  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10648  &width,&height);
10649  scale_factor=(MagickRealType) windows->image.ximage->width/width;
10650  paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10651  scale_factor=(MagickRealType) windows->image.ximage->height/height;
10652  paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10653  (void) XCheckDefineCursor(display,windows->image.id,cursor);
10654  paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10655  paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10656  break;
10657  }
10658  case ButtonRelease:
10659  {
10660  if (resource_info->debug != MagickFalse)
10661  (void) LogMagickEvent(X11Event,GetMagickModule(),
10662  "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10663  event.xbutton.button,event.xbutton.x,event.xbutton.y);
10664  if (event.xbutton.button != Button1)
10665  break;
10666  if (event.xbutton.window != windows->image.id)
10667  break;
10668  if ((paste_info.width != 0) && (paste_info.height != 0))
10669  {
10670  /*
10671  User has selected the location of the paste image.
10672  */
10673  paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10674  paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10675  state|=ExitState;
10676  }
10677  break;
10678  }
10679  case Expose:
10680  break;
10681  case KeyPress:
10682  {
10683  char
10684  command[MaxTextExtent];
10685 
10686  KeySym
10687  key_symbol;
10688 
10689  int
10690  length;
10691 
10692  if (event.xkey.window != windows->image.id)
10693  break;
10694  /*
10695  Respond to a user key press.
10696  */
10697  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10698  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10699  *(command+length)='\0';
10700  if (resource_info->debug != MagickFalse)
10701  (void) LogMagickEvent(X11Event,GetMagickModule(),
10702  "Key press: 0x%lx (%s)",(long) key_symbol,command);
10703  switch ((int) key_symbol)
10704  {
10705  case XK_Escape:
10706  case XK_F20:
10707  {
10708  /*
10709  Prematurely exit.
10710  */
10711  paste_image=DestroyImage(paste_image);
10712  state|=EscapeState;
10713  state|=ExitState;
10714  break;
10715  }
10716  case XK_F1:
10717  case XK_Help:
10718  {
10719  (void) XSetFunction(display,windows->image.highlight_context,
10720  GXcopy);
10721  XTextViewHelp(display,resource_info,windows,MagickFalse,
10722  "Help Viewer - Image Composite",ImagePasteHelp);
10723  (void) XSetFunction(display,windows->image.highlight_context,
10724  GXinvert);
10725  break;
10726  }
10727  default:
10728  {
10729  (void) XBell(display,0);
10730  break;
10731  }
10732  }
10733  break;
10734  }
10735  case MotionNotify:
10736  {
10737  /*
10738  Map and unmap Info widget as text cursor crosses its boundaries.
10739  */
10740  x=event.xmotion.x;
10741  y=event.xmotion.y;
10742  if (windows->info.mapped != MagickFalse)
10743  {
10744  if ((x < (int) (windows->info.x+windows->info.width)) &&
10745  (y < (int) (windows->info.y+windows->info.height)))
10746  (void) XWithdrawWindow(display,windows->info.id,
10747  windows->info.screen);
10748  }
10749  else
10750  if ((x > (int) (windows->info.x+windows->info.width)) ||
10751  (y > (int) (windows->info.y+windows->info.height)))
10752  (void) XMapWindow(display,windows->info.id);
10753  paste_info.x=(ssize_t) windows->image.x+x;
10754  paste_info.y=(ssize_t) windows->image.y+y;
10755  break;
10756  }
10757  default:
10758  {
10759  if (resource_info->debug != MagickFalse)
10760  (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10761  event.type);
10762  break;
10763  }
10764  }
10765  } while ((state & ExitState) == 0);
10766  (void) XSelectInput(display,windows->image.id,
10767  windows->image.attributes.event_mask);
10768  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10769  XSetCursorState(display,windows,MagickFalse);
10770  (void) XFreeCursor(display,cursor);
10771  if ((state & EscapeState) != 0)
10772  return(MagickTrue);
10773  /*
10774  Image pasting is relative to image configuration.
10775  */
10776  XSetCursorState(display,windows,MagickTrue);
10777  XCheckRefreshWindows(display,windows);
10778  width=(unsigned int) image->columns;
10779  height=(unsigned int) image->rows;
10780  x=0;
10781  y=0;
10782  if (windows->image.crop_geometry != (char *) NULL)
10783  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10784  scale_factor=(MagickRealType) width/windows->image.ximage->width;
10785  paste_info.x+=x;
10786  paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10787  paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10788  scale_factor=(MagickRealType) height/windows->image.ximage->height;
10789  paste_info.y+=y;
10790  paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10791  paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10792  /*
10793  Paste image with X Image window.
10794  */
10795  (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10796  paste_image=DestroyImage(paste_image);
10797  XSetCursorState(display,windows,MagickFalse);
10798  /*
10799  Update image colormap.
10800  */
10801  XConfigureImageColormap(display,resource_info,windows,image);
10802  (void) XConfigureImage(display,resource_info,windows,image);
10803  return(MagickTrue);
10804 }
10805 ␌
10806 /*
10807 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10808 % %
10809 % %
10810 % %
10811 + X P r i n t I m a g e %
10812 % %
10813 % %
10814 % %
10815 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10816 %
10817 % XPrintImage() prints an image to a Postscript printer.
10818 %
10819 % The format of the XPrintImage method is:
10820 %
10821 % MagickBooleanType XPrintImage(Display *display,
10822 % XResourceInfo *resource_info,XWindows *windows,Image *image)
10823 %
10824 % A description of each parameter follows:
10825 %
10826 % o display: Specifies a connection to an X server; returned from
10827 % XOpenDisplay.
10828 %
10829 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10830 %
10831 % o windows: Specifies a pointer to a XWindows structure.
10832 %
10833 % o image: the image.
10834 %
10835 */
10836 static MagickBooleanType XPrintImage(Display *display,
10837  XResourceInfo *resource_info,XWindows *windows,Image *image)
10838 {
10839  char
10840  filename[MaxTextExtent],
10841  geometry[MaxTextExtent];
10842 
10843  const char
10844  *const PageSizes[] =
10845  {
10846  "Letter",
10847  "Tabloid",
10848  "Ledger",
10849  "Legal",
10850  "Statement",
10851  "Executive",
10852  "A3",
10853  "A4",
10854  "A5",
10855  "B4",
10856  "B5",
10857  "Folio",
10858  "Quarto",
10859  "10x14",
10860  (char *) NULL
10861  };
10862 
10863  Image
10864  *print_image;
10865 
10866  ImageInfo
10867  *image_info;
10868 
10869  MagickStatusType
10870  status;
10871 
10872  /*
10873  Request Postscript page geometry from user.
10874  */
10875  image_info=CloneImageInfo(resource_info->image_info);
10876  (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10877  if (image_info->page != (char *) NULL)
10878  (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10879  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10880  "Select Postscript Page Geometry:",geometry);
10881  if (*geometry == '\0')
10882  return(MagickTrue);
10883  image_info->page=GetPageGeometry(geometry);
10884  /*
10885  Apply image transforms.
10886  */
10887  XSetCursorState(display,windows,MagickTrue);
10888  XCheckRefreshWindows(display,windows);
10889  print_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10890  if (print_image == (Image *) NULL)
10891  return(MagickFalse);
10892  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
10893  windows->image.ximage->width,windows->image.ximage->height);
10894  (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10895  /*
10896  Print image.
10897  */
10898  (void) AcquireUniqueFilename(filename);
10899  (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
10900  filename);
10901  status=WriteImage(image_info,print_image);
10902  (void) RelinquishUniqueFileResource(filename);
10903  print_image=DestroyImage(print_image);
10904  image_info=DestroyImageInfo(image_info);
10905  XSetCursorState(display,windows,MagickFalse);
10906  return(status != 0 ? MagickTrue : MagickFalse);
10907 }
10908 ␌
10909 /*
10910 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10911 % %
10912 % %
10913 % %
10914 + X R O I I m a g e %
10915 % %
10916 % %
10917 % %
10918 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10919 %
10920 % XROIImage() applies an image processing technique to a region of interest.
10921 %
10922 % The format of the XROIImage method is:
10923 %
10924 % MagickBooleanType XROIImage(Display *display,
10925 % XResourceInfo *resource_info,XWindows *windows,Image **image)
10926 %
10927 % A description of each parameter follows:
10928 %
10929 % o display: Specifies a connection to an X server; returned from
10930 % XOpenDisplay.
10931 %
10932 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10933 %
10934 % o windows: Specifies a pointer to a XWindows structure.
10935 %
10936 % o image: the image; returned from ReadImage.
10937 %
10938 */
10939 static MagickBooleanType XROIImage(Display *display,
10940  XResourceInfo *resource_info,XWindows *windows,Image **image)
10941 {
10942 #define ApplyMenus 7
10943 
10944  const char
10945  *const ROIMenu[] =
10946  {
10947  "Help",
10948  "Dismiss",
10949  (char *) NULL
10950  },
10951  *const ApplyMenu[] =
10952  {
10953  "File",
10954  "Edit",
10955  "Transform",
10956  "Enhance",
10957  "Effects",
10958  "F/X",
10959  "Miscellany",
10960  "Help",
10961  "Dismiss",
10962  (char *) NULL
10963  },
10964  *const FileMenu[] =
10965  {
10966  "Save...",
10967  "Print...",
10968  (char *) NULL
10969  },
10970  *const EditMenu[] =
10971  {
10972  "Undo",
10973  "Redo",
10974  (char *) NULL
10975  },
10976  *const TransformMenu[] =
10977  {
10978  "Flop",
10979  "Flip",
10980  "Rotate Right",
10981  "Rotate Left",
10982  (char *) NULL
10983  },
10984  *const EnhanceMenu[] =
10985  {
10986  "Hue...",
10987  "Saturation...",
10988  "Brightness...",
10989  "Gamma...",
10990  "Spiff",
10991  "Dull",
10992  "Contrast Stretch...",
10993  "Sigmoidal Contrast...",
10994  "Normalize",
10995  "Equalize",
10996  "Negate",
10997  "Grayscale",
10998  "Map...",
10999  "Quantize...",
11000  (char *) NULL
11001  },
11002  *const EffectsMenu[] =
11003  {
11004  "Despeckle",
11005  "Emboss",
11006  "Reduce Noise",
11007  "Add Noise",
11008  "Sharpen...",
11009  "Blur...",
11010  "Threshold...",
11011  "Edge Detect...",
11012  "Spread...",
11013  "Shade...",
11014  "Raise...",
11015  "Segment...",
11016  (char *) NULL
11017  },
11018  *const FXMenu[] =
11019  {
11020  "Solarize...",
11021  "Sepia Tone...",
11022  "Swirl...",
11023  "Implode...",
11024  "Vignette...",
11025  "Wave...",
11026  "Oil Paint...",
11027  "Charcoal Draw...",
11028  (char *) NULL
11029  },
11030  *const MiscellanyMenu[] =
11031  {
11032  "Image Info",
11033  "Zoom Image",
11034  "Show Preview...",
11035  "Show Histogram",
11036  "Show Matte",
11037  (char *) NULL
11038  };
11039 
11040  const char
11041  *const *Menus[ApplyMenus] =
11042  {
11043  FileMenu,
11044  EditMenu,
11045  TransformMenu,
11046  EnhanceMenu,
11047  EffectsMenu,
11048  FXMenu,
11049  MiscellanyMenu
11050  };
11051 
11052  static const DisplayCommand
11053  ApplyCommands[] =
11054  {
11055  NullCommand,
11056  NullCommand,
11057  NullCommand,
11058  NullCommand,
11059  NullCommand,
11060  NullCommand,
11061  NullCommand,
11062  HelpCommand,
11063  QuitCommand
11064  },
11065  FileCommands[] =
11066  {
11067  SaveCommand,
11068  PrintCommand
11069  },
11070  EditCommands[] =
11071  {
11072  UndoCommand,
11073  RedoCommand
11074  },
11075  TransformCommands[] =
11076  {
11077  FlopCommand,
11078  FlipCommand,
11079  RotateRightCommand,
11080  RotateLeftCommand
11081  },
11082  EnhanceCommands[] =
11083  {
11084  HueCommand,
11085  SaturationCommand,
11086  BrightnessCommand,
11087  GammaCommand,
11088  SpiffCommand,
11089  DullCommand,
11090  ContrastStretchCommand,
11091  SigmoidalContrastCommand,
11092  NormalizeCommand,
11093  EqualizeCommand,
11094  NegateCommand,
11095  GrayscaleCommand,
11096  MapCommand,
11097  QuantizeCommand
11098  },
11099  EffectsCommands[] =
11100  {
11101  DespeckleCommand,
11102  EmbossCommand,
11103  ReduceNoiseCommand,
11104  AddNoiseCommand,
11105  SharpenCommand,
11106  BlurCommand,
11107  ThresholdCommand,
11108  EdgeDetectCommand,
11109  SpreadCommand,
11110  ShadeCommand,
11111  RaiseCommand,
11112  SegmentCommand
11113  },
11114  FXCommands[] =
11115  {
11116  SolarizeCommand,
11117  SepiaToneCommand,
11118  SwirlCommand,
11119  ImplodeCommand,
11120  VignetteCommand,
11121  WaveCommand,
11122  OilPaintCommand,
11123  CharcoalDrawCommand
11124  },
11125  MiscellanyCommands[] =
11126  {
11127  InfoCommand,
11128  ZoomCommand,
11129  ShowPreviewCommand,
11130  ShowHistogramCommand,
11131  ShowMatteCommand
11132  },
11133  ROICommands[] =
11134  {
11135  ROIHelpCommand,
11136  ROIDismissCommand
11137  };
11138 
11139  static const DisplayCommand
11140  *Commands[ApplyMenus] =
11141  {
11142  FileCommands,
11143  EditCommands,
11144  TransformCommands,
11145  EnhanceCommands,
11146  EffectsCommands,
11147  FXCommands,
11148  MiscellanyCommands
11149  };
11150 
11151  char
11152  command[MaxTextExtent],
11153  text[MaxTextExtent];
11154 
11155  DisplayCommand
11156  display_command;
11157 
11158  Cursor
11159  cursor;
11160 
11161  Image
11162  *roi_image;
11163 
11164  int
11165  entry,
11166  id,
11167  x,
11168  y;
11169 
11170  MagickRealType
11171  scale_factor;
11172 
11173  MagickProgressMonitor
11174  progress_monitor;
11175 
11177  crop_info,
11178  highlight_info,
11179  roi_info;
11180 
11181  unsigned int
11182  height,
11183  width;
11184 
11185  size_t
11186  state;
11187 
11188  XEvent
11189  event;
11190 
11191  /*
11192  Map Command widget.
11193  */
11194  (void) CloneString(&windows->command.name,"ROI");
11195  windows->command.data=0;
11196  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11197  (void) XMapRaised(display,windows->command.id);
11198  XClientMessage(display,windows->image.id,windows->im_protocols,
11199  windows->im_update_widget,CurrentTime);
11200  /*
11201  Track pointer until button 1 is pressed.
11202  */
11203  XQueryPosition(display,windows->image.id,&x,&y);
11204  (void) XSelectInput(display,windows->image.id,
11205  windows->image.attributes.event_mask | PointerMotionMask);
11206  crop_info.width=0;
11207  crop_info.height=0;
11208  crop_info.x=0;
11209  crop_info.y=0;
11210  roi_info.x=(ssize_t) windows->image.x+x;
11211  roi_info.y=(ssize_t) windows->image.y+y;
11212  roi_info.width=0;
11213  roi_info.height=0;
11214  cursor=XCreateFontCursor(display,XC_fleur);
11215  state=DefaultState;
11216  do
11217  {
11218  if (windows->info.mapped != MagickFalse)
11219  {
11220  /*
11221  Display pointer position.
11222  */
11223  (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11224  (long) roi_info.x,(long) roi_info.y);
11225  XInfoWidget(display,windows,text);
11226  }
11227  /*
11228  Wait for next event.
11229  */
11230  XScreenEvent(display,windows,&event);
11231  if (event.xany.window == windows->command.id)
11232  {
11233  /*
11234  Select a command from the Command widget.
11235  */
11236  id=XCommandWidget(display,windows,ROIMenu,&event);
11237  if (id < 0)
11238  continue;
11239  switch (ROICommands[id])
11240  {
11241  case ROIHelpCommand:
11242  {
11243  XTextViewHelp(display,resource_info,windows,MagickFalse,
11244  "Help Viewer - Region of Interest",ImageROIHelp);
11245  break;
11246  }
11247  case ROIDismissCommand:
11248  {
11249  /*
11250  Prematurely exit.
11251  */
11252  state|=EscapeState;
11253  state|=ExitState;
11254  break;
11255  }
11256  default:
11257  break;
11258  }
11259  continue;
11260  }
11261  switch (event.type)
11262  {
11263  case ButtonPress:
11264  {
11265  if (event.xbutton.button != Button1)
11266  break;
11267  if (event.xbutton.window != windows->image.id)
11268  break;
11269  /*
11270  Note first corner of region of interest rectangle-- exit loop.
11271  */
11272  (void) XCheckDefineCursor(display,windows->image.id,cursor);
11273  roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11274  roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11275  state|=ExitState;
11276  break;
11277  }
11278  case ButtonRelease:
11279  break;
11280  case Expose:
11281  break;
11282  case KeyPress:
11283  {
11284  KeySym
11285  key_symbol;
11286 
11287  if (event.xkey.window != windows->image.id)
11288  break;
11289  /*
11290  Respond to a user key press.
11291  */
11292  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11293  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11294  switch ((int) key_symbol)
11295  {
11296  case XK_Escape:
11297  case XK_F20:
11298  {
11299  /*
11300  Prematurely exit.
11301  */
11302  state|=EscapeState;
11303  state|=ExitState;
11304  break;
11305  }
11306  case XK_F1:
11307  case XK_Help:
11308  {
11309  XTextViewHelp(display,resource_info,windows,MagickFalse,
11310  "Help Viewer - Region of Interest",ImageROIHelp);
11311  break;
11312  }
11313  default:
11314  {
11315  (void) XBell(display,0);
11316  break;
11317  }
11318  }
11319  break;
11320  }
11321  case MotionNotify:
11322  {
11323  /*
11324  Map and unmap Info widget as text cursor crosses its boundaries.
11325  */
11326  x=event.xmotion.x;
11327  y=event.xmotion.y;
11328  if (windows->info.mapped != MagickFalse)
11329  {
11330  if ((x < (int) (windows->info.x+windows->info.width)) &&
11331  (y < (int) (windows->info.y+windows->info.height)))
11332  (void) XWithdrawWindow(display,windows->info.id,
11333  windows->info.screen);
11334  }
11335  else
11336  if ((x > (int) (windows->info.x+windows->info.width)) ||
11337  (y > (int) (windows->info.y+windows->info.height)))
11338  (void) XMapWindow(display,windows->info.id);
11339  roi_info.x=(ssize_t) windows->image.x+x;
11340  roi_info.y=(ssize_t) windows->image.y+y;
11341  break;
11342  }
11343  default:
11344  break;
11345  }
11346  } while ((state & ExitState) == 0);
11347  (void) XSelectInput(display,windows->image.id,
11348  windows->image.attributes.event_mask);
11349  if ((state & EscapeState) != 0)
11350  {
11351  /*
11352  User want to exit without region of interest.
11353  */
11354  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11355  (void) XFreeCursor(display,cursor);
11356  return(MagickTrue);
11357  }
11358  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11359  do
11360  {
11361  /*
11362  Size rectangle as pointer moves until the mouse button is released.
11363  */
11364  x=(int) roi_info.x;
11365  y=(int) roi_info.y;
11366  roi_info.width=0;
11367  roi_info.height=0;
11368  state=DefaultState;
11369  do
11370  {
11371  highlight_info=roi_info;
11372  highlight_info.x=roi_info.x-windows->image.x;
11373  highlight_info.y=roi_info.y-windows->image.y;
11374  if ((highlight_info.width > 3) && (highlight_info.height > 3))
11375  {
11376  /*
11377  Display info and draw region of interest rectangle.
11378  */
11379  if (windows->info.mapped == MagickFalse)
11380  (void) XMapWindow(display,windows->info.id);
11381  (void) FormatLocaleString(text,MaxTextExtent,
11382  " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11383  roi_info.height,(double) roi_info.x,(double) roi_info.y);
11384  XInfoWidget(display,windows,text);
11385  XHighlightRectangle(display,windows->image.id,
11386  windows->image.highlight_context,&highlight_info);
11387  }
11388  else
11389  if (windows->info.mapped != MagickFalse)
11390  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11391  /*
11392  Wait for next event.
11393  */
11394  XScreenEvent(display,windows,&event);
11395  if ((highlight_info.width > 3) && (highlight_info.height > 3))
11396  XHighlightRectangle(display,windows->image.id,
11397  windows->image.highlight_context,&highlight_info);
11398  switch (event.type)
11399  {
11400  case ButtonPress:
11401  {
11402  roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11403  roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11404  break;
11405  }
11406  case ButtonRelease:
11407  {
11408  /*
11409  User has committed to region of interest rectangle.
11410  */
11411  roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11412  roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11413  XSetCursorState(display,windows,MagickFalse);
11414  state|=ExitState;
11415  if (LocaleCompare(windows->command.name,"Apply") == 0)
11416  break;
11417  (void) CloneString(&windows->command.name,"Apply");
11418  windows->command.data=ApplyMenus;
11419  (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11420  break;
11421  }
11422  case Expose:
11423  break;
11424  case MotionNotify:
11425  {
11426  roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11427  roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11428  }
11429  default:
11430  break;
11431  }
11432  if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11433  ((state & ExitState) != 0))
11434  {
11435  /*
11436  Check boundary conditions.
11437  */
11438  if (roi_info.x < 0)
11439  roi_info.x=0;
11440  else
11441  if (roi_info.x > (ssize_t) windows->image.ximage->width)
11442  roi_info.x=(ssize_t) windows->image.ximage->width;
11443  if ((int) roi_info.x < x)
11444  roi_info.width=(unsigned int) (x-roi_info.x);
11445  else
11446  {
11447  roi_info.width=(unsigned int) (roi_info.x-x);
11448  roi_info.x=(ssize_t) x;
11449  }
11450  if (roi_info.y < 0)
11451  roi_info.y=0;
11452  else
11453  if (roi_info.y > (ssize_t) windows->image.ximage->height)
11454  roi_info.y=(ssize_t) windows->image.ximage->height;
11455  if ((int) roi_info.y < y)
11456  roi_info.height=(unsigned int) (y-roi_info.y);
11457  else
11458  {
11459  roi_info.height=(unsigned int) (roi_info.y-y);
11460  roi_info.y=(ssize_t) y;
11461  }
11462  }
11463  } while ((state & ExitState) == 0);
11464  /*
11465  Wait for user to grab a corner of the rectangle or press return.
11466  */
11467  state=DefaultState;
11468  display_command=NullCommand;
11469  (void) XMapWindow(display,windows->info.id);
11470  do
11471  {
11472  if (windows->info.mapped != MagickFalse)
11473  {
11474  /*
11475  Display pointer position.
11476  */
11477  (void) FormatLocaleString(text,MaxTextExtent,
11478  " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11479  roi_info.height,(double) roi_info.x,(double) roi_info.y);
11480  XInfoWidget(display,windows,text);
11481  }
11482  highlight_info=roi_info;
11483  highlight_info.x=roi_info.x-windows->image.x;
11484  highlight_info.y=roi_info.y-windows->image.y;
11485  if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11486  {
11487  state|=EscapeState;
11488  state|=ExitState;
11489  break;
11490  }
11491  if ((state & UpdateRegionState) != 0)
11492  {
11493  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11494  switch (display_command)
11495  {
11496  case UndoCommand:
11497  case RedoCommand:
11498  {
11499  (void) XMagickCommand(display,resource_info,windows,
11500  display_command,image);
11501  break;
11502  }
11503  default:
11504  {
11505  /*
11506  Region of interest is relative to image configuration.
11507  */
11508  progress_monitor=SetImageProgressMonitor(*image,
11509  (MagickProgressMonitor) NULL,(*image)->client_data);
11510  crop_info=roi_info;
11511  width=(unsigned int) (*image)->columns;
11512  height=(unsigned int) (*image)->rows;
11513  x=0;
11514  y=0;
11515  if (windows->image.crop_geometry != (char *) NULL)
11516  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11517  &width,&height);
11518  scale_factor=(MagickRealType) width/windows->image.ximage->width;
11519  crop_info.x+=x;
11520  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11521  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11522  scale_factor=(MagickRealType)
11523  height/windows->image.ximage->height;
11524  crop_info.y+=y;
11525  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11526  crop_info.height=(unsigned int)
11527  (scale_factor*crop_info.height+0.5);
11528  roi_image=CropImage(*image,&crop_info,&(*image)->exception);
11529  (void) SetImageProgressMonitor(*image,progress_monitor,
11530  (*image)->client_data);
11531  if (roi_image == (Image *) NULL)
11532  continue;
11533  /*
11534  Apply image processing technique to the region of interest.
11535  */
11536  windows->image.orphan=MagickTrue;
11537  (void) XMagickCommand(display,resource_info,windows,
11538  display_command,&roi_image);
11539  progress_monitor=SetImageProgressMonitor(*image,
11540  (MagickProgressMonitor) NULL,(*image)->client_data);
11541  (void) XMagickCommand(display,resource_info,windows,
11542  SaveToUndoBufferCommand,image);
11543  windows->image.orphan=MagickFalse;
11544  (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11545  crop_info.x,crop_info.y);
11546  roi_image=DestroyImage(roi_image);
11547  (void) SetImageProgressMonitor(*image,progress_monitor,
11548  (*image)->client_data);
11549  break;
11550  }
11551  }
11552  if (display_command != InfoCommand)
11553  {
11554  XConfigureImageColormap(display,resource_info,windows,*image);
11555  (void) XConfigureImage(display,resource_info,windows,*image);
11556  }
11557  XCheckRefreshWindows(display,windows);
11558  XInfoWidget(display,windows,text);
11559  (void) XSetFunction(display,windows->image.highlight_context,
11560  GXinvert);
11561  state&=(~UpdateRegionState);
11562  }
11563  XHighlightRectangle(display,windows->image.id,
11564  windows->image.highlight_context,&highlight_info);
11565  XScreenEvent(display,windows,&event);
11566  if (event.xany.window == windows->command.id)
11567  {
11568  /*
11569  Select a command from the Command widget.
11570  */
11571  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11572  display_command=NullCommand;
11573  id=XCommandWidget(display,windows,ApplyMenu,&event);
11574  if (id >= 0)
11575  {
11576  (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11577  display_command=ApplyCommands[id];
11578  if (id < ApplyMenus)
11579  {
11580  /*
11581  Select a command from a pop-up menu.
11582  */
11583  entry=XMenuWidget(display,windows,ApplyMenu[id],
11584  (const char **) Menus[id],command);
11585  if (entry >= 0)
11586  {
11587  (void) CopyMagickString(command,Menus[id][entry],
11588  MaxTextExtent);
11589  display_command=Commands[id][entry];
11590  }
11591  }
11592  }
11593  (void) XSetFunction(display,windows->image.highlight_context,
11594  GXinvert);
11595  XHighlightRectangle(display,windows->image.id,
11596  windows->image.highlight_context,&highlight_info);
11597  if (display_command == HelpCommand)
11598  {
11599  (void) XSetFunction(display,windows->image.highlight_context,
11600  GXcopy);
11601  XTextViewHelp(display,resource_info,windows,MagickFalse,
11602  "Help Viewer - Region of Interest",ImageROIHelp);
11603  (void) XSetFunction(display,windows->image.highlight_context,
11604  GXinvert);
11605  continue;
11606  }
11607  if (display_command == QuitCommand)
11608  {
11609  /*
11610  exit.
11611  */
11612  state|=EscapeState;
11613  state|=ExitState;
11614  continue;
11615  }
11616  if (display_command != NullCommand)
11617  state|=UpdateRegionState;
11618  continue;
11619  }
11620  XHighlightRectangle(display,windows->image.id,
11621  windows->image.highlight_context,&highlight_info);
11622  switch (event.type)
11623  {
11624  case ButtonPress:
11625  {
11626  x=windows->image.x;
11627  y=windows->image.y;
11628  if (event.xbutton.button != Button1)
11629  break;
11630  if (event.xbutton.window != windows->image.id)
11631  break;
11632  x=windows->image.x+event.xbutton.x;
11633  y=windows->image.y+event.xbutton.y;
11634  if ((x < (int) (roi_info.x+RoiDelta)) &&
11635  (x > (int) (roi_info.x-RoiDelta)) &&
11636  (y < (int) (roi_info.y+RoiDelta)) &&
11637  (y > (int) (roi_info.y-RoiDelta)))
11638  {
11639  roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11640  roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11641  state|=UpdateConfigurationState;
11642  break;
11643  }
11644  if ((x < (int) (roi_info.x+RoiDelta)) &&
11645  (x > (int) (roi_info.x-RoiDelta)) &&
11646  (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11647  (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11648  {
11649  roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11650  state|=UpdateConfigurationState;
11651  break;
11652  }
11653  if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11654  (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11655  (y < (int) (roi_info.y+RoiDelta)) &&
11656  (y > (int) (roi_info.y-RoiDelta)))
11657  {
11658  roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11659  state|=UpdateConfigurationState;
11660  break;
11661  }
11662  if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11663  (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11664  (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11665  (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11666  {
11667  state|=UpdateConfigurationState;
11668  break;
11669  }
11670  magick_fallthrough;
11671  }
11672  case ButtonRelease:
11673  {
11674  if (event.xbutton.window == windows->pan.id)
11675  if ((highlight_info.x != crop_info.x-windows->image.x) ||
11676  (highlight_info.y != crop_info.y-windows->image.y))
11677  XHighlightRectangle(display,windows->image.id,
11678  windows->image.highlight_context,&highlight_info);
11679  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11680  event.xbutton.time);
11681  break;
11682  }
11683  case Expose:
11684  {
11685  if (event.xexpose.window == windows->image.id)
11686  if (event.xexpose.count == 0)
11687  {
11688  event.xexpose.x=(int) highlight_info.x;
11689  event.xexpose.y=(int) highlight_info.y;
11690  event.xexpose.width=(int) highlight_info.width;
11691  event.xexpose.height=(int) highlight_info.height;
11692  XRefreshWindow(display,&windows->image,&event);
11693  }
11694  if (event.xexpose.window == windows->info.id)
11695  if (event.xexpose.count == 0)
11696  XInfoWidget(display,windows,text);
11697  break;
11698  }
11699  case KeyPress:
11700  {
11701  KeySym
11702  key_symbol;
11703 
11704  if (event.xkey.window != windows->image.id)
11705  break;
11706  /*
11707  Respond to a user key press.
11708  */
11709  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11710  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11711  switch ((int) key_symbol)
11712  {
11713  case XK_Shift_L:
11714  case XK_Shift_R:
11715  break;
11716  case XK_Escape:
11717  case XK_F20:
11718  {
11719  state|=EscapeState;
11720  magick_fallthrough;
11721  }
11722  case XK_Return:
11723  {
11724  state|=ExitState;
11725  break;
11726  }
11727  case XK_Home:
11728  case XK_KP_Home:
11729  {
11730  roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11731  roi_info.y=(ssize_t) (windows->image.height/2L-
11732  roi_info.height/2L);
11733  break;
11734  }
11735  case XK_Left:
11736  case XK_KP_Left:
11737  {
11738  roi_info.x--;
11739  break;
11740  }
11741  case XK_Up:
11742  case XK_KP_Up:
11743  case XK_Next:
11744  {
11745  roi_info.y--;
11746  break;
11747  }
11748  case XK_Right:
11749  case XK_KP_Right:
11750  {
11751  roi_info.x++;
11752  break;
11753  }
11754  case XK_Prior:
11755  case XK_Down:
11756  case XK_KP_Down:
11757  {
11758  roi_info.y++;
11759  break;
11760  }
11761  case XK_F1:
11762  case XK_Help:
11763  {
11764  (void) XSetFunction(display,windows->image.highlight_context,
11765  GXcopy);
11766  XTextViewHelp(display,resource_info,windows,MagickFalse,
11767  "Help Viewer - Region of Interest",ImageROIHelp);
11768  (void) XSetFunction(display,windows->image.highlight_context,
11769  GXinvert);
11770  break;
11771  }
11772  default:
11773  {
11774  display_command=XImageWindowCommand(display,resource_info,windows,
11775  event.xkey.state,key_symbol,image);
11776  if (display_command != NullCommand)
11777  state|=UpdateRegionState;
11778  break;
11779  }
11780  }
11781  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11782  event.xkey.time);
11783  break;
11784  }
11785  case KeyRelease:
11786  break;
11787  case MotionNotify:
11788  {
11789  if (event.xbutton.window != windows->image.id)
11790  break;
11791  /*
11792  Map and unmap Info widget as text cursor crosses its boundaries.
11793  */
11794  x=event.xmotion.x;
11795  y=event.xmotion.y;
11796  if (windows->info.mapped != MagickFalse)
11797  {
11798  if ((x < (int) (windows->info.x+windows->info.width)) &&
11799  (y < (int) (windows->info.y+windows->info.height)))
11800  (void) XWithdrawWindow(display,windows->info.id,
11801  windows->info.screen);
11802  }
11803  else
11804  if ((x > (int) (windows->info.x+windows->info.width)) ||
11805  (y > (int) (windows->info.y+windows->info.height)))
11806  (void) XMapWindow(display,windows->info.id);
11807  roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11808  roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11809  break;
11810  }
11811  case SelectionRequest:
11812  {
11813  XSelectionEvent
11814  notify;
11815 
11816  XSelectionRequestEvent
11817  *request;
11818 
11819  /*
11820  Set primary selection.
11821  */
11822  (void) FormatLocaleString(text,MaxTextExtent,
11823  "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11824  roi_info.height,(double) roi_info.x,(double) roi_info.y);
11825  request=(&(event.xselectionrequest));
11826  (void) XChangeProperty(request->display,request->requestor,
11827  request->property,request->target,8,PropModeReplace,
11828  (unsigned char *) text,(int) strlen(text));
11829  notify.type=SelectionNotify;
11830  notify.display=request->display;
11831  notify.requestor=request->requestor;
11832  notify.selection=request->selection;
11833  notify.target=request->target;
11834  notify.time=request->time;
11835  if (request->property == None)
11836  notify.property=request->target;
11837  else
11838  notify.property=request->property;
11839  (void) XSendEvent(request->display,request->requestor,False,0,
11840  (XEvent *) &notify);
11841  }
11842  default:
11843  break;
11844  }
11845  if ((state & UpdateConfigurationState) != 0)
11846  {
11847  (void) XPutBackEvent(display,&event);
11848  (void) XCheckDefineCursor(display,windows->image.id,cursor);
11849  break;
11850  }
11851  } while ((state & ExitState) == 0);
11852  } while ((state & ExitState) == 0);
11853  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11854  XSetCursorState(display,windows,MagickFalse);
11855  if ((state & EscapeState) != 0)
11856  return(MagickTrue);
11857  return(MagickTrue);
11858 }
11859 ␌
11860 /*
11861 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11862 % %
11863 % %
11864 % %
11865 + X R o t a t e I m a g e %
11866 % %
11867 % %
11868 % %
11869 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11870 %
11871 % XRotateImage() rotates the X image. If the degrees parameter if zero, the
11872 % rotation angle is computed from the slope of a line drawn by the user.
11873 %
11874 % The format of the XRotateImage method is:
11875 %
11876 % MagickBooleanType XRotateImage(Display *display,
11877 % XResourceInfo *resource_info,XWindows *windows,double degrees,
11878 % Image **image)
11879 %
11880 % A description of each parameter follows:
11881 %
11882 % o display: Specifies a connection to an X server; returned from
11883 % XOpenDisplay.
11884 %
11885 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11886 %
11887 % o windows: Specifies a pointer to a XWindows structure.
11888 %
11889 % o degrees: Specifies the number of degrees to rotate the image.
11890 %
11891 % o image: the image.
11892 %
11893 */
11894 static MagickBooleanType XRotateImage(Display *display,
11895  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image)
11896 {
11897  const char
11898  *const RotateMenu[] =
11899  {
11900  "Pixel Color",
11901  "Direction",
11902  "Help",
11903  "Dismiss",
11904  (char *) NULL
11905  };
11906 
11907  static ModeType
11908  direction = HorizontalRotateCommand;
11909 
11910  static const ModeType
11911  DirectionCommands[] =
11912  {
11913  HorizontalRotateCommand,
11914  VerticalRotateCommand
11915  },
11916  RotateCommands[] =
11917  {
11918  RotateColorCommand,
11919  RotateDirectionCommand,
11920  RotateHelpCommand,
11921  RotateDismissCommand
11922  };
11923 
11924  static unsigned int
11925  pen_id = 0;
11926 
11927  char
11928  command[MaxTextExtent],
11929  text[MaxTextExtent];
11930 
11931  Image
11932  *rotate_image;
11933 
11934  int
11935  id,
11936  x,
11937  y;
11938 
11939  MagickRealType
11940  normalized_degrees;
11941 
11942  int
11943  i;
11944 
11945  unsigned int
11946  height,
11947  rotations,
11948  width;
11949 
11950  if (degrees == 0.0)
11951  {
11952  unsigned int
11953  distance;
11954 
11955  size_t
11956  state;
11957 
11958  XEvent
11959  event;
11960 
11961  XSegment
11962  rotate_info;
11963 
11964  /*
11965  Map Command widget.
11966  */
11967  (void) CloneString(&windows->command.name,"Rotate");
11968  windows->command.data=2;
11969  (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
11970  (void) XMapRaised(display,windows->command.id);
11971  XClientMessage(display,windows->image.id,windows->im_protocols,
11972  windows->im_update_widget,CurrentTime);
11973  /*
11974  Wait for first button press.
11975  */
11976  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11977  XQueryPosition(display,windows->image.id,&x,&y);
11978  rotate_info.x1=x;
11979  rotate_info.y1=y;
11980  rotate_info.x2=x;
11981  rotate_info.y2=y;
11982  state=DefaultState;
11983  do
11984  {
11985  XHighlightLine(display,windows->image.id,
11986  windows->image.highlight_context,&rotate_info);
11987  /*
11988  Wait for next event.
11989  */
11990  XScreenEvent(display,windows,&event);
11991  XHighlightLine(display,windows->image.id,
11992  windows->image.highlight_context,&rotate_info);
11993  if (event.xany.window == windows->command.id)
11994  {
11995  /*
11996  Select a command from the Command widget.
11997  */
11998  id=XCommandWidget(display,windows,RotateMenu,&event);
11999  if (id < 0)
12000  continue;
12001  (void) XSetFunction(display,windows->image.highlight_context,
12002  GXcopy);
12003  switch (RotateCommands[id])
12004  {
12005  case RotateColorCommand:
12006  {
12007  const char
12008  *ColorMenu[MaxNumberPens];
12009 
12010  int
12011  pen_number;
12012 
12013  XColor
12014  color;
12015 
12016  /*
12017  Initialize menu selections.
12018  */
12019  for (i=0; i < (int) (MaxNumberPens-2); i++)
12020  ColorMenu[i]=resource_info->pen_colors[i];
12021  ColorMenu[MaxNumberPens-2]="Browser...";
12022  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12023  /*
12024  Select a pen color from the pop-up menu.
12025  */
12026  pen_number=XMenuWidget(display,windows,RotateMenu[id],
12027  (const char **) ColorMenu,command);
12028  if (pen_number < 0)
12029  break;
12030  if (pen_number == (MaxNumberPens-2))
12031  {
12032  static char
12033  color_name[MaxTextExtent] = "gray";
12034 
12035  /*
12036  Select a pen color from a dialog.
12037  */
12038  resource_info->pen_colors[pen_number]=color_name;
12039  XColorBrowserWidget(display,windows,"Select",color_name);
12040  if (*color_name == '\0')
12041  break;
12042  }
12043  /*
12044  Set pen color.
12045  */
12046  (void) XParseColor(display,windows->map_info->colormap,
12047  resource_info->pen_colors[pen_number],&color);
12048  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12049  (unsigned int) MaxColors,&color);
12050  windows->pixel_info->pen_colors[pen_number]=color;
12051  pen_id=(unsigned int) pen_number;
12052  break;
12053  }
12054  case RotateDirectionCommand:
12055  {
12056  const char
12057  *const Directions[] =
12058  {
12059  "horizontal",
12060  "vertical",
12061  (char *) NULL,
12062  };
12063 
12064  /*
12065  Select a command from the pop-up menu.
12066  */
12067  id=XMenuWidget(display,windows,RotateMenu[id],
12068  Directions,command);
12069  if (id >= 0)
12070  direction=DirectionCommands[id];
12071  break;
12072  }
12073  case RotateHelpCommand:
12074  {
12075  XTextViewHelp(display,resource_info,windows,MagickFalse,
12076  "Help Viewer - Image Rotation",ImageRotateHelp);
12077  break;
12078  }
12079  case RotateDismissCommand:
12080  {
12081  /*
12082  Prematurely exit.
12083  */
12084  state|=EscapeState;
12085  state|=ExitState;
12086  break;
12087  }
12088  default:
12089  break;
12090  }
12091  (void) XSetFunction(display,windows->image.highlight_context,
12092  GXinvert);
12093  continue;
12094  }
12095  switch (event.type)
12096  {
12097  case ButtonPress:
12098  {
12099  if (event.xbutton.button != Button1)
12100  break;
12101  if (event.xbutton.window != windows->image.id)
12102  break;
12103  /*
12104  exit loop.
12105  */
12106  (void) XSetFunction(display,windows->image.highlight_context,
12107  GXcopy);
12108  rotate_info.x1=event.xbutton.x;
12109  rotate_info.y1=event.xbutton.y;
12110  state|=ExitState;
12111  break;
12112  }
12113  case ButtonRelease:
12114  break;
12115  case Expose:
12116  break;
12117  case KeyPress:
12118  {
12119  char
12120  command[MaxTextExtent];
12121 
12122  KeySym
12123  key_symbol;
12124 
12125  if (event.xkey.window != windows->image.id)
12126  break;
12127  /*
12128  Respond to a user key press.
12129  */
12130  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12131  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12132  switch ((int) key_symbol)
12133  {
12134  case XK_Escape:
12135  case XK_F20:
12136  {
12137  /*
12138  Prematurely exit.
12139  */
12140  state|=EscapeState;
12141  state|=ExitState;
12142  break;
12143  }
12144  case XK_F1:
12145  case XK_Help:
12146  {
12147  (void) XSetFunction(display,windows->image.highlight_context,
12148  GXcopy);
12149  XTextViewHelp(display,resource_info,windows,MagickFalse,
12150  "Help Viewer - Image Rotation",ImageRotateHelp);
12151  (void) XSetFunction(display,windows->image.highlight_context,
12152  GXinvert);
12153  break;
12154  }
12155  default:
12156  {
12157  (void) XBell(display,0);
12158  break;
12159  }
12160  }
12161  break;
12162  }
12163  case MotionNotify:
12164  {
12165  rotate_info.x1=event.xmotion.x;
12166  rotate_info.y1=event.xmotion.y;
12167  }
12168  }
12169  rotate_info.x2=rotate_info.x1;
12170  rotate_info.y2=rotate_info.y1;
12171  if (direction == HorizontalRotateCommand)
12172  rotate_info.x2+=32;
12173  else
12174  rotate_info.y2-=32;
12175  } while ((state & ExitState) == 0);
12176  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12177  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12178  if ((state & EscapeState) != 0)
12179  return(MagickTrue);
12180  /*
12181  Draw line as pointer moves until the mouse button is released.
12182  */
12183  distance=0;
12184  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12185  state=DefaultState;
12186  do
12187  {
12188  if (distance > 9)
12189  {
12190  /*
12191  Display info and draw rotation line.
12192  */
12193  if (windows->info.mapped == MagickFalse)
12194  (void) XMapWindow(display,windows->info.id);
12195  (void) FormatLocaleString(text,MaxTextExtent," %g",
12196  direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12197  XInfoWidget(display,windows,text);
12198  XHighlightLine(display,windows->image.id,
12199  windows->image.highlight_context,&rotate_info);
12200  }
12201  else
12202  if (windows->info.mapped != MagickFalse)
12203  (void) XWithdrawWindow(display,windows->info.id,
12204  windows->info.screen);
12205  /*
12206  Wait for next event.
12207  */
12208  XScreenEvent(display,windows,&event);
12209  if (distance > 9)
12210  XHighlightLine(display,windows->image.id,
12211  windows->image.highlight_context,&rotate_info);
12212  switch (event.type)
12213  {
12214  case ButtonPress:
12215  break;
12216  case ButtonRelease:
12217  {
12218  /*
12219  User has committed to rotation line.
12220  */
12221  rotate_info.x2=event.xbutton.x;
12222  rotate_info.y2=event.xbutton.y;
12223  state|=ExitState;
12224  break;
12225  }
12226  case Expose:
12227  break;
12228  case MotionNotify:
12229  {
12230  rotate_info.x2=event.xmotion.x;
12231  rotate_info.y2=event.xmotion.y;
12232  }
12233  default:
12234  break;
12235  }
12236  /*
12237  Check boundary conditions.
12238  */
12239  if (rotate_info.x2 < 0)
12240  rotate_info.x2=0;
12241  else
12242  if (rotate_info.x2 > (int) windows->image.width)
12243  rotate_info.x2=(short) windows->image.width;
12244  if (rotate_info.y2 < 0)
12245  rotate_info.y2=0;
12246  else
12247  if (rotate_info.y2 > (int) windows->image.height)
12248  rotate_info.y2=(short) windows->image.height;
12249  /*
12250  Compute rotation angle from the slope of the line.
12251  */
12252  degrees=0.0;
12253  distance=(unsigned int)
12254  ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12255  ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12256  if (distance > 9)
12257  degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12258  rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12259  } while ((state & ExitState) == 0);
12260  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12261  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12262  if (distance <= 9)
12263  return(MagickTrue);
12264  }
12265  if (direction == VerticalRotateCommand)
12266  degrees-=90.0;
12267  if (degrees == 0.0)
12268  return(MagickTrue);
12269  /*
12270  Rotate image.
12271  */
12272  normalized_degrees=degrees;
12273  while (normalized_degrees < -45.0)
12274  normalized_degrees+=360.0;
12275  for (rotations=0; normalized_degrees > 45.0; rotations++)
12276  normalized_degrees-=90.0;
12277  if (normalized_degrees != 0.0)
12278  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
12279  XSetCursorState(display,windows,MagickTrue);
12280  XCheckRefreshWindows(display,windows);
12281  (*image)->background_color.red=ScaleShortToQuantum(
12282  windows->pixel_info->pen_colors[pen_id].red);
12283  (*image)->background_color.green=ScaleShortToQuantum(
12284  windows->pixel_info->pen_colors[pen_id].green);
12285  (*image)->background_color.blue=ScaleShortToQuantum(
12286  windows->pixel_info->pen_colors[pen_id].blue);
12287  rotate_image=RotateImage(*image,degrees,&(*image)->exception);
12288  XSetCursorState(display,windows,MagickFalse);
12289  if (rotate_image == (Image *) NULL)
12290  return(MagickFalse);
12291  *image=DestroyImage(*image);
12292  *image=rotate_image;
12293  if (windows->image.crop_geometry != (char *) NULL)
12294  {
12295  /*
12296  Rotate crop geometry.
12297  */
12298  width=(unsigned int) (*image)->columns;
12299  height=(unsigned int) (*image)->rows;
12300  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12301  switch (rotations % 4)
12302  {
12303  default:
12304  case 0:
12305  break;
12306  case 1:
12307  {
12308  /*
12309  Rotate 90 degrees.
12310  */
12311  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12312  "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12313  (int) height-y,x);
12314  break;
12315  }
12316  case 2:
12317  {
12318  /*
12319  Rotate 180 degrees.
12320  */
12321  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12322  "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12323  break;
12324  }
12325  case 3:
12326  {
12327  /*
12328  Rotate 270 degrees.
12329  */
12330  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12331  "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12332  break;
12333  }
12334  }
12335  }
12336  if (windows->image.orphan != MagickFalse)
12337  return(MagickTrue);
12338  if (normalized_degrees != 0.0)
12339  {
12340  /*
12341  Update image colormap.
12342  */
12343  windows->image.window_changes.width=(int) (*image)->columns;
12344  windows->image.window_changes.height=(int) (*image)->rows;
12345  if (windows->image.crop_geometry != (char *) NULL)
12346  {
12347  /*
12348  Obtain dimensions of image from crop geometry.
12349  */
12350  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12351  &width,&height);
12352  windows->image.window_changes.width=(int) width;
12353  windows->image.window_changes.height=(int) height;
12354  }
12355  XConfigureImageColormap(display,resource_info,windows,*image);
12356  }
12357  else
12358  if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12359  {
12360  windows->image.window_changes.width=windows->image.ximage->height;
12361  windows->image.window_changes.height=windows->image.ximage->width;
12362  }
12363  /*
12364  Update image configuration.
12365  */
12366  (void) XConfigureImage(display,resource_info,windows,*image);
12367  return(MagickTrue);
12368 }
12369 ␌
12370 /*
12371 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12372 % %
12373 % %
12374 % %
12375 + X S a v e I m a g e %
12376 % %
12377 % %
12378 % %
12379 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12380 %
12381 % XSaveImage() saves an image to a file.
12382 %
12383 % The format of the XSaveImage method is:
12384 %
12385 % MagickBooleanType XSaveImage(Display *display,
12386 % XResourceInfo *resource_info,XWindows *windows,Image *image)
12387 %
12388 % A description of each parameter follows:
12389 %
12390 % o display: Specifies a connection to an X server; returned from
12391 % XOpenDisplay.
12392 %
12393 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12394 %
12395 % o windows: Specifies a pointer to a XWindows structure.
12396 %
12397 % o image: the image.
12398 %
12399 */
12400 static MagickBooleanType XSaveImage(Display *display,
12401  XResourceInfo *resource_info,XWindows *windows,Image *image)
12402 {
12403  char
12404  filename[MaxTextExtent],
12405  geometry[MaxTextExtent];
12406 
12407  Image
12408  *save_image;
12409 
12410  ImageInfo
12411  *image_info;
12412 
12413  MagickStatusType
12414  status;
12415 
12416  /*
12417  Request file name from user.
12418  */
12419  if (resource_info->write_filename != (char *) NULL)
12420  (void) CopyMagickString(filename,resource_info->write_filename,
12421  MaxTextExtent);
12422  else
12423  {
12424  char
12425  path[MaxTextExtent];
12426 
12427  int
12428  status;
12429 
12430  GetPathComponent(image->filename,HeadPath,path);
12431  GetPathComponent(image->filename,TailPath,filename);
12432  if (*path != '\0')
12433  {
12434  status=chdir(path);
12435  if (status == -1)
12436  (void) ThrowMagickException(&image->exception,GetMagickModule(),
12437  FileOpenError,"UnableToOpenFile","%s",path);
12438  }
12439  }
12440  XFileBrowserWidget(display,windows,"Save",filename);
12441  if (*filename == '\0')
12442  return(MagickTrue);
12443  if (IsPathAccessible(filename) != MagickFalse)
12444  {
12445  int
12446  status;
12447 
12448  /*
12449  File exists-- seek user's permission before overwriting.
12450  */
12451  status=XConfirmWidget(display,windows,"Overwrite",filename);
12452  if (status <= 0)
12453  return(MagickTrue);
12454  }
12455  image_info=CloneImageInfo(resource_info->image_info);
12456  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12457  (void) SetImageInfo(image_info,1,&image->exception);
12458  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12459  (LocaleCompare(image_info->magick,"JPG") == 0))
12460  {
12461  char
12462  quality[MaxTextExtent];
12463 
12464  int
12465  status;
12466 
12467  /*
12468  Request JPEG quality from user.
12469  */
12470  (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12471  image->quality);
12472  status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12473  quality);
12474  if (*quality == '\0')
12475  return(MagickTrue);
12476  image->quality=StringToUnsignedLong(quality);
12477  image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12478  }
12479  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12480  (LocaleCompare(image_info->magick,"PDF") == 0) ||
12481  (LocaleCompare(image_info->magick,"PS") == 0) ||
12482  (LocaleCompare(image_info->magick,"PS2") == 0))
12483  {
12484  char
12485  geometry[MaxTextExtent];
12486 
12487  const char
12488  *const PageSizes[] =
12489  {
12490  "Letter",
12491  "Tabloid",
12492  "Ledger",
12493  "Legal",
12494  "Statement",
12495  "Executive",
12496  "A3",
12497  "A4",
12498  "A5",
12499  "B4",
12500  "B5",
12501  "Folio",
12502  "Quarto",
12503  "10x14",
12504  (char *) NULL
12505  };
12506 
12507  /*
12508  Request page geometry from user.
12509  */
12510  (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12511  if (LocaleCompare(image_info->magick,"PDF") == 0)
12512  (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12513  if (image_info->page != (char *) NULL)
12514  (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12515  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12516  "Select page geometry:",geometry);
12517  if (*geometry != '\0')
12518  image_info->page=GetPageGeometry(geometry);
12519  }
12520  /*
12521  Apply image transforms.
12522  */
12523  XSetCursorState(display,windows,MagickTrue);
12524  XCheckRefreshWindows(display,windows);
12525  save_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12526  if (save_image == (Image *) NULL)
12527  return(MagickFalse);
12528  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12529  windows->image.ximage->width,windows->image.ximage->height);
12530  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12531  /*
12532  Write image.
12533  */
12534  (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12535  status=WriteImage(image_info,save_image);
12536  if (status != MagickFalse)
12537  image->taint=MagickFalse;
12538  save_image=DestroyImage(save_image);
12539  image_info=DestroyImageInfo(image_info);
12540  XSetCursorState(display,windows,MagickFalse);
12541  return(status != 0 ? MagickTrue : MagickFalse);
12542 }
12543 ␌
12544 /*
12545 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12546 % %
12547 % %
12548 % %
12549 + X S c r e e n E v e n t %
12550 % %
12551 % %
12552 % %
12553 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12554 %
12555 % XScreenEvent() handles global events associated with the Pan and Magnify
12556 % windows.
12557 %
12558 % The format of the XScreenEvent function is:
12559 %
12560 % void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12561 %
12562 % A description of each parameter follows:
12563 %
12564 % o display: Specifies a pointer to the Display structure; returned from
12565 % XOpenDisplay.
12566 %
12567 % o windows: Specifies a pointer to a XWindows structure.
12568 %
12569 % o event: Specifies a pointer to a X11 XEvent structure.
12570 %
12571 %
12572 */
12573 
12574 #if defined(__cplusplus) || defined(c_plusplus)
12575 extern "C" {
12576 #endif
12577 
12578 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12579 {
12580  XWindows
12581  *windows;
12582 
12583  magick_unreferenced(display);
12584 
12585  windows=(XWindows *) data;
12586  if ((event->type == ClientMessage) &&
12587  (event->xclient.window == windows->image.id))
12588  return(MagickFalse);
12589  return(MagickTrue);
12590 }
12591 
12592 #if defined(__cplusplus) || defined(c_plusplus)
12593 }
12594 #endif
12595 
12596 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12597 {
12598  int
12599  x,
12600  y;
12601 
12602  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12603  if (event->xany.window == windows->command.id)
12604  return;
12605  switch (event->type)
12606  {
12607  case ButtonPress:
12608  case ButtonRelease:
12609  {
12610  if ((event->xbutton.button == Button3) &&
12611  (event->xbutton.state & Mod1Mask))
12612  {
12613  /*
12614  Convert Alt-Button3 to Button2.
12615  */
12616  event->xbutton.button=Button2;
12617  event->xbutton.state&=(~Mod1Mask);
12618  }
12619  if (event->xbutton.window == windows->backdrop.id)
12620  {
12621  (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12622  event->xbutton.time);
12623  break;
12624  }
12625  if (event->xbutton.window == windows->pan.id)
12626  {
12627  XPanImage(display,windows,event);
12628  break;
12629  }
12630  if (event->xbutton.window == windows->image.id)
12631  if (event->xbutton.button == Button2)
12632  {
12633  /*
12634  Update magnified image.
12635  */
12636  x=event->xbutton.x;
12637  y=event->xbutton.y;
12638  if (x < 0)
12639  x=0;
12640  else
12641  if (x >= (int) windows->image.width)
12642  x=(int) (windows->image.width-1);
12643  windows->magnify.x=(int) windows->image.x+x;
12644  if (y < 0)
12645  y=0;
12646  else
12647  if (y >= (int) windows->image.height)
12648  y=(int) (windows->image.height-1);
12649  windows->magnify.y=windows->image.y+y;
12650  if (windows->magnify.mapped == MagickFalse)
12651  (void) XMapRaised(display,windows->magnify.id);
12652  XMakeMagnifyImage(display,windows);
12653  if (event->type == ButtonRelease)
12654  (void) XWithdrawWindow(display,windows->info.id,
12655  windows->info.screen);
12656  break;
12657  }
12658  break;
12659  }
12660  case ClientMessage:
12661  {
12662  /*
12663  If client window delete message, exit.
12664  */
12665  if (event->xclient.message_type != windows->wm_protocols)
12666  break;
12667  if (*event->xclient.data.l != (long) windows->wm_delete_window)
12668  break;
12669  if (event->xclient.window == windows->magnify.id)
12670  {
12671  (void) XWithdrawWindow(display,windows->magnify.id,
12672  windows->magnify.screen);
12673  break;
12674  }
12675  break;
12676  }
12677  case ConfigureNotify:
12678  {
12679  if (event->xconfigure.window == windows->magnify.id)
12680  {
12681  unsigned int
12682  magnify;
12683 
12684  /*
12685  Magnify window has a new configuration.
12686  */
12687  windows->magnify.width=(unsigned int) event->xconfigure.width;
12688  windows->magnify.height=(unsigned int) event->xconfigure.height;
12689  if (windows->magnify.mapped == MagickFalse)
12690  break;
12691  magnify=1;
12692  while ((int) magnify <= event->xconfigure.width)
12693  magnify<<=1;
12694  while ((int) magnify <= event->xconfigure.height)
12695  magnify<<=1;
12696  magnify>>=1;
12697  if (((int) magnify != event->xconfigure.width) ||
12698  ((int) magnify != event->xconfigure.height))
12699  {
12700  XWindowChanges
12701  window_changes;
12702 
12703  window_changes.width=(int) magnify;
12704  window_changes.height=(int) magnify;
12705  (void) XReconfigureWMWindow(display,windows->magnify.id,
12706  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12707  &window_changes);
12708  break;
12709  }
12710  XMakeMagnifyImage(display,windows);
12711  break;
12712  }
12713  break;
12714  }
12715  case Expose:
12716  {
12717  if (event->xexpose.window == windows->image.id)
12718  {
12719  XRefreshWindow(display,&windows->image,event);
12720  break;
12721  }
12722  if (event->xexpose.window == windows->pan.id)
12723  if (event->xexpose.count == 0)
12724  {
12725  XDrawPanRectangle(display,windows);
12726  break;
12727  }
12728  if (event->xexpose.window == windows->magnify.id)
12729  if (event->xexpose.count == 0)
12730  {
12731  XMakeMagnifyImage(display,windows);
12732  break;
12733  }
12734  break;
12735  }
12736  case KeyPress:
12737  {
12738  char
12739  command[MaxTextExtent];
12740 
12741  KeySym
12742  key_symbol;
12743 
12744  if (event->xkey.window != windows->magnify.id)
12745  break;
12746  /*
12747  Respond to a user key press.
12748  */
12749  (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12750  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12751  XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12752  break;
12753  }
12754  case MapNotify:
12755  {
12756  if (event->xmap.window == windows->magnify.id)
12757  {
12758  windows->magnify.mapped=MagickTrue;
12759  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12760  break;
12761  }
12762  if (event->xmap.window == windows->info.id)
12763  {
12764  windows->info.mapped=MagickTrue;
12765  break;
12766  }
12767  break;
12768  }
12769  case MotionNotify:
12770  {
12771  while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12772  if (event->xmotion.window == windows->image.id)
12773  if (windows->magnify.mapped != MagickFalse)
12774  {
12775  /*
12776  Update magnified image.
12777  */
12778  x=event->xmotion.x;
12779  y=event->xmotion.y;
12780  if (x < 0)
12781  x=0;
12782  else
12783  if (x >= (int) windows->image.width)
12784  x=(int) (windows->image.width-1);
12785  windows->magnify.x=(int) windows->image.x+x;
12786  if (y < 0)
12787  y=0;
12788  else
12789  if (y >= (int) windows->image.height)
12790  y=(int) (windows->image.height-1);
12791  windows->magnify.y=windows->image.y+y;
12792  XMakeMagnifyImage(display,windows);
12793  }
12794  break;
12795  }
12796  case UnmapNotify:
12797  {
12798  if (event->xunmap.window == windows->magnify.id)
12799  {
12800  windows->magnify.mapped=MagickFalse;
12801  break;
12802  }
12803  if (event->xunmap.window == windows->info.id)
12804  {
12805  windows->info.mapped=MagickFalse;
12806  break;
12807  }
12808  break;
12809  }
12810  default:
12811  break;
12812  }
12813 }
12814 ␌
12815 /*
12816 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12817 % %
12818 % %
12819 % %
12820 + X S e t C r o p G e o m e t r y %
12821 % %
12822 % %
12823 % %
12824 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12825 %
12826 % XSetCropGeometry() accepts a cropping geometry relative to the Image window
12827 % and translates it to a cropping geometry relative to the image.
12828 %
12829 % The format of the XSetCropGeometry method is:
12830 %
12831 % void XSetCropGeometry(Display *display,XWindows *windows,
12832 % RectangleInfo *crop_info,Image *image)
12833 %
12834 % A description of each parameter follows:
12835 %
12836 % o display: Specifies a connection to an X server; returned from
12837 % XOpenDisplay.
12838 %
12839 % o windows: Specifies a pointer to a XWindows structure.
12840 %
12841 % o crop_info: A pointer to a RectangleInfo that defines a region of the
12842 % Image window to crop.
12843 %
12844 % o image: the image.
12845 %
12846 */
12847 static void XSetCropGeometry(Display *display,XWindows *windows,
12848  RectangleInfo *crop_info,Image *image)
12849 {
12850  char
12851  text[MaxTextExtent];
12852 
12853  int
12854  x,
12855  y;
12856 
12857  MagickRealType
12858  scale_factor;
12859 
12860  unsigned int
12861  height,
12862  width;
12863 
12864  if (windows->info.mapped != MagickFalse)
12865  {
12866  /*
12867  Display info on cropping rectangle.
12868  */
12869  (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12870  (double) crop_info->width,(double) crop_info->height,(double)
12871  crop_info->x,(double) crop_info->y);
12872  XInfoWidget(display,windows,text);
12873  }
12874  /*
12875  Cropping geometry is relative to any previous crop geometry.
12876  */
12877  x=0;
12878  y=0;
12879  width=(unsigned int) image->columns;
12880  height=(unsigned int) image->rows;
12881  if (windows->image.crop_geometry != (char *) NULL)
12882  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12883  else
12884  windows->image.crop_geometry=AcquireString((char *) NULL);
12885  /*
12886  Define the crop geometry string from the cropping rectangle.
12887  */
12888  scale_factor=(MagickRealType) width/windows->image.ximage->width;
12889  if (crop_info->x > 0)
12890  x+=(int) (scale_factor*crop_info->x+0.5);
12891  width=(unsigned int) (scale_factor*crop_info->width+0.5);
12892  if (width == 0)
12893  width=1;
12894  scale_factor=(MagickRealType) height/windows->image.ximage->height;
12895  if (crop_info->y > 0)
12896  y+=(int) (scale_factor*crop_info->y+0.5);
12897  height=(unsigned int) (scale_factor*crop_info->height+0.5);
12898  if (height == 0)
12899  height=1;
12900  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12901  "%ux%u%+d%+d",width,height,x,y);
12902 }
12903 ␌
12904 /*
12905 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12906 % %
12907 % %
12908 % %
12909 + X T i l e I m a g e %
12910 % %
12911 % %
12912 % %
12913 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12914 %
12915 % XTileImage() loads or deletes a selected tile from a visual image directory.
12916 % The load or delete command is chosen from a menu.
12917 %
12918 % The format of the XTileImage method is:
12919 %
12920 % Image *XTileImage(Display *display,XResourceInfo *resource_info,
12921 % XWindows *windows,Image *image,XEvent *event)
12922 %
12923 % A description of each parameter follows:
12924 %
12925 % o tile_image: XTileImage reads or deletes the tile image
12926 % and returns it. A null image is returned if an error occurs.
12927 %
12928 % o display: Specifies a connection to an X server; returned from
12929 % XOpenDisplay.
12930 %
12931 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12932 %
12933 % o windows: Specifies a pointer to a XWindows structure.
12934 %
12935 % o image: the image; returned from ReadImage.
12936 %
12937 % o event: Specifies a pointer to a XEvent structure. If it is NULL,
12938 % the entire image is refreshed.
12939 %
12940 */
12941 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
12942  XWindows *windows,Image *image,XEvent *event)
12943 {
12944  const char
12945  *const VerbMenu[] =
12946  {
12947  "Load",
12948  "Next",
12949  "Former",
12950  "Delete",
12951  "Update",
12952  (char *) NULL,
12953  };
12954 
12955  static const ModeType
12956  TileCommands[] =
12957  {
12958  TileLoadCommand,
12959  TileNextCommand,
12960  TileFormerCommand,
12961  TileDeleteCommand,
12962  TileUpdateCommand
12963  };
12964 
12965  char
12966  command[MaxTextExtent],
12967  filename[MaxTextExtent];
12968 
12969  Image
12970  *tile_image;
12971 
12972  int
12973  id,
12974  status,
12975  tile,
12976  x,
12977  y;
12978 
12979  MagickRealType
12980  scale_factor;
12981 
12982  char
12983  *p,
12984  *q;
12985 
12986  int
12987  i;
12988 
12989  unsigned int
12990  height,
12991  width;
12992 
12993  /*
12994  Tile image is relative to montage image configuration.
12995  */
12996  x=0;
12997  y=0;
12998  width=(unsigned int) image->columns;
12999  height=(unsigned int) image->rows;
13000  if (windows->image.crop_geometry != (char *) NULL)
13001  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13002  scale_factor=(MagickRealType) width/windows->image.ximage->width;
13003  event->xbutton.x+=windows->image.x;
13004  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13005  scale_factor=(MagickRealType) height/windows->image.ximage->height;
13006  event->xbutton.y+=windows->image.y;
13007  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13008  /*
13009  Determine size and location of each tile in the visual image directory.
13010  */
13011  width=(unsigned int) image->columns;
13012  height=(unsigned int) image->rows;
13013  x=0;
13014  y=0;
13015  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13016  tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13017  (event->xbutton.x-x)/width;
13018  if (tile < 0)
13019  {
13020  /*
13021  Button press is outside any tile.
13022  */
13023  (void) XBell(display,0);
13024  return((Image *) NULL);
13025  }
13026  /*
13027  Determine file name from the tile directory.
13028  */
13029  p=image->directory;
13030  for (i=tile; (i != 0) && (*p != '\0'); )
13031  {
13032  if (*p == '\xff')
13033  i--;
13034  p++;
13035  }
13036  if (*p == '\0')
13037  {
13038  /*
13039  Button press is outside any tile.
13040  */
13041  (void) XBell(display,0);
13042  return((Image *) NULL);
13043  }
13044  /*
13045  Select a command from the pop-up menu.
13046  */
13047  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13048  if (id < 0)
13049  return((Image *) NULL);
13050  q=p;
13051  while ((*q != '\xff') && (*q != '\0'))
13052  q++;
13053  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13054  /*
13055  Perform command for the selected tile.
13056  */
13057  XSetCursorState(display,windows,MagickTrue);
13058  XCheckRefreshWindows(display,windows);
13059  tile_image=NewImageList();
13060  switch (TileCommands[id])
13061  {
13062  case TileLoadCommand:
13063  {
13064  /*
13065  Load tile image.
13066  */
13067  XCheckRefreshWindows(display,windows);
13068  (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13069  MaxTextExtent);
13070  (void) CopyMagickString(resource_info->image_info->filename,filename,
13071  MaxTextExtent);
13072  tile_image=ReadImage(resource_info->image_info,&image->exception);
13073  CatchException(&image->exception);
13074  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13075  break;
13076  }
13077  case TileNextCommand:
13078  {
13079  /*
13080  Display next image.
13081  */
13082  XClientMessage(display,windows->image.id,windows->im_protocols,
13083  windows->im_next_image,CurrentTime);
13084  break;
13085  }
13086  case TileFormerCommand:
13087  {
13088  /*
13089  Display former image.
13090  */
13091  XClientMessage(display,windows->image.id,windows->im_protocols,
13092  windows->im_former_image,CurrentTime);
13093  break;
13094  }
13095  case TileDeleteCommand:
13096  {
13097  /*
13098  Delete tile image.
13099  */
13100  if (IsPathAccessible(filename) == MagickFalse)
13101  {
13102  XNoticeWidget(display,windows,"Image file does not exist:",filename);
13103  break;
13104  }
13105  status=XConfirmWidget(display,windows,"Really delete tile",filename);
13106  if (status <= 0)
13107  break;
13108  status=ShredFile(filename);
13109  status|=remove_utf8(filename);
13110  if (status != MagickFalse)
13111  {
13112  XNoticeWidget(display,windows,"Unable to delete image file:",
13113  filename);
13114  break;
13115  }
13116  magick_fallthrough;
13117  }
13118  case TileUpdateCommand:
13119  {
13121  *exception;
13122 
13123  int
13124  x_offset,
13125  y_offset;
13126 
13127  PixelPacket
13128  pixel;
13129 
13130  int
13131  j;
13132 
13133  PixelPacket
13134  *s;
13135 
13136  /*
13137  Ensure all the images exist.
13138  */
13139  tile=0;
13140  for (p=image->directory; *p != '\0'; p++)
13141  {
13142  CacheView
13143  *image_view;
13144 
13145  q=p;
13146  while ((*q != '\xff') && (*q != '\0'))
13147  q++;
13148  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13149  p=q;
13150  if (IsPathAccessible(filename) != MagickFalse)
13151  {
13152  tile++;
13153  continue;
13154  }
13155  /*
13156  Overwrite tile with background color.
13157  */
13158  x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13159  y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13160  exception=(&image->exception);
13161  image_view=AcquireAuthenticCacheView(image,exception);
13162  (void) GetOneCacheViewVirtualPixel(image_view,0,0,&pixel,exception);
13163  for (i=0; i < (int) height; i++)
13164  {
13165  s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13166  y_offset+i,width,1,exception);
13167  if (s == (PixelPacket *) NULL)
13168  break;
13169  for (j=0; j < (int) width; j++)
13170  *s++=pixel;
13171  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13172  break;
13173  }
13174  image_view=DestroyCacheView(image_view);
13175  tile++;
13176  }
13177  windows->image.window_changes.width=(int) image->columns;
13178  windows->image.window_changes.height=(int) image->rows;
13179  XConfigureImageColormap(display,resource_info,windows,image);
13180  (void) XConfigureImage(display,resource_info,windows,image);
13181  break;
13182  }
13183  default:
13184  break;
13185  }
13186  XSetCursorState(display,windows,MagickFalse);
13187  return(tile_image);
13188 }
13189 ␌
13190 /*
13191 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13192 % %
13193 % %
13194 % %
13195 + X T r a n s l a t e I m a g e %
13196 % %
13197 % %
13198 % %
13199 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13200 %
13201 % XTranslateImage() translates the image within an Image window by one pixel
13202 % as specified by the key symbol. If the image has a `montage string the
13203 % translation is respect to the width and height contained within the string.
13204 %
13205 % The format of the XTranslateImage method is:
13206 %
13207 % void XTranslateImage(Display *display,XWindows *windows,
13208 % Image *image,const KeySym key_symbol)
13209 %
13210 % A description of each parameter follows:
13211 %
13212 % o display: Specifies a connection to an X server; returned from
13213 % XOpenDisplay.
13214 %
13215 % o windows: Specifies a pointer to a XWindows structure.
13216 %
13217 % o image: the image.
13218 %
13219 % o key_symbol: Specifies a KeySym which indicates which side of the image
13220 % to trim.
13221 %
13222 */
13223 static void XTranslateImage(Display *display,XWindows *windows,
13224  Image *image,const KeySym key_symbol)
13225 {
13226  char
13227  text[MaxTextExtent];
13228 
13229  int
13230  x,
13231  y;
13232 
13233  unsigned int
13234  x_offset,
13235  y_offset;
13236 
13237  /*
13238  User specified a pan position offset.
13239  */
13240  x_offset=windows->image.width;
13241  y_offset=windows->image.height;
13242  if (image->montage != (char *) NULL)
13243  (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13244  switch ((int) key_symbol)
13245  {
13246  case XK_Home:
13247  case XK_KP_Home:
13248  {
13249  windows->image.x=(int) windows->image.width/2;
13250  windows->image.y=(int) windows->image.height/2;
13251  break;
13252  }
13253  case XK_Left:
13254  case XK_KP_Left:
13255  {
13256  windows->image.x-=x_offset;
13257  break;
13258  }
13259  case XK_Next:
13260  case XK_Up:
13261  case XK_KP_Up:
13262  {
13263  windows->image.y-=y_offset;
13264  break;
13265  }
13266  case XK_Right:
13267  case XK_KP_Right:
13268  {
13269  windows->image.x+=x_offset;
13270  break;
13271  }
13272  case XK_Prior:
13273  case XK_Down:
13274  case XK_KP_Down:
13275  {
13276  windows->image.y+=y_offset;
13277  break;
13278  }
13279  default:
13280  return;
13281  }
13282  /*
13283  Check boundary conditions.
13284  */
13285  if (windows->image.x < 0)
13286  windows->image.x=0;
13287  else
13288  if ((int) (windows->image.x+windows->image.width) >
13289  windows->image.ximage->width)
13290  windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13291  if (windows->image.y < 0)
13292  windows->image.y=0;
13293  else
13294  if ((int) (windows->image.y+windows->image.height) >
13295  windows->image.ximage->height)
13296  windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13297  /*
13298  Refresh Image window.
13299  */
13300  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13301  windows->image.width,windows->image.height,windows->image.x,
13302  windows->image.y);
13303  XInfoWidget(display,windows,text);
13304  XCheckRefreshWindows(display,windows);
13305  XDrawPanRectangle(display,windows);
13306  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13307  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13308 }
13309 ␌
13310 /*
13311 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13312 % %
13313 % %
13314 % %
13315 + X T r i m I m a g e %
13316 % %
13317 % %
13318 % %
13319 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13320 %
13321 % XTrimImage() trims the edges from the Image window.
13322 %
13323 % The format of the XTrimImage method is:
13324 %
13325 % MagickBooleanType XTrimImage(Display *display,
13326 % XResourceInfo *resource_info,XWindows *windows,Image *image)
13327 %
13328 % A description of each parameter follows:
13329 %
13330 % o display: Specifies a connection to an X server; returned from
13331 % XOpenDisplay.
13332 %
13333 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13334 %
13335 % o windows: Specifies a pointer to a XWindows structure.
13336 %
13337 % o image: the image.
13338 %
13339 */
13340 static MagickBooleanType XTrimImage(Display *display,
13341  XResourceInfo *resource_info,XWindows *windows,Image *image)
13342 {
13344  trim_info;
13345 
13346  int
13347  x,
13348  y;
13349 
13350  size_t
13351  background,
13352  pixel;
13353 
13354  /*
13355  Trim edges from image.
13356  */
13357  XSetCursorState(display,windows,MagickTrue);
13358  XCheckRefreshWindows(display,windows);
13359  /*
13360  Crop the left edge.
13361  */
13362  background=XGetPixel(windows->image.ximage,0,0);
13363  trim_info.width=(size_t) windows->image.ximage->width;
13364  for (x=0; x < windows->image.ximage->width; x++)
13365  {
13366  for (y=0; y < windows->image.ximage->height; y++)
13367  {
13368  pixel=XGetPixel(windows->image.ximage,x,y);
13369  if (pixel != background)
13370  break;
13371  }
13372  if (y < windows->image.ximage->height)
13373  break;
13374  }
13375  trim_info.x=(ssize_t) x;
13376  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13377  {
13378  XSetCursorState(display,windows,MagickFalse);
13379  return(MagickFalse);
13380  }
13381  /*
13382  Crop the right edge.
13383  */
13384  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13385  for (x=windows->image.ximage->width-1; x != 0; x--)
13386  {
13387  for (y=0; y < windows->image.ximage->height; y++)
13388  {
13389  pixel=XGetPixel(windows->image.ximage,x,y);
13390  if (pixel != background)
13391  break;
13392  }
13393  if (y < windows->image.ximage->height)
13394  break;
13395  }
13396  trim_info.width=(size_t) (x-trim_info.x+1);
13397  /*
13398  Crop the top edge.
13399  */
13400  background=XGetPixel(windows->image.ximage,0,0);
13401  trim_info.height=(size_t) windows->image.ximage->height;
13402  for (y=0; y < windows->image.ximage->height; y++)
13403  {
13404  for (x=0; x < windows->image.ximage->width; x++)
13405  {
13406  pixel=XGetPixel(windows->image.ximage,x,y);
13407  if (pixel != background)
13408  break;
13409  }
13410  if (x < windows->image.ximage->width)
13411  break;
13412  }
13413  trim_info.y=(ssize_t) y;
13414  /*
13415  Crop the bottom edge.
13416  */
13417  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13418  for (y=windows->image.ximage->height-1; y != 0; y--)
13419  {
13420  for (x=0; x < windows->image.ximage->width; x++)
13421  {
13422  pixel=XGetPixel(windows->image.ximage,x,y);
13423  if (pixel != background)
13424  break;
13425  }
13426  if (x < windows->image.ximage->width)
13427  break;
13428  }
13429  trim_info.height=(size_t) y-trim_info.y+1;
13430  if (((unsigned int) trim_info.width != windows->image.width) ||
13431  ((unsigned int) trim_info.height != windows->image.height))
13432  {
13433  /*
13434  Reconfigure Image window as defined by the trimming rectangle.
13435  */
13436  XSetCropGeometry(display,windows,&trim_info,image);
13437  windows->image.window_changes.width=(int) trim_info.width;
13438  windows->image.window_changes.height=(int) trim_info.height;
13439  (void) XConfigureImage(display,resource_info,windows,image);
13440  }
13441  XSetCursorState(display,windows,MagickFalse);
13442  return(MagickTrue);
13443 }
13444 ␌
13445 /*
13446 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13447 % %
13448 % %
13449 % %
13450 + X V i s u a l D i r e c t o r y I m a g e %
13451 % %
13452 % %
13453 % %
13454 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13455 %
13456 % XVisualDirectoryImage() creates a Visual Image Directory.
13457 %
13458 % The format of the XVisualDirectoryImage method is:
13459 %
13460 % Image *XVisualDirectoryImage(Display *display,
13461 % XResourceInfo *resource_info,XWindows *windows)
13462 %
13463 % A description of each parameter follows:
13464 %
13465 % o nexus: Method XVisualDirectoryImage returns a visual image
13466 % directory if it can be created successfully. Otherwise a null image
13467 % is returned.
13468 %
13469 % o display: Specifies a connection to an X server; returned from
13470 % XOpenDisplay.
13471 %
13472 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13473 %
13474 % o windows: Specifies a pointer to a XWindows structure.
13475 %
13476 */
13477 static Image *XVisualDirectoryImage(Display *display,
13478  XResourceInfo *resource_info,XWindows *windows)
13479 {
13480 #define TileImageTag "Scale/Image"
13481 #define XClientName "montage"
13482 
13483  char
13484  **filelist;
13485 
13487  *exception;
13488 
13489  Image
13490  *images,
13491  *montage_image,
13492  *next_image,
13493  *thumbnail_image;
13494 
13495  ImageInfo
13496  *read_info;
13497 
13498  int
13499  number_files;
13500 
13501  MagickBooleanType
13502  backdrop;
13503 
13504  MagickStatusType
13505  status;
13506 
13507  MontageInfo
13508  *montage_info;
13509 
13511  geometry;
13512 
13513  int
13514  i;
13515 
13516  static char
13517  filename[MaxTextExtent] = "\0",
13518  filenames[MaxTextExtent] = "*";
13519 
13520  XResourceInfo
13521  background_resources;
13522 
13523  /*
13524  Request file name from user.
13525  */
13526  XFileBrowserWidget(display,windows,"Directory",filenames);
13527  if (*filenames == '\0')
13528  return((Image *) NULL);
13529  /*
13530  Expand the filenames.
13531  */
13532  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13533  if (filelist == (char **) NULL)
13534  {
13535  ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13536  filenames);
13537  return((Image *) NULL);
13538  }
13539  number_files=1;
13540  filelist[0]=filenames;
13541  status=ExpandFilenames(&number_files,&filelist);
13542  if ((status == MagickFalse) || (number_files == 0))
13543  {
13544  if (number_files == 0)
13545  ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13546  else
13547  ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13548  filenames);
13549  return((Image *) NULL);
13550  }
13551  /*
13552  Set image background resources.
13553  */
13554  background_resources=(*resource_info);
13555  background_resources.window_id=AcquireString("");
13556  (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13557  "0x%lx",windows->image.id);
13558  background_resources.backdrop=MagickTrue;
13559  /*
13560  Read each image and convert them to a tile.
13561  */
13562  backdrop=(windows->visual_info->klass == TrueColor) ||
13563  (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13564  read_info=CloneImageInfo(resource_info->image_info);
13565  (void) SetImageOption(read_info,"jpeg:size","120x120");
13566  (void) CloneString(&read_info->size,DefaultTileGeometry);
13567  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13568  (void *) NULL);
13569  images=NewImageList();
13570  exception=AcquireExceptionInfo();
13571  XSetCursorState(display,windows,MagickTrue);
13572  XCheckRefreshWindows(display,windows);
13573  for (i=0; i < (int) number_files; i++)
13574  {
13575  (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13576  filelist[i]=DestroyString(filelist[i]);
13577  *read_info->magick='\0';
13578  next_image=ReadImage(read_info,exception);
13579  CatchException(exception);
13580  if (next_image != (Image *) NULL)
13581  {
13582  (void) DeleteImageProperty(next_image,"label");
13583  (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13584  read_info,next_image,DefaultTileLabel));
13585  (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13586  exception);
13587  thumbnail_image=ThumbnailImage(next_image,geometry.width,
13588  geometry.height,exception);
13589  if (thumbnail_image != (Image *) NULL)
13590  {
13591  next_image=DestroyImage(next_image);
13592  next_image=thumbnail_image;
13593  }
13594  if (backdrop)
13595  {
13596  (void) XDisplayBackgroundImage(display,&background_resources,
13597  next_image);
13598  XSetCursorState(display,windows,MagickTrue);
13599  }
13600  AppendImageToList(&images,next_image);
13601  if (images->progress_monitor != (MagickProgressMonitor) NULL)
13602  {
13603  MagickBooleanType
13604  proceed;
13605 
13606  proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13607  (MagickSizeType) number_files);
13608  if (proceed == MagickFalse)
13609  break;
13610  }
13611  }
13612  }
13613  exception=DestroyExceptionInfo(exception);
13614  filelist=(char **) RelinquishMagickMemory(filelist);
13615  if (images == (Image *) NULL)
13616  {
13617  read_info=DestroyImageInfo(read_info);
13618  XSetCursorState(display,windows,MagickFalse);
13619  ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13620  return((Image *) NULL);
13621  }
13622  /*
13623  Create the Visual Image Directory.
13624  */
13625  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13626  montage_info->pointsize=10;
13627  if (resource_info->font != (char *) NULL)
13628  (void) CloneString(&montage_info->font,resource_info->font);
13629  (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13630  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13631  images),&images->exception);
13632  images=DestroyImageList(images);
13633  montage_info=DestroyMontageInfo(montage_info);
13634  read_info=DestroyImageInfo(read_info);
13635  XSetCursorState(display,windows,MagickFalse);
13636  if (montage_image == (Image *) NULL)
13637  return(montage_image);
13638  XClientMessage(display,windows->image.id,windows->im_protocols,
13639  windows->im_next_image,CurrentTime);
13640  return(montage_image);
13641 }
13642 ␌
13643 /*
13644 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13645 % %
13646 % %
13647 % %
13648 % X D i s p l a y B a c k g r o u n d I m a g e %
13649 % %
13650 % %
13651 % %
13652 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13653 %
13654 % XDisplayBackgroundImage() displays an image in the background of a window.
13655 %
13656 % The format of the XDisplayBackgroundImage method is:
13657 %
13658 % MagickBooleanType XDisplayBackgroundImage(Display *display,
13659 % XResourceInfo *resource_info,Image *image)
13660 %
13661 % A description of each parameter follows:
13662 %
13663 % o display: Specifies a connection to an X server; returned from
13664 % XOpenDisplay.
13665 %
13666 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13667 %
13668 % o image: the image.
13669 %
13670 */
13671 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13672  XResourceInfo *resource_info,Image *image)
13673 {
13674  char
13675  geometry[MaxTextExtent],
13676  visual_type[MaxTextExtent];
13677 
13678  int
13679  height,
13680  status,
13681  width;
13682 
13684  geometry_info;
13685 
13686  static XPixelInfo
13687  pixel;
13688 
13689  static XStandardColormap
13690  *map_info;
13691 
13692  static XVisualInfo
13693  *visual_info = (XVisualInfo *) NULL;
13694 
13695  static XWindowInfo
13696  window_info;
13697 
13698  size_t
13699  delay;
13700 
13701  Window
13702  root_window;
13703 
13704  XGCValues
13705  context_values;
13706 
13707  XResourceInfo
13708  resources;
13709 
13710  XWindowAttributes
13711  window_attributes;
13712 
13713  /*
13714  Determine target window.
13715  */
13716  assert(image != (Image *) NULL);
13717  assert(image->signature == MagickCoreSignature);
13718  if (IsEventLogging() != MagickFalse)
13719  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13720  resources=(*resource_info);
13721  window_info.id=(Window) NULL;
13722  root_window=XRootWindow(display,XDefaultScreen(display));
13723  if (LocaleCompare(resources.window_id,"root") == 0)
13724  window_info.id=root_window;
13725  else
13726  {
13727  if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13728  window_info.id=XWindowByID(display,root_window,
13729  (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13730  if (window_info.id == (Window) NULL)
13731  window_info.id=XWindowByName(display,root_window,resources.window_id);
13732  }
13733  if (window_info.id == (Window) NULL)
13734  {
13735  ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13736  resources.window_id);
13737  }
13738  /*
13739  Determine window visual id.
13740  */
13741  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13742  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13743  (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13744  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13745  if (status != 0)
13746  (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13747  XVisualIDFromVisual(window_attributes.visual));
13748  if (visual_info == (XVisualInfo *) NULL)
13749  {
13750  /*
13751  Allocate standard colormap.
13752  */
13753  map_info=XAllocStandardColormap();
13754  if (map_info == (XStandardColormap *) NULL)
13755  ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13756  image->filename);
13757  map_info->colormap=(Colormap) NULL;
13758  pixel.pixels=(unsigned long *) NULL;
13759  /*
13760  Initialize visual info.
13761  */
13762  resources.map_type=(char *) NULL;
13763  resources.visual_type=visual_type;
13764  visual_info=XBestVisualInfo(display,map_info,&resources);
13765  if (visual_info == (XVisualInfo *) NULL)
13766  ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13767  resources.visual_type);
13768  /*
13769  Initialize window info.
13770  */
13771  window_info.ximage=(XImage *) NULL;
13772  window_info.matte_image=(XImage *) NULL;
13773  window_info.pixmap=(Pixmap) NULL;
13774  window_info.matte_pixmap=(Pixmap) NULL;
13775  }
13776  /*
13777  Free previous root colors.
13778  */
13779  if (window_info.id == root_window)
13780  (void) XDestroyWindowColors(display,root_window);
13781  /*
13782  Initialize Standard Colormap.
13783  */
13784  resources.colormap=SharedColormap;
13785  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13786  /*
13787  Graphic context superclass.
13788  */
13789  context_values.background=pixel.foreground_color.pixel;
13790  context_values.foreground=pixel.background_color.pixel;
13791  pixel.annotate_context=XCreateGC(display,window_info.id,
13792  (size_t) (GCBackground | GCForeground),&context_values);
13793  if (pixel.annotate_context == (GC) NULL)
13794  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13795  image->filename);
13796  /*
13797  Initialize Image window attributes.
13798  */
13799  window_info.name=AcquireString("\0");
13800  window_info.icon_name=AcquireString("\0");
13801  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13802  &resources,&window_info);
13803  /*
13804  Create the X image.
13805  */
13806  window_info.width=(unsigned int) image->columns;
13807  window_info.height=(unsigned int) image->rows;
13808  if ((image->columns != window_info.width) ||
13809  (image->rows != window_info.height))
13810  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13811  image->filename);
13812  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13813  window_attributes.width,window_attributes.height);
13814  geometry_info.width=window_info.width;
13815  geometry_info.height=window_info.height;
13816  geometry_info.x=(ssize_t) window_info.x;
13817  geometry_info.y=(ssize_t) window_info.y;
13818  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13819  &geometry_info.width,&geometry_info.height);
13820  window_info.width=(unsigned int) geometry_info.width;
13821  window_info.height=(unsigned int) geometry_info.height;
13822  window_info.x=(int) geometry_info.x;
13823  window_info.y=(int) geometry_info.y;
13824  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13825  window_info.height);
13826  if (status == MagickFalse)
13827  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13828  image->filename);
13829  window_info.x=0;
13830  window_info.y=0;
13831  if (resource_info->debug != MagickFalse)
13832  {
13833  (void) LogMagickEvent(X11Event,GetMagickModule(),
13834  "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13835  (double) image->columns,(double) image->rows);
13836  if (image->colors != 0)
13837  (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13838  image->colors);
13839  (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13840  }
13841  /*
13842  Adjust image dimensions as specified by backdrop or geometry options.
13843  */
13844  width=(int) window_info.width;
13845  height=(int) window_info.height;
13846  if (resources.backdrop != MagickFalse)
13847  {
13848  /*
13849  Center image on window.
13850  */
13851  window_info.x=(window_attributes.width/2)-(window_info.ximage->width/2);
13852  window_info.y=(window_attributes.height/2)-(window_info.ximage->height/2);
13853  width=window_attributes.width;
13854  height=window_attributes.height;
13855  }
13856  if ((resources.image_geometry != (char *) NULL) &&
13857  (*resources.image_geometry != '\0'))
13858  {
13859  char
13860  default_geometry[MaxTextExtent];
13861 
13862  int
13863  flags,
13864  gravity;
13865 
13866  XSizeHints
13867  *size_hints;
13868 
13869  /*
13870  User specified geometry.
13871  */
13872  size_hints=XAllocSizeHints();
13873  if (size_hints == (XSizeHints *) NULL)
13874  ThrowXWindowFatalException(ResourceLimitFatalError,
13875  "MemoryAllocationFailed",image->filename);
13876  size_hints->flags=0L;
13877  (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13878  width,height);
13879  flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13880  default_geometry,window_info.border_width,size_hints,&window_info.x,
13881  &window_info.y,&width,&height,&gravity);
13882  if (flags & (XValue | YValue))
13883  {
13884  width=window_attributes.width;
13885  height=window_attributes.height;
13886  }
13887  (void) XFree((void *) size_hints);
13888  }
13889  /*
13890  Create the X pixmap.
13891  */
13892  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13893  (unsigned int) height,window_info.depth);
13894  if (window_info.pixmap == (Pixmap) NULL)
13895  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13896  image->filename);
13897  /*
13898  Display pixmap on the window.
13899  */
13900  if (((unsigned int) width > window_info.width) ||
13901  ((unsigned int) height > window_info.height))
13902  (void) XFillRectangle(display,window_info.pixmap,
13903  window_info.annotate_context,0,0,(unsigned int) width,
13904  (unsigned int) height);
13905  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13906  window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13907  window_info.width,(unsigned int) window_info.height);
13908  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13909  (void) XClearWindow(display,window_info.id);
13910  delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13911  XDelay(display,delay == 0UL ? 10UL : delay);
13912  (void) XSync(display,MagickFalse);
13913  return(window_info.id == root_window ? MagickTrue : MagickFalse);
13914 }
13915 ␌
13916 /*
13917 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13918 % %
13919 % %
13920 % %
13921 + X D i s p l a y I m a g e %
13922 % %
13923 % %
13924 % %
13925 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13926 %
13927 % XDisplayImage() displays an image via X11. A new image is created and
13928 % returned if the user interactively transforms the displayed image.
13929 %
13930 % The format of the XDisplayImage method is:
13931 %
13932 % Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13933 % char **argv,int argc,Image **image,size_t *state)
13934 %
13935 % A description of each parameter follows:
13936 %
13937 % o nexus: Method XDisplayImage returns an image when the
13938 % user chooses 'Open Image' from the command menu or picks a tile
13939 % from the image directory. Otherwise a null image is returned.
13940 %
13941 % o display: Specifies a connection to an X server; returned from
13942 % XOpenDisplay.
13943 %
13944 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13945 %
13946 % o argv: Specifies the application's argument list.
13947 %
13948 % o argc: Specifies the number of arguments.
13949 %
13950 % o image: Specifies an address to an address of an Image structure;
13951 %
13952 */
13953 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13954  char **argv,int argc,Image **image,size_t *state)
13955 {
13956 #define MagnifySize 256 /* must be a power of 2 */
13957 #define MagickMenus 10
13958 #define MagickTitle "Commands"
13959 
13960  const char
13961  *const CommandMenu[] =
13962  {
13963  "File",
13964  "Edit",
13965  "View",
13966  "Transform",
13967  "Enhance",
13968  "Effects",
13969  "F/X",
13970  "Image Edit",
13971  "Miscellany",
13972  "Help",
13973  (char *) NULL
13974  },
13975  *const FileMenu[] =
13976  {
13977  "Open...",
13978  "Next",
13979  "Former",
13980  "Select...",
13981  "Save...",
13982  "Print...",
13983  "Delete...",
13984  "New...",
13985  "Visual Directory...",
13986  "Quit",
13987  (char *) NULL
13988  },
13989  *const EditMenu[] =
13990  {
13991  "Undo",
13992  "Redo",
13993  "Cut",
13994  "Copy",
13995  "Paste",
13996  (char *) NULL
13997  },
13998  *const ViewMenu[] =
13999  {
14000  "Half Size",
14001  "Original Size",
14002  "Double Size",
14003  "Resize...",
14004  "Apply",
14005  "Refresh",
14006  "Restore",
14007  (char *) NULL
14008  },
14009  *const TransformMenu[] =
14010  {
14011  "Crop",
14012  "Chop",
14013  "Flop",
14014  "Flip",
14015  "Rotate Right",
14016  "Rotate Left",
14017  "Rotate...",
14018  "Shear...",
14019  "Roll...",
14020  "Trim Edges",
14021  (char *) NULL
14022  },
14023  *const EnhanceMenu[] =
14024  {
14025  "Hue...",
14026  "Saturation...",
14027  "Brightness...",
14028  "Gamma...",
14029  "Spiff",
14030  "Dull",
14031  "Contrast Stretch...",
14032  "Sigmoidal Contrast...",
14033  "Normalize",
14034  "Equalize",
14035  "Negate",
14036  "Grayscale",
14037  "Map...",
14038  "Quantize...",
14039  (char *) NULL
14040  },
14041  *const EffectsMenu[] =
14042  {
14043  "Despeckle",
14044  "Emboss",
14045  "Reduce Noise",
14046  "Add Noise...",
14047  "Sharpen...",
14048  "Blur...",
14049  "Threshold...",
14050  "Edge Detect...",
14051  "Spread...",
14052  "Shade...",
14053  "Raise...",
14054  "Segment...",
14055  (char *) NULL
14056  },
14057  *const FXMenu[] =
14058  {
14059  "Solarize...",
14060  "Sepia Tone...",
14061  "Swirl...",
14062  "Implode...",
14063  "Vignette...",
14064  "Wave...",
14065  "Oil Paint...",
14066  "Charcoal Draw...",
14067  (char *) NULL
14068  },
14069  *const ImageEditMenu[] =
14070  {
14071  "Annotate...",
14072  "Draw...",
14073  "Color...",
14074  "Matte...",
14075  "Composite...",
14076  "Add Border...",
14077  "Add Frame...",
14078  "Comment...",
14079  "Launch...",
14080  "Region of Interest...",
14081  (char *) NULL
14082  },
14083  *const MiscellanyMenu[] =
14084  {
14085  "Image Info",
14086  "Zoom Image",
14087  "Show Preview...",
14088  "Show Histogram",
14089  "Show Matte",
14090  "Background...",
14091  "Slide Show...",
14092  "Preferences...",
14093  (char *) NULL
14094  },
14095  *const HelpMenu[] =
14096  {
14097  "Overview",
14098  "Browse Documentation",
14099  "About Display",
14100  (char *) NULL
14101  },
14102  *const ShortCutsMenu[] =
14103  {
14104  "Next",
14105  "Former",
14106  "Open...",
14107  "Save...",
14108  "Print...",
14109  "Undo",
14110  "Restore",
14111  "Image Info",
14112  "Quit",
14113  (char *) NULL
14114  },
14115  *const VirtualMenu[] =
14116  {
14117  "Image Info",
14118  "Print",
14119  "Next",
14120  "Quit",
14121  (char *) NULL
14122  };
14123 
14124  const char
14125  *const *Menus[MagickMenus] =
14126  {
14127  FileMenu,
14128  EditMenu,
14129  ViewMenu,
14130  TransformMenu,
14131  EnhanceMenu,
14132  EffectsMenu,
14133  FXMenu,
14134  ImageEditMenu,
14135  MiscellanyMenu,
14136  HelpMenu
14137  };
14138 
14139  static DisplayCommand
14140  CommandMenus[] =
14141  {
14142  NullCommand,
14143  NullCommand,
14144  NullCommand,
14145  NullCommand,
14146  NullCommand,
14147  NullCommand,
14148  NullCommand,
14149  NullCommand,
14150  NullCommand,
14151  NullCommand,
14152  },
14153  FileCommands[] =
14154  {
14155  OpenCommand,
14156  NextCommand,
14157  FormerCommand,
14158  SelectCommand,
14159  SaveCommand,
14160  PrintCommand,
14161  DeleteCommand,
14162  NewCommand,
14163  VisualDirectoryCommand,
14164  QuitCommand
14165  },
14166  EditCommands[] =
14167  {
14168  UndoCommand,
14169  RedoCommand,
14170  CutCommand,
14171  CopyCommand,
14172  PasteCommand
14173  },
14174  ViewCommands[] =
14175  {
14176  HalfSizeCommand,
14177  OriginalSizeCommand,
14178  DoubleSizeCommand,
14179  ResizeCommand,
14180  ApplyCommand,
14181  RefreshCommand,
14182  RestoreCommand
14183  },
14184  TransformCommands[] =
14185  {
14186  CropCommand,
14187  ChopCommand,
14188  FlopCommand,
14189  FlipCommand,
14190  RotateRightCommand,
14191  RotateLeftCommand,
14192  RotateCommand,
14193  ShearCommand,
14194  RollCommand,
14195  TrimCommand
14196  },
14197  EnhanceCommands[] =
14198  {
14199  HueCommand,
14200  SaturationCommand,
14201  BrightnessCommand,
14202  GammaCommand,
14203  SpiffCommand,
14204  DullCommand,
14205  ContrastStretchCommand,
14206  SigmoidalContrastCommand,
14207  NormalizeCommand,
14208  EqualizeCommand,
14209  NegateCommand,
14210  GrayscaleCommand,
14211  MapCommand,
14212  QuantizeCommand
14213  },
14214  EffectsCommands[] =
14215  {
14216  DespeckleCommand,
14217  EmbossCommand,
14218  ReduceNoiseCommand,
14219  AddNoiseCommand,
14220  SharpenCommand,
14221  BlurCommand,
14222  ThresholdCommand,
14223  EdgeDetectCommand,
14224  SpreadCommand,
14225  ShadeCommand,
14226  RaiseCommand,
14227  SegmentCommand
14228  },
14229  FXCommands[] =
14230  {
14231  SolarizeCommand,
14232  SepiaToneCommand,
14233  SwirlCommand,
14234  ImplodeCommand,
14235  VignetteCommand,
14236  WaveCommand,
14237  OilPaintCommand,
14238  CharcoalDrawCommand
14239  },
14240  ImageEditCommands[] =
14241  {
14242  AnnotateCommand,
14243  DrawCommand,
14244  ColorCommand,
14245  MatteCommand,
14246  CompositeCommand,
14247  AddBorderCommand,
14248  AddFrameCommand,
14249  CommentCommand,
14250  LaunchCommand,
14251  RegionOfInterestCommand
14252  },
14253  MiscellanyCommands[] =
14254  {
14255  InfoCommand,
14256  ZoomCommand,
14257  ShowPreviewCommand,
14258  ShowHistogramCommand,
14259  ShowMatteCommand,
14260  BackgroundCommand,
14261  SlideShowCommand,
14262  PreferencesCommand
14263  },
14264  HelpCommands[] =
14265  {
14266  HelpCommand,
14267  BrowseDocumentationCommand,
14268  VersionCommand
14269  },
14270  ShortCutsCommands[] =
14271  {
14272  NextCommand,
14273  FormerCommand,
14274  OpenCommand,
14275  SaveCommand,
14276  PrintCommand,
14277  UndoCommand,
14278  RestoreCommand,
14279  InfoCommand,
14280  QuitCommand
14281  },
14282  VirtualCommands[] =
14283  {
14284  InfoCommand,
14285  PrintCommand,
14286  NextCommand,
14287  QuitCommand
14288  };
14289 
14290  static DisplayCommand
14291  *Commands[MagickMenus] =
14292  {
14293  FileCommands,
14294  EditCommands,
14295  ViewCommands,
14296  TransformCommands,
14297  EnhanceCommands,
14298  EffectsCommands,
14299  FXCommands,
14300  ImageEditCommands,
14301  MiscellanyCommands,
14302  HelpCommands
14303  };
14304 
14305  char
14306  command[MaxTextExtent],
14307  *directory,
14308  geometry[MaxTextExtent],
14309  resource_name[MaxTextExtent];
14310 
14311  DisplayCommand
14312  display_command;
14313 
14314  Image
14315  *display_image,
14316  *nexus;
14317 
14318  int
14319  entry,
14320  id;
14321 
14322  KeySym
14323  key_symbol;
14324 
14325  MagickStatusType
14326  context_mask,
14327  status;
14328 
14330  geometry_info;
14331 
14332  int
14333  i;
14334 
14335  static char
14336  working_directory[MaxTextExtent];
14337 
14338  static XPoint
14339  vid_info;
14340 
14341  static XWindowInfo
14342  *magick_windows[MaxXWindows];
14343 
14344  static unsigned int
14345  number_windows;
14346 
14347  struct stat
14348  attributes;
14349 
14350  time_t
14351  timer,
14352  timestamp,
14353  update_time;
14354 
14355  unsigned int
14356  height,
14357  width;
14358 
14359  size_t
14360  delay;
14361 
14362  WarningHandler
14363  warning_handler;
14364 
14365  Window
14366  root_window;
14367 
14368  XClassHint
14369  *class_hints;
14370 
14371  XEvent
14372  event;
14373 
14374  XFontStruct
14375  *font_info;
14376 
14377  XGCValues
14378  context_values;
14379 
14380  XPixelInfo
14381  *icon_pixel,
14382  *pixel;
14383 
14384  XResourceInfo
14385  *icon_resources;
14386 
14387  XStandardColormap
14388  *icon_map,
14389  *map_info;
14390 
14391  XVisualInfo
14392  *icon_visual,
14393  *visual_info;
14394 
14395  XWindowChanges
14396  window_changes;
14397 
14398  XWindows
14399  *windows;
14400 
14401  XWMHints
14402  *manager_hints;
14403 
14404  assert(image != (Image **) NULL);
14405  assert((*image)->signature == MagickCoreSignature);
14406  if (IsEventLogging() != MagickFalse)
14407  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14408  display_image=(*image);
14409  warning_handler=(WarningHandler) NULL;
14410  windows=XSetWindows((XWindows *) ~0);
14411  if (windows != (XWindows *) NULL)
14412  {
14413  int
14414  status;
14415 
14416  if (*working_directory == '\0')
14417  (void) CopyMagickString(working_directory,".",MaxTextExtent);
14418  status=chdir(working_directory);
14419  if (status == -1)
14420  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
14421  FileOpenError,"UnableToOpenFile","%s",working_directory);
14422  warning_handler=resource_info->display_warnings ?
14423  SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14424  warning_handler=resource_info->display_warnings ?
14425  SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14426  }
14427  else
14428  {
14429  /*
14430  Allocate windows structure.
14431  */
14432  resource_info->colors=display_image->colors;
14433  windows=XSetWindows(XInitializeWindows(display,resource_info));
14434  if (windows == (XWindows *) NULL)
14435  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14436  (*image)->filename);
14437  /*
14438  Initialize window id's.
14439  */
14440  number_windows=0;
14441  magick_windows[number_windows++]=(&windows->icon);
14442  magick_windows[number_windows++]=(&windows->backdrop);
14443  magick_windows[number_windows++]=(&windows->image);
14444  magick_windows[number_windows++]=(&windows->info);
14445  magick_windows[number_windows++]=(&windows->command);
14446  magick_windows[number_windows++]=(&windows->widget);
14447  magick_windows[number_windows++]=(&windows->popup);
14448  magick_windows[number_windows++]=(&windows->magnify);
14449  magick_windows[number_windows++]=(&windows->pan);
14450  for (i=0; i < (int) number_windows; i++)
14451  magick_windows[i]->id=(Window) NULL;
14452  vid_info.x=0;
14453  vid_info.y=0;
14454  }
14455  /*
14456  Initialize font info.
14457  */
14458  if (windows->font_info != (XFontStruct *) NULL)
14459  (void) XFreeFont(display,windows->font_info);
14460  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14461  if (windows->font_info == (XFontStruct *) NULL)
14462  ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14463  resource_info->font);
14464  /*
14465  Initialize Standard Colormap.
14466  */
14467  map_info=windows->map_info;
14468  icon_map=windows->icon_map;
14469  visual_info=windows->visual_info;
14470  icon_visual=windows->icon_visual;
14471  pixel=windows->pixel_info;
14472  icon_pixel=windows->icon_pixel;
14473  font_info=windows->font_info;
14474  icon_resources=windows->icon_resources;
14475  class_hints=windows->class_hints;
14476  manager_hints=windows->manager_hints;
14477  root_window=XRootWindow(display,visual_info->screen);
14478  nexus=NewImageList();
14479  if (resource_info->debug != MagickFalse)
14480  {
14481  (void) LogMagickEvent(X11Event,GetMagickModule(),
14482  "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14483  (double) display_image->scene,(double) display_image->columns,
14484  (double) display_image->rows);
14485  if (display_image->colors != 0)
14486  (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14487  display_image->colors);
14488  (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14489  display_image->magick);
14490  }
14491  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14492  map_info,pixel);
14493  display_image->taint=MagickFalse;
14494  /*
14495  Initialize graphic context.
14496  */
14497  windows->context.id=(Window) NULL;
14498  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14499  resource_info,&windows->context);
14500  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14501  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14502  class_hints->res_class[0]=(char) LocaleToUppercase((int)
14503  class_hints->res_class[0]);
14504  manager_hints->flags=InputHint | StateHint;
14505  manager_hints->input=MagickFalse;
14506  manager_hints->initial_state=WithdrawnState;
14507  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14508  &windows->context);
14509  if (resource_info->debug != MagickFalse)
14510  (void) LogMagickEvent(X11Event,GetMagickModule(),
14511  "Window id: 0x%lx (context)",windows->context.id);
14512  context_values.background=pixel->background_color.pixel;
14513  context_values.font=font_info->fid;
14514  context_values.foreground=pixel->foreground_color.pixel;
14515  context_values.graphics_exposures=MagickFalse;
14516  context_mask=(MagickStatusType)
14517  (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14518  if (pixel->annotate_context != (GC) NULL)
14519  (void) XFreeGC(display,pixel->annotate_context);
14520  pixel->annotate_context=XCreateGC(display,windows->context.id,
14521  context_mask,&context_values);
14522  if (pixel->annotate_context == (GC) NULL)
14523  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14524  display_image->filename);
14525  context_values.background=pixel->depth_color.pixel;
14526  if (pixel->widget_context != (GC) NULL)
14527  (void) XFreeGC(display,pixel->widget_context);
14528  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14529  &context_values);
14530  if (pixel->widget_context == (GC) NULL)
14531  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14532  display_image->filename);
14533  context_values.background=pixel->foreground_color.pixel;
14534  context_values.foreground=pixel->background_color.pixel;
14535  context_values.plane_mask=context_values.background ^
14536  context_values.foreground;
14537  if (pixel->highlight_context != (GC) NULL)
14538  (void) XFreeGC(display,pixel->highlight_context);
14539  pixel->highlight_context=XCreateGC(display,windows->context.id,
14540  (size_t) (context_mask | GCPlaneMask),&context_values);
14541  if (pixel->highlight_context == (GC) NULL)
14542  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14543  display_image->filename);
14544  (void) XDestroyWindow(display,windows->context.id);
14545  /*
14546  Initialize icon window.
14547  */
14548  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14549  icon_resources,&windows->icon);
14550  windows->icon.geometry=resource_info->icon_geometry;
14551  XBestIconSize(display,&windows->icon,display_image);
14552  windows->icon.attributes.colormap=XDefaultColormap(display,
14553  icon_visual->screen);
14554  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14555  manager_hints->flags=InputHint | StateHint;
14556  manager_hints->input=MagickFalse;
14557  manager_hints->initial_state=IconicState;
14558  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14559  &windows->icon);
14560  if (resource_info->debug != MagickFalse)
14561  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14562  windows->icon.id);
14563  /*
14564  Initialize graphic context for icon window.
14565  */
14566  if (icon_pixel->annotate_context != (GC) NULL)
14567  (void) XFreeGC(display,icon_pixel->annotate_context);
14568  context_values.background=icon_pixel->background_color.pixel;
14569  context_values.foreground=icon_pixel->foreground_color.pixel;
14570  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14571  (size_t) (GCBackground | GCForeground),&context_values);
14572  if (icon_pixel->annotate_context == (GC) NULL)
14573  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14574  display_image->filename);
14575  windows->icon.annotate_context=icon_pixel->annotate_context;
14576  /*
14577  Initialize Image window.
14578  */
14579  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14580  &windows->image);
14581  windows->image.shape=MagickTrue; /* non-rectangular shape hint */
14582  if (resource_info->use_shared_memory == MagickFalse)
14583  windows->image.shared_memory=MagickFalse;
14584  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14585  {
14586  char
14587  *title;
14588 
14589  title=InterpretImageProperties(resource_info->image_info,display_image,
14590  resource_info->title);
14591  (void) CloneString(&windows->image.name,title);
14592  (void) CloneString(&windows->image.icon_name,title);
14593  title=DestroyString(title);
14594  }
14595  else
14596  {
14597  char
14598  filename[MaxTextExtent],
14599  window_name[MaxTextExtent];
14600 
14601  /*
14602  Window name is the base of the filename.
14603  */
14604  GetPathComponent(display_image->magick_filename,TailPath,filename);
14605  if (display_image->scene == 0)
14606  (void) FormatLocaleString(window_name,MaxTextExtent,"%s: %s",
14607  MagickPackageName,filename);
14608  else
14609  (void) FormatLocaleString(window_name,MaxTextExtent,
14610  "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14611  (double) display_image->scene,(double) GetImageListLength(
14612  display_image));
14613  (void) CloneString(&windows->image.name,window_name);
14614  (void) CloneString(&windows->image.icon_name,filename);
14615  }
14616  if (resource_info->immutable)
14617  windows->image.immutable=MagickTrue;
14618  windows->image.use_pixmap=resource_info->use_pixmap;
14619  windows->image.geometry=resource_info->image_geometry;
14620  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14621  XDisplayWidth(display,visual_info->screen),
14622  XDisplayHeight(display,visual_info->screen));
14623  geometry_info.width=display_image->columns;
14624  geometry_info.height=display_image->rows;
14625  geometry_info.x=0;
14626  geometry_info.y=0;
14627  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14628  &geometry_info.width,&geometry_info.height);
14629  windows->image.width=(unsigned int) geometry_info.width;
14630  windows->image.height=(unsigned int) geometry_info.height;
14631  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14632  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14633  KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14634  PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14635  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14636  resource_info,&windows->backdrop);
14637  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14638  {
14639  /*
14640  Initialize backdrop window.
14641  */
14642  windows->backdrop.x=0;
14643  windows->backdrop.y=0;
14644  (void) CloneString(&windows->backdrop.name,"Backdrop");
14645  windows->backdrop.flags=(size_t) (USSize | USPosition);
14646  windows->backdrop.width=(unsigned int)
14647  XDisplayWidth(display,visual_info->screen);
14648  windows->backdrop.height=(unsigned int)
14649  XDisplayHeight(display,visual_info->screen);
14650  windows->backdrop.border_width=0;
14651  windows->backdrop.immutable=MagickTrue;
14652  windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14653  ButtonReleaseMask;
14654  windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14655  StructureNotifyMask;
14656  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14657  manager_hints->icon_window=windows->icon.id;
14658  manager_hints->input=MagickTrue;
14659  manager_hints->initial_state=resource_info->iconic ? IconicState :
14660  NormalState;
14661  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14662  &windows->backdrop);
14663  if (resource_info->debug != MagickFalse)
14664  (void) LogMagickEvent(X11Event,GetMagickModule(),
14665  "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14666  (void) XMapWindow(display,windows->backdrop.id);
14667  (void) XClearWindow(display,windows->backdrop.id);
14668  if (windows->image.id != (Window) NULL)
14669  {
14670  (void) XDestroyWindow(display,windows->image.id);
14671  windows->image.id=(Window) NULL;
14672  }
14673  /*
14674  Position image in the center the backdrop.
14675  */
14676  windows->image.flags|=USPosition;
14677  windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14678  (windows->image.width/2);
14679  windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14680  (windows->image.height/2);
14681  }
14682  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14683  manager_hints->icon_window=windows->icon.id;
14684  manager_hints->input=MagickTrue;
14685  manager_hints->initial_state=resource_info->iconic ? IconicState :
14686  NormalState;
14687  if (windows->group_leader.id != (Window) NULL)
14688  {
14689  /*
14690  Follow the leader.
14691  */
14692  manager_hints->flags|=WindowGroupHint;
14693  manager_hints->window_group=windows->group_leader.id;
14694  (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14695  if (resource_info->debug != MagickFalse)
14696  (void) LogMagickEvent(X11Event,GetMagickModule(),
14697  "Window id: 0x%lx (group leader)",windows->group_leader.id);
14698  }
14699  XMakeWindow(display,
14700  (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14701  argv,argc,class_hints,manager_hints,&windows->image);
14702  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14703  XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14704  if (windows->group_leader.id != (Window) NULL)
14705  (void) XSetTransientForHint(display,windows->image.id,
14706  windows->group_leader.id);
14707  if (resource_info->debug != MagickFalse)
14708  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14709  windows->image.id);
14710  /*
14711  Initialize Info widget.
14712  */
14713  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14714  &windows->info);
14715  (void) CloneString(&windows->info.name,"Info");
14716  (void) CloneString(&windows->info.icon_name,"Info");
14717  windows->info.border_width=1;
14718  windows->info.x=2;
14719  windows->info.y=2;
14720  windows->info.flags|=PPosition;
14721  windows->info.attributes.win_gravity=UnmapGravity;
14722  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14723  StructureNotifyMask;
14724  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14725  manager_hints->input=MagickFalse;
14726  manager_hints->initial_state=NormalState;
14727  manager_hints->window_group=windows->image.id;
14728  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14729  &windows->info);
14730  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14731  windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14732  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14733  windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14734  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14735  if (windows->image.mapped != MagickFalse)
14736  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14737  if (resource_info->debug != MagickFalse)
14738  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14739  windows->info.id);
14740  /*
14741  Initialize Command widget.
14742  */
14743  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14744  resource_info,&windows->command);
14745  windows->command.data=MagickMenus;
14746  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14747  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14748  resource_info->client_name);
14749  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14750  resource_name,"geometry",(char *) NULL);
14751  (void) CloneString(&windows->command.name,MagickTitle);
14752  windows->command.border_width=0;
14753  windows->command.flags|=PPosition;
14754  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14755  ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14756  OwnerGrabButtonMask | StructureNotifyMask;
14757  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14758  manager_hints->input=MagickTrue;
14759  manager_hints->initial_state=NormalState;
14760  manager_hints->window_group=windows->image.id;
14761  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14762  &windows->command);
14763  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14764  windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14765  HighlightHeight);
14766  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14767  windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14768  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14769  if (windows->command.mapped != MagickFalse)
14770  (void) XMapRaised(display,windows->command.id);
14771  if (resource_info->debug != MagickFalse)
14772  (void) LogMagickEvent(X11Event,GetMagickModule(),
14773  "Window id: 0x%lx (command)",windows->command.id);
14774  /*
14775  Initialize Widget window.
14776  */
14777  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14778  resource_info,&windows->widget);
14779  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14780  resource_info->client_name);
14781  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14782  resource_name,"geometry",(char *) NULL);
14783  windows->widget.border_width=0;
14784  windows->widget.flags|=PPosition;
14785  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14786  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14787  KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14788  StructureNotifyMask;
14789  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14790  manager_hints->input=MagickTrue;
14791  manager_hints->initial_state=NormalState;
14792  manager_hints->window_group=windows->image.id;
14793  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14794  &windows->widget);
14795  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14796  windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14797  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14798  windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14799  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14800  if (resource_info->debug != MagickFalse)
14801  (void) LogMagickEvent(X11Event,GetMagickModule(),
14802  "Window id: 0x%lx (widget)",windows->widget.id);
14803  /*
14804  Initialize popup window.
14805  */
14806  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14807  resource_info,&windows->popup);
14808  windows->popup.border_width=0;
14809  windows->popup.flags|=PPosition;
14810  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14811  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14812  KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14813  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14814  manager_hints->input=MagickTrue;
14815  manager_hints->initial_state=NormalState;
14816  manager_hints->window_group=windows->image.id;
14817  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14818  &windows->popup);
14819  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14820  windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14821  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14822  windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14823  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14824  if (resource_info->debug != MagickFalse)
14825  (void) LogMagickEvent(X11Event,GetMagickModule(),
14826  "Window id: 0x%lx (pop up)",windows->popup.id);
14827  /*
14828  Initialize Magnify window and cursor.
14829  */
14830  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14831  resource_info,&windows->magnify);
14832  if (resource_info->use_shared_memory == MagickFalse)
14833  windows->magnify.shared_memory=MagickFalse;
14834  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14835  resource_info->client_name);
14836  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14837  resource_name,"geometry",(char *) NULL);
14838  (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14839  resource_info->magnify);
14840  if (windows->magnify.cursor != (Cursor) NULL)
14841  (void) XFreeCursor(display,windows->magnify.cursor);
14842  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14843  map_info->colormap,resource_info->background_color,
14844  resource_info->foreground_color);
14845  if (windows->magnify.cursor == (Cursor) NULL)
14846  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14847  display_image->filename);
14848  windows->magnify.width=MagnifySize;
14849  windows->magnify.height=MagnifySize;
14850  windows->magnify.flags|=PPosition;
14851  windows->magnify.min_width=MagnifySize;
14852  windows->magnify.min_height=MagnifySize;
14853  windows->magnify.width_inc=MagnifySize;
14854  windows->magnify.height_inc=MagnifySize;
14855  windows->magnify.data=resource_info->magnify;
14856  windows->magnify.attributes.cursor=windows->magnify.cursor;
14857  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14858  ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14859  StructureNotifyMask;
14860  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14861  manager_hints->input=MagickTrue;
14862  manager_hints->initial_state=NormalState;
14863  manager_hints->window_group=windows->image.id;
14864  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14865  &windows->magnify);
14866  if (resource_info->debug != MagickFalse)
14867  (void) LogMagickEvent(X11Event,GetMagickModule(),
14868  "Window id: 0x%lx (magnify)",windows->magnify.id);
14869  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14870  /*
14871  Initialize panning window.
14872  */
14873  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14874  resource_info,&windows->pan);
14875  (void) CloneString(&windows->pan.name,"Pan Icon");
14876  windows->pan.width=windows->icon.width;
14877  windows->pan.height=windows->icon.height;
14878  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14879  resource_info->client_name);
14880  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14881  resource_name,"geometry",(char *) NULL);
14882  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14883  &windows->pan.width,&windows->pan.height);
14884  windows->pan.flags|=PPosition;
14885  windows->pan.immutable=MagickTrue;
14886  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14887  ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14888  StructureNotifyMask;
14889  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14890  manager_hints->input=MagickFalse;
14891  manager_hints->initial_state=NormalState;
14892  manager_hints->window_group=windows->image.id;
14893  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14894  &windows->pan);
14895  if (resource_info->debug != MagickFalse)
14896  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14897  windows->pan.id);
14898  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14899  if (windows->info.mapped != MagickFalse)
14900  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14901  if ((windows->image.mapped == MagickFalse) ||
14902  (windows->backdrop.id != (Window) NULL))
14903  (void) XMapWindow(display,windows->image.id);
14904  /*
14905  Set our progress monitor and warning handlers.
14906  */
14907  if (warning_handler == (WarningHandler) NULL)
14908  {
14909  warning_handler=resource_info->display_warnings ?
14910  SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14911  warning_handler=resource_info->display_warnings ?
14912  SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14913  }
14914  /*
14915  Initialize Image and Magnify X images.
14916  */
14917  windows->image.x=0;
14918  windows->image.y=0;
14919  windows->magnify.shape=MagickFalse;
14920  width=(unsigned int) display_image->columns;
14921  height=(unsigned int) display_image->rows;
14922  if ((display_image->columns != width) || (display_image->rows != height))
14923  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14924  display_image->filename);
14925  status=XMakeImage(display,resource_info,&windows->image,display_image,
14926  width,height);
14927  if (status == MagickFalse)
14928  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14929  display_image->filename);
14930  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14931  windows->magnify.width,windows->magnify.height);
14932  if (status == MagickFalse)
14933  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14934  display_image->filename);
14935  if (windows->magnify.mapped != MagickFalse)
14936  (void) XMapRaised(display,windows->magnify.id);
14937  if (windows->pan.mapped != MagickFalse)
14938  (void) XMapRaised(display,windows->pan.id);
14939  windows->image.window_changes.width=(int) display_image->columns;
14940  windows->image.window_changes.height=(int) display_image->rows;
14941  (void) XConfigureImage(display,resource_info,windows,display_image);
14942  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14943  (void) XSync(display,MagickFalse);
14944  /*
14945  Respond to events.
14946  */
14947  delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
14948  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
14949  update_time=0;
14950  if (resource_info->update != MagickFalse)
14951  {
14952  MagickBooleanType
14953  status;
14954 
14955  /*
14956  Determine when file data was last modified.
14957  */
14958  status=GetPathAttributes(display_image->filename,&attributes);
14959  if (status != MagickFalse)
14960  update_time=attributes.st_mtime;
14961  }
14962  *state&=(~FormerImageState);
14963  *state&=(~MontageImageState);
14964  *state&=(~NextImageState);
14965  do
14966  {
14967  /*
14968  Handle a window event.
14969  */
14970  if (windows->image.mapped != MagickFalse)
14971  if ((display_image->delay != 0) || (resource_info->update != 0))
14972  {
14973  if (timer < GetMagickTime())
14974  {
14975  if (resource_info->update == MagickFalse)
14976  *state|=NextImageState | ExitState;
14977  else
14978  {
14979  MagickBooleanType
14980  status;
14981 
14982  /*
14983  Determine if image file was modified.
14984  */
14985  status=GetPathAttributes(display_image->filename,&attributes);
14986  if (status != MagickFalse)
14987  if (update_time != attributes.st_mtime)
14988  {
14989  /*
14990  Redisplay image.
14991  */
14992  (void) FormatLocaleString(
14993  resource_info->image_info->filename,MaxTextExtent,
14994  "%s:%s",display_image->magick,
14995  display_image->filename);
14996  nexus=ReadImage(resource_info->image_info,
14997  &display_image->exception);
14998  if (nexus != (Image *) NULL)
14999  *state|=NextImageState | ExitState;
15000  }
15001  delay=display_image->delay/MagickMax(
15002  display_image->ticks_per_second,1L);
15003  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15004  }
15005  }
15006  if (XEventsQueued(display,QueuedAfterFlush) == 0)
15007  {
15008  /*
15009  Do not block if delay > 0.
15010  */
15011  XDelay(display,SuspendTime << 2);
15012  continue;
15013  }
15014  }
15015  timestamp=GetMagickTime();
15016  (void) XNextEvent(display,&event);
15017  if (windows->image.stasis == MagickFalse)
15018  windows->image.stasis=(GetMagickTime()-timestamp) > 0 ?
15019  MagickTrue : MagickFalse;
15020  if (windows->magnify.stasis == MagickFalse)
15021  windows->magnify.stasis=(GetMagickTime()-timestamp) > 0 ?
15022  MagickTrue : MagickFalse;
15023  if (event.xany.window == windows->command.id)
15024  {
15025  /*
15026  Select a command from the Command widget.
15027  */
15028  id=XCommandWidget(display,windows,CommandMenu,&event);
15029  if (id < 0)
15030  continue;
15031  (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15032  display_command=CommandMenus[id];
15033  if (id < MagickMenus)
15034  {
15035  /*
15036  Select a command from a pop-up menu.
15037  */
15038  entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15039  command);
15040  if (entry < 0)
15041  continue;
15042  (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15043  display_command=Commands[id][entry];
15044  }
15045  if (display_command != NullCommand)
15046  nexus=XMagickCommand(display,resource_info,windows,display_command,
15047  &display_image);
15048  continue;
15049  }
15050  switch (event.type)
15051  {
15052  case ButtonPress:
15053  {
15054  if (resource_info->debug != MagickFalse)
15055  (void) LogMagickEvent(X11Event,GetMagickModule(),
15056  "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15057  event.xbutton.button,event.xbutton.x,event.xbutton.y);
15058  if ((event.xbutton.button == Button3) &&
15059  (event.xbutton.state & Mod1Mask))
15060  {
15061  /*
15062  Convert Alt-Button3 to Button2.
15063  */
15064  event.xbutton.button=Button2;
15065  event.xbutton.state&=(~Mod1Mask);
15066  }
15067  if (event.xbutton.window == windows->backdrop.id)
15068  {
15069  (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15070  event.xbutton.time);
15071  break;
15072  }
15073  if (event.xbutton.window == windows->image.id)
15074  {
15075  switch (event.xbutton.button)
15076  {
15077  case Button1:
15078  {
15079  if (resource_info->immutable)
15080  {
15081  /*
15082  Select a command from the Virtual menu.
15083  */
15084  entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15085  command);
15086  if (entry >= 0)
15087  nexus=XMagickCommand(display,resource_info,windows,
15088  VirtualCommands[entry],&display_image);
15089  break;
15090  }
15091  /*
15092  Map/unmap Command widget.
15093  */
15094  if (windows->command.mapped != MagickFalse)
15095  (void) XWithdrawWindow(display,windows->command.id,
15096  windows->command.screen);
15097  else
15098  {
15099  (void) XCommandWidget(display,windows,CommandMenu,
15100  (XEvent *) NULL);
15101  (void) XMapRaised(display,windows->command.id);
15102  }
15103  break;
15104  }
15105  case Button2:
15106  {
15107  /*
15108  User pressed the image magnify button.
15109  */
15110  (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15111  &display_image);
15112  XMagnifyImage(display,windows,&event);
15113  break;
15114  }
15115  case Button3:
15116  {
15117  if (resource_info->immutable)
15118  {
15119  /*
15120  Select a command from the Virtual menu.
15121  */
15122  entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15123  command);
15124  if (entry >= 0)
15125  nexus=XMagickCommand(display,resource_info,windows,
15126  VirtualCommands[entry],&display_image);
15127  break;
15128  }
15129  if (display_image->montage != (char *) NULL)
15130  {
15131  /*
15132  Open or delete a tile from a visual image directory.
15133  */
15134  nexus=XTileImage(display,resource_info,windows,
15135  display_image,&event);
15136  if (nexus != (Image *) NULL)
15137  *state|=MontageImageState | NextImageState | ExitState;
15138  vid_info.x=(short int) windows->image.x;
15139  vid_info.y=(short int) windows->image.y;
15140  break;
15141  }
15142  /*
15143  Select a command from the Short Cuts menu.
15144  */
15145  entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15146  command);
15147  if (entry >= 0)
15148  nexus=XMagickCommand(display,resource_info,windows,
15149  ShortCutsCommands[entry],&display_image);
15150  break;
15151  }
15152  case Button4:
15153  {
15154  /*
15155  Wheel up.
15156  */
15157  XTranslateImage(display,windows,*image,XK_Up);
15158  break;
15159  }
15160  case Button5:
15161  {
15162  /*
15163  Wheel down.
15164  */
15165  XTranslateImage(display,windows,*image,XK_Down);
15166  break;
15167  }
15168  default:
15169  break;
15170  }
15171  break;
15172  }
15173  if (event.xbutton.window == windows->magnify.id)
15174  {
15175  const char
15176  *const MagnifyMenu[] =
15177  {
15178  "2",
15179  "4",
15180  "5",
15181  "6",
15182  "7",
15183  "8",
15184  "9",
15185  "3",
15186  (char *) NULL,
15187  };
15188 
15189  int
15190  factor;
15191 
15192  static KeySym
15193  MagnifyCommands[] =
15194  {
15195  XK_2,
15196  XK_4,
15197  XK_5,
15198  XK_6,
15199  XK_7,
15200  XK_8,
15201  XK_9,
15202  XK_3
15203  };
15204 
15205  /*
15206  Select a magnify factor from the pop-up menu.
15207  */
15208  factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15209  if (factor >= 0)
15210  XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15211  break;
15212  }
15213  if (event.xbutton.window == windows->pan.id)
15214  {
15215  switch (event.xbutton.button)
15216  {
15217  case Button4:
15218  {
15219  /*
15220  Wheel up.
15221  */
15222  XTranslateImage(display,windows,*image,XK_Up);
15223  break;
15224  }
15225  case Button5:
15226  {
15227  /*
15228  Wheel down.
15229  */
15230  XTranslateImage(display,windows,*image,XK_Down);
15231  break;
15232  }
15233  default:
15234  {
15235  XPanImage(display,windows,&event);
15236  break;
15237  }
15238  }
15239  break;
15240  }
15241  delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15242  1L);
15243  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15244  break;
15245  }
15246  case ButtonRelease:
15247  {
15248  if (resource_info->debug != MagickFalse)
15249  (void) LogMagickEvent(X11Event,GetMagickModule(),
15250  "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15251  event.xbutton.button,event.xbutton.x,event.xbutton.y);
15252  break;
15253  }
15254  case ClientMessage:
15255  {
15256  if (resource_info->debug != MagickFalse)
15257  (void) LogMagickEvent(X11Event,GetMagickModule(),
15258  "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15259  event.xclient.message_type,event.xclient.format,(unsigned long)
15260  event.xclient.data.l[0]);
15261  if (event.xclient.message_type == windows->im_protocols)
15262  {
15263  if (*event.xclient.data.l == (long) windows->im_update_widget)
15264  {
15265  (void) CloneString(&windows->command.name,MagickTitle);
15266  windows->command.data=MagickMenus;
15267  (void) XCommandWidget(display,windows,CommandMenu,
15268  (XEvent *) NULL);
15269  break;
15270  }
15271  if (*event.xclient.data.l == (long) windows->im_update_colormap)
15272  {
15273  /*
15274  Update graphic context and window colormap.
15275  */
15276  for (i=0; i < (int) number_windows; i++)
15277  {
15278  if (magick_windows[i]->id == windows->icon.id)
15279  continue;
15280  context_values.background=pixel->background_color.pixel;
15281  context_values.foreground=pixel->foreground_color.pixel;
15282  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15283  context_mask,&context_values);
15284  (void) XChangeGC(display,magick_windows[i]->widget_context,
15285  context_mask,&context_values);
15286  context_values.background=pixel->foreground_color.pixel;
15287  context_values.foreground=pixel->background_color.pixel;
15288  context_values.plane_mask=context_values.background ^
15289  context_values.foreground;
15290  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15291  (size_t) (context_mask | GCPlaneMask),
15292  &context_values);
15293  magick_windows[i]->attributes.background_pixel=
15294  pixel->background_color.pixel;
15295  magick_windows[i]->attributes.border_pixel=
15296  pixel->border_color.pixel;
15297  magick_windows[i]->attributes.colormap=map_info->colormap;
15298  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15299  (unsigned long) magick_windows[i]->mask,
15300  &magick_windows[i]->attributes);
15301  }
15302  if (windows->pan.mapped != MagickFalse)
15303  {
15304  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15305  windows->pan.pixmap);
15306  (void) XClearWindow(display,windows->pan.id);
15307  XDrawPanRectangle(display,windows);
15308  }
15309  if (windows->backdrop.id != (Window) NULL)
15310  (void) XInstallColormap(display,map_info->colormap);
15311  break;
15312  }
15313  if (*event.xclient.data.l == (long) windows->im_former_image)
15314  {
15315  *state|=FormerImageState | ExitState;
15316  break;
15317  }
15318  if (*event.xclient.data.l == (long) windows->im_next_image)
15319  {
15320  *state|=NextImageState | ExitState;
15321  break;
15322  }
15323  if (*event.xclient.data.l == (long) windows->im_retain_colors)
15324  {
15325  *state|=RetainColorsState;
15326  break;
15327  }
15328  if (*event.xclient.data.l == (long) windows->im_exit)
15329  {
15330  *state|=ExitState;
15331  break;
15332  }
15333  break;
15334  }
15335  if (event.xclient.message_type == windows->dnd_protocols)
15336  {
15337  Atom
15338  selection,
15339  type;
15340 
15341  int
15342  format,
15343  status;
15344 
15345  unsigned char
15346  *data;
15347 
15348  unsigned long
15349  after,
15350  length;
15351 
15352  /*
15353  Display image named by the Drag-and-Drop selection.
15354  */
15355  if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15356  break;
15357  selection=XInternAtom(display,"DndSelection",MagickFalse);
15358  status=XGetWindowProperty(display,root_window,selection,0L,(long)
15359  MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15360  &length,&after,&data);
15361  if ((status != Success) || (length == 0))
15362  break;
15363  if (*event.xclient.data.l == 2)
15364  {
15365  /*
15366  Offix DND.
15367  */
15368  (void) CopyMagickString(resource_info->image_info->filename,
15369  (char *) data,MaxTextExtent);
15370  }
15371  else
15372  {
15373  /*
15374  XDND.
15375  */
15376  if (strncmp((char *) data, "file:", 5) != 0)
15377  {
15378  (void) XFree((void *) data);
15379  break;
15380  }
15381  (void) CopyMagickString(resource_info->image_info->filename,
15382  ((char *) data)+5,MaxTextExtent);
15383  }
15384  nexus=ReadImage(resource_info->image_info,
15385  &display_image->exception);
15386  CatchException(&display_image->exception);
15387  if (nexus != (Image *) NULL)
15388  *state|=NextImageState | ExitState;
15389  (void) XFree((void *) data);
15390  break;
15391  }
15392  /*
15393  If client window delete message, exit.
15394  */
15395  if (event.xclient.message_type != windows->wm_protocols)
15396  break;
15397  if (*event.xclient.data.l != (long) windows->wm_delete_window)
15398  break;
15399  (void) XWithdrawWindow(display,event.xclient.window,
15400  visual_info->screen);
15401  if (event.xclient.window == windows->image.id)
15402  {
15403  *state|=ExitState;
15404  break;
15405  }
15406  if (event.xclient.window == windows->pan.id)
15407  {
15408  /*
15409  Restore original image size when pan window is deleted.
15410  */
15411  windows->image.window_changes.width=windows->image.ximage->width;
15412  windows->image.window_changes.height=windows->image.ximage->height;
15413  (void) XConfigureImage(display,resource_info,windows,
15414  display_image);
15415  }
15416  break;
15417  }
15418  case ConfigureNotify:
15419  {
15420  if (resource_info->debug != MagickFalse)
15421  (void) LogMagickEvent(X11Event,GetMagickModule(),
15422  "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15423  event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15424  event.xconfigure.y,event.xconfigure.send_event);
15425  if (event.xconfigure.window == windows->image.id)
15426  {
15427  /*
15428  Image window has a new configuration.
15429  */
15430  if (event.xconfigure.send_event != 0)
15431  {
15432  XWindowChanges
15433  window_changes;
15434 
15435  /*
15436  Position the transient windows relative of the Image window.
15437  */
15438  if (windows->command.geometry == (char *) NULL)
15439  if (windows->command.mapped == MagickFalse)
15440  {
15441  windows->command.x=event.xconfigure.x-
15442  windows->command.width-25;
15443  windows->command.y=event.xconfigure.y;
15444  XConstrainWindowPosition(display,&windows->command);
15445  window_changes.x=windows->command.x;
15446  window_changes.y=windows->command.y;
15447  (void) XReconfigureWMWindow(display,windows->command.id,
15448  windows->command.screen,(unsigned int) (CWX | CWY),
15449  &window_changes);
15450  }
15451  if (windows->widget.geometry == (char *) NULL)
15452  if (windows->widget.mapped == MagickFalse)
15453  {
15454  windows->widget.x=event.xconfigure.x+
15455  event.xconfigure.width/10;
15456  windows->widget.y=event.xconfigure.y+
15457  event.xconfigure.height/10;
15458  XConstrainWindowPosition(display,&windows->widget);
15459  window_changes.x=windows->widget.x;
15460  window_changes.y=windows->widget.y;
15461  (void) XReconfigureWMWindow(display,windows->widget.id,
15462  windows->widget.screen,(unsigned int) (CWX | CWY),
15463  &window_changes);
15464  }
15465  if (windows->magnify.geometry == (char *) NULL)
15466  if (windows->magnify.mapped == MagickFalse)
15467  {
15468  windows->magnify.x=event.xconfigure.x+
15469  event.xconfigure.width+25;
15470  windows->magnify.y=event.xconfigure.y;
15471  XConstrainWindowPosition(display,&windows->magnify);
15472  window_changes.x=windows->magnify.x;
15473  window_changes.y=windows->magnify.y;
15474  (void) XReconfigureWMWindow(display,windows->magnify.id,
15475  windows->magnify.screen,(unsigned int) (CWX | CWY),
15476  &window_changes);
15477  }
15478  if (windows->pan.geometry == (char *) NULL)
15479  if (windows->pan.mapped == MagickFalse)
15480  {
15481  windows->pan.x=event.xconfigure.x+
15482  event.xconfigure.width+25;
15483  windows->pan.y=event.xconfigure.y+
15484  windows->magnify.height+50;
15485  XConstrainWindowPosition(display,&windows->pan);
15486  window_changes.x=windows->pan.x;
15487  window_changes.y=windows->pan.y;
15488  (void) XReconfigureWMWindow(display,windows->pan.id,
15489  windows->pan.screen,(unsigned int) (CWX | CWY),
15490  &window_changes);
15491  }
15492  }
15493  if ((event.xconfigure.width == (int) windows->image.width) &&
15494  (event.xconfigure.height == (int) windows->image.height))
15495  break;
15496  windows->image.width=(unsigned int) event.xconfigure.width;
15497  windows->image.height=(unsigned int) event.xconfigure.height;
15498  windows->image.x=0;
15499  windows->image.y=0;
15500  if (display_image->montage != (char *) NULL)
15501  {
15502  windows->image.x=vid_info.x;
15503  windows->image.y=vid_info.y;
15504  }
15505  if ((windows->image.mapped != MagickFalse) &&
15506  (windows->image.stasis != MagickFalse))
15507  {
15508  /*
15509  Update image window configuration.
15510  */
15511  windows->image.window_changes.width=event.xconfigure.width;
15512  windows->image.window_changes.height=event.xconfigure.height;
15513  (void) XConfigureImage(display,resource_info,windows,
15514  display_image);
15515  }
15516  /*
15517  Update pan window configuration.
15518  */
15519  if ((event.xconfigure.width < windows->image.ximage->width) ||
15520  (event.xconfigure.height < windows->image.ximage->height))
15521  {
15522  (void) XMapRaised(display,windows->pan.id);
15523  XDrawPanRectangle(display,windows);
15524  }
15525  else
15526  if (windows->pan.mapped != MagickFalse)
15527  (void) XWithdrawWindow(display,windows->pan.id,
15528  windows->pan.screen);
15529  break;
15530  }
15531  if (event.xconfigure.window == windows->magnify.id)
15532  {
15533  unsigned int
15534  magnify;
15535 
15536  /*
15537  Magnify window has a new configuration.
15538  */
15539  windows->magnify.width=(unsigned int) event.xconfigure.width;
15540  windows->magnify.height=(unsigned int) event.xconfigure.height;
15541  if (windows->magnify.mapped == MagickFalse)
15542  break;
15543  magnify=1;
15544  while ((int) magnify <= event.xconfigure.width)
15545  magnify<<=1;
15546  while ((int) magnify <= event.xconfigure.height)
15547  magnify<<=1;
15548  magnify>>=1;
15549  if (((int) magnify != event.xconfigure.width) ||
15550  ((int) magnify != event.xconfigure.height))
15551  {
15552  window_changes.width=(int) magnify;
15553  window_changes.height=(int) magnify;
15554  (void) XReconfigureWMWindow(display,windows->magnify.id,
15555  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15556  &window_changes);
15557  break;
15558  }
15559  if ((windows->magnify.mapped != MagickFalse) &&
15560  (windows->magnify.stasis != MagickFalse))
15561  {
15562  status=XMakeImage(display,resource_info,&windows->magnify,
15563  display_image,windows->magnify.width,windows->magnify.height);
15564  XMakeMagnifyImage(display,windows);
15565  }
15566  break;
15567  }
15568  if ((windows->magnify.mapped != MagickFalse) &&
15569  (event.xconfigure.window == windows->pan.id))
15570  {
15571  /*
15572  Pan icon window has a new configuration.
15573  */
15574  if (event.xconfigure.send_event != 0)
15575  {
15576  windows->pan.x=event.xconfigure.x;
15577  windows->pan.y=event.xconfigure.y;
15578  }
15579  windows->pan.width=(unsigned int) event.xconfigure.width;
15580  windows->pan.height=(unsigned int) event.xconfigure.height;
15581  break;
15582  }
15583  if (event.xconfigure.window == windows->icon.id)
15584  {
15585  /*
15586  Icon window has a new configuration.
15587  */
15588  windows->icon.width=(unsigned int) event.xconfigure.width;
15589  windows->icon.height=(unsigned int) event.xconfigure.height;
15590  break;
15591  }
15592  break;
15593  }
15594  case DestroyNotify:
15595  {
15596  /*
15597  Group leader has exited.
15598  */
15599  if (resource_info->debug != MagickFalse)
15600  (void) LogMagickEvent(X11Event,GetMagickModule(),
15601  "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15602  if (event.xdestroywindow.window == windows->group_leader.id)
15603  {
15604  *state|=ExitState;
15605  break;
15606  }
15607  break;
15608  }
15609  case EnterNotify:
15610  {
15611  /*
15612  Selectively install colormap.
15613  */
15614  if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15615  if (event.xcrossing.mode != NotifyUngrab)
15616  XInstallColormap(display,map_info->colormap);
15617  break;
15618  }
15619  case Expose:
15620  {
15621  if (resource_info->debug != MagickFalse)
15622  (void) LogMagickEvent(X11Event,GetMagickModule(),
15623  "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15624  event.xexpose.width,event.xexpose.height,event.xexpose.x,
15625  event.xexpose.y);
15626  /*
15627  Refresh windows that are now exposed.
15628  */
15629  if ((event.xexpose.window == windows->image.id) &&
15630  (windows->image.mapped != MagickFalse))
15631  {
15632  XRefreshWindow(display,&windows->image,&event);
15633  delay=display_image->delay/MagickMax(
15634  display_image->ticks_per_second,1L);
15635  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15636  break;
15637  }
15638  if ((event.xexpose.window == windows->magnify.id) &&
15639  (windows->magnify.mapped != MagickFalse))
15640  {
15641  XMakeMagnifyImage(display,windows);
15642  break;
15643  }
15644  if (event.xexpose.window == windows->pan.id)
15645  {
15646  XDrawPanRectangle(display,windows);
15647  break;
15648  }
15649  if (event.xexpose.window == windows->icon.id)
15650  {
15651  XRefreshWindow(display,&windows->icon,&event);
15652  break;
15653  }
15654  break;
15655  }
15656  case KeyPress:
15657  {
15658  int
15659  length;
15660 
15661  /*
15662  Respond to a user key press.
15663  */
15664  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15665  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15666  *(command+length)='\0';
15667  if (resource_info->debug != MagickFalse)
15668  (void) LogMagickEvent(X11Event,GetMagickModule(),
15669  "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15670  key_symbol,command);
15671  if (event.xkey.window == windows->image.id)
15672  {
15673  display_command=XImageWindowCommand(display,resource_info,windows,
15674  event.xkey.state,key_symbol,&display_image);
15675  if (display_command != NullCommand)
15676  nexus=XMagickCommand(display,resource_info,windows,display_command,
15677  &display_image);
15678  }
15679  if (event.xkey.window == windows->magnify.id)
15680  XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15681  if (event.xkey.window == windows->pan.id)
15682  {
15683  if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15684  (void) XWithdrawWindow(display,windows->pan.id,
15685  windows->pan.screen);
15686  else
15687  if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15688  XTextViewHelp(display,resource_info,windows,MagickFalse,
15689  "Help Viewer - Image Pan",ImagePanHelp);
15690  else
15691  XTranslateImage(display,windows,*image,key_symbol);
15692  }
15693  delay=display_image->delay/MagickMax(
15694  display_image->ticks_per_second,1L);
15695  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15696  break;
15697  }
15698  case KeyRelease:
15699  {
15700  /*
15701  Respond to a user key release.
15702  */
15703  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15704  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15705  if (resource_info->debug != MagickFalse)
15706  (void) LogMagickEvent(X11Event,GetMagickModule(),
15707  "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15708  break;
15709  }
15710  case LeaveNotify:
15711  {
15712  /*
15713  Selectively uninstall colormap.
15714  */
15715  if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15716  if (event.xcrossing.mode != NotifyUngrab)
15717  XUninstallColormap(display,map_info->colormap);
15718  break;
15719  }
15720  case MapNotify:
15721  {
15722  if (resource_info->debug != MagickFalse)
15723  (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15724  event.xmap.window);
15725  if (event.xmap.window == windows->backdrop.id)
15726  {
15727  (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15728  CurrentTime);
15729  windows->backdrop.mapped=MagickTrue;
15730  break;
15731  }
15732  if (event.xmap.window == windows->image.id)
15733  {
15734  if (windows->backdrop.id != (Window) NULL)
15735  (void) XInstallColormap(display,map_info->colormap);
15736  if (LocaleCompare(display_image->magick,"LOGO") == 0)
15737  {
15738  if (LocaleCompare(display_image->filename,"LOGO") == 0)
15739  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15740  }
15741  if (((int) windows->image.width < windows->image.ximage->width) ||
15742  ((int) windows->image.height < windows->image.ximage->height))
15743  (void) XMapRaised(display,windows->pan.id);
15744  windows->image.mapped=MagickTrue;
15745  break;
15746  }
15747  if (event.xmap.window == windows->magnify.id)
15748  {
15749  XMakeMagnifyImage(display,windows);
15750  windows->magnify.mapped=MagickTrue;
15751  (void) XWithdrawWindow(display,windows->info.id,
15752  windows->info.screen);
15753  break;
15754  }
15755  if (event.xmap.window == windows->pan.id)
15756  {
15757  XMakePanImage(display,resource_info,windows,display_image);
15758  windows->pan.mapped=MagickTrue;
15759  break;
15760  }
15761  if (event.xmap.window == windows->info.id)
15762  {
15763  windows->info.mapped=MagickTrue;
15764  break;
15765  }
15766  if (event.xmap.window == windows->icon.id)
15767  {
15768  MagickBooleanType
15769  taint;
15770 
15771  /*
15772  Create an icon image.
15773  */
15774  taint=display_image->taint;
15775  XMakeStandardColormap(display,icon_visual,icon_resources,
15776  display_image,icon_map,icon_pixel);
15777  (void) XMakeImage(display,icon_resources,&windows->icon,
15778  display_image,windows->icon.width,windows->icon.height);
15779  display_image->taint=taint;
15780  (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15781  windows->icon.pixmap);
15782  (void) XClearWindow(display,windows->icon.id);
15783  (void) XWithdrawWindow(display,windows->info.id,
15784  windows->info.screen);
15785  windows->icon.mapped=MagickTrue;
15786  break;
15787  }
15788  if (event.xmap.window == windows->command.id)
15789  {
15790  windows->command.mapped=MagickTrue;
15791  break;
15792  }
15793  if (event.xmap.window == windows->popup.id)
15794  {
15795  windows->popup.mapped=MagickTrue;
15796  break;
15797  }
15798  if (event.xmap.window == windows->widget.id)
15799  {
15800  windows->widget.mapped=MagickTrue;
15801  break;
15802  }
15803  break;
15804  }
15805  case MappingNotify:
15806  {
15807  (void) XRefreshKeyboardMapping(&event.xmapping);
15808  break;
15809  }
15810  case NoExpose:
15811  break;
15812  case PropertyNotify:
15813  {
15814  Atom
15815  type;
15816 
15817  int
15818  format,
15819  status;
15820 
15821  unsigned char
15822  *data;
15823 
15824  unsigned long
15825  after,
15826  length;
15827 
15828  if (resource_info->debug != MagickFalse)
15829  (void) LogMagickEvent(X11Event,GetMagickModule(),
15830  "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15831  event.xproperty.atom,event.xproperty.state);
15832  if (event.xproperty.atom != windows->im_remote_command)
15833  break;
15834  /*
15835  Display image named by the remote command protocol.
15836  */
15837  status=XGetWindowProperty(display,event.xproperty.window,
15838  event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15839  AnyPropertyType,&type,&format,&length,&after,&data);
15840  if ((status != Success) || (length == 0))
15841  break;
15842  if (LocaleCompare((char *) data,"-quit") == 0)
15843  {
15844  XClientMessage(display,windows->image.id,windows->im_protocols,
15845  windows->im_exit,CurrentTime);
15846  (void) XFree((void *) data);
15847  break;
15848  }
15849  (void) CopyMagickString(resource_info->image_info->filename,
15850  (char *) data,MaxTextExtent);
15851  (void) XFree((void *) data);
15852  nexus=ReadImage(resource_info->image_info,&display_image->exception);
15853  CatchException(&display_image->exception);
15854  if (nexus != (Image *) NULL)
15855  *state|=NextImageState | ExitState;
15856  break;
15857  }
15858  case ReparentNotify:
15859  {
15860  if (resource_info->debug != MagickFalse)
15861  (void) LogMagickEvent(X11Event,GetMagickModule(),
15862  "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15863  event.xreparent.window);
15864  break;
15865  }
15866  case UnmapNotify:
15867  {
15868  if (resource_info->debug != MagickFalse)
15869  (void) LogMagickEvent(X11Event,GetMagickModule(),
15870  "Unmap Notify: 0x%lx",event.xunmap.window);
15871  if (event.xunmap.window == windows->backdrop.id)
15872  {
15873  windows->backdrop.mapped=MagickFalse;
15874  break;
15875  }
15876  if (event.xunmap.window == windows->image.id)
15877  {
15878  windows->image.mapped=MagickFalse;
15879  break;
15880  }
15881  if (event.xunmap.window == windows->magnify.id)
15882  {
15883  windows->magnify.mapped=MagickFalse;
15884  break;
15885  }
15886  if (event.xunmap.window == windows->pan.id)
15887  {
15888  windows->pan.mapped=MagickFalse;
15889  break;
15890  }
15891  if (event.xunmap.window == windows->info.id)
15892  {
15893  windows->info.mapped=MagickFalse;
15894  break;
15895  }
15896  if (event.xunmap.window == windows->icon.id)
15897  {
15898  if (map_info->colormap == icon_map->colormap)
15899  XConfigureImageColormap(display,resource_info,windows,
15900  display_image);
15901  (void) XFreeStandardColormap(display,icon_visual,icon_map,
15902  icon_pixel);
15903  windows->icon.mapped=MagickFalse;
15904  break;
15905  }
15906  if (event.xunmap.window == windows->command.id)
15907  {
15908  windows->command.mapped=MagickFalse;
15909  break;
15910  }
15911  if (event.xunmap.window == windows->popup.id)
15912  {
15913  if (windows->backdrop.id != (Window) NULL)
15914  (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15915  CurrentTime);
15916  windows->popup.mapped=MagickFalse;
15917  break;
15918  }
15919  if (event.xunmap.window == windows->widget.id)
15920  {
15921  if (windows->backdrop.id != (Window) NULL)
15922  (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15923  CurrentTime);
15924  windows->widget.mapped=MagickFalse;
15925  break;
15926  }
15927  break;
15928  }
15929  default:
15930  {
15931  if (resource_info->debug != MagickFalse)
15932  (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
15933  event.type);
15934  break;
15935  }
15936  }
15937  } while (!(*state & ExitState));
15938  if ((*state & ExitState) == 0)
15939  (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
15940  &display_image);
15941  else
15942  if (resource_info->confirm_edit != MagickFalse)
15943  {
15944  /*
15945  Query user if image has changed.
15946  */
15947  if ((resource_info->immutable == MagickFalse) &&
15948  (display_image->taint != MagickFalse))
15949  {
15950  int
15951  status;
15952 
15953  status=XConfirmWidget(display,windows,"Your image changed.",
15954  "Do you want to save it");
15955  if (status == 0)
15956  *state&=(~ExitState);
15957  else
15958  if (status > 0)
15959  (void) XMagickCommand(display,resource_info,windows,SaveCommand,
15960  &display_image);
15961  }
15962  }
15963  if ((windows->visual_info->klass == GrayScale) ||
15964  (windows->visual_info->klass == PseudoColor) ||
15965  (windows->visual_info->klass == DirectColor))
15966  {
15967  /*
15968  Withdraw pan and Magnify window.
15969  */
15970  if (windows->info.mapped != MagickFalse)
15971  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15972  if (windows->magnify.mapped != MagickFalse)
15973  (void) XWithdrawWindow(display,windows->magnify.id,
15974  windows->magnify.screen);
15975  if (windows->command.mapped != MagickFalse)
15976  (void) XWithdrawWindow(display,windows->command.id,
15977  windows->command.screen);
15978  }
15979  if (windows->pan.mapped != MagickFalse)
15980  (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
15981  if (resource_info->backdrop == MagickFalse)
15982  if (windows->backdrop.mapped)
15983  {
15984  (void) XWithdrawWindow(display,windows->backdrop.id,
15985  windows->backdrop.screen);
15986  (void) XDestroyWindow(display,windows->backdrop.id);
15987  windows->backdrop.id=(Window) NULL;
15988  (void) XWithdrawWindow(display,windows->image.id,
15989  windows->image.screen);
15990  (void) XDestroyWindow(display,windows->image.id);
15991  windows->image.id=(Window) NULL;
15992  }
15993  XSetCursorState(display,windows,MagickTrue);
15994  XCheckRefreshWindows(display,windows);
15995  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
15996  *state&=(~ExitState);
15997  if (*state & ExitState)
15998  {
15999  /*
16000  Free Standard Colormap.
16001  */
16002  (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16003  if (resource_info->map_type == (char *) NULL)
16004  (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16005  /*
16006  Free X resources.
16007  */
16008  if (resource_info->copy_image != (Image *) NULL)
16009  resource_info->copy_image=DestroyImage(resource_info->copy_image);
16010  DestroyXResources();
16011  }
16012  (void) XSync(display,MagickFalse);
16013  /*
16014  Restore our progress monitor and warning handlers.
16015  */
16016  (void) SetErrorHandler(warning_handler);
16017  (void) SetWarningHandler(warning_handler);
16018  /*
16019  Change to home directory.
16020  */
16021  directory=getcwd(working_directory,MaxTextExtent);
16022  (void) directory;
16023  {
16024  int
16025  status;
16026 
16027  if (*resource_info->home_directory == '\0')
16028  (void) CopyMagickString(resource_info->home_directory,".",MaxTextExtent);
16029  status=chdir(resource_info->home_directory);
16030  if (status == -1)
16031  (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
16032  FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
16033  }
16034  *image=display_image;
16035  return(nexus);
16036 }
16037 #else
16038 ␌
16039 /*
16040 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16041 % %
16042 % %
16043 % %
16044 + D i s p l a y I m a g e s %
16045 % %
16046 % %
16047 % %
16048 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16049 %
16050 % DisplayImages() displays an image sequence to any X window screen. It
16051 % returns a value other than 0 if successful. Check the exception member
16052 % of image to determine the reason for any failure.
16053 %
16054 % The format of the DisplayImages method is:
16055 %
16056 % MagickBooleanType DisplayImages(const ImageInfo *image_info,
16057 % Image *images)
16058 %
16059 % A description of each parameter follows:
16060 %
16061 % o image_info: the image info.
16062 %
16063 % o image: the image.
16064 %
16065 */
16066 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16067  Image *image)
16068 {
16069  assert(image_info != (const ImageInfo *) NULL);
16070  assert(image_info->signature == MagickCoreSignature);
16071  assert(image != (Image *) NULL);
16072  assert(image->signature == MagickCoreSignature);
16073  if (IsEventLogging() != MagickFalse)
16074  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16075  (void) image_info;
16076  (void) ThrowMagickException(&image->exception,GetMagickModule(),
16077  MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
16078  image->filename);
16079  return(MagickFalse);
16080 }
16081 ␌
16082 /*
16083 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16084 % %
16085 % %
16086 % %
16087 + R e m o t e D i s p l a y C o m m a n d %
16088 % %
16089 % %
16090 % %
16091 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16092 %
16093 % RemoteDisplayCommand() encourages a remote display program to display the
16094 % specified image filename.
16095 %
16096 % The format of the RemoteDisplayCommand method is:
16097 %
16098 % MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16099 % const char *window,const char *filename,ExceptionInfo *exception)
16100 %
16101 % A description of each parameter follows:
16102 %
16103 % o image_info: the image info.
16104 %
16105 % o window: Specifies the name or id of an X window.
16106 %
16107 % o filename: the name of the image filename to display.
16108 %
16109 % o exception: return any errors or warnings in this structure.
16110 %
16111 */
16112 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16113  const char *window,const char *filename,ExceptionInfo *exception)
16114 {
16115  assert(image_info != (const ImageInfo *) NULL);
16116  assert(image_info->signature == MagickCoreSignature);
16117  assert(filename != (char *) NULL);
16118  if (IsEventLogging() != MagickFalse)
16119  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16120  (void) window;
16121  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16122  "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16123  return(MagickFalse);
16124 }
16125 #endif
Definition: image.h:134