MagickCore  7.0.9
display.c
Go to the documentation of this file.
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-2019 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 "MagickCore/studio.h"
43 #include "MagickCore/artifact.h"
44 #include "MagickCore/attribute.h"
45 #include "MagickCore/blob.h"
46 #include "MagickCore/cache.h"
48 #include "MagickCore/channel.h"
49 #include "MagickCore/client.h"
50 #include "MagickCore/color.h"
51 #include "MagickCore/colorspace.h"
52 #include "MagickCore/composite.h"
53 #include "MagickCore/constitute.h"
54 #include "MagickCore/decorate.h"
55 #include "MagickCore/delegate.h"
56 #include "MagickCore/display.h"
58 #include "MagickCore/distort.h"
59 #include "MagickCore/draw.h"
60 #include "MagickCore/effect.h"
61 #include "MagickCore/enhance.h"
62 #include "MagickCore/exception.h"
64 #include "MagickCore/fx.h"
65 #include "MagickCore/geometry.h"
66 #include "MagickCore/image.h"
68 #include "MagickCore/list.h"
69 #include "MagickCore/log.h"
70 #include "MagickCore/magick.h"
71 #include "MagickCore/memory_.h"
72 #include "MagickCore/monitor.h"
74 #include "MagickCore/montage.h"
76 #include "MagickCore/option.h"
77 #include "MagickCore/paint.h"
78 #include "MagickCore/pixel.h"
80 #include "MagickCore/property.h"
81 #include "MagickCore/quantum.h"
83 #include "MagickCore/resize.h"
84 #include "MagickCore/resource_.h"
85 #include "MagickCore/shear.h"
86 #include "MagickCore/segment.h"
87 #include "MagickCore/statistic.h"
88 #include "MagickCore/string_.h"
91 #include "MagickCore/transform.h"
93 #include "MagickCore/threshold.h"
94 #include "MagickCore/utility.h"
96 #include "MagickCore/version.h"
97 #include "MagickCore/widget.h"
99 #include "MagickCore/xwindow.h"
101 
102 #if defined(MAGICKCORE_X11_DELEGATE)
103 /*
104  Define declarations.
105 */
106 #define MaxColors MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
107 
108 /*
109  Constant declarations.
110 */
111 static const unsigned char
112  HighlightBitmap[8] =
113  {
114  0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
115  },
116  OpaqueBitmap[8] =
117  {
118  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
119  },
120  ShadowBitmap[8] =
121  {
122  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
123  };
124 
125 /*
126  Help widget declarations.
127 */
128 static const char
129  ImageAnnotateHelp[] =
130  {
131  "In annotate mode, the Command widget has these options:\n"
132  "\n"
133  " Font Name\n"
134  " fixed\n"
135  " variable\n"
136  " 5x8\n"
137  " 6x10\n"
138  " 7x13bold\n"
139  " 8x13bold\n"
140  " 9x15bold\n"
141  " 10x20\n"
142  " 12x24\n"
143  " Browser...\n"
144  " Font Color\n"
145  " black\n"
146  " blue\n"
147  " cyan\n"
148  " green\n"
149  " gray\n"
150  " red\n"
151  " magenta\n"
152  " yellow\n"
153  " white\n"
154  " transparent\n"
155  " Browser...\n"
156  " Font Color\n"
157  " black\n"
158  " blue\n"
159  " cyan\n"
160  " green\n"
161  " gray\n"
162  " red\n"
163  " magenta\n"
164  " yellow\n"
165  " white\n"
166  " transparent\n"
167  " Browser...\n"
168  " Rotate Text\n"
169  " -90\n"
170  " -45\n"
171  " -30\n"
172  " 0\n"
173  " 30\n"
174  " 45\n"
175  " 90\n"
176  " 180\n"
177  " Dialog...\n"
178  " Help\n"
179  " Dismiss\n"
180  "\n"
181  "Choose a font name from the Font Name sub-menu. Additional\n"
182  "font names can be specified with the font browser. You can\n"
183  "change the menu names by setting the X resources font1\n"
184  "through font9.\n"
185  "\n"
186  "Choose a font color from the Font Color sub-menu.\n"
187  "Additional font colors can be specified with the color\n"
188  "browser. You can change the menu colors by setting the X\n"
189  "resources pen1 through pen9.\n"
190  "\n"
191  "If you select the color browser and press Grab, you can\n"
192  "choose the font color by moving the pointer to the desired\n"
193  "color on the screen and press any button.\n"
194  "\n"
195  "If you choose to rotate the text, choose Rotate Text from the\n"
196  "menu and select an angle. Typically you will only want to\n"
197  "rotate one line of text at a time. Depending on the angle you\n"
198  "choose, subsequent lines may end up overwriting each other.\n"
199  "\n"
200  "Choosing a font and its color is optional. The default font\n"
201  "is fixed and the default color is black. However, you must\n"
202  "choose a location to begin entering text and press button 1.\n"
203  "An underscore character will appear at the location of the\n"
204  "pointer. The cursor changes to a pencil to indicate you are\n"
205  "in text mode. To exit immediately, press Dismiss.\n"
206  "\n"
207  "In text mode, any key presses will display the character at\n"
208  "the location of the underscore and advance the underscore\n"
209  "cursor. Enter your text and once completed press Apply to\n"
210  "finish your image annotation. To correct errors press BACK\n"
211  "SPACE. To delete an entire line of text, press DELETE. Any\n"
212  "text that exceeds the boundaries of the image window is\n"
213  "automagically continued onto the next line.\n"
214  "\n"
215  "The actual color you request for the font is saved in the\n"
216  "image. However, the color that appears in your image window\n"
217  "may be different. For example, on a monochrome screen the\n"
218  "text will appear black or white even if you choose the color\n"
219  "red as the font color. However, the image saved to a file\n"
220  "with -write is written with red lettering. To assure the\n"
221  "correct color text in the final image, any PseudoClass image\n"
222  "is promoted to DirectClass (see miff(5)). To force a\n"
223  "PseudoClass image to remain PseudoClass, use -colors.\n"
224  },
225  ImageChopHelp[] =
226  {
227  "In chop mode, the Command widget has these options:\n"
228  "\n"
229  " Direction\n"
230  " horizontal\n"
231  " vertical\n"
232  " Help\n"
233  " Dismiss\n"
234  "\n"
235  "If the you choose the horizontal direction (this the\n"
236  "default), the area of the image between the two horizontal\n"
237  "endpoints of the chop line is removed. Otherwise, the area\n"
238  "of the image between the two vertical endpoints of the chop\n"
239  "line is removed.\n"
240  "\n"
241  "Select a location within the image window to begin your chop,\n"
242  "press and hold any button. Next, move the pointer to\n"
243  "another location in the image. As you move a line will\n"
244  "connect the initial location and the pointer. When you\n"
245  "release the button, the area within the image to chop is\n"
246  "determined by which direction you choose from the Command\n"
247  "widget.\n"
248  "\n"
249  "To cancel the image chopping, move the pointer back to the\n"
250  "starting point of the line and release the button.\n"
251  },
252  ImageColorEditHelp[] =
253  {
254  "In color edit mode, the Command widget has these options:\n"
255  "\n"
256  " Method\n"
257  " point\n"
258  " replace\n"
259  " floodfill\n"
260  " filltoborder\n"
261  " reset\n"
262  " Pixel Color\n"
263  " black\n"
264  " blue\n"
265  " cyan\n"
266  " green\n"
267  " gray\n"
268  " red\n"
269  " magenta\n"
270  " yellow\n"
271  " white\n"
272  " Browser...\n"
273  " Border Color\n"
274  " black\n"
275  " blue\n"
276  " cyan\n"
277  " green\n"
278  " gray\n"
279  " red\n"
280  " magenta\n"
281  " yellow\n"
282  " white\n"
283  " Browser...\n"
284  " Fuzz\n"
285  " 0%\n"
286  " 2%\n"
287  " 5%\n"
288  " 10%\n"
289  " 15%\n"
290  " Dialog...\n"
291  " Undo\n"
292  " Help\n"
293  " Dismiss\n"
294  "\n"
295  "Choose a color editing method from the Method sub-menu\n"
296  "of the Command widget. The point method recolors any pixel\n"
297  "selected with the pointer until the button is released. The\n"
298  "replace method recolors any pixel that matches the color of\n"
299  "the pixel you select with a button press. Floodfill recolors\n"
300  "any pixel that matches the color of the pixel you select with\n"
301  "a button press and is a neighbor. Whereas filltoborder recolors\n"
302  "any neighbor pixel that is not the border color. Finally reset\n"
303  "changes the entire image to the designated color.\n"
304  "\n"
305  "Next, choose a pixel color from the Pixel Color sub-menu.\n"
306  "Additional pixel colors can be specified with the color\n"
307  "browser. You can change the menu colors by setting the X\n"
308  "resources pen1 through pen9.\n"
309  "\n"
310  "Now press button 1 to select a pixel within the image window\n"
311  "to change its color. Additional pixels may be recolored as\n"
312  "prescribed by the method you choose.\n"
313  "\n"
314  "If the Magnify widget is mapped, it can be helpful in positioning\n"
315  "your pointer within the image (refer to button 2).\n"
316  "\n"
317  "The actual color you request for the pixels is saved in the\n"
318  "image. However, the color that appears in your image window\n"
319  "may be different. For example, on a monochrome screen the\n"
320  "pixel will appear black or white even if you choose the\n"
321  "color red as the pixel color. However, the image saved to a\n"
322  "file with -write is written with red pixels. To assure the\n"
323  "correct color text in the final image, any PseudoClass image\n"
324  "is promoted to DirectClass (see miff(5)). To force a\n"
325  "PseudoClass image to remain PseudoClass, use -colors.\n"
326  },
327  ImageCompositeHelp[] =
328  {
329  "First a widget window is displayed requesting you to enter an\n"
330  "image name. Press Composite, Grab or type a file name.\n"
331  "Press Cancel if you choose not to create a composite image.\n"
332  "When you choose Grab, move the pointer to the desired window\n"
333  "and press any button.\n"
334  "\n"
335  "If the Composite image does not have any matte information,\n"
336  "you are informed and the file browser is displayed again.\n"
337  "Enter the name of a mask image. The image is typically\n"
338  "grayscale and the same size as the composite image. If the\n"
339  "image is not grayscale, it is converted to grayscale and the\n"
340  "resulting intensities are used as matte information.\n"
341  "\n"
342  "A small window appears showing the location of the cursor in\n"
343  "the image window. You are now in composite mode. To exit\n"
344  "immediately, press Dismiss. In composite mode, the Command\n"
345  "widget has these options:\n"
346  "\n"
347  " Operators\n"
348  " Over\n"
349  " In\n"
350  " Out\n"
351  " Atop\n"
352  " Xor\n"
353  " Plus\n"
354  " Minus\n"
355  " Add\n"
356  " Subtract\n"
357  " Difference\n"
358  " Multiply\n"
359  " Bumpmap\n"
360  " Copy\n"
361  " CopyRed\n"
362  " CopyGreen\n"
363  " CopyBlue\n"
364  " CopyOpacity\n"
365  " Clear\n"
366  " Dissolve\n"
367  " Displace\n"
368  " Help\n"
369  " Dismiss\n"
370  "\n"
371  "Choose a composite operation from the Operators sub-menu of\n"
372  "the Command widget. How each operator behaves is described\n"
373  "below. Image window is the image currently displayed on\n"
374  "your X server and image is the image obtained with the File\n"
375  "Browser widget.\n"
376  "\n"
377  "Over The result is the union of the two image shapes,\n"
378  " with image obscuring image window in the region of\n"
379  " overlap.\n"
380  "\n"
381  "In The result is simply image cut by the shape of\n"
382  " image window. None of the image data of image\n"
383  " window is in the result.\n"
384  "\n"
385  "Out The resulting image is image with the shape of\n"
386  " image window cut out.\n"
387  "\n"
388  "Atop The result is the same shape as image image window,\n"
389  " with image obscuring image window where the image\n"
390  " shapes overlap. Note this differs from over\n"
391  " because the portion of image outside image window's\n"
392  " shape does not appear in the result.\n"
393  "\n"
394  "Xor The result is the image data from both image and\n"
395  " image window that is outside the overlap region.\n"
396  " The overlap region is blank.\n"
397  "\n"
398  "Plus The result is just the sum of the image data.\n"
399  " Output values are cropped to QuantumRange (no overflow).\n"
400  "\n"
401  "Minus The result of image - image window, with underflow\n"
402  " cropped to zero.\n"
403  "\n"
404  "Add The result of image + image window, with overflow\n"
405  " wrapping around (mod 256).\n"
406  "\n"
407  "Subtract The result of image - image window, with underflow\n"
408  " wrapping around (mod 256). The add and subtract\n"
409  " operators can be used to perform reversible\n"
410  " transformations.\n"
411  "\n"
412  "Difference\n"
413  " The result of abs(image - image window). This\n"
414  " useful for comparing two very similar images.\n"
415  "\n"
416  "Multiply\n"
417  " The result of image * image window. This\n"
418  " useful for the creation of drop-shadows.\n"
419  "\n"
420  "Bumpmap The result of surface normals from image * image\n"
421  " window.\n"
422  "\n"
423  "Copy The resulting image is image window replaced with\n"
424  " image. Here the matte information is ignored.\n"
425  "\n"
426  "CopyRed The red layer of the image window is replace with\n"
427  " the red layer of the image. The other layers are\n"
428  " untouched.\n"
429  "\n"
430  "CopyGreen\n"
431  " The green layer of the image window is replace with\n"
432  " the green layer of the image. The other layers are\n"
433  " untouched.\n"
434  "\n"
435  "CopyBlue The blue layer of the image window is replace with\n"
436  " the blue layer of the image. The other layers are\n"
437  " untouched.\n"
438  "\n"
439  "CopyOpacity\n"
440  " The matte layer of the image window is replace with\n"
441  " the matte layer of the image. The other layers are\n"
442  " untouched.\n"
443  "\n"
444  "The image compositor requires a matte, or alpha channel in\n"
445  "the image for some operations. This extra channel usually\n"
446  "defines a mask which represents a sort of a cookie-cutter\n"
447  "for the image. This the case when matte is opaque (full\n"
448  "coverage) for pixels inside the shape, zero outside, and\n"
449  "between 0 and QuantumRange on the boundary. If image does not\n"
450  "have a matte channel, it is initialized with 0 for any pixel\n"
451  "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
452  "\n"
453  "If you choose Dissolve, the composite operator becomes Over. The\n"
454  "image matte channel percent transparency is initialized to factor.\n"
455  "The image window is initialized to (100-factor). Where factor is the\n"
456  "value you specify in the Dialog widget.\n"
457  "\n"
458  "Displace shifts the image pixels as defined by a displacement\n"
459  "map. With this option, image is used as a displacement map.\n"
460  "Black, within the displacement map, is a maximum positive\n"
461  "displacement. White is a maximum negative displacement and\n"
462  "middle gray is neutral. The displacement is scaled to determine\n"
463  "the pixel shift. By default, the displacement applies in both the\n"
464  "horizontal and vertical directions. However, if you specify a mask,\n"
465  "image is the horizontal X displacement and mask the vertical Y\n"
466  "displacement.\n"
467  "\n"
468  "Note that matte information for image window is not retained\n"
469  "for colormapped X server visuals (e.g. StaticColor,\n"
470  "StaticColor, GrayScale, PseudoColor). Correct compositing\n"
471  "behavior may require a TrueColor or DirectColor visual or a\n"
472  "Standard Colormap.\n"
473  "\n"
474  "Choosing a composite operator is optional. The default\n"
475  "operator is replace. However, you must choose a location to\n"
476  "composite your image and press button 1. Press and hold the\n"
477  "button before releasing and an outline of the image will\n"
478  "appear to help you identify your location.\n"
479  "\n"
480  "The actual colors of the composite image is saved. However,\n"
481  "the color that appears in image window may be different.\n"
482  "For example, on a monochrome screen image window will appear\n"
483  "black or white even though your composited image may have\n"
484  "many colors. If the image is saved to a file it is written\n"
485  "with the correct colors. To assure the correct colors are\n"
486  "saved in the final image, any PseudoClass image is promoted\n"
487  "to DirectClass (see miff(5)). To force a PseudoClass image\n"
488  "to remain PseudoClass, use -colors.\n"
489  },
490  ImageCutHelp[] =
491  {
492  "In cut mode, the Command widget has these options:\n"
493  "\n"
494  " Help\n"
495  " Dismiss\n"
496  "\n"
497  "To define a cut region, press button 1 and drag. The\n"
498  "cut region is defined by a highlighted rectangle that\n"
499  "expands or contracts as it follows the pointer. Once you\n"
500  "are satisfied with the cut region, release the button.\n"
501  "You are now in rectify mode. In rectify mode, the Command\n"
502  "widget has these options:\n"
503  "\n"
504  " Cut\n"
505  " Help\n"
506  " Dismiss\n"
507  "\n"
508  "You can make adjustments by moving the pointer to one of the\n"
509  "cut rectangle corners, pressing a button, and dragging.\n"
510  "Finally, press Cut to commit your copy region. To\n"
511  "exit without cutting the image, press Dismiss.\n"
512  },
513  ImageCopyHelp[] =
514  {
515  "In copy mode, the Command widget has these options:\n"
516  "\n"
517  " Help\n"
518  " Dismiss\n"
519  "\n"
520  "To define a copy region, press button 1 and drag. The\n"
521  "copy region is defined by a highlighted rectangle that\n"
522  "expands or contracts as it follows the pointer. Once you\n"
523  "are satisfied with the copy region, release the button.\n"
524  "You are now in rectify mode. In rectify mode, the Command\n"
525  "widget has these options:\n"
526  "\n"
527  " Copy\n"
528  " Help\n"
529  " Dismiss\n"
530  "\n"
531  "You can make adjustments by moving the pointer to one of the\n"
532  "copy rectangle corners, pressing a button, and dragging.\n"
533  "Finally, press Copy to commit your copy region. To\n"
534  "exit without copying the image, press Dismiss.\n"
535  },
536  ImageCropHelp[] =
537  {
538  "In crop mode, the Command widget has these options:\n"
539  "\n"
540  " Help\n"
541  " Dismiss\n"
542  "\n"
543  "To define a cropping region, press button 1 and drag. The\n"
544  "cropping region is defined by a highlighted rectangle that\n"
545  "expands or contracts as it follows the pointer. Once you\n"
546  "are satisfied with the cropping region, release the button.\n"
547  "You are now in rectify mode. In rectify mode, the Command\n"
548  "widget has these options:\n"
549  "\n"
550  " Crop\n"
551  " Help\n"
552  " Dismiss\n"
553  "\n"
554  "You can make adjustments by moving the pointer to one of the\n"
555  "cropping rectangle corners, pressing a button, and dragging.\n"
556  "Finally, press Crop to commit your cropping region. To\n"
557  "exit without cropping the image, press Dismiss.\n"
558  },
559  ImageDrawHelp[] =
560  {
561  "The cursor changes to a crosshair to indicate you are in\n"
562  "draw mode. To exit immediately, press Dismiss. In draw mode,\n"
563  "the Command widget has these options:\n"
564  "\n"
565  " Element\n"
566  " point\n"
567  " line\n"
568  " rectangle\n"
569  " fill rectangle\n"
570  " circle\n"
571  " fill circle\n"
572  " ellipse\n"
573  " fill ellipse\n"
574  " polygon\n"
575  " fill polygon\n"
576  " Color\n"
577  " black\n"
578  " blue\n"
579  " cyan\n"
580  " green\n"
581  " gray\n"
582  " red\n"
583  " magenta\n"
584  " yellow\n"
585  " white\n"
586  " transparent\n"
587  " Browser...\n"
588  " Stipple\n"
589  " Brick\n"
590  " Diagonal\n"
591  " Scales\n"
592  " Vertical\n"
593  " Wavy\n"
594  " Translucent\n"
595  " Opaque\n"
596  " Open...\n"
597  " Width\n"
598  " 1\n"
599  " 2\n"
600  " 4\n"
601  " 8\n"
602  " 16\n"
603  " Dialog...\n"
604  " Undo\n"
605  " Help\n"
606  " Dismiss\n"
607  "\n"
608  "Choose a drawing primitive from the Element sub-menu.\n"
609  "\n"
610  "Choose a color from the Color sub-menu. Additional\n"
611  "colors can be specified with the color browser.\n"
612  "\n"
613  "If you choose the color browser and press Grab, you can\n"
614  "select the color by moving the pointer to the desired\n"
615  "color on the screen and press any button. The transparent\n"
616  "color updates the image matte channel and is useful for\n"
617  "image compositing.\n"
618  "\n"
619  "Choose a stipple, if appropriate, from the Stipple sub-menu.\n"
620  "Additional stipples can be specified with the file browser.\n"
621  "Stipples obtained from the file browser must be on disk in the\n"
622  "X11 bitmap format.\n"
623  "\n"
624  "Choose a width, if appropriate, from the Width sub-menu. To\n"
625  "choose a specific width select the Dialog widget.\n"
626  "\n"
627  "Choose a point in the Image window and press button 1 and\n"
628  "hold. Next, move the pointer to another location in the\n"
629  "image. As you move, a line connects the initial location and\n"
630  "the pointer. When you release the button, the image is\n"
631  "updated with the primitive you just drew. For polygons, the\n"
632  "image is updated when you press and release the button without\n"
633  "moving the pointer.\n"
634  "\n"
635  "To cancel image drawing, move the pointer back to the\n"
636  "starting point of the line and release the button.\n"
637  },
638  DisplayHelp[] =
639  {
640  "BUTTONS\n"
641  " The effects of each button press is described below. Three\n"
642  " buttons are required. If you have a two button mouse,\n"
643  " button 1 and 3 are returned. Press ALT and button 3 to\n"
644  " simulate button 2.\n"
645  "\n"
646  " 1 Press this button to map or unmap the Command widget.\n"
647  "\n"
648  " 2 Press and drag to define a region of the image to\n"
649  " magnify.\n"
650  "\n"
651  " 3 Press and drag to choose from a select set of commands.\n"
652  " This button behaves differently if the image being\n"
653  " displayed is a visual image directory. Here, choose a\n"
654  " particular tile of the directory and press this button and\n"
655  " drag to select a command from a pop-up menu. Choose from\n"
656  " these menu items:\n"
657  "\n"
658  " Open\n"
659  " Next\n"
660  " Former\n"
661  " Delete\n"
662  " Update\n"
663  "\n"
664  " If you choose Open, the image represented by the tile is\n"
665  " displayed. To return to the visual image directory, choose\n"
666  " Next from the Command widget. Next and Former moves to the\n"
667  " next or former image respectively. Choose Delete to delete\n"
668  " a particular image tile. Finally, choose Update to\n"
669  " synchronize all the image tiles with their respective\n"
670  " images.\n"
671  "\n"
672  "COMMAND WIDGET\n"
673  " The Command widget lists a number of sub-menus and commands.\n"
674  " They are\n"
675  "\n"
676  " File\n"
677  " Open...\n"
678  " Next\n"
679  " Former\n"
680  " Select...\n"
681  " Save...\n"
682  " Print...\n"
683  " Delete...\n"
684  " New...\n"
685  " Visual Directory...\n"
686  " Quit\n"
687  " Edit\n"
688  " Undo\n"
689  " Redo\n"
690  " Cut\n"
691  " Copy\n"
692  " Paste\n"
693  " View\n"
694  " Half Size\n"
695  " Original Size\n"
696  " Double Size\n"
697  " Resize...\n"
698  " Apply\n"
699  " Refresh\n"
700  " Restore\n"
701  " Transform\n"
702  " Crop\n"
703  " Chop\n"
704  " Flop\n"
705  " Flip\n"
706  " Rotate Right\n"
707  " Rotate Left\n"
708  " Rotate...\n"
709  " Shear...\n"
710  " Roll...\n"
711  " Trim Edges\n"
712  " Enhance\n"
713  " Brightness...\n"
714  " Saturation...\n"
715  " Hue...\n"
716  " Gamma...\n"
717  " Sharpen...\n"
718  " Dull\n"
719  " Contrast Stretch...\n"
720  " Sigmoidal Contrast...\n"
721  " Normalize\n"
722  " Equalize\n"
723  " Negate\n"
724  " Grayscale\n"
725  " Map...\n"
726  " Quantize...\n"
727  " Effects\n"
728  " Despeckle\n"
729  " Emboss\n"
730  " Reduce Noise\n"
731  " Add Noise\n"
732  " Sharpen...\n"
733  " Blur...\n"
734  " Threshold...\n"
735  " Edge Detect...\n"
736  " Spread...\n"
737  " Shade...\n"
738  " Painting...\n"
739  " Segment...\n"
740  " F/X\n"
741  " Solarize...\n"
742  " Sepia Tone...\n"
743  " Swirl...\n"
744  " Implode...\n"
745  " Vignette...\n"
746  " Wave...\n"
747  " Oil Painting...\n"
748  " Charcoal Drawing...\n"
749  " Image Edit\n"
750  " Annotate...\n"
751  " Draw...\n"
752  " Color...\n"
753  " Matte...\n"
754  " Composite...\n"
755  " Add Border...\n"
756  " Add Frame...\n"
757  " Comment...\n"
758  " Launch...\n"
759  " Region of Interest...\n"
760  " Miscellany\n"
761  " Image Info\n"
762  " Zoom Image\n"
763  " Show Preview...\n"
764  " Show Histogram\n"
765  " Show Matte\n"
766  " Background...\n"
767  " Slide Show\n"
768  " Preferences...\n"
769  " Help\n"
770  " Overview\n"
771  " Browse Documentation\n"
772  " About Display\n"
773  "\n"
774  " Menu items with a indented triangle have a sub-menu. They\n"
775  " are represented above as the indented items. To access a\n"
776  " sub-menu item, move the pointer to the appropriate menu and\n"
777  " press a button and drag. When you find the desired sub-menu\n"
778  " item, release the button and the command is executed. Move\n"
779  " the pointer away from the sub-menu if you decide not to\n"
780  " execute a particular command.\n"
781  "\n"
782  "KEYBOARD ACCELERATORS\n"
783  " Accelerators are one or two key presses that effect a\n"
784  " particular command. The keyboard accelerators that\n"
785  " display(1) understands is:\n"
786  "\n"
787  " Ctl+O Press to open an image from a file.\n"
788  "\n"
789  " space Press to display the next image.\n"
790  "\n"
791  " If the image is a multi-paged document such as a Postscript\n"
792  " document, you can skip ahead several pages by preceding\n"
793  " this command with a number. For example to display the\n"
794  " third page beyond the current page, press 3<space>.\n"
795  "\n"
796  " backspace Press to display the former image.\n"
797  "\n"
798  " If the image is a multi-paged document such as a Postscript\n"
799  " document, you can skip behind several pages by preceding\n"
800  " this command with a number. For example to display the\n"
801  " third page preceding the current page, press 3<backspace>.\n"
802  "\n"
803  " Ctl+S Press to write the image to a file.\n"
804  "\n"
805  " Ctl+P Press to print the image to a Postscript printer.\n"
806  "\n"
807  " Ctl+D Press to delete an image file.\n"
808  "\n"
809  " Ctl+N Press to create a blank canvas.\n"
810  "\n"
811  " Ctl+Q Press to discard all images and exit program.\n"
812  "\n"
813  " Ctl+Z Press to undo last image transformation.\n"
814  "\n"
815  " Ctl+R Press to redo last image transformation.\n"
816  "\n"
817  " Ctl+X Press to cut a region of the image.\n"
818  "\n"
819  " Ctl+C Press to copy a region of the image.\n"
820  "\n"
821  " Ctl+V Press to paste a region to the image.\n"
822  "\n"
823  " < Press to half the image size.\n"
824  "\n"
825  " - Press to return to the original image size.\n"
826  "\n"
827  " > Press to double the image size.\n"
828  "\n"
829  " % Press to resize the image to a width and height you\n"
830  " specify.\n"
831  "\n"
832  "Cmd-A Press to make any image transformations permanent."
833  "\n"
834  " By default, any image size transformations are applied\n"
835  " to the original image to create the image displayed on\n"
836  " the X server. However, the transformations are not\n"
837  " permanent (i.e. the original image does not change\n"
838  " size only the X image does). For example, if you\n"
839  " press > the X image will appear to double in size,\n"
840  " but the original image will in fact remain the same size.\n"
841  " To force the original image to double in size, press >\n"
842  " followed by Cmd-A.\n"
843  "\n"
844  " @ Press to refresh the image window.\n"
845  "\n"
846  " C Press to cut out a rectangular region of the image.\n"
847  "\n"
848  " [ Press to chop the image.\n"
849  "\n"
850  " H Press to flop image in the horizontal direction.\n"
851  "\n"
852  " V Press to flip image in the vertical direction.\n"
853  "\n"
854  " / Press to rotate the image 90 degrees clockwise.\n"
855  "\n"
856  " \\ Press to rotate the image 90 degrees counter-clockwise.\n"
857  "\n"
858  " * Press to rotate the image the number of degrees you\n"
859  " specify.\n"
860  "\n"
861  " S Press to shear the image the number of degrees you\n"
862  " specify.\n"
863  "\n"
864  " R Press to roll the image.\n"
865  "\n"
866  " T Press to trim the image edges.\n"
867  "\n"
868  " Shft-H Press to vary the image hue.\n"
869  "\n"
870  " Shft-S Press to vary the color saturation.\n"
871  "\n"
872  " Shft-L Press to vary the color brightness.\n"
873  "\n"
874  " Shft-G Press to gamma correct the image.\n"
875  "\n"
876  " Shft-C Press to sharpen the image contrast.\n"
877  "\n"
878  " Shft-Z Press to dull the image contrast.\n"
879  "\n"
880  " = Press to perform histogram equalization on the image.\n"
881  "\n"
882  " Shft-N Press to perform histogram normalization on the image.\n"
883  "\n"
884  " Shft-~ Press to negate the colors of the image.\n"
885  "\n"
886  " . Press to convert the image colors to gray.\n"
887  "\n"
888  " Shft-# Press to set the maximum number of unique colors in the\n"
889  " image.\n"
890  "\n"
891  " F2 Press to reduce the speckles in an image.\n"
892  "\n"
893  " F3 Press to eliminate peak noise from an image.\n"
894  "\n"
895  " F4 Press to add noise to an image.\n"
896  "\n"
897  " F5 Press to sharpen an image.\n"
898  "\n"
899  " F6 Press to delete an image file.\n"
900  "\n"
901  " F7 Press to threshold the image.\n"
902  "\n"
903  " F8 Press to detect edges within an image.\n"
904  "\n"
905  " F9 Press to emboss an image.\n"
906  "\n"
907  " F10 Press to displace pixels by a random amount.\n"
908  "\n"
909  " F11 Press to negate all pixels above the threshold level.\n"
910  "\n"
911  " F12 Press to shade the image using a distant light source.\n"
912  "\n"
913  " F13 Press to lighten or darken image edges to create a 3-D effect.\n"
914  "\n"
915  " F14 Press to segment the image by color.\n"
916  "\n"
917  " Meta-S Press to swirl image pixels about the center.\n"
918  "\n"
919  " Meta-I Press to implode image pixels about the center.\n"
920  "\n"
921  " Meta-W Press to alter an image along a sine wave.\n"
922  "\n"
923  " Meta-P Press to simulate an oil painting.\n"
924  "\n"
925  " Meta-C Press to simulate a charcoal drawing.\n"
926  "\n"
927  " Alt-A Press to annotate the image with text.\n"
928  "\n"
929  " Alt-D Press to draw on an image.\n"
930  "\n"
931  " Alt-P Press to edit an image pixel color.\n"
932  "\n"
933  " Alt-M Press to edit the image matte information.\n"
934  "\n"
935  " Alt-V Press to composite the image with another.\n"
936  "\n"
937  " Alt-B Press to add a border to the image.\n"
938  "\n"
939  " Alt-F Press to add an ornamental border to the image.\n"
940  "\n"
941  " Alt-Shft-!\n"
942  " Press to add an image comment.\n"
943  "\n"
944  " Ctl-A Press to apply image processing techniques to a region\n"
945  " of interest.\n"
946  "\n"
947  " Shft-? Press to display information about the image.\n"
948  "\n"
949  " Shft-+ Press to map the zoom image window.\n"
950  "\n"
951  " Shft-P Press to preview an image enhancement, effect, or f/x.\n"
952  "\n"
953  " F1 Press to display helpful information about display(1).\n"
954  "\n"
955  " Find Press to browse documentation about ImageMagick.\n"
956  "\n"
957  " 1-9 Press to change the level of magnification.\n"
958  "\n"
959  " Use the arrow keys to move the image one pixel up, down,\n"
960  " left, or right within the magnify window. Be sure to first\n"
961  " map the magnify window by pressing button 2.\n"
962  "\n"
963  " Press ALT and one of the arrow keys to trim off one pixel\n"
964  " from any side of the image.\n"
965  },
966  ImageMatteEditHelp[] =
967  {
968  "Matte information within an image is useful for some\n"
969  "operations such as image compositing (See IMAGE\n"
970  "COMPOSITING). This extra channel usually defines a mask\n"
971  "which represents a sort of a cookie-cutter for the image.\n"
972  "This the case when matte is opaque (full coverage) for\n"
973  "pixels inside the shape, zero outside, and between 0 and\n"
974  "QuantumRange on the boundary.\n"
975  "\n"
976  "A small window appears showing the location of the cursor in\n"
977  "the image window. You are now in matte edit mode. To exit\n"
978  "immediately, press Dismiss. In matte edit mode, the Command\n"
979  "widget has these options:\n"
980  "\n"
981  " Method\n"
982  " point\n"
983  " replace\n"
984  " floodfill\n"
985  " filltoborder\n"
986  " reset\n"
987  " Border Color\n"
988  " black\n"
989  " blue\n"
990  " cyan\n"
991  " green\n"
992  " gray\n"
993  " red\n"
994  " magenta\n"
995  " yellow\n"
996  " white\n"
997  " Browser...\n"
998  " Fuzz\n"
999  " 0%\n"
1000  " 2%\n"
1001  " 5%\n"
1002  " 10%\n"
1003  " 15%\n"
1004  " Dialog...\n"
1005  " Matte\n"
1006  " Opaque\n"
1007  " Transparent\n"
1008  " Dialog...\n"
1009  " Undo\n"
1010  " Help\n"
1011  " Dismiss\n"
1012  "\n"
1013  "Choose a matte editing method from the Method sub-menu of\n"
1014  "the Command widget. The point method changes the matte value\n"
1015  "of any pixel selected with the pointer until the button is\n"
1016  "is released. The replace method changes the matte value of\n"
1017  "any pixel that matches the color of the pixel you select with\n"
1018  "a button press. Floodfill changes the matte value of any pixel\n"
1019  "that matches the color of the pixel you select with a button\n"
1020  "press and is a neighbor. Whereas filltoborder changes the matte\n"
1021  "value any neighbor pixel that is not the border color. Finally\n"
1022  "reset changes the entire image to the designated matte value.\n"
1023  "\n"
1024  "Choose Matte Value and pick Opaque or Transarent. For other values\n"
1025  "select the Dialog entry. Here a dialog appears requesting a matte\n"
1026  "value. The value you select is assigned as the opacity value of the\n"
1027  "selected pixel or pixels.\n"
1028  "\n"
1029  "Now, press any button to select a pixel within the image\n"
1030  "window to change its matte value.\n"
1031  "\n"
1032  "If the Magnify widget is mapped, it can be helpful in positioning\n"
1033  "your pointer within the image (refer to button 2).\n"
1034  "\n"
1035  "Matte information is only valid in a DirectClass image.\n"
1036  "Therefore, any PseudoClass image is promoted to DirectClass\n"
1037  "(see miff(5)). Note that matte information for PseudoClass\n"
1038  "is not retained for colormapped X server visuals (e.g.\n"
1039  "StaticColor, StaticColor, GrayScale, PseudoColor) unless you\n"
1040  "immediately save your image to a file (refer to Write).\n"
1041  "Correct matte editing behavior may require a TrueColor or\n"
1042  "DirectColor visual or a Standard Colormap.\n"
1043  },
1044  ImagePanHelp[] =
1045  {
1046  "When an image exceeds the width or height of the X server\n"
1047  "screen, display maps a small panning icon. The rectangle\n"
1048  "within the panning icon shows the area that is currently\n"
1049  "displayed in the image window. To pan about the image,\n"
1050  "press any button and drag the pointer within the panning\n"
1051  "icon. The pan rectangle moves with the pointer and the\n"
1052  "image window is updated to reflect the location of the\n"
1053  "rectangle within the panning icon. When you have selected\n"
1054  "the area of the image you wish to view, release the button.\n"
1055  "\n"
1056  "Use the arrow keys to pan the image one pixel up, down,\n"
1057  "left, or right within the image window.\n"
1058  "\n"
1059  "The panning icon is withdrawn if the image becomes smaller\n"
1060  "than the dimensions of the X server screen.\n"
1061  },
1062  ImagePasteHelp[] =
1063  {
1064  "A small window appears showing the location of the cursor in\n"
1065  "the image window. You are now in paste mode. To exit\n"
1066  "immediately, press Dismiss. In paste mode, the Command\n"
1067  "widget has these options:\n"
1068  "\n"
1069  " Operators\n"
1070  " over\n"
1071  " in\n"
1072  " out\n"
1073  " atop\n"
1074  " xor\n"
1075  " plus\n"
1076  " minus\n"
1077  " add\n"
1078  " subtract\n"
1079  " difference\n"
1080  " replace\n"
1081  " Help\n"
1082  " Dismiss\n"
1083  "\n"
1084  "Choose a composite operation from the Operators sub-menu of\n"
1085  "the Command widget. How each operator behaves is described\n"
1086  "below. Image window is the image currently displayed on\n"
1087  "your X server and image is the image obtained with the File\n"
1088  "Browser widget.\n"
1089  "\n"
1090  "Over The result is the union of the two image shapes,\n"
1091  " with image obscuring image window in the region of\n"
1092  " overlap.\n"
1093  "\n"
1094  "In The result is simply image cut by the shape of\n"
1095  " image window. None of the image data of image\n"
1096  " window is in the result.\n"
1097  "\n"
1098  "Out The resulting image is image with the shape of\n"
1099  " image window cut out.\n"
1100  "\n"
1101  "Atop The result is the same shape as image image window,\n"
1102  " with image obscuring image window where the image\n"
1103  " shapes overlap. Note this differs from over\n"
1104  " because the portion of image outside image window's\n"
1105  " shape does not appear in the result.\n"
1106  "\n"
1107  "Xor The result is the image data from both image and\n"
1108  " image window that is outside the overlap region.\n"
1109  " The overlap region is blank.\n"
1110  "\n"
1111  "Plus The result is just the sum of the image data.\n"
1112  " Output values are cropped to QuantumRange (no overflow).\n"
1113  " This operation is independent of the matte\n"
1114  " channels.\n"
1115  "\n"
1116  "Minus The result of image - image window, with underflow\n"
1117  " cropped to zero.\n"
1118  "\n"
1119  "Add The result of image + image window, with overflow\n"
1120  " wrapping around (mod 256).\n"
1121  "\n"
1122  "Subtract The result of image - image window, with underflow\n"
1123  " wrapping around (mod 256). The add and subtract\n"
1124  " operators can be used to perform reversible\n"
1125  " transformations.\n"
1126  "\n"
1127  "Difference\n"
1128  " The result of abs(image - image window). This\n"
1129  " useful for comparing two very similar images.\n"
1130  "\n"
1131  "Copy The resulting image is image window replaced with\n"
1132  " image. Here the matte information is ignored.\n"
1133  "\n"
1134  "CopyRed The red layer of the image window is replace with\n"
1135  " the red layer of the image. The other layers are\n"
1136  " untouched.\n"
1137  "\n"
1138  "CopyGreen\n"
1139  " The green layer of the image window is replace with\n"
1140  " the green layer of the image. The other layers are\n"
1141  " untouched.\n"
1142  "\n"
1143  "CopyBlue The blue layer of the image window is replace with\n"
1144  " the blue layer of the image. The other layers are\n"
1145  " untouched.\n"
1146  "\n"
1147  "CopyOpacity\n"
1148  " The matte layer of the image window is replace with\n"
1149  " the matte layer of the image. The other layers are\n"
1150  " untouched.\n"
1151  "\n"
1152  "The image compositor requires a matte, or alpha channel in\n"
1153  "the image for some operations. This extra channel usually\n"
1154  "defines a mask which represents a sort of a cookie-cutter\n"
1155  "for the image. This the case when matte is opaque (full\n"
1156  "coverage) for pixels inside the shape, zero outside, and\n"
1157  "between 0 and QuantumRange on the boundary. If image does not\n"
1158  "have a matte channel, it is initialized with 0 for any pixel\n"
1159  "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
1160  "\n"
1161  "Note that matte information for image window is not retained\n"
1162  "for colormapped X server visuals (e.g. StaticColor,\n"
1163  "StaticColor, GrayScale, PseudoColor). Correct compositing\n"
1164  "behavior may require a TrueColor or DirectColor visual or a\n"
1165  "Standard Colormap.\n"
1166  "\n"
1167  "Choosing a composite operator is optional. The default\n"
1168  "operator is replace. However, you must choose a location to\n"
1169  "paste your image and press button 1. Press and hold the\n"
1170  "button before releasing and an outline of the image will\n"
1171  "appear to help you identify your location.\n"
1172  "\n"
1173  "The actual colors of the pasted image is saved. However,\n"
1174  "the color that appears in image window may be different.\n"
1175  "For example, on a monochrome screen image window will appear\n"
1176  "black or white even though your pasted image may have\n"
1177  "many colors. If the image is saved to a file it is written\n"
1178  "with the correct colors. To assure the correct colors are\n"
1179  "saved in the final image, any PseudoClass image is promoted\n"
1180  "to DirectClass (see miff(5)). To force a PseudoClass image\n"
1181  "to remain PseudoClass, use -colors.\n"
1182  },
1183  ImageROIHelp[] =
1184  {
1185  "In region of interest mode, the Command widget has these\n"
1186  "options:\n"
1187  "\n"
1188  " Help\n"
1189  " Dismiss\n"
1190  "\n"
1191  "To define a region of interest, press button 1 and drag.\n"
1192  "The region of interest is defined by a highlighted rectangle\n"
1193  "that expands or contracts as it follows the pointer. Once\n"
1194  "you are satisfied with the region of interest, release the\n"
1195  "button. You are now in apply mode. In apply mode the\n"
1196  "Command widget has these options:\n"
1197  "\n"
1198  " File\n"
1199  " Save...\n"
1200  " Print...\n"
1201  " Edit\n"
1202  " Undo\n"
1203  " Redo\n"
1204  " Transform\n"
1205  " Flop\n"
1206  " Flip\n"
1207  " Rotate Right\n"
1208  " Rotate Left\n"
1209  " Enhance\n"
1210  " Hue...\n"
1211  " Saturation...\n"
1212  " Brightness...\n"
1213  " Gamma...\n"
1214  " Spiff\n"
1215  " Dull\n"
1216  " Contrast Stretch\n"
1217  " Sigmoidal Contrast...\n"
1218  " Normalize\n"
1219  " Equalize\n"
1220  " Negate\n"
1221  " Grayscale\n"
1222  " Map...\n"
1223  " Quantize...\n"
1224  " Effects\n"
1225  " Despeckle\n"
1226  " Emboss\n"
1227  " Reduce Noise\n"
1228  " Sharpen...\n"
1229  " Blur...\n"
1230  " Threshold...\n"
1231  " Edge Detect...\n"
1232  " Spread...\n"
1233  " Shade...\n"
1234  " Raise...\n"
1235  " Segment...\n"
1236  " F/X\n"
1237  " Solarize...\n"
1238  " Sepia Tone...\n"
1239  " Swirl...\n"
1240  " Implode...\n"
1241  " Vignette...\n"
1242  " Wave...\n"
1243  " Oil Painting...\n"
1244  " Charcoal Drawing...\n"
1245  " Miscellany\n"
1246  " Image Info\n"
1247  " Zoom Image\n"
1248  " Show Preview...\n"
1249  " Show Histogram\n"
1250  " Show Matte\n"
1251  " Help\n"
1252  " Dismiss\n"
1253  "\n"
1254  "You can make adjustments to the region of interest by moving\n"
1255  "the pointer to one of the rectangle corners, pressing a\n"
1256  "button, and dragging. Finally, choose an image processing\n"
1257  "technique from the Command widget. You can choose more than\n"
1258  "one image processing technique to apply to an area.\n"
1259  "Alternatively, you can move the region of interest before\n"
1260  "applying another image processing technique. To exit, press\n"
1261  "Dismiss.\n"
1262  },
1263  ImageRotateHelp[] =
1264  {
1265  "In rotate mode, the Command widget has these options:\n"
1266  "\n"
1267  " Pixel Color\n"
1268  " black\n"
1269  " blue\n"
1270  " cyan\n"
1271  " green\n"
1272  " gray\n"
1273  " red\n"
1274  " magenta\n"
1275  " yellow\n"
1276  " white\n"
1277  " Browser...\n"
1278  " Direction\n"
1279  " horizontal\n"
1280  " vertical\n"
1281  " Help\n"
1282  " Dismiss\n"
1283  "\n"
1284  "Choose a background color from the Pixel Color sub-menu.\n"
1285  "Additional background colors can be specified with the color\n"
1286  "browser. You can change the menu colors by setting the X\n"
1287  "resources pen1 through pen9.\n"
1288  "\n"
1289  "If you choose the color browser and press Grab, you can\n"
1290  "select the background color by moving the pointer to the\n"
1291  "desired color on the screen and press any button.\n"
1292  "\n"
1293  "Choose a point in the image window and press this button and\n"
1294  "hold. Next, move the pointer to another location in the\n"
1295  "image. As you move a line connects the initial location and\n"
1296  "the pointer. When you release the button, the degree of\n"
1297  "image rotation is determined by the slope of the line you\n"
1298  "just drew. The slope is relative to the direction you\n"
1299  "choose from the Direction sub-menu of the Command widget.\n"
1300  "\n"
1301  "To cancel the image rotation, move the pointer back to the\n"
1302  "starting point of the line and release the button.\n"
1303  };
1304 
1305 /*
1306  Enumeration declarations.
1307 */
1308 typedef enum
1309 {
1310  CopyMode,
1311  CropMode,
1312  CutMode
1313 } ClipboardMode;
1314 
1315 typedef enum
1316 {
1317  OpenCommand,
1318  NextCommand,
1319  FormerCommand,
1320  SelectCommand,
1321  SaveCommand,
1322  PrintCommand,
1323  DeleteCommand,
1324  NewCommand,
1325  VisualDirectoryCommand,
1326  QuitCommand,
1327  UndoCommand,
1328  RedoCommand,
1329  CutCommand,
1330  CopyCommand,
1331  PasteCommand,
1332  HalfSizeCommand,
1333  OriginalSizeCommand,
1334  DoubleSizeCommand,
1335  ResizeCommand,
1336  ApplyCommand,
1337  RefreshCommand,
1338  RestoreCommand,
1339  CropCommand,
1340  ChopCommand,
1341  FlopCommand,
1342  FlipCommand,
1343  RotateRightCommand,
1344  RotateLeftCommand,
1345  RotateCommand,
1346  ShearCommand,
1347  RollCommand,
1348  TrimCommand,
1349  HueCommand,
1350  SaturationCommand,
1351  BrightnessCommand,
1352  GammaCommand,
1353  SpiffCommand,
1354  DullCommand,
1355  ContrastStretchCommand,
1356  SigmoidalContrastCommand,
1357  NormalizeCommand,
1358  EqualizeCommand,
1359  NegateCommand,
1360  GrayscaleCommand,
1361  MapCommand,
1362  QuantizeCommand,
1363  DespeckleCommand,
1364  EmbossCommand,
1365  ReduceNoiseCommand,
1366  AddNoiseCommand,
1367  SharpenCommand,
1368  BlurCommand,
1369  ThresholdCommand,
1370  EdgeDetectCommand,
1371  SpreadCommand,
1372  ShadeCommand,
1373  RaiseCommand,
1374  SegmentCommand,
1375  SolarizeCommand,
1376  SepiaToneCommand,
1377  SwirlCommand,
1378  ImplodeCommand,
1379  VignetteCommand,
1380  WaveCommand,
1381  OilPaintCommand,
1382  CharcoalDrawCommand,
1383  AnnotateCommand,
1384  DrawCommand,
1385  ColorCommand,
1386  MatteCommand,
1387  CompositeCommand,
1388  AddBorderCommand,
1389  AddFrameCommand,
1390  CommentCommand,
1391  LaunchCommand,
1392  RegionofInterestCommand,
1393  ROIHelpCommand,
1394  ROIDismissCommand,
1395  InfoCommand,
1396  ZoomCommand,
1397  ShowPreviewCommand,
1398  ShowHistogramCommand,
1399  ShowMatteCommand,
1400  BackgroundCommand,
1401  SlideShowCommand,
1402  PreferencesCommand,
1403  HelpCommand,
1404  BrowseDocumentationCommand,
1405  VersionCommand,
1406  SaveToUndoBufferCommand,
1407  FreeBuffersCommand,
1408  NullCommand
1409 } CommandType;
1410 
1411 typedef enum
1412 {
1413  AnnotateNameCommand,
1414  AnnotateFontColorCommand,
1415  AnnotateBackgroundColorCommand,
1416  AnnotateRotateCommand,
1417  AnnotateHelpCommand,
1418  AnnotateDismissCommand,
1419  TextHelpCommand,
1420  TextApplyCommand,
1421  ChopDirectionCommand,
1422  ChopHelpCommand,
1423  ChopDismissCommand,
1424  HorizontalChopCommand,
1425  VerticalChopCommand,
1426  ColorEditMethodCommand,
1427  ColorEditColorCommand,
1428  ColorEditBorderCommand,
1429  ColorEditFuzzCommand,
1430  ColorEditUndoCommand,
1431  ColorEditHelpCommand,
1432  ColorEditDismissCommand,
1433  CompositeOperatorsCommand,
1434  CompositeDissolveCommand,
1435  CompositeDisplaceCommand,
1436  CompositeHelpCommand,
1437  CompositeDismissCommand,
1438  CropHelpCommand,
1439  CropDismissCommand,
1440  RectifyCopyCommand,
1441  RectifyHelpCommand,
1442  RectifyDismissCommand,
1443  DrawElementCommand,
1444  DrawColorCommand,
1445  DrawStippleCommand,
1446  DrawWidthCommand,
1447  DrawUndoCommand,
1448  DrawHelpCommand,
1449  DrawDismissCommand,
1450  MatteEditMethod,
1451  MatteEditBorderCommand,
1452  MatteEditFuzzCommand,
1453  MatteEditValueCommand,
1454  MatteEditUndoCommand,
1455  MatteEditHelpCommand,
1456  MatteEditDismissCommand,
1457  PasteOperatorsCommand,
1458  PasteHelpCommand,
1459  PasteDismissCommand,
1460  RotateColorCommand,
1461  RotateDirectionCommand,
1462  RotateCropCommand,
1463  RotateSharpenCommand,
1464  RotateHelpCommand,
1465  RotateDismissCommand,
1466  HorizontalRotateCommand,
1467  VerticalRotateCommand,
1468  TileLoadCommand,
1469  TileNextCommand,
1470  TileFormerCommand,
1471  TileDeleteCommand,
1472  TileUpdateCommand
1473 } ModeType;
1474 
1475 /*
1476  Stipples.
1477 */
1478 #define BricksWidth 20
1479 #define BricksHeight 20
1480 #define DiagonalWidth 16
1481 #define DiagonalHeight 16
1482 #define HighlightWidth 8
1483 #define HighlightHeight 8
1484 #define OpaqueWidth 8
1485 #define OpaqueHeight 8
1486 #define ScalesWidth 16
1487 #define ScalesHeight 16
1488 #define ShadowWidth 8
1489 #define ShadowHeight 8
1490 #define VerticalWidth 16
1491 #define VerticalHeight 16
1492 #define WavyWidth 16
1493 #define WavyHeight 16
1494 
1495 /*
1496  Constant declaration.
1497 */
1498 static const int
1499  RoiDelta = 8;
1500 
1501 static const unsigned char
1502  BricksBitmap[] =
1503  {
1504  0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1505  0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1506  0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1507  0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1508  0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1509  },
1510  DiagonalBitmap[] =
1511  {
1512  0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1513  0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1514  0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1515  },
1516  ScalesBitmap[] =
1517  {
1518  0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1519  0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1520  0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1521  },
1522  VerticalBitmap[] =
1523  {
1524  0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1525  0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1526  0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1527  },
1528  WavyBitmap[] =
1529  {
1530  0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1531  0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1532  0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1533  };
1534 
1535 /*
1536  Function prototypes.
1537 */
1538 static CommandType
1539  XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1540  const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1541 
1542 static Image
1543  *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1544  Image **,ExceptionInfo *),
1545  *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1546  *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1547  ExceptionInfo *),
1548  *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1549  ExceptionInfo *);
1550 
1551 static MagickBooleanType
1552  XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1553  ExceptionInfo *),
1554  XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1555  ExceptionInfo *),
1556  XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1557  ExceptionInfo *),
1558  XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1559  ExceptionInfo *),
1560  XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1561  ExceptionInfo *),
1562  XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1563  ExceptionInfo *),
1564  XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1565  XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1566  ExceptionInfo *),
1567  XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1568  ExceptionInfo *),
1569  XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1570  XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1571  XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1572  ExceptionInfo *),
1573  XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1574  XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1575  XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1576 
1577 static void
1578  XDrawPanRectangle(Display *,XWindows *),
1579  XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1580  ExceptionInfo *),
1581  XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1582  XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1583  XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1584  XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1585  const KeySym,ExceptionInfo *),
1586  XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1587  XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1588  XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1589 
1590 /*
1591 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1592 % %
1593 % %
1594 % %
1595 % D i s p l a y I m a g e s %
1596 % %
1597 % %
1598 % %
1599 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1600 %
1601 % DisplayImages() displays an image sequence to any X window screen. It
1602 % returns a value other than 0 if successful. Check the exception member
1603 % of image to determine the reason for any failure.
1604 %
1605 % The format of the DisplayImages method is:
1606 %
1607 % MagickBooleanType DisplayImages(const ImageInfo *image_info,
1608 % Image *images,ExceptionInfo *exception)
1609 %
1610 % A description of each parameter follows:
1611 %
1612 % o image_info: the image info.
1613 %
1614 % o image: the image.
1615 %
1616 % o exception: return any errors or warnings in this structure.
1617 %
1618 */
1620  Image *images,ExceptionInfo *exception)
1621 {
1622  char
1623  *argv[1];
1624 
1625  Display
1626  *display;
1627 
1628  Image
1629  *image;
1630 
1631  register ssize_t
1632  i;
1633 
1634  size_t
1635  state;
1636 
1637  XrmDatabase
1638  resource_database;
1639 
1640  XResourceInfo
1641  resource_info;
1642 
1643  assert(image_info != (const ImageInfo *) NULL);
1644  assert(image_info->signature == MagickCoreSignature);
1645  assert(images != (Image *) NULL);
1646  assert(images->signature == MagickCoreSignature);
1647  if (images->debug != MagickFalse )
1648  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1649  display=XOpenDisplay(image_info->server_name);
1650  if (display == (Display *) NULL)
1651  {
1653  "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1654  return(MagickFalse);
1655  }
1656  if (exception->severity != UndefinedException)
1657  CatchException(exception);
1658  (void) XSetErrorHandler(XError);
1659  resource_database=XGetResourceDatabase(display,GetClientName());
1660  (void) memset(&resource_info,0,sizeof(resource_info));
1661  XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1662  if (image_info->page != (char *) NULL)
1663  resource_info.image_geometry=AcquireString(image_info->page);
1664  resource_info.immutable=MagickTrue;
1665  argv[0]=AcquireString(GetClientName());
1666  state=DefaultState;
1667  for (i=0; (state & ExitState) == 0; i++)
1668  {
1669  if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1670  break;
1671  image=GetImageFromList(images,i % GetImageListLength(images));
1672  (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1673  }
1674  (void) SetErrorHandler((ErrorHandler) NULL);
1675  (void) SetWarningHandler((WarningHandler) NULL);
1676  argv[0]=DestroyString(argv[0]);
1677  (void) XCloseDisplay(display);
1678  XDestroyResourceInfo(&resource_info);
1679  if (exception->severity != UndefinedException)
1680  return(MagickFalse);
1681  return(MagickTrue);
1682 }
1683 
1684 /*
1685 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1686 % %
1687 % %
1688 % %
1689 % R e m o t e D i s p l a y C o m m a n d %
1690 % %
1691 % %
1692 % %
1693 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1694 %
1695 % RemoteDisplayCommand() encourages a remote display program to display the
1696 % specified image filename.
1697 %
1698 % The format of the RemoteDisplayCommand method is:
1699 %
1700 % MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1701 % const char *window,const char *filename,ExceptionInfo *exception)
1702 %
1703 % A description of each parameter follows:
1704 %
1705 % o image_info: the image info.
1706 %
1707 % o window: Specifies the name or id of an X window.
1708 %
1709 % o filename: the name of the image filename to display.
1710 %
1711 % o exception: return any errors or warnings in this structure.
1712 %
1713 */
1715  const char *window,const char *filename,ExceptionInfo *exception)
1716 {
1717  Display
1718  *display;
1719 
1721  status;
1722 
1723  assert(image_info != (const ImageInfo *) NULL);
1724  assert(image_info->signature == MagickCoreSignature);
1725  assert(filename != (char *) NULL);
1726  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1727  display=XOpenDisplay(image_info->server_name);
1728  if (display == (Display *) NULL)
1729  {
1731  "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1732  return(MagickFalse);
1733  }
1734  (void) XSetErrorHandler(XError);
1735  status=XRemoteCommand(display,window,filename);
1736  (void) XCloseDisplay(display);
1737  return(status != 0 ? MagickTrue : MagickFalse);
1738 }
1739 
1740 /*
1741 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1742 % %
1743 % %
1744 % %
1745 + X A n n o t a t e E d i t I m a g e %
1746 % %
1747 % %
1748 % %
1749 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1750 %
1751 % XAnnotateEditImage() annotates the image with text.
1752 %
1753 % The format of the XAnnotateEditImage method is:
1754 %
1755 % MagickBooleanType XAnnotateEditImage(Display *display,
1756 % XResourceInfo *resource_info,XWindows *windows,Image *image,
1757 % ExceptionInfo *exception)
1758 %
1759 % A description of each parameter follows:
1760 %
1761 % o display: Specifies a connection to an X server; returned from
1762 % XOpenDisplay.
1763 %
1764 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1765 %
1766 % o windows: Specifies a pointer to a XWindows structure.
1767 %
1768 % o image: the image; returned from ReadImage.
1769 %
1770 */
1771 
1772 static MagickBooleanType XAnnotateEditImage(Display *display,
1773  XResourceInfo *resource_info,XWindows *windows,Image *image,
1774  ExceptionInfo *exception)
1775 {
1776  const char
1777  *const AnnotateMenu[] =
1778  {
1779  "Font Name",
1780  "Font Color",
1781  "Box Color",
1782  "Rotate Text",
1783  "Help",
1784  "Dismiss",
1785  (char *) NULL
1786  },
1787  *const TextMenu[] =
1788  {
1789  "Help",
1790  "Apply",
1791  (char *) NULL
1792  };
1793 
1794  static const ModeType
1795  AnnotateCommands[] =
1796  {
1797  AnnotateNameCommand,
1798  AnnotateFontColorCommand,
1799  AnnotateBackgroundColorCommand,
1800  AnnotateRotateCommand,
1801  AnnotateHelpCommand,
1802  AnnotateDismissCommand
1803  },
1804  TextCommands[] =
1805  {
1806  TextHelpCommand,
1807  TextApplyCommand
1808  };
1809 
1810  static MagickBooleanType
1811  transparent_box = MagickTrue,
1812  transparent_pen = MagickFalse;
1813 
1814  static double
1815  degrees = 0.0;
1816 
1817  static unsigned int
1818  box_id = MaxNumberPens-2,
1819  font_id = 0,
1820  pen_id = 0;
1821 
1822  char
1823  command[MagickPathExtent],
1824  text[MagickPathExtent];
1825 
1826  const char
1827  *ColorMenu[MaxNumberPens+1];
1828 
1829  Cursor
1830  cursor;
1831 
1832  GC
1833  annotate_context;
1834 
1835  int
1836  id,
1837  pen_number,
1838  status,
1839  x,
1840  y;
1841 
1842  KeySym
1843  key_symbol;
1844 
1845  register char
1846  *p;
1847 
1848  register ssize_t
1849  i;
1850 
1851  unsigned int
1852  height,
1853  width;
1854 
1855  size_t
1856  state;
1857 
1858  XAnnotateInfo
1859  *annotate_info,
1860  *previous_info;
1861 
1862  XColor
1863  color;
1864 
1865  XFontStruct
1866  *font_info;
1867 
1868  XEvent
1869  event,
1870  text_event;
1871 
1872  /*
1873  Map Command widget.
1874  */
1875  (void) CloneString(&windows->command.name,"Annotate");
1876  windows->command.data=4;
1877  (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1878  (void) XMapRaised(display,windows->command.id);
1879  XClientMessage(display,windows->image.id,windows->im_protocols,
1880  windows->im_update_widget,CurrentTime);
1881  /*
1882  Track pointer until button 1 is pressed.
1883  */
1884  XQueryPosition(display,windows->image.id,&x,&y);
1885  (void) XSelectInput(display,windows->image.id,
1886  windows->image.attributes.event_mask | PointerMotionMask);
1887  cursor=XCreateFontCursor(display,XC_left_side);
1888  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1889  state=DefaultState;
1890  do
1891  {
1892  if (windows->info.mapped != MagickFalse )
1893  {
1894  /*
1895  Display pointer position.
1896  */
1897  (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
1898  x+windows->image.x,y+windows->image.y);
1899  XInfoWidget(display,windows,text);
1900  }
1901  /*
1902  Wait for next event.
1903  */
1904  XScreenEvent(display,windows,&event,exception);
1905  if (event.xany.window == windows->command.id)
1906  {
1907  /*
1908  Select a command from the Command widget.
1909  */
1910  id=XCommandWidget(display,windows,AnnotateMenu,&event);
1911  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1912  if (id < 0)
1913  continue;
1914  switch (AnnotateCommands[id])
1915  {
1916  case AnnotateNameCommand:
1917  {
1918  const char
1919  *FontMenu[MaxNumberFonts];
1920 
1921  int
1922  font_number;
1923 
1924  /*
1925  Initialize menu selections.
1926  */
1927  for (i=0; i < MaxNumberFonts; i++)
1928  FontMenu[i]=resource_info->font_name[i];
1929  FontMenu[MaxNumberFonts-2]="Browser...";
1930  FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1931  /*
1932  Select a font name from the pop-up menu.
1933  */
1934  font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1935  (const char **) FontMenu,command);
1936  if (font_number < 0)
1937  break;
1938  if (font_number == (MaxNumberFonts-2))
1939  {
1940  static char
1941  font_name[MagickPathExtent] = "fixed";
1942 
1943  /*
1944  Select a font name from a browser.
1945  */
1946  resource_info->font_name[font_number]=font_name;
1947  XFontBrowserWidget(display,windows,"Select",font_name);
1948  if (*font_name == '\0')
1949  break;
1950  }
1951  /*
1952  Initialize font info.
1953  */
1954  font_info=XLoadQueryFont(display,resource_info->font_name[
1955  font_number]);
1956  if (font_info == (XFontStruct *) NULL)
1957  {
1958  XNoticeWidget(display,windows,"Unable to load font:",
1959  resource_info->font_name[font_number]);
1960  break;
1961  }
1962  font_id=(unsigned int) font_number;
1963  (void) XFreeFont(display,font_info);
1964  break;
1965  }
1966  case AnnotateFontColorCommand:
1967  {
1968  /*
1969  Initialize menu selections.
1970  */
1971  for (i=0; i < (int) (MaxNumberPens-2); i++)
1972  ColorMenu[i]=resource_info->pen_colors[i];
1973  ColorMenu[MaxNumberPens-2]="transparent";
1974  ColorMenu[MaxNumberPens-1]="Browser...";
1975  ColorMenu[MaxNumberPens]=(const char *) NULL;
1976  /*
1977  Select a pen color from the pop-up menu.
1978  */
1979  pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
1980  (const char **) ColorMenu,command);
1981  if (pen_number < 0)
1982  break;
1983  transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
1984  MagickFalse;
1985  if (transparent_pen != MagickFalse )
1986  break;
1987  if (pen_number == (MaxNumberPens-1))
1988  {
1989  static char
1990  color_name[MagickPathExtent] = "gray";
1991 
1992  /*
1993  Select a pen color from a dialog.
1994  */
1995  resource_info->pen_colors[pen_number]=color_name;
1996  XColorBrowserWidget(display,windows,"Select",color_name);
1997  if (*color_name == '\0')
1998  break;
1999  }
2000  /*
2001  Set pen color.
2002  */
2003  (void) XParseColor(display,windows->map_info->colormap,
2004  resource_info->pen_colors[pen_number],&color);
2005  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2006  (unsigned int) MaxColors,&color);
2007  windows->pixel_info->pen_colors[pen_number]=color;
2008  pen_id=(unsigned int) pen_number;
2009  break;
2010  }
2011  case AnnotateBackgroundColorCommand:
2012  {
2013  /*
2014  Initialize menu selections.
2015  */
2016  for (i=0; i < (int) (MaxNumberPens-2); i++)
2017  ColorMenu[i]=resource_info->pen_colors[i];
2018  ColorMenu[MaxNumberPens-2]="transparent";
2019  ColorMenu[MaxNumberPens-1]="Browser...";
2020  ColorMenu[MaxNumberPens]=(const char *) NULL;
2021  /*
2022  Select a pen color from the pop-up menu.
2023  */
2024  pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2025  (const char **) ColorMenu,command);
2026  if (pen_number < 0)
2027  break;
2028  transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2029  MagickFalse;
2030  if (transparent_box != MagickFalse )
2031  break;
2032  if (pen_number == (MaxNumberPens-1))
2033  {
2034  static char
2035  color_name[MagickPathExtent] = "gray";
2036 
2037  /*
2038  Select a pen color from a dialog.
2039  */
2040  resource_info->pen_colors[pen_number]=color_name;
2041  XColorBrowserWidget(display,windows,"Select",color_name);
2042  if (*color_name == '\0')
2043  break;
2044  }
2045  /*
2046  Set pen color.
2047  */
2048  (void) XParseColor(display,windows->map_info->colormap,
2049  resource_info->pen_colors[pen_number],&color);
2050  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2051  (unsigned int) MaxColors,&color);
2052  windows->pixel_info->pen_colors[pen_number]=color;
2053  box_id=(unsigned int) pen_number;
2054  break;
2055  }
2056  case AnnotateRotateCommand:
2057  {
2058  int
2059  entry;
2060 
2061  const char
2062  *const RotateMenu[] =
2063  {
2064  "-90",
2065  "-45",
2066  "-30",
2067  "0",
2068  "30",
2069  "45",
2070  "90",
2071  "180",
2072  "Dialog...",
2073  (char *) NULL,
2074  };
2075 
2076  static char
2077  angle[MagickPathExtent] = "30.0";
2078 
2079  /*
2080  Select a command from the pop-up menu.
2081  */
2082  entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2083  command);
2084  if (entry < 0)
2085  break;
2086  if (entry != 8)
2087  {
2088  degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2089  break;
2090  }
2091  (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2092  angle);
2093  if (*angle == '\0')
2094  break;
2095  degrees=StringToDouble(angle,(char **) NULL);
2096  break;
2097  }
2098  case AnnotateHelpCommand:
2099  {
2100  XTextViewHelp(display,resource_info,windows,MagickFalse,
2101  "Help Viewer - Image Annotation",ImageAnnotateHelp);
2102  break;
2103  }
2104  case AnnotateDismissCommand:
2105  {
2106  /*
2107  Prematurely exit.
2108  */
2109  state|=EscapeState;
2110  state|=ExitState;
2111  break;
2112  }
2113  default:
2114  break;
2115  }
2116  continue;
2117  }
2118  switch (event.type)
2119  {
2120  case ButtonPress:
2121  {
2122  if (event.xbutton.button != Button1)
2123  break;
2124  if (event.xbutton.window != windows->image.id)
2125  break;
2126  /*
2127  Change to text entering mode.
2128  */
2129  x=event.xbutton.x;
2130  y=event.xbutton.y;
2131  state|=ExitState;
2132  break;
2133  }
2134  case ButtonRelease:
2135  break;
2136  case Expose:
2137  break;
2138  case KeyPress:
2139  {
2140  if (event.xkey.window != windows->image.id)
2141  break;
2142  /*
2143  Respond to a user key press.
2144  */
2145  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2146  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2147  switch ((int) key_symbol)
2148  {
2149  case XK_Escape:
2150  case XK_F20:
2151  {
2152  /*
2153  Prematurely exit.
2154  */
2155  state|=EscapeState;
2156  state|=ExitState;
2157  break;
2158  }
2159  case XK_F1:
2160  case XK_Help:
2161  {
2162  XTextViewHelp(display,resource_info,windows,MagickFalse,
2163  "Help Viewer - Image Annotation",ImageAnnotateHelp);
2164  break;
2165  }
2166  default:
2167  {
2168  (void) XBell(display,0);
2169  break;
2170  }
2171  }
2172  break;
2173  }
2174  case MotionNotify:
2175  {
2176  /*
2177  Map and unmap Info widget as cursor crosses its boundaries.
2178  */
2179  x=event.xmotion.x;
2180  y=event.xmotion.y;
2181  if (windows->info.mapped != MagickFalse )
2182  {
2183  if ((x < (int) (windows->info.x+windows->info.width)) &&
2184  (y < (int) (windows->info.y+windows->info.height)))
2185  (void) XWithdrawWindow(display,windows->info.id,
2186  windows->info.screen);
2187  }
2188  else
2189  if ((x > (int) (windows->info.x+windows->info.width)) ||
2190  (y > (int) (windows->info.y+windows->info.height)))
2191  (void) XMapWindow(display,windows->info.id);
2192  break;
2193  }
2194  default:
2195  break;
2196  }
2197  } while ((state & ExitState) == 0);
2198  (void) XSelectInput(display,windows->image.id,
2199  windows->image.attributes.event_mask);
2200  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2201  if ((state & EscapeState) != 0)
2202  return(MagickTrue);
2203  /*
2204  Set font info and check boundary conditions.
2205  */
2206  font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2207  if (font_info == (XFontStruct *) NULL)
2208  {
2209  XNoticeWidget(display,windows,"Unable to load font:",
2210  resource_info->font_name[font_id]);
2211  font_info=windows->font_info;
2212  }
2213  if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2214  x=(int) windows->image.width-font_info->max_bounds.width;
2215  if (y < (int) (font_info->ascent+font_info->descent))
2216  y=(int) font_info->ascent+font_info->descent;
2217  if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2218  ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2219  return(MagickFalse);
2220  /*
2221  Initialize annotate structure.
2222  */
2223  annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2224  if (annotate_info == (XAnnotateInfo *) NULL)
2225  return(MagickFalse);
2226  XGetAnnotateInfo(annotate_info);
2227  annotate_info->x=x;
2228  annotate_info->y=y;
2229  if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2230  annotate_info->stencil=OpaqueStencil;
2231  else
2232  if (transparent_box == MagickFalse)
2233  annotate_info->stencil=BackgroundStencil;
2234  else
2235  annotate_info->stencil=ForegroundStencil;
2236  annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2237  annotate_info->degrees=degrees;
2238  annotate_info->font_info=font_info;
2239  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2240  windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2241  sizeof(*annotate_info->text));
2242  if (annotate_info->text == (char *) NULL)
2243  return(MagickFalse);
2244  /*
2245  Create cursor and set graphic context.
2246  */
2247  cursor=XCreateFontCursor(display,XC_pencil);
2248  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2249  annotate_context=windows->image.annotate_context;
2250  (void) XSetFont(display,annotate_context,font_info->fid);
2251  (void) XSetBackground(display,annotate_context,
2252  windows->pixel_info->pen_colors[box_id].pixel);
2253  (void) XSetForeground(display,annotate_context,
2254  windows->pixel_info->pen_colors[pen_id].pixel);
2255  /*
2256  Begin annotating the image with text.
2257  */
2258  (void) CloneString(&windows->command.name,"Text");
2259  windows->command.data=0;
2260  (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2261  state=DefaultState;
2262  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2263  text_event.xexpose.width=(int) font_info->max_bounds.width;
2264  text_event.xexpose.height=font_info->max_bounds.ascent+
2265  font_info->max_bounds.descent;
2266  p=annotate_info->text;
2267  do
2268  {
2269  /*
2270  Display text cursor.
2271  */
2272  *p='\0';
2273  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2274  /*
2275  Wait for next event.
2276  */
2277  XScreenEvent(display,windows,&event,exception);
2278  if (event.xany.window == windows->command.id)
2279  {
2280  /*
2281  Select a command from the Command widget.
2282  */
2283  (void) XSetBackground(display,annotate_context,
2284  windows->pixel_info->background_color.pixel);
2285  (void) XSetForeground(display,annotate_context,
2286  windows->pixel_info->foreground_color.pixel);
2287  id=XCommandWidget(display,windows,AnnotateMenu,&event);
2288  (void) XSetBackground(display,annotate_context,
2289  windows->pixel_info->pen_colors[box_id].pixel);
2290  (void) XSetForeground(display,annotate_context,
2291  windows->pixel_info->pen_colors[pen_id].pixel);
2292  if (id < 0)
2293  continue;
2294  switch (TextCommands[id])
2295  {
2296  case TextHelpCommand:
2297  {
2298  XTextViewHelp(display,resource_info,windows,MagickFalse,
2299  "Help Viewer - Image Annotation",ImageAnnotateHelp);
2300  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2301  break;
2302  }
2303  case TextApplyCommand:
2304  {
2305  /*
2306  Finished annotating.
2307  */
2308  annotate_info->width=(unsigned int) XTextWidth(font_info,
2309  annotate_info->text,(int) strlen(annotate_info->text));
2310  XRefreshWindow(display,&windows->image,&text_event);
2311  state|=ExitState;
2312  break;
2313  }
2314  default:
2315  break;
2316  }
2317  continue;
2318  }
2319  /*
2320  Erase text cursor.
2321  */
2322  text_event.xexpose.x=x;
2323  text_event.xexpose.y=y-font_info->max_bounds.ascent;
2324  (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2325  (unsigned int) text_event.xexpose.width,(unsigned int)
2326  text_event.xexpose.height,MagickFalse);
2327  XRefreshWindow(display,&windows->image,&text_event);
2328  switch (event.type)
2329  {
2330  case ButtonPress:
2331  {
2332  if (event.xbutton.window != windows->image.id)
2333  break;
2334  if (event.xbutton.button == Button2)
2335  {
2336  /*
2337  Request primary selection.
2338  */
2339  (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2340  windows->image.id,CurrentTime);
2341  break;
2342  }
2343  break;
2344  }
2345  case Expose:
2346  {
2347  if (event.xexpose.count == 0)
2348  {
2349  XAnnotateInfo
2350  *text_info;
2351 
2352  /*
2353  Refresh Image window.
2354  */
2355  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2356  text_info=annotate_info;
2357  while (text_info != (XAnnotateInfo *) NULL)
2358  {
2359  if (annotate_info->stencil == ForegroundStencil)
2360  (void) XDrawString(display,windows->image.id,annotate_context,
2361  text_info->x,text_info->y,text_info->text,
2362  (int) strlen(text_info->text));
2363  else
2364  (void) XDrawImageString(display,windows->image.id,
2365  annotate_context,text_info->x,text_info->y,text_info->text,
2366  (int) strlen(text_info->text));
2367  text_info=text_info->previous;
2368  }
2369  (void) XDrawString(display,windows->image.id,annotate_context,
2370  x,y,"_",1);
2371  }
2372  break;
2373  }
2374  case KeyPress:
2375  {
2376  int
2377  length;
2378 
2379  if (event.xkey.window != windows->image.id)
2380  break;
2381  /*
2382  Respond to a user key press.
2383  */
2384  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2385  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2386  *(command+length)='\0';
2387  if (((event.xkey.state & ControlMask) != 0) ||
2388  ((event.xkey.state & Mod1Mask) != 0))
2389  state|=ModifierState;
2390  if ((state & ModifierState) != 0)
2391  switch ((int) key_symbol)
2392  {
2393  case XK_u:
2394  case XK_U:
2395  {
2396  key_symbol=DeleteCommand;
2397  break;
2398  }
2399  default:
2400  break;
2401  }
2402  switch ((int) key_symbol)
2403  {
2404  case XK_BackSpace:
2405  {
2406  /*
2407  Erase one character.
2408  */
2409  if (p == annotate_info->text)
2410  {
2411  if (annotate_info->previous == (XAnnotateInfo *) NULL)
2412  break;
2413  else
2414  {
2415  /*
2416  Go to end of the previous line of text.
2417  */
2418  annotate_info=annotate_info->previous;
2419  p=annotate_info->text;
2420  x=annotate_info->x+annotate_info->width;
2421  y=annotate_info->y;
2422  if (annotate_info->width != 0)
2423  p+=strlen(annotate_info->text);
2424  break;
2425  }
2426  }
2427  p--;
2428  x-=XTextWidth(font_info,p,1);
2429  text_event.xexpose.x=x;
2430  text_event.xexpose.y=y-font_info->max_bounds.ascent;
2431  XRefreshWindow(display,&windows->image,&text_event);
2432  break;
2433  }
2434  case XK_bracketleft:
2435  {
2436  key_symbol=XK_Escape;
2437  break;
2438  }
2439  case DeleteCommand:
2440  {
2441  /*
2442  Erase the entire line of text.
2443  */
2444  while (p != annotate_info->text)
2445  {
2446  p--;
2447  x-=XTextWidth(font_info,p,1);
2448  text_event.xexpose.x=x;
2449  XRefreshWindow(display,&windows->image,&text_event);
2450  }
2451  break;
2452  }
2453  case XK_Escape:
2454  case XK_F20:
2455  {
2456  /*
2457  Finished annotating.
2458  */
2459  annotate_info->width=(unsigned int) XTextWidth(font_info,
2460  annotate_info->text,(int) strlen(annotate_info->text));
2461  XRefreshWindow(display,&windows->image,&text_event);
2462  state|=ExitState;
2463  break;
2464  }
2465  default:
2466  {
2467  /*
2468  Draw a single character on the Image window.
2469  */
2470  if ((state & ModifierState) != 0)
2471  break;
2472  if (*command == '\0')
2473  break;
2474  *p=(*command);
2475  if (annotate_info->stencil == ForegroundStencil)
2476  (void) XDrawString(display,windows->image.id,annotate_context,
2477  x,y,p,1);
2478  else
2479  (void) XDrawImageString(display,windows->image.id,
2480  annotate_context,x,y,p,1);
2481  x+=XTextWidth(font_info,p,1);
2482  p++;
2483  if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2484  break;
2485  }
2486  case XK_Return:
2487  case XK_KP_Enter:
2488  {
2489  /*
2490  Advance to the next line of text.
2491  */
2492  *p='\0';
2493  annotate_info->width=(unsigned int) XTextWidth(font_info,
2494  annotate_info->text,(int) strlen(annotate_info->text));
2495  if (annotate_info->next != (XAnnotateInfo *) NULL)
2496  {
2497  /*
2498  Line of text already exists.
2499  */
2500  annotate_info=annotate_info->next;
2501  x=annotate_info->x;
2502  y=annotate_info->y;
2503  p=annotate_info->text;
2504  break;
2505  }
2506  annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2507  sizeof(*annotate_info->next));
2508  if (annotate_info->next == (XAnnotateInfo *) NULL)
2509  return(MagickFalse);
2510  *annotate_info->next=(*annotate_info);
2511  annotate_info->next->previous=annotate_info;
2512  annotate_info=annotate_info->next;
2513  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2514  windows->image.width/MagickMax((ssize_t)
2515  font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2516  if (annotate_info->text == (char *) NULL)
2517  return(MagickFalse);
2518  annotate_info->y+=annotate_info->height;
2519  if (annotate_info->y > (int) windows->image.height)
2520  annotate_info->y=(int) annotate_info->height;
2521  annotate_info->next=(XAnnotateInfo *) NULL;
2522  x=annotate_info->x;
2523  y=annotate_info->y;
2524  p=annotate_info->text;
2525  break;
2526  }
2527  }
2528  break;
2529  }
2530  case KeyRelease:
2531  {
2532  /*
2533  Respond to a user key release.
2534  */
2535  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2536  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2537  state&=(~ModifierState);
2538  break;
2539  }
2540  case SelectionNotify:
2541  {
2542  Atom
2543  type;
2544 
2545  int
2546  format;
2547 
2548  unsigned char
2549  *data;
2550 
2551  unsigned long
2552  after,
2553  length;
2554 
2555  /*
2556  Obtain response from primary selection.
2557  */
2558  if (event.xselection.property == (Atom) None)
2559  break;
2560  status=XGetWindowProperty(display,event.xselection.requestor,
2561  event.xselection.property,0L,(long) MagickPathExtent,True,XA_STRING,
2562  &type,&format,&length,&after,&data);
2563  if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2564  (length == 0))
2565  break;
2566  /*
2567  Annotate Image window with primary selection.
2568  */
2569  for (i=0; i < (ssize_t) length; i++)
2570  {
2571  if ((char) data[i] != '\n')
2572  {
2573  /*
2574  Draw a single character on the Image window.
2575  */
2576  *p=(char) data[i];
2577  (void) XDrawString(display,windows->image.id,annotate_context,
2578  x,y,p,1);
2579  x+=XTextWidth(font_info,p,1);
2580  p++;
2581  if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2582  continue;
2583  }
2584  /*
2585  Advance to the next line of text.
2586  */
2587  *p='\0';
2588  annotate_info->width=(unsigned int) XTextWidth(font_info,
2589  annotate_info->text,(int) strlen(annotate_info->text));
2590  if (annotate_info->next != (XAnnotateInfo *) NULL)
2591  {
2592  /*
2593  Line of text already exists.
2594  */
2595  annotate_info=annotate_info->next;
2596  x=annotate_info->x;
2597  y=annotate_info->y;
2598  p=annotate_info->text;
2599  continue;
2600  }
2601  annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2602  sizeof(*annotate_info->next));
2603  if (annotate_info->next == (XAnnotateInfo *) NULL)
2604  return(MagickFalse);
2605  *annotate_info->next=(*annotate_info);
2606  annotate_info->next->previous=annotate_info;
2607  annotate_info=annotate_info->next;
2608  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2609  windows->image.width/MagickMax((ssize_t)
2610  font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2611  if (annotate_info->text == (char *) NULL)
2612  return(MagickFalse);
2613  annotate_info->y+=annotate_info->height;
2614  if (annotate_info->y > (int) windows->image.height)
2615  annotate_info->y=(int) annotate_info->height;
2616  annotate_info->next=(XAnnotateInfo *) NULL;
2617  x=annotate_info->x;
2618  y=annotate_info->y;
2619  p=annotate_info->text;
2620  }
2621  (void) XFree((void *) data);
2622  break;
2623  }
2624  default:
2625  break;
2626  }
2627  } while ((state & ExitState) == 0);
2628  (void) XFreeCursor(display,cursor);
2629  /*
2630  Annotation is relative to image configuration.
2631  */
2632  width=(unsigned int) image->columns;
2633  height=(unsigned int) image->rows;
2634  x=0;
2635  y=0;
2636  if (windows->image.crop_geometry != (char *) NULL)
2637  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2638  /*
2639  Initialize annotated image.
2640  */
2641  XSetCursorState(display,windows,MagickTrue);
2642  XCheckRefreshWindows(display,windows);
2643  while (annotate_info != (XAnnotateInfo *) NULL)
2644  {
2645  if (annotate_info->width == 0)
2646  {
2647  /*
2648  No text on this line-- go to the next line of text.
2649  */
2650  previous_info=annotate_info->previous;
2651  annotate_info->text=(char *)
2652  RelinquishMagickMemory(annotate_info->text);
2653  annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2654  annotate_info=previous_info;
2655  continue;
2656  }
2657  /*
2658  Determine pixel index for box and pen color.
2659  */
2660  windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2661  if (windows->pixel_info->colors != 0)
2662  for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2663  if (windows->pixel_info->pixels[i] ==
2664  windows->pixel_info->pen_colors[box_id].pixel)
2665  {
2666  windows->pixel_info->box_index=(unsigned short) i;
2667  break;
2668  }
2669  windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2670  if (windows->pixel_info->colors != 0)
2671  for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2672  if (windows->pixel_info->pixels[i] ==
2673  windows->pixel_info->pen_colors[pen_id].pixel)
2674  {
2675  windows->pixel_info->pen_index=(unsigned short) i;
2676  break;
2677  }
2678  /*
2679  Define the annotate geometry string.
2680  */
2681  annotate_info->x=(int)
2682  width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2683  annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2684  windows->image.y)/windows->image.ximage->height;
2685  (void) FormatLocaleString(annotate_info->geometry,MagickPathExtent,
2686  "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2687  height*annotate_info->height/windows->image.ximage->height,
2688  annotate_info->x+x,annotate_info->y+y);
2689  /*
2690  Annotate image with text.
2691  */
2692  status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2693  exception);
2694  if (status == 0)
2695  return(MagickFalse);
2696  /*
2697  Free up memory.
2698  */
2699  previous_info=annotate_info->previous;
2700  annotate_info->text=DestroyString(annotate_info->text);
2701  annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2702  annotate_info=previous_info;
2703  }
2704  (void) XSetForeground(display,annotate_context,
2705  windows->pixel_info->foreground_color.pixel);
2706  (void) XSetBackground(display,annotate_context,
2707  windows->pixel_info->background_color.pixel);
2708  (void) XSetFont(display,annotate_context,windows->font_info->fid);
2709  XSetCursorState(display,windows,MagickFalse);
2710  (void) XFreeFont(display,font_info);
2711  /*
2712  Update image configuration.
2713  */
2714  XConfigureImageColormap(display,resource_info,windows,image,exception);
2715  (void) XConfigureImage(display,resource_info,windows,image,exception);
2716  return(MagickTrue);
2717 }
2718 
2719 /*
2720 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2721 % %
2722 % %
2723 % %
2724 + X B a c k g r o u n d I m a g e %
2725 % %
2726 % %
2727 % %
2728 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2729 %
2730 % XBackgroundImage() displays the image in the background of a window.
2731 %
2732 % The format of the XBackgroundImage method is:
2733 %
2734 % MagickBooleanType XBackgroundImage(Display *display,
2735 % XResourceInfo *resource_info,XWindows *windows,Image **image,
2736 % ExceptionInfo *exception)
2737 %
2738 % A description of each parameter follows:
2739 %
2740 % o display: Specifies a connection to an X server; returned from
2741 % XOpenDisplay.
2742 %
2743 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2744 %
2745 % o windows: Specifies a pointer to a XWindows structure.
2746 %
2747 % o image: the image.
2748 %
2749 % o exception: return any errors or warnings in this structure.
2750 %
2751 */
2752 static MagickBooleanType XBackgroundImage(Display *display,
2753  XResourceInfo *resource_info,XWindows *windows,Image **image,
2754  ExceptionInfo *exception)
2755 {
2756 #define BackgroundImageTag "Background/Image"
2757 
2758  int
2759  status;
2760 
2761  static char
2762  window_id[MagickPathExtent] = "root";
2763 
2764  XResourceInfo
2765  background_resources;
2766 
2767  /*
2768  Put image in background.
2769  */
2770  status=XDialogWidget(display,windows,"Background",
2771  "Enter window id (id 0x00 selects window with pointer):",window_id);
2772  if (*window_id == '\0')
2773  return(MagickFalse);
2774  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2775  exception);
2776  XInfoWidget(display,windows,BackgroundImageTag);
2777  XSetCursorState(display,windows,MagickTrue);
2778  XCheckRefreshWindows(display,windows);
2779  background_resources=(*resource_info);
2780  background_resources.window_id=window_id;
2781  background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2782  status=XDisplayBackgroundImage(display,&background_resources,*image,
2783  exception);
2784  if (status != MagickFalse)
2785  XClientMessage(display,windows->image.id,windows->im_protocols,
2786  windows->im_retain_colors,CurrentTime);
2787  XSetCursorState(display,windows,MagickFalse);
2788  (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2789  exception);
2790  return(MagickTrue);
2791 }
2792 
2793 /*
2794 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2795 % %
2796 % %
2797 % %
2798 + X C h o p I m a g e %
2799 % %
2800 % %
2801 % %
2802 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2803 %
2804 % XChopImage() chops the X image.
2805 %
2806 % The format of the XChopImage method is:
2807 %
2808 % MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2809 % XWindows *windows,Image **image,ExceptionInfo *exception)
2810 %
2811 % A description of each parameter follows:
2812 %
2813 % o display: Specifies a connection to an X server; returned from
2814 % XOpenDisplay.
2815 %
2816 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2817 %
2818 % o windows: Specifies a pointer to a XWindows structure.
2819 %
2820 % o image: the image.
2821 %
2822 % o exception: return any errors or warnings in this structure.
2823 %
2824 */
2825 static MagickBooleanType XChopImage(Display *display,
2826  XResourceInfo *resource_info,XWindows *windows,Image **image,
2827  ExceptionInfo *exception)
2828 {
2829  const char
2830  *const ChopMenu[] =
2831  {
2832  "Direction",
2833  "Help",
2834  "Dismiss",
2835  (char *) NULL
2836  };
2837 
2838  static ModeType
2839  direction = HorizontalChopCommand;
2840 
2841  static const ModeType
2842  ChopCommands[] =
2843  {
2844  ChopDirectionCommand,
2845  ChopHelpCommand,
2846  ChopDismissCommand
2847  },
2848  DirectionCommands[] =
2849  {
2850  HorizontalChopCommand,
2851  VerticalChopCommand
2852  };
2853 
2854  char
2855  text[MagickPathExtent];
2856 
2857  Image
2858  *chop_image;
2859 
2860  int
2861  id,
2862  x,
2863  y;
2864 
2865  double
2866  scale_factor;
2867 
2869  chop_info;
2870 
2871  unsigned int
2872  distance,
2873  height,
2874  width;
2875 
2876  size_t
2877  state;
2878 
2879  XEvent
2880  event;
2881 
2882  XSegment
2883  segment_info;
2884 
2885  /*
2886  Map Command widget.
2887  */
2888  (void) CloneString(&windows->command.name,"Chop");
2889  windows->command.data=1;
2890  (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2891  (void) XMapRaised(display,windows->command.id);
2892  XClientMessage(display,windows->image.id,windows->im_protocols,
2893  windows->im_update_widget,CurrentTime);
2894  /*
2895  Track pointer until button 1 is pressed.
2896  */
2897  XQueryPosition(display,windows->image.id,&x,&y);
2898  (void) XSelectInput(display,windows->image.id,
2899  windows->image.attributes.event_mask | PointerMotionMask);
2900  state=DefaultState;
2901  (void) memset(&segment_info,0,sizeof(segment_info));
2902  do
2903  {
2904  if (windows->info.mapped != MagickFalse )
2905  {
2906  /*
2907  Display pointer position.
2908  */
2909  (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
2910  x+windows->image.x,y+windows->image.y);
2911  XInfoWidget(display,windows,text);
2912  }
2913  /*
2914  Wait for next event.
2915  */
2916  XScreenEvent(display,windows,&event,exception);
2917  if (event.xany.window == windows->command.id)
2918  {
2919  /*
2920  Select a command from the Command widget.
2921  */
2922  id=XCommandWidget(display,windows,ChopMenu,&event);
2923  if (id < 0)
2924  continue;
2925  switch (ChopCommands[id])
2926  {
2927  case ChopDirectionCommand:
2928  {
2929  char
2930  command[MagickPathExtent];
2931 
2932  const char
2933  *const Directions[] =
2934  {
2935  "horizontal",
2936  "vertical",
2937  (char *) NULL,
2938  };
2939 
2940  /*
2941  Select a command from the pop-up menu.
2942  */
2943  id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2944  if (id >= 0)
2945  direction=DirectionCommands[id];
2946  break;
2947  }
2948  case ChopHelpCommand:
2949  {
2950  XTextViewHelp(display,resource_info,windows,MagickFalse,
2951  "Help Viewer - Image Chop",ImageChopHelp);
2952  break;
2953  }
2954  case ChopDismissCommand:
2955  {
2956  /*
2957  Prematurely exit.
2958  */
2959  state|=EscapeState;
2960  state|=ExitState;
2961  break;
2962  }
2963  default:
2964  break;
2965  }
2966  continue;
2967  }
2968  switch (event.type)
2969  {
2970  case ButtonPress:
2971  {
2972  if (event.xbutton.button != Button1)
2973  break;
2974  if (event.xbutton.window != windows->image.id)
2975  break;
2976  /*
2977  User has committed to start point of chopping line.
2978  */
2979  segment_info.x1=(short int) event.xbutton.x;
2980  segment_info.x2=(short int) event.xbutton.x;
2981  segment_info.y1=(short int) event.xbutton.y;
2982  segment_info.y2=(short int) event.xbutton.y;
2983  state|=ExitState;
2984  break;
2985  }
2986  case ButtonRelease:
2987  break;
2988  case Expose:
2989  break;
2990  case KeyPress:
2991  {
2992  char
2993  command[MagickPathExtent];
2994 
2995  KeySym
2996  key_symbol;
2997 
2998  if (event.xkey.window != windows->image.id)
2999  break;
3000  /*
3001  Respond to a user key press.
3002  */
3003  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3004  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3005  switch ((int) key_symbol)
3006  {
3007  case XK_Escape:
3008  case XK_F20:
3009  {
3010  /*
3011  Prematurely exit.
3012  */
3013  state|=EscapeState;
3014  state|=ExitState;
3015  break;
3016  }
3017  case XK_F1:
3018  case XK_Help:
3019  {
3020  (void) XSetFunction(display,windows->image.highlight_context,
3021  GXcopy);
3022  XTextViewHelp(display,resource_info,windows,MagickFalse,
3023  "Help Viewer - Image Chop",ImageChopHelp);
3024  (void) XSetFunction(display,windows->image.highlight_context,
3025  GXinvert);
3026  break;
3027  }
3028  default:
3029  {
3030  (void) XBell(display,0);
3031  break;
3032  }
3033  }
3034  break;
3035  }
3036  case MotionNotify:
3037  {
3038  /*
3039  Map and unmap Info widget as text cursor crosses its boundaries.
3040  */
3041  x=event.xmotion.x;
3042  y=event.xmotion.y;
3043  if (windows->info.mapped != MagickFalse )
3044  {
3045  if ((x < (int) (windows->info.x+windows->info.width)) &&
3046  (y < (int) (windows->info.y+windows->info.height)))
3047  (void) XWithdrawWindow(display,windows->info.id,
3048  windows->info.screen);
3049  }
3050  else
3051  if ((x > (int) (windows->info.x+windows->info.width)) ||
3052  (y > (int) (windows->info.y+windows->info.height)))
3053  (void) XMapWindow(display,windows->info.id);
3054  }
3055  }
3056  } while ((state & ExitState) == 0);
3057  (void) XSelectInput(display,windows->image.id,
3058  windows->image.attributes.event_mask);
3059  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3060  if ((state & EscapeState) != 0)
3061  return(MagickTrue);
3062  /*
3063  Draw line as pointer moves until the mouse button is released.
3064  */
3065  chop_info.width=0;
3066  chop_info.height=0;
3067  chop_info.x=0;
3068  chop_info.y=0;
3069  distance=0;
3070  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3071  state=DefaultState;
3072  do
3073  {
3074  if (distance > 9)
3075  {
3076  /*
3077  Display info and draw chopping line.
3078  */
3079  if (windows->info.mapped == MagickFalse)
3080  (void) XMapWindow(display,windows->info.id);
3082  " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3083  chop_info.height,(double) chop_info.x,(double) chop_info.y);
3084  XInfoWidget(display,windows,text);
3085  XHighlightLine(display,windows->image.id,
3086  windows->image.highlight_context,&segment_info);
3087  }
3088  else
3089  if (windows->info.mapped != MagickFalse )
3090  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3091  /*
3092  Wait for next event.
3093  */
3094  XScreenEvent(display,windows,&event,exception);
3095  if (distance > 9)
3096  XHighlightLine(display,windows->image.id,
3097  windows->image.highlight_context,&segment_info);
3098  switch (event.type)
3099  {
3100  case ButtonPress:
3101  {
3102  segment_info.x2=(short int) event.xmotion.x;
3103  segment_info.y2=(short int) event.xmotion.y;
3104  break;
3105  }
3106  case ButtonRelease:
3107  {
3108  /*
3109  User has committed to chopping line.
3110  */
3111  segment_info.x2=(short int) event.xbutton.x;
3112  segment_info.y2=(short int) event.xbutton.y;
3113  state|=ExitState;
3114  break;
3115  }
3116  case Expose:
3117  break;
3118  case MotionNotify:
3119  {
3120  segment_info.x2=(short int) event.xmotion.x;
3121  segment_info.y2=(short int) event.xmotion.y;
3122  }
3123  default:
3124  break;
3125  }
3126  /*
3127  Check boundary conditions.
3128  */
3129  if (segment_info.x2 < 0)
3130  segment_info.x2=0;
3131  else
3132  if (segment_info.x2 > windows->image.ximage->width)
3133  segment_info.x2=windows->image.ximage->width;
3134  if (segment_info.y2 < 0)
3135  segment_info.y2=0;
3136  else
3137  if (segment_info.y2 > windows->image.ximage->height)
3138  segment_info.y2=windows->image.ximage->height;
3139  distance=(unsigned int)
3140  (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3141  ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3142  /*
3143  Compute chopping geometry.
3144  */
3145  if (direction == HorizontalChopCommand)
3146  {
3147  chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3148  chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3149  chop_info.height=0;
3150  chop_info.y=0;
3151  if (segment_info.x1 > (int) segment_info.x2)
3152  {
3153  chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3154  chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3155  }
3156  }
3157  else
3158  {
3159  chop_info.width=0;
3160  chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3161  chop_info.x=0;
3162  chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3163  if (segment_info.y1 > segment_info.y2)
3164  {
3165  chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3166  chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3167  }
3168  }
3169  } while ((state & ExitState) == 0);
3170  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3171  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3172  if (distance <= 9)
3173  return(MagickTrue);
3174  /*
3175  Image chopping is relative to image configuration.
3176  */
3177  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3178  exception);
3179  XSetCursorState(display,windows,MagickTrue);
3180  XCheckRefreshWindows(display,windows);
3181  windows->image.window_changes.width=windows->image.ximage->width-
3182  (unsigned int) chop_info.width;
3183  windows->image.window_changes.height=windows->image.ximage->height-
3184  (unsigned int) chop_info.height;
3185  width=(unsigned int) (*image)->columns;
3186  height=(unsigned int) (*image)->rows;
3187  x=0;
3188  y=0;
3189  if (windows->image.crop_geometry != (char *) NULL)
3190  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3191  scale_factor=(double) width/windows->image.ximage->width;
3192  chop_info.x+=x;
3193  chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3194  chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3195  scale_factor=(double) height/windows->image.ximage->height;
3196  chop_info.y+=y;
3197  chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3198  chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3199  /*
3200  Chop image.
3201  */
3202  chop_image=ChopImage(*image,&chop_info,exception);
3203  XSetCursorState(display,windows,MagickFalse);
3204  if (chop_image == (Image *) NULL)
3205  return(MagickFalse);
3206  *image=DestroyImage(*image);
3207  *image=chop_image;
3208  /*
3209  Update image configuration.
3210  */
3211  XConfigureImageColormap(display,resource_info,windows,*image,exception);
3212  (void) XConfigureImage(display,resource_info,windows,*image,exception);
3213  return(MagickTrue);
3214 }
3215 
3216 /*
3217 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3218 % %
3219 % %
3220 % %
3221 + X C o l o r E d i t I m a g e %
3222 % %
3223 % %
3224 % %
3225 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3226 %
3227 % XColorEditImage() allows the user to interactively change the color of one
3228 % pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3229 %
3230 % The format of the XColorEditImage method is:
3231 %
3232 % MagickBooleanType XColorEditImage(Display *display,
3233 % XResourceInfo *resource_info,XWindows *windows,Image **image,
3234 % ExceptionInfo *exception)
3235 %
3236 % A description of each parameter follows:
3237 %
3238 % o display: Specifies a connection to an X server; returned from
3239 % XOpenDisplay.
3240 %
3241 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3242 %
3243 % o windows: Specifies a pointer to a XWindows structure.
3244 %
3245 % o image: the image; returned from ReadImage.
3246 %
3247 % o exception: return any errors or warnings in this structure.
3248 %
3249 */
3250 static MagickBooleanType XColorEditImage(Display *display,
3251  XResourceInfo *resource_info,XWindows *windows,Image **image,
3252  ExceptionInfo *exception)
3253 {
3254  const char
3255  *const ColorEditMenu[] =
3256  {
3257  "Method",
3258  "Pixel Color",
3259  "Border Color",
3260  "Fuzz",
3261  "Undo",
3262  "Help",
3263  "Dismiss",
3264  (char *) NULL
3265  };
3266 
3267  static const ModeType
3268  ColorEditCommands[] =
3269  {
3270  ColorEditMethodCommand,
3271  ColorEditColorCommand,
3272  ColorEditBorderCommand,
3273  ColorEditFuzzCommand,
3274  ColorEditUndoCommand,
3275  ColorEditHelpCommand,
3276  ColorEditDismissCommand
3277  };
3278 
3279  static PaintMethod
3280  method = PointMethod;
3281 
3282  static unsigned int
3283  pen_id = 0;
3284 
3285  static XColor
3286  border_color = { 0, 0, 0, 0, 0, 0 };
3287 
3288  char
3289  command[MagickPathExtent],
3290  text[MagickPathExtent];
3291 
3292  Cursor
3293  cursor;
3294 
3295  int
3296  entry,
3297  id,
3298  x,
3299  x_offset,
3300  y,
3301  y_offset;
3302 
3303  register Quantum
3304  *q;
3305 
3306  register ssize_t
3307  i;
3308 
3309  unsigned int
3310  height,
3311  width;
3312 
3313  size_t
3314  state;
3315 
3316  XColor
3317  color;
3318 
3319  XEvent
3320  event;
3321 
3322  /*
3323  Map Command widget.
3324  */
3325  (void) CloneString(&windows->command.name,"Color Edit");
3326  windows->command.data=4;
3327  (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3328  (void) XMapRaised(display,windows->command.id);
3329  XClientMessage(display,windows->image.id,windows->im_protocols,
3330  windows->im_update_widget,CurrentTime);
3331  /*
3332  Make cursor.
3333  */
3334  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3335  resource_info->background_color,resource_info->foreground_color);
3336  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3337  /*
3338  Track pointer until button 1 is pressed.
3339  */
3340  XQueryPosition(display,windows->image.id,&x,&y);
3341  (void) XSelectInput(display,windows->image.id,
3342  windows->image.attributes.event_mask | PointerMotionMask);
3343  state=DefaultState;
3344  do
3345  {
3346  if (windows->info.mapped != MagickFalse )
3347  {
3348  /*
3349  Display pointer position.
3350  */
3351  (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
3352  x+windows->image.x,y+windows->image.y);
3353  XInfoWidget(display,windows,text);
3354  }
3355  /*
3356  Wait for next event.
3357  */
3358  XScreenEvent(display,windows,&event,exception);
3359  if (event.xany.window == windows->command.id)
3360  {
3361  /*
3362  Select a command from the Command widget.
3363  */
3364  id=XCommandWidget(display,windows,ColorEditMenu,&event);
3365  if (id < 0)
3366  {
3367  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3368  continue;
3369  }
3370  switch (ColorEditCommands[id])
3371  {
3372  case ColorEditMethodCommand:
3373  {
3374  char
3375  **methods;
3376 
3377  /*
3378  Select a method from the pop-up menu.
3379  */
3380  methods=(char **) GetCommandOptions(MagickMethodOptions);
3381  if (methods == (char **) NULL)
3382  break;
3383  entry=XMenuWidget(display,windows,ColorEditMenu[id],
3384  (const char **) methods,command);
3385  if (entry >= 0)
3387  MagickFalse,methods[entry]);
3388  methods=DestroyStringList(methods);
3389  break;
3390  }
3391  case ColorEditColorCommand:
3392  {
3393  const char
3394  *ColorMenu[MaxNumberPens];
3395 
3396  int
3397  pen_number;
3398 
3399  /*
3400  Initialize menu selections.
3401  */
3402  for (i=0; i < (int) (MaxNumberPens-2); i++)
3403  ColorMenu[i]=resource_info->pen_colors[i];
3404  ColorMenu[MaxNumberPens-2]="Browser...";
3405  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3406  /*
3407  Select a pen color from the pop-up menu.
3408  */
3409  pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3410  (const char **) ColorMenu,command);
3411  if (pen_number < 0)
3412  break;
3413  if (pen_number == (MaxNumberPens-2))
3414  {
3415  static char
3416  color_name[MagickPathExtent] = "gray";
3417 
3418  /*
3419  Select a pen color from a dialog.
3420  */
3421  resource_info->pen_colors[pen_number]=color_name;
3422  XColorBrowserWidget(display,windows,"Select",color_name);
3423  if (*color_name == '\0')
3424  break;
3425  }
3426  /*
3427  Set pen color.
3428  */
3429  (void) XParseColor(display,windows->map_info->colormap,
3430  resource_info->pen_colors[pen_number],&color);
3431  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3432  (unsigned int) MaxColors,&color);
3433  windows->pixel_info->pen_colors[pen_number]=color;
3434  pen_id=(unsigned int) pen_number;
3435  break;
3436  }
3437  case ColorEditBorderCommand:
3438  {
3439  const char
3440  *ColorMenu[MaxNumberPens];
3441 
3442  int
3443  pen_number;
3444 
3445  /*
3446  Initialize menu selections.
3447  */
3448  for (i=0; i < (int) (MaxNumberPens-2); i++)
3449  ColorMenu[i]=resource_info->pen_colors[i];
3450  ColorMenu[MaxNumberPens-2]="Browser...";
3451  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3452  /*
3453  Select a pen color from the pop-up menu.
3454  */
3455  pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3456  (const char **) ColorMenu,command);
3457  if (pen_number < 0)
3458  break;
3459  if (pen_number == (MaxNumberPens-2))
3460  {
3461  static char
3462  color_name[MagickPathExtent] = "gray";
3463 
3464  /*
3465  Select a pen color from a dialog.
3466  */
3467  resource_info->pen_colors[pen_number]=color_name;
3468  XColorBrowserWidget(display,windows,"Select",color_name);
3469  if (*color_name == '\0')
3470  break;
3471  }
3472  /*
3473  Set border color.
3474  */
3475  (void) XParseColor(display,windows->map_info->colormap,
3476  resource_info->pen_colors[pen_number],&border_color);
3477  break;
3478  }
3479  case ColorEditFuzzCommand:
3480  {
3481  const char
3482  *const FuzzMenu[] =
3483  {
3484  "0%",
3485  "2%",
3486  "5%",
3487  "10%",
3488  "15%",
3489  "Dialog...",
3490  (char *) NULL,
3491  };
3492 
3493  static char
3494  fuzz[MagickPathExtent];
3495 
3496  /*
3497  Select a command from the pop-up menu.
3498  */
3499  entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3500  command);
3501  if (entry < 0)
3502  break;
3503  if (entry != 5)
3504  {
3505  (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3506  QuantumRange+1.0);
3507  break;
3508  }
3509  (void) (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
3510  (void) XDialogWidget(display,windows,"Ok",
3511  "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3512  if (*fuzz == '\0')
3513  break;
3514  (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
3515  (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3516  1.0);
3517  break;
3518  }
3519  case ColorEditUndoCommand:
3520  {
3521  (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3522  image,exception);
3523  break;
3524  }
3525  case ColorEditHelpCommand:
3526  default:
3527  {
3528  XTextViewHelp(display,resource_info,windows,MagickFalse,
3529  "Help Viewer - Image Annotation",ImageColorEditHelp);
3530  break;
3531  }
3532  case ColorEditDismissCommand:
3533  {
3534  /*
3535  Prematurely exit.
3536  */
3537  state|=EscapeState;
3538  state|=ExitState;
3539  break;
3540  }
3541  }
3542  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3543  continue;
3544  }
3545  switch (event.type)
3546  {
3547  case ButtonPress:
3548  {
3549  if (event.xbutton.button != Button1)
3550  break;
3551  if ((event.xbutton.window != windows->image.id) &&
3552  (event.xbutton.window != windows->magnify.id))
3553  break;
3554  /*
3555  exit loop.
3556  */
3557  x=event.xbutton.x;
3558  y=event.xbutton.y;
3559  (void) XMagickCommand(display,resource_info,windows,
3560  SaveToUndoBufferCommand,image,exception);
3561  state|=UpdateConfigurationState;
3562  break;
3563  }
3564  case ButtonRelease:
3565  {
3566  if (event.xbutton.button != Button1)
3567  break;
3568  if ((event.xbutton.window != windows->image.id) &&
3569  (event.xbutton.window != windows->magnify.id))
3570  break;
3571  /*
3572  Update colormap information.
3573  */
3574  x=event.xbutton.x;
3575  y=event.xbutton.y;
3576  XConfigureImageColormap(display,resource_info,windows,*image,exception);
3577  (void) XConfigureImage(display,resource_info,windows,*image,exception);
3578  XInfoWidget(display,windows,text);
3579  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3580  state&=(~UpdateConfigurationState);
3581  break;
3582  }
3583  case Expose:
3584  break;
3585  case KeyPress:
3586  {
3587  KeySym
3588  key_symbol;
3589 
3590  if (event.xkey.window == windows->magnify.id)
3591  {
3592  Window
3593  window;
3594 
3595  window=windows->magnify.id;
3596  while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3597  }
3598  if (event.xkey.window != windows->image.id)
3599  break;
3600  /*
3601  Respond to a user key press.
3602  */
3603  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3604  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3605  switch ((int) key_symbol)
3606  {
3607  case XK_Escape:
3608  case XK_F20:
3609  {
3610  /*
3611  Prematurely exit.
3612  */
3613  state|=ExitState;
3614  break;
3615  }
3616  case XK_F1:
3617  case XK_Help:
3618  {
3619  XTextViewHelp(display,resource_info,windows,MagickFalse,
3620  "Help Viewer - Image Annotation",ImageColorEditHelp);
3621  break;
3622  }
3623  default:
3624  {
3625  (void) XBell(display,0);
3626  break;
3627  }
3628  }
3629  break;
3630  }
3631  case MotionNotify:
3632  {
3633  /*
3634  Map and unmap Info widget as cursor crosses its boundaries.
3635  */
3636  x=event.xmotion.x;
3637  y=event.xmotion.y;
3638  if (windows->info.mapped != MagickFalse )
3639  {
3640  if ((x < (int) (windows->info.x+windows->info.width)) &&
3641  (y < (int) (windows->info.y+windows->info.height)))
3642  (void) XWithdrawWindow(display,windows->info.id,
3643  windows->info.screen);
3644  }
3645  else
3646  if ((x > (int) (windows->info.x+windows->info.width)) ||
3647  (y > (int) (windows->info.y+windows->info.height)))
3648  (void) XMapWindow(display,windows->info.id);
3649  break;
3650  }
3651  default:
3652  break;
3653  }
3654  if (event.xany.window == windows->magnify.id)
3655  {
3656  x=windows->magnify.x-windows->image.x;
3657  y=windows->magnify.y-windows->image.y;
3658  }
3659  x_offset=x;
3660  y_offset=y;
3661  if ((state & UpdateConfigurationState) != 0)
3662  {
3663  CacheView
3664  *image_view;
3665 
3666  int
3667  x,
3668  y;
3669 
3670  /*
3671  Pixel edit is relative to image configuration.
3672  */
3673  (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3674  MagickTrue);
3675  color=windows->pixel_info->pen_colors[pen_id];
3676  XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3677  width=(unsigned int) (*image)->columns;
3678  height=(unsigned int) (*image)->rows;
3679  x=0;
3680  y=0;
3681  if (windows->image.crop_geometry != (char *) NULL)
3682  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3683  &width,&height);
3684  x_offset=(int)
3685  (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3686  y_offset=(int)
3687  (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3688  if ((x_offset < 0) || (y_offset < 0))
3689  continue;
3690  if ((x_offset >= (int) (*image)->columns) ||
3691  (y_offset >= (int) (*image)->rows))
3692  continue;
3693  image_view=AcquireAuthenticCacheView(*image,exception);
3694  switch (method)
3695  {
3696  case PointMethod:
3697  default:
3698  {
3699  /*
3700  Update color information using point algorithm.
3701  */
3702  if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3703  return(MagickFalse);
3704  q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3705  (ssize_t) y_offset,1,1,exception);
3706  if (q == (Quantum *) NULL)
3707  break;
3708  SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3709  SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3710  SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3711  (void) SyncCacheViewAuthenticPixels(image_view,exception);
3712  break;
3713  }
3714  case ReplaceMethod:
3715  {
3716  PixelInfo
3717  pixel,
3718  target;
3719 
3720  /*
3721  Update color information using replace algorithm.
3722  */
3723  (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3724  x_offset,(ssize_t) y_offset,&target,exception);
3725  if ((*image)->storage_class == DirectClass)
3726  {
3727  for (y=0; y < (int) (*image)->rows; y++)
3728  {
3729  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3730  (*image)->columns,1,exception);
3731  if (q == (Quantum *) NULL)
3732  break;
3733  for (x=0; x < (int) (*image)->columns; x++)
3734  {
3735  GetPixelInfoPixel(*image,q,&pixel);
3736  if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3737  {
3738  SetPixelRed(*image,ScaleShortToQuantum(
3739  color.red),q);
3740  SetPixelGreen(*image,ScaleShortToQuantum(
3741  color.green),q);
3742  SetPixelBlue(*image,ScaleShortToQuantum(
3743  color.blue),q);
3744  }
3745  q+=GetPixelChannels(*image);
3746  }
3747  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3748  break;
3749  }
3750  }
3751  else
3752  {
3753  for (i=0; i < (ssize_t) (*image)->colors; i++)
3754  if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3755  {
3756  (*image)->colormap[i].red=(double) ScaleShortToQuantum(
3757  color.red);
3758  (*image)->colormap[i].green=(double) ScaleShortToQuantum(
3759  color.green);
3760  (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
3761  color.blue);
3762  }
3763  (void) SyncImage(*image,exception);
3764  }
3765  break;
3766  }
3767  case FloodfillMethod:
3768  case FillToBorderMethod:
3769  {
3770  DrawInfo
3771  *draw_info;
3772 
3773  PixelInfo
3774  target;
3775 
3776  /*
3777  Update color information using floodfill algorithm.
3778  */
3779  (void) GetOneVirtualPixelInfo(*image,
3780  GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3781  y_offset,&target,exception);
3782  if (method == FillToBorderMethod)
3783  {
3784  target.red=(double)
3785  ScaleShortToQuantum(border_color.red);
3786  target.green=(double)
3787  ScaleShortToQuantum(border_color.green);
3788  target.blue=(double)
3789  ScaleShortToQuantum(border_color.blue);
3790  }
3791  draw_info=CloneDrawInfo(resource_info->image_info,
3792  (DrawInfo *) NULL);
3793  (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3794  AllCompliance,&draw_info->fill,exception);
3795  (void) FloodfillPaintImage(*image,draw_info,&target,
3796  (ssize_t)x_offset,(ssize_t)y_offset,
3797  method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
3798  draw_info=DestroyDrawInfo(draw_info);
3799  break;
3800  }
3801  case ResetMethod:
3802  {
3803  /*
3804  Update color information using reset algorithm.
3805  */
3806  if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3807  return(MagickFalse);
3808  for (y=0; y < (int) (*image)->rows; y++)
3809  {
3810  q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3811  (*image)->columns,1,exception);
3812  if (q == (Quantum *) NULL)
3813  break;
3814  for (x=0; x < (int) (*image)->columns; x++)
3815  {
3816  SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3817  SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3818  SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3819  q+=GetPixelChannels(*image);
3820  }
3821  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3822  break;
3823  }
3824  break;
3825  }
3826  }
3827  image_view=DestroyCacheView(image_view);
3828  state&=(~UpdateConfigurationState);
3829  }
3830  } while ((state & ExitState) == 0);
3831  (void) XSelectInput(display,windows->image.id,
3832  windows->image.attributes.event_mask);
3833  XSetCursorState(display,windows,MagickFalse);
3834  (void) XFreeCursor(display,cursor);
3835  return(MagickTrue);
3836 }
3837 
3838 /*
3839 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3840 % %
3841 % %
3842 % %
3843 + X C o m p o s i t e I m a g e %
3844 % %
3845 % %
3846 % %
3847 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3848 %
3849 % XCompositeImage() requests an image name from the user, reads the image and
3850 % composites it with the X window image at a location the user chooses with
3851 % the pointer.
3852 %
3853 % The format of the XCompositeImage method is:
3854 %
3855 % MagickBooleanType XCompositeImage(Display *display,
3856 % XResourceInfo *resource_info,XWindows *windows,Image *image,
3857 % ExceptionInfo *exception)
3858 %
3859 % A description of each parameter follows:
3860 %
3861 % o display: Specifies a connection to an X server; returned from
3862 % XOpenDisplay.
3863 %
3864 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3865 %
3866 % o windows: Specifies a pointer to a XWindows structure.
3867 %
3868 % o image: the image; returned from ReadImage.
3869 %
3870 % o exception: return any errors or warnings in this structure.
3871 %
3872 */
3873 static MagickBooleanType XCompositeImage(Display *display,
3874  XResourceInfo *resource_info,XWindows *windows,Image *image,
3875  ExceptionInfo *exception)
3876 {
3877  const char
3878  *const CompositeMenu[] =
3879  {
3880  "Operators",
3881  "Dissolve",
3882  "Displace",
3883  "Help",
3884  "Dismiss",
3885  (char *) NULL
3886  };
3887 
3888  static char
3889  displacement_geometry[MagickPathExtent] = "30x30",
3890  filename[MagickPathExtent] = "\0";
3891 
3892  static CompositeOperator
3893  compose = CopyCompositeOp;
3894 
3895  static const ModeType
3896  CompositeCommands[] =
3897  {
3898  CompositeOperatorsCommand,
3899  CompositeDissolveCommand,
3900  CompositeDisplaceCommand,
3901  CompositeHelpCommand,
3902  CompositeDismissCommand
3903  };
3904 
3905  char
3906  text[MagickPathExtent];
3907 
3908  Cursor
3909  cursor;
3910 
3911  Image
3912  *composite_image;
3913 
3914  int
3915  entry,
3916  id,
3917  x,
3918  y;
3919 
3920  double
3921  blend,
3922  scale_factor;
3923 
3925  highlight_info,
3926  composite_info;
3927 
3928  unsigned int
3929  height,
3930  width;
3931 
3932  size_t
3933  state;
3934 
3935  XEvent
3936  event;
3937 
3938  /*
3939  Request image file name from user.
3940  */
3941  XFileBrowserWidget(display,windows,"Composite",filename);
3942  if (*filename == '\0')
3943  return(MagickTrue);
3944  /*
3945  Read image.
3946  */
3947  XSetCursorState(display,windows,MagickTrue);
3948  XCheckRefreshWindows(display,windows);
3949  (void) CopyMagickString(resource_info->image_info->filename,filename,
3951  composite_image=ReadImage(resource_info->image_info,exception);
3952  CatchException(exception);
3953  XSetCursorState(display,windows,MagickFalse);
3954  if (composite_image == (Image *) NULL)
3955  return(MagickFalse);
3956  /*
3957  Map Command widget.
3958  */
3959  (void) CloneString(&windows->command.name,"Composite");
3960  windows->command.data=1;
3961  (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3962  (void) XMapRaised(display,windows->command.id);
3963  XClientMessage(display,windows->image.id,windows->im_protocols,
3964  windows->im_update_widget,CurrentTime);
3965  /*
3966  Track pointer until button 1 is pressed.
3967  */
3968  XQueryPosition(display,windows->image.id,&x,&y);
3969  (void) XSelectInput(display,windows->image.id,
3970  windows->image.attributes.event_mask | PointerMotionMask);
3971  composite_info.x=(ssize_t) windows->image.x+x;
3972  composite_info.y=(ssize_t) windows->image.y+y;
3973  composite_info.width=0;
3974  composite_info.height=0;
3975  cursor=XCreateFontCursor(display,XC_ul_angle);
3976  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3977  blend=0.0;
3978  state=DefaultState;
3979  do
3980  {
3981  if (windows->info.mapped != MagickFalse )
3982  {
3983  /*
3984  Display pointer position.
3985  */
3986  (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
3987  (long) composite_info.x,(long) composite_info.y);
3988  XInfoWidget(display,windows,text);
3989  }
3990  highlight_info=composite_info;
3991  highlight_info.x=composite_info.x-windows->image.x;
3992  highlight_info.y=composite_info.y-windows->image.y;
3993  XHighlightRectangle(display,windows->image.id,
3994  windows->image.highlight_context,&highlight_info);
3995  /*
3996  Wait for next event.
3997  */
3998  XScreenEvent(display,windows,&event,exception);
3999  XHighlightRectangle(display,windows->image.id,
4000  windows->image.highlight_context,&highlight_info);
4001  if (event.xany.window == windows->command.id)
4002  {
4003  /*
4004  Select a command from the Command widget.
4005  */
4006  id=XCommandWidget(display,windows,CompositeMenu,&event);
4007  if (id < 0)
4008  continue;
4009  switch (CompositeCommands[id])
4010  {
4011  case CompositeOperatorsCommand:
4012  {
4013  char
4014  command[MagickPathExtent],
4015  **operators;
4016 
4017  /*
4018  Select a command from the pop-up menu.
4019  */
4021  if (operators == (char **) NULL)
4022  break;
4023  entry=XMenuWidget(display,windows,CompositeMenu[id],
4024  (const char **) operators,command);
4025  if (entry >= 0)
4027  MagickComposeOptions,MagickFalse,operators[entry]);
4028  operators=DestroyStringList(operators);
4029  break;
4030  }
4031  case CompositeDissolveCommand:
4032  {
4033  static char
4034  factor[MagickPathExtent] = "20.0";
4035 
4036  /*
4037  Dissolve the two images a given percent.
4038  */
4039  (void) XSetFunction(display,windows->image.highlight_context,
4040  GXcopy);
4041  (void) XDialogWidget(display,windows,"Dissolve",
4042  "Enter the blend factor (0.0 - 99.9%):",factor);
4043  (void) XSetFunction(display,windows->image.highlight_context,
4044  GXinvert);
4045  if (*factor == '\0')
4046  break;
4047  blend=StringToDouble(factor,(char **) NULL);
4048  compose=DissolveCompositeOp;
4049  break;
4050  }
4051  case CompositeDisplaceCommand:
4052  {
4053  /*
4054  Get horizontal and vertical scale displacement geometry.
4055  */
4056  (void) XSetFunction(display,windows->image.highlight_context,
4057  GXcopy);
4058  (void) XDialogWidget(display,windows,"Displace",
4059  "Enter the horizontal and vertical scale:",displacement_geometry);
4060  (void) XSetFunction(display,windows->image.highlight_context,
4061  GXinvert);
4062  if (*displacement_geometry == '\0')
4063  break;
4064  compose=DisplaceCompositeOp;
4065  break;
4066  }
4067  case CompositeHelpCommand:
4068  {
4069  (void) XSetFunction(display,windows->image.highlight_context,
4070  GXcopy);
4071  XTextViewHelp(display,resource_info,windows,MagickFalse,
4072  "Help Viewer - Image Composite",ImageCompositeHelp);
4073  (void) XSetFunction(display,windows->image.highlight_context,
4074  GXinvert);
4075  break;
4076  }
4077  case CompositeDismissCommand:
4078  {
4079  /*
4080  Prematurely exit.
4081  */
4082  state|=EscapeState;
4083  state|=ExitState;
4084  break;
4085  }
4086  default:
4087  break;
4088  }
4089  continue;
4090  }
4091  switch (event.type)
4092  {
4093  case ButtonPress:
4094  {
4095  if (image->debug != MagickFalse )
4097  "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4098  event.xbutton.button,event.xbutton.x,event.xbutton.y);
4099  if (event.xbutton.button != Button1)
4100  break;
4101  if (event.xbutton.window != windows->image.id)
4102  break;
4103  /*
4104  Change cursor.
4105  */
4106  composite_info.width=composite_image->columns;
4107  composite_info.height=composite_image->rows;
4108  (void) XCheckDefineCursor(display,windows->image.id,cursor);
4109  composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4110  composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4111  break;
4112  }
4113  case ButtonRelease:
4114  {
4115  if (image->debug != MagickFalse )
4117  "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4118  event.xbutton.button,event.xbutton.x,event.xbutton.y);
4119  if (event.xbutton.button != Button1)
4120  break;
4121  if (event.xbutton.window != windows->image.id)
4122  break;
4123  if ((composite_info.width != 0) && (composite_info.height != 0))
4124  {
4125  /*
4126  User has selected the location of the composite image.
4127  */
4128  composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4129  composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4130  state|=ExitState;
4131  }
4132  break;
4133  }
4134  case Expose:
4135  break;
4136  case KeyPress:
4137  {
4138  char
4139  command[MagickPathExtent];
4140 
4141  KeySym
4142  key_symbol;
4143 
4144  int
4145  length;
4146 
4147  if (event.xkey.window != windows->image.id)
4148  break;
4149  /*
4150  Respond to a user key press.
4151  */
4152  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4153  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4154  *(command+length)='\0';
4155  if (image->debug != MagickFalse )
4157  "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4158  switch ((int) key_symbol)
4159  {
4160  case XK_Escape:
4161  case XK_F20:
4162  {
4163  /*
4164  Prematurely exit.
4165  */
4166  composite_image=DestroyImage(composite_image);
4167  state|=EscapeState;
4168  state|=ExitState;
4169  break;
4170  }
4171  case XK_F1:
4172  case XK_Help:
4173  {
4174  (void) XSetFunction(display,windows->image.highlight_context,
4175  GXcopy);
4176  XTextViewHelp(display,resource_info,windows,MagickFalse,
4177  "Help Viewer - Image Composite",ImageCompositeHelp);
4178  (void) XSetFunction(display,windows->image.highlight_context,
4179  GXinvert);
4180  break;
4181  }
4182  default:
4183  {
4184  (void) XBell(display,0);
4185  break;
4186  }
4187  }
4188  break;
4189  }
4190  case MotionNotify:
4191  {
4192  /*
4193  Map and unmap Info widget as text cursor crosses its boundaries.
4194  */
4195  x=event.xmotion.x;
4196  y=event.xmotion.y;
4197  if (windows->info.mapped != MagickFalse )
4198  {
4199  if ((x < (int) (windows->info.x+windows->info.width)) &&
4200  (y < (int) (windows->info.y+windows->info.height)))
4201  (void) XWithdrawWindow(display,windows->info.id,
4202  windows->info.screen);
4203  }
4204  else
4205  if ((x > (int) (windows->info.x+windows->info.width)) ||
4206  (y > (int) (windows->info.y+windows->info.height)))
4207  (void) XMapWindow(display,windows->info.id);
4208  composite_info.x=(ssize_t) windows->image.x+x;
4209  composite_info.y=(ssize_t) windows->image.y+y;
4210  break;
4211  }
4212  default:
4213  {
4214  if (image->debug != MagickFalse )
4215  (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4216  event.type);
4217  break;
4218  }
4219  }
4220  } while ((state & ExitState) == 0);
4221  (void) XSelectInput(display,windows->image.id,
4222  windows->image.attributes.event_mask);
4223  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4224  XSetCursorState(display,windows,MagickFalse);
4225  (void) XFreeCursor(display,cursor);
4226  if ((state & EscapeState) != 0)
4227  return(MagickTrue);
4228  /*
4229  Image compositing is relative to image configuration.
4230  */
4231  XSetCursorState(display,windows,MagickTrue);
4232  XCheckRefreshWindows(display,windows);
4233  width=(unsigned int) image->columns;
4234  height=(unsigned int) image->rows;
4235  x=0;
4236  y=0;
4237  if (windows->image.crop_geometry != (char *) NULL)
4238  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4239  scale_factor=(double) width/windows->image.ximage->width;
4240  composite_info.x+=x;
4241  composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4242  composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4243  scale_factor=(double) height/windows->image.ximage->height;
4244  composite_info.y+=y;
4245  composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4246  composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4247  if ((composite_info.width != composite_image->columns) ||
4248  (composite_info.height != composite_image->rows))
4249  {
4250  Image
4251  *resize_image;
4252 
4253  /*
4254  Scale composite image.
4255  */
4256  resize_image=ResizeImage(composite_image,composite_info.width,
4257  composite_info.height,composite_image->filter,exception);
4258  composite_image=DestroyImage(composite_image);
4259  if (resize_image == (Image *) NULL)
4260  {
4261  XSetCursorState(display,windows,MagickFalse);
4262  return(MagickFalse);
4263  }
4264  composite_image=resize_image;
4265  }
4266  if (compose == DisplaceCompositeOp)
4267  (void) SetImageArtifact(composite_image,"compose:args",
4268  displacement_geometry);
4269  if (blend != 0.0)
4270  {
4271  CacheView
4272  *image_view;
4273 
4274  int
4275  y;
4276 
4277  Quantum
4278  opacity;
4279 
4280  register int
4281  x;
4282 
4283  register Quantum
4284  *q;
4285 
4286  /*
4287  Create mattes for blending.
4288  */
4289  (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4290  opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4291  ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4292  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4293  return(MagickFalse);
4295  image_view=AcquireAuthenticCacheView(image,exception);
4296  for (y=0; y < (int) image->rows; y++)
4297  {
4298  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4299  exception);
4300  if (q == (Quantum *) NULL)
4301  break;
4302  for (x=0; x < (int) image->columns; x++)
4303  {
4304  SetPixelAlpha(image,opacity,q);
4305  q+=GetPixelChannels(image);
4306  }
4307  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4308  break;
4309  }
4310  image_view=DestroyCacheView(image_view);
4311  }
4312  /*
4313  Composite image with X Image window.
4314  */
4315  (void) CompositeImage(image,composite_image,compose,MagickTrue,
4316  composite_info.x,composite_info.y,exception);
4317  composite_image=DestroyImage(composite_image);
4318  XSetCursorState(display,windows,MagickFalse);
4319  /*
4320  Update image configuration.
4321  */
4322  XConfigureImageColormap(display,resource_info,windows,image,exception);
4323  (void) XConfigureImage(display,resource_info,windows,image,exception);
4324  return(MagickTrue);
4325 }
4326 
4327 /*
4328 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4329 % %
4330 % %
4331 % %
4332 + X C o n f i g u r e I m a g e %
4333 % %
4334 % %
4335 % %
4336 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4337 %
4338 % XConfigureImage() creates a new X image. It also notifies the window
4339 % manager of the new image size and configures the transient widows.
4340 %
4341 % The format of the XConfigureImage method is:
4342 %
4343 % MagickBooleanType XConfigureImage(Display *display,
4344 % XResourceInfo *resource_info,XWindows *windows,Image *image,
4345 % ExceptionInfo *exception)
4346 %
4347 % A description of each parameter follows:
4348 %
4349 % o display: Specifies a connection to an X server; returned from
4350 % XOpenDisplay.
4351 %
4352 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4353 %
4354 % o windows: Specifies a pointer to a XWindows structure.
4355 %
4356 % o image: the image.
4357 %
4358 % o exception: return any errors or warnings in this structure.
4359 %
4360 % o exception: return any errors or warnings in this structure.
4361 %
4362 */
4363 static MagickBooleanType XConfigureImage(Display *display,
4364  XResourceInfo *resource_info,XWindows *windows,Image *image,
4365  ExceptionInfo *exception)
4366 {
4367  char
4368  geometry[MagickPathExtent];
4369 
4371  status;
4372 
4373  size_t
4374  mask,
4375  height,
4376  width;
4377 
4378  ssize_t
4379  x,
4380  y;
4381 
4382  XSizeHints
4383  *size_hints;
4384 
4385  XWindowChanges
4386  window_changes;
4387 
4388  /*
4389  Dismiss if window dimensions are zero.
4390  */
4391  width=(unsigned int) windows->image.window_changes.width;
4392  height=(unsigned int) windows->image.window_changes.height;
4393  if (image->debug != MagickFalse )
4395  "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4396  windows->image.ximage->height,(double) width,(double) height);
4397  if ((width*height) == 0)
4398  return(MagickTrue);
4399  x=0;
4400  y=0;
4401  /*
4402  Resize image to fit Image window dimensions.
4403  */
4404  XSetCursorState(display,windows,MagickTrue);
4405  (void) XFlush(display);
4406  if (((int) width != windows->image.ximage->width) ||
4407  ((int) height != windows->image.ximage->height))
4408  image->taint=MagickTrue;
4409  windows->magnify.x=(int)
4410  width*windows->magnify.x/windows->image.ximage->width;
4411  windows->magnify.y=(int)
4412  height*windows->magnify.y/windows->image.ximage->height;
4413  windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4414  windows->image.y=(int)
4415  (height*windows->image.y/windows->image.ximage->height);
4416  status=XMakeImage(display,resource_info,&windows->image,image,
4417  (unsigned int) width,(unsigned int) height,exception);
4418  if (status == MagickFalse)
4419  XNoticeWidget(display,windows,"Unable to configure X image:",
4420  windows->image.name);
4421  /*
4422  Notify window manager of the new configuration.
4423  */
4424  if (resource_info->image_geometry != (char *) NULL)
4425  (void) FormatLocaleString(geometry,MagickPathExtent,"%s>!",
4426  resource_info->image_geometry);
4427  else
4428  (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
4429  XDisplayWidth(display,windows->image.screen),
4430  XDisplayHeight(display,windows->image.screen));
4431  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4432  window_changes.width=(int) width;
4433  if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4434  window_changes.width=XDisplayWidth(display,windows->image.screen);
4435  window_changes.height=(int) height;
4436  if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4437  window_changes.height=XDisplayHeight(display,windows->image.screen);
4438  mask=(size_t) (CWWidth | CWHeight);
4439  if (resource_info->backdrop)
4440  {
4441  mask|=CWX | CWY;
4442  window_changes.x=(int)
4443  ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4444  window_changes.y=(int)
4445  ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4446  }
4447  (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4448  (unsigned int) mask,&window_changes);
4449  (void) XClearWindow(display,windows->image.id);
4450  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4451  /*
4452  Update Magnify window configuration.
4453  */
4454  if (windows->magnify.mapped != MagickFalse )
4455  XMakeMagnifyImage(display,windows,exception);
4456  windows->pan.crop_geometry=windows->image.crop_geometry;
4457  XBestIconSize(display,&windows->pan,image);
4458  while (((windows->pan.width << 1) < MaxIconSize) &&
4459  ((windows->pan.height << 1) < MaxIconSize))
4460  {
4461  windows->pan.width<<=1;
4462  windows->pan.height<<=1;
4463  }
4464  if (windows->pan.geometry != (char *) NULL)
4465  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4466  &windows->pan.width,&windows->pan.height);
4467  window_changes.width=(int) windows->pan.width;
4468  window_changes.height=(int) windows->pan.height;
4469  size_hints=XAllocSizeHints();
4470  if (size_hints != (XSizeHints *) NULL)
4471  {
4472  /*
4473  Set new size hints.
4474  */
4475  size_hints->flags=PSize | PMinSize | PMaxSize;
4476  size_hints->width=window_changes.width;
4477  size_hints->height=window_changes.height;
4478  size_hints->min_width=size_hints->width;
4479  size_hints->min_height=size_hints->height;
4480  size_hints->max_width=size_hints->width;
4481  size_hints->max_height=size_hints->height;
4482  (void) XSetNormalHints(display,windows->pan.id,size_hints);
4483  (void) XFree((void *) size_hints);
4484  }
4485  (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4486  (unsigned int) (CWWidth | CWHeight),&window_changes);
4487  /*
4488  Update icon window configuration.
4489  */
4490  windows->icon.crop_geometry=windows->image.crop_geometry;
4491  XBestIconSize(display,&windows->icon,image);
4492  window_changes.width=(int) windows->icon.width;
4493  window_changes.height=(int) windows->icon.height;
4494  (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4495  (unsigned int) (CWWidth | CWHeight),&window_changes);
4496  XSetCursorState(display,windows,MagickFalse);
4497  return(status != 0 ? MagickTrue : MagickFalse);
4498 }
4499 
4500 /*
4501 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4502 % %
4503 % %
4504 % %
4505 + X C r o p I m a g e %
4506 % %
4507 % %
4508 % %
4509 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4510 %
4511 % XCropImage() allows the user to select a region of the image and crop, copy,
4512 % or cut it. For copy or cut, the image can subsequently be composited onto
4513 % the image with XPasteImage.
4514 %
4515 % The format of the XCropImage method is:
4516 %
4517 % MagickBooleanType XCropImage(Display *display,
4518 % XResourceInfo *resource_info,XWindows *windows,Image *image,
4519 % const ClipboardMode mode,ExceptionInfo *exception)
4520 %
4521 % A description of each parameter follows:
4522 %
4523 % o display: Specifies a connection to an X server; returned from
4524 % XOpenDisplay.
4525 %
4526 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4527 %
4528 % o windows: Specifies a pointer to a XWindows structure.
4529 %
4530 % o image: the image; returned from ReadImage.
4531 %
4532 % o mode: This unsigned value specified whether the image should be
4533 % cropped, copied, or cut.
4534 %
4535 % o exception: return any errors or warnings in this structure.
4536 %
4537 */
4538 static MagickBooleanType XCropImage(Display *display,
4539  XResourceInfo *resource_info,XWindows *windows,Image *image,
4540  const ClipboardMode mode,ExceptionInfo *exception)
4541 {
4542  const char
4543  *const CropModeMenu[] =
4544  {
4545  "Help",
4546  "Dismiss",
4547  (char *) NULL
4548  },
4549  *RectifyModeMenu[] =
4550  {
4551  "Crop",
4552  "Help",
4553  "Dismiss",
4554  (char *) NULL
4555  };
4556 
4557  static const ModeType
4558  CropCommands[] =
4559  {
4560  CropHelpCommand,
4561  CropDismissCommand
4562  },
4563  RectifyCommands[] =
4564  {
4565  RectifyCopyCommand,
4566  RectifyHelpCommand,
4567  RectifyDismissCommand
4568  };
4569 
4570  CacheView
4571  *image_view;
4572 
4573  char
4574  command[MagickPathExtent],
4575  text[MagickPathExtent];
4576 
4577  Cursor
4578  cursor;
4579 
4580  int
4581  id,
4582  x,
4583  y;
4584 
4585  KeySym
4586  key_symbol;
4587 
4588  Image
4589  *crop_image;
4590 
4591  double
4592  scale_factor;
4593 
4595  crop_info,
4596  highlight_info;
4597 
4598  register Quantum
4599  *q;
4600 
4601  unsigned int
4602  height,
4603  width;
4604 
4605  size_t
4606  state;
4607 
4608  XEvent
4609  event;
4610 
4611  /*
4612  Map Command widget.
4613  */
4614  switch (mode)
4615  {
4616  case CopyMode:
4617  {
4618  (void) CloneString(&windows->command.name,"Copy");
4619  break;
4620  }
4621  case CropMode:
4622  {
4623  (void) CloneString(&windows->command.name,"Crop");
4624  break;
4625  }
4626  case CutMode:
4627  {
4628  (void) CloneString(&windows->command.name,"Cut");
4629  break;
4630  }
4631  }
4632  RectifyModeMenu[0]=windows->command.name;
4633  windows->command.data=0;
4634  (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4635  (void) XMapRaised(display,windows->command.id);
4636  XClientMessage(display,windows->image.id,windows->im_protocols,
4637  windows->im_update_widget,CurrentTime);
4638  /*
4639  Track pointer until button 1 is pressed.
4640  */
4641  XQueryPosition(display,windows->image.id,&x,&y);
4642  (void) XSelectInput(display,windows->image.id,
4643  windows->image.attributes.event_mask | PointerMotionMask);
4644  crop_info.x=(ssize_t) windows->image.x+x;
4645  crop_info.y=(ssize_t) windows->image.y+y;
4646  crop_info.width=0;
4647  crop_info.height=0;
4648  cursor=XCreateFontCursor(display,XC_fleur);
4649  state=DefaultState;
4650  do
4651  {
4652  if (windows->info.mapped != MagickFalse )
4653  {
4654  /*
4655  Display pointer position.
4656  */
4657  (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
4658  (long) crop_info.x,(long) crop_info.y);
4659  XInfoWidget(display,windows,text);
4660  }
4661  /*
4662  Wait for next event.
4663  */
4664  XScreenEvent(display,windows,&event,exception);
4665  if (event.xany.window == windows->command.id)
4666  {
4667  /*
4668  Select a command from the Command widget.
4669  */
4670  id=XCommandWidget(display,windows,CropModeMenu,&event);
4671  if (id < 0)
4672  continue;
4673  switch (CropCommands[id])
4674  {
4675  case CropHelpCommand:
4676  {
4677  switch (mode)
4678  {
4679  case CopyMode:
4680  {
4681  XTextViewHelp(display,resource_info,windows,MagickFalse,
4682  "Help Viewer - Image Copy",ImageCopyHelp);
4683  break;
4684  }
4685  case CropMode:
4686  {
4687  XTextViewHelp(display,resource_info,windows,MagickFalse,
4688  "Help Viewer - Image Crop",ImageCropHelp);
4689  break;
4690  }
4691  case CutMode:
4692  {
4693  XTextViewHelp(display,resource_info,windows,MagickFalse,
4694  "Help Viewer - Image Cut",ImageCutHelp);
4695  break;
4696  }
4697  }
4698  break;
4699  }
4700  case CropDismissCommand:
4701  {
4702  /*
4703  Prematurely exit.
4704  */
4705  state|=EscapeState;
4706  state|=ExitState;
4707  break;
4708  }
4709  default:
4710  break;
4711  }
4712  continue;
4713  }
4714  switch (event.type)
4715  {
4716  case ButtonPress:
4717  {
4718  if (event.xbutton.button != Button1)
4719  break;
4720  if (event.xbutton.window != windows->image.id)
4721  break;
4722  /*
4723  Note first corner of cropping rectangle-- exit loop.
4724  */
4725  (void) XCheckDefineCursor(display,windows->image.id,cursor);
4726  crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4727  crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4728  state|=ExitState;
4729  break;
4730  }
4731  case ButtonRelease:
4732  break;
4733  case Expose:
4734  break;
4735  case KeyPress:
4736  {
4737  if (event.xkey.window != windows->image.id)
4738  break;
4739  /*
4740  Respond to a user key press.
4741  */
4742  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4743  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4744  switch ((int) key_symbol)
4745  {
4746  case XK_Escape:
4747  case XK_F20:
4748  {
4749  /*
4750  Prematurely exit.
4751  */
4752  state|=EscapeState;
4753  state|=ExitState;
4754  break;
4755  }
4756  case XK_F1:
4757  case XK_Help:
4758  {
4759  switch (mode)
4760  {
4761  case CopyMode:
4762  {
4763  XTextViewHelp(display,resource_info,windows,MagickFalse,
4764  "Help Viewer - Image Copy",ImageCopyHelp);
4765  break;
4766  }
4767  case CropMode:
4768  {
4769  XTextViewHelp(display,resource_info,windows,MagickFalse,
4770  "Help Viewer - Image Crop",ImageCropHelp);
4771  break;
4772  }
4773  case CutMode:
4774  {
4775  XTextViewHelp(display,resource_info,windows,MagickFalse,
4776  "Help Viewer - Image Cut",ImageCutHelp);
4777  break;
4778  }
4779  }
4780  break;
4781  }
4782  default:
4783  {
4784  (void) XBell(display,0);
4785  break;
4786  }
4787  }
4788  break;
4789  }
4790  case MotionNotify:
4791  {
4792  if (event.xmotion.window != windows->image.id)
4793  break;
4794  /*
4795  Map and unmap Info widget as text cursor crosses its boundaries.
4796  */
4797  x=event.xmotion.x;
4798  y=event.xmotion.y;
4799  if (windows->info.mapped != MagickFalse )
4800  {
4801  if ((x < (int) (windows->info.x+windows->info.width)) &&
4802  (y < (int) (windows->info.y+windows->info.height)))
4803  (void) XWithdrawWindow(display,windows->info.id,
4804  windows->info.screen);
4805  }
4806  else
4807  if ((x > (int) (windows->info.x+windows->info.width)) ||
4808  (y > (int) (windows->info.y+windows->info.height)))
4809  (void) XMapWindow(display,windows->info.id);
4810  crop_info.x=(ssize_t) windows->image.x+x;
4811  crop_info.y=(ssize_t) windows->image.y+y;
4812  break;
4813  }
4814  default:
4815  break;
4816  }
4817  } while ((state & ExitState) == 0);
4818  (void) XSelectInput(display,windows->image.id,
4819  windows->image.attributes.event_mask);
4820  if ((state & EscapeState) != 0)
4821  {
4822  /*
4823  User want to exit without cropping.
4824  */
4825  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4826  (void) XFreeCursor(display,cursor);
4827  return(MagickTrue);
4828  }
4829  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4830  do
4831  {
4832  /*
4833  Size rectangle as pointer moves until the mouse button is released.
4834  */
4835  x=(int) crop_info.x;
4836  y=(int) crop_info.y;
4837  crop_info.width=0;
4838  crop_info.height=0;
4839  state=DefaultState;
4840  do
4841  {
4842  highlight_info=crop_info;
4843  highlight_info.x=crop_info.x-windows->image.x;
4844  highlight_info.y=crop_info.y-windows->image.y;
4845  if ((highlight_info.width > 3) && (highlight_info.height > 3))
4846  {
4847  /*
4848  Display info and draw cropping rectangle.
4849  */
4850  if (windows->info.mapped == MagickFalse)
4851  (void) XMapWindow(display,windows->info.id);
4853  " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4854  crop_info.height,(double) crop_info.x,(double) crop_info.y);
4855  XInfoWidget(display,windows,text);
4856  XHighlightRectangle(display,windows->image.id,
4857  windows->image.highlight_context,&highlight_info);
4858  }
4859  else
4860  if (windows->info.mapped != MagickFalse )
4861  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4862  /*
4863  Wait for next event.
4864  */
4865  XScreenEvent(display,windows,&event,exception);
4866  if ((highlight_info.width > 3) && (highlight_info.height > 3))
4867  XHighlightRectangle(display,windows->image.id,
4868  windows->image.highlight_context,&highlight_info);
4869  switch (event.type)
4870  {
4871  case ButtonPress:
4872  {
4873  crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4874  crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4875  break;
4876  }
4877  case ButtonRelease:
4878  {
4879  /*
4880  User has committed to cropping rectangle.
4881  */
4882  crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4883  crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4884  XSetCursorState(display,windows,MagickFalse);
4885  state|=ExitState;
4886  windows->command.data=0;
4887  (void) XCommandWidget(display,windows,RectifyModeMenu,
4888  (XEvent *) NULL);
4889  break;
4890  }
4891  case Expose:
4892  break;
4893  case MotionNotify:
4894  {
4895  crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4896  crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4897  }
4898  default:
4899  break;
4900  }
4901  if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4902  ((state & ExitState) != 0))
4903  {
4904  /*
4905  Check boundary conditions.
4906  */
4907  if (crop_info.x < 0)
4908  crop_info.x=0;
4909  else
4910  if (crop_info.x > (ssize_t) windows->image.ximage->width)
4911  crop_info.x=(ssize_t) windows->image.ximage->width;
4912  if ((int) crop_info.x < x)
4913  crop_info.width=(unsigned int) (x-crop_info.x);
4914  else
4915  {
4916  crop_info.width=(unsigned int) (crop_info.x-x);
4917  crop_info.x=(ssize_t) x;
4918  }
4919  if (crop_info.y < 0)
4920  crop_info.y=0;
4921  else
4922  if (crop_info.y > (ssize_t) windows->image.ximage->height)
4923  crop_info.y=(ssize_t) windows->image.ximage->height;
4924  if ((int) crop_info.y < y)
4925  crop_info.height=(unsigned int) (y-crop_info.y);
4926  else
4927  {
4928  crop_info.height=(unsigned int) (crop_info.y-y);
4929  crop_info.y=(ssize_t) y;
4930  }
4931  }
4932  } while ((state & ExitState) == 0);
4933  /*
4934  Wait for user to grab a corner of the rectangle or press return.
4935  */
4936  state=DefaultState;
4937  (void) XMapWindow(display,windows->info.id);
4938  do
4939  {
4940  if (windows->info.mapped != MagickFalse )
4941  {
4942  /*
4943  Display pointer position.
4944  */
4946  " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4947  crop_info.height,(double) crop_info.x,(double) crop_info.y);
4948  XInfoWidget(display,windows,text);
4949  }
4950  highlight_info=crop_info;
4951  highlight_info.x=crop_info.x-windows->image.x;
4952  highlight_info.y=crop_info.y-windows->image.y;
4953  if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4954  {
4955  state|=EscapeState;
4956  state|=ExitState;
4957  break;
4958  }
4959  XHighlightRectangle(display,windows->image.id,
4960  windows->image.highlight_context,&highlight_info);
4961  XScreenEvent(display,windows,&event,exception);
4962  if (event.xany.window == windows->command.id)
4963  {
4964  /*
4965  Select a command from the Command widget.
4966  */
4967  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4968  id=XCommandWidget(display,windows,RectifyModeMenu,&event);
4969  (void) XSetFunction(display,windows->image.highlight_context,
4970  GXinvert);
4971  XHighlightRectangle(display,windows->image.id,
4972  windows->image.highlight_context,&highlight_info);
4973  if (id >= 0)
4974  switch (RectifyCommands[id])
4975  {
4976  case RectifyCopyCommand:
4977  {
4978  state|=ExitState;
4979  break;
4980  }
4981  case RectifyHelpCommand:
4982  {
4983  (void) XSetFunction(display,windows->image.highlight_context,
4984  GXcopy);
4985  switch (mode)
4986  {
4987  case CopyMode:
4988  {
4989  XTextViewHelp(display,resource_info,windows,MagickFalse,
4990  "Help Viewer - Image Copy",ImageCopyHelp);
4991  break;
4992  }
4993  case CropMode:
4994  {
4995  XTextViewHelp(display,resource_info,windows,MagickFalse,
4996  "Help Viewer - Image Crop",ImageCropHelp);
4997  break;
4998  }
4999  case CutMode:
5000  {
5001  XTextViewHelp(display,resource_info,windows,MagickFalse,
5002  "Help Viewer - Image Cut",ImageCutHelp);
5003  break;
5004  }
5005  }
5006  (void) XSetFunction(display,windows->image.highlight_context,
5007  GXinvert);
5008  break;
5009  }
5010  case RectifyDismissCommand:
5011  {
5012  /*
5013  Prematurely exit.
5014  */
5015  state|=EscapeState;
5016  state|=ExitState;
5017  break;
5018  }
5019  default:
5020  break;
5021  }
5022  continue;
5023  }
5024  XHighlightRectangle(display,windows->image.id,
5025  windows->image.highlight_context,&highlight_info);
5026  switch (event.type)
5027  {
5028  case ButtonPress:
5029  {
5030  if (event.xbutton.button != Button1)
5031  break;
5032  if (event.xbutton.window != windows->image.id)
5033  break;
5034  x=windows->image.x+event.xbutton.x;
5035  y=windows->image.y+event.xbutton.y;
5036  if ((x < (int) (crop_info.x+RoiDelta)) &&
5037  (x > (int) (crop_info.x-RoiDelta)) &&
5038  (y < (int) (crop_info.y+RoiDelta)) &&
5039  (y > (int) (crop_info.y-RoiDelta)))
5040  {
5041  crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5042  crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5043  state|=UpdateConfigurationState;
5044  break;
5045  }
5046  if ((x < (int) (crop_info.x+RoiDelta)) &&
5047  (x > (int) (crop_info.x-RoiDelta)) &&
5048  (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5049  (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5050  {
5051  crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5052  state|=UpdateConfigurationState;
5053  break;
5054  }
5055  if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5056  (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5057  (y < (int) (crop_info.y+RoiDelta)) &&
5058  (y > (int) (crop_info.y-RoiDelta)))
5059  {
5060  crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5061  state|=UpdateConfigurationState;
5062  break;
5063  }
5064  if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5065  (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5066  (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5067  (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5068  {
5069  state|=UpdateConfigurationState;
5070  break;
5071  }
5072  }
5073  case ButtonRelease:
5074  {
5075  if (event.xbutton.window == windows->pan.id)
5076  if ((highlight_info.x != crop_info.x-windows->image.x) ||
5077  (highlight_info.y != crop_info.y-windows->image.y))
5078  XHighlightRectangle(display,windows->image.id,
5079  windows->image.highlight_context,&highlight_info);
5080  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5081  event.xbutton.time);
5082  break;
5083  }
5084  case Expose:
5085  {
5086  if (event.xexpose.window == windows->image.id)
5087  if (event.xexpose.count == 0)
5088  {
5089  event.xexpose.x=(int) highlight_info.x;
5090  event.xexpose.y=(int) highlight_info.y;
5091  event.xexpose.width=(int) highlight_info.width;
5092  event.xexpose.height=(int) highlight_info.height;
5093  XRefreshWindow(display,&windows->image,&event);
5094  }
5095  if (event.xexpose.window == windows->info.id)
5096  if (event.xexpose.count == 0)
5097  XInfoWidget(display,windows,text);
5098  break;
5099  }
5100  case KeyPress:
5101  {
5102  if (event.xkey.window != windows->image.id)
5103  break;
5104  /*
5105  Respond to a user key press.
5106  */
5107  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5108  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5109  switch ((int) key_symbol)
5110  {
5111  case XK_Escape:
5112  case XK_F20:
5113  state|=EscapeState;
5114  case XK_Return:
5115  {
5116  state|=ExitState;
5117  break;
5118  }
5119  case XK_Home:
5120  case XK_KP_Home:
5121  {
5122  crop_info.x=(ssize_t) (windows->image.width/2L-crop_info.width/
5123  2L);
5124  crop_info.y=(ssize_t) (windows->image.height/2L-crop_info.height/
5125  2L);
5126  break;
5127  }
5128  case XK_Left:
5129  case XK_KP_Left:
5130  {
5131  crop_info.x--;
5132  break;
5133  }
5134  case XK_Up:
5135  case XK_KP_Up:
5136  case XK_Next:
5137  {
5138  crop_info.y--;
5139  break;
5140  }
5141  case XK_Right:
5142  case XK_KP_Right:
5143  {
5144  crop_info.x++;
5145  break;
5146  }
5147  case XK_Prior:
5148  case XK_Down:
5149  case XK_KP_Down:
5150  {
5151  crop_info.y++;
5152  break;
5153  }
5154  case XK_F1:
5155  case XK_Help:
5156  {
5157  (void) XSetFunction(display,windows->image.highlight_context,
5158  GXcopy);
5159  switch (mode)
5160  {
5161  case CopyMode:
5162  {
5163  XTextViewHelp(display,resource_info,windows,MagickFalse,
5164  "Help Viewer - Image Copy",ImageCopyHelp);
5165  break;
5166  }
5167  case CropMode:
5168  {
5169  XTextViewHelp(display,resource_info,windows,MagickFalse,
5170  "Help Viewer - Image Cropg",ImageCropHelp);
5171  break;
5172  }
5173  case CutMode:
5174  {
5175  XTextViewHelp(display,resource_info,windows,MagickFalse,
5176  "Help Viewer - Image Cutg",ImageCutHelp);
5177  break;
5178  }
5179  }
5180  (void) XSetFunction(display,windows->image.highlight_context,
5181  GXinvert);
5182  break;
5183  }
5184  default:
5185  {
5186  (void) XBell(display,0);
5187  break;
5188  }
5189  }
5190  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5191  event.xkey.time);
5192  break;
5193  }
5194  case KeyRelease:
5195  break;
5196  case MotionNotify:
5197  {
5198  if (event.xmotion.window != windows->image.id)
5199  break;
5200  /*
5201  Map and unmap Info widget as text cursor crosses its boundaries.
5202  */
5203  x=event.xmotion.x;
5204  y=event.xmotion.y;
5205  if (windows->info.mapped != MagickFalse )
5206  {
5207  if ((x < (int) (windows->info.x+windows->info.width)) &&
5208  (y < (int) (windows->info.y+windows->info.height)))
5209  (void) XWithdrawWindow(display,windows->info.id,
5210  windows->info.screen);
5211  }
5212  else
5213  if ((x > (int) (windows->info.x+windows->info.width)) ||
5214  (y > (int) (windows->info.y+windows->info.height)))
5215  (void) XMapWindow(display,windows->info.id);
5216  crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5217  crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5218  break;
5219  }
5220  case SelectionRequest:
5221  {
5222  XSelectionEvent
5223  notify;
5224 
5225  XSelectionRequestEvent
5226  *request;
5227 
5228  /*
5229  Set primary selection.
5230  */
5232  "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5233  crop_info.height,(double) crop_info.x,(double) crop_info.y);
5234  request=(&(event.xselectionrequest));
5235  (void) XChangeProperty(request->display,request->requestor,
5236  request->property,request->target,8,PropModeReplace,
5237  (unsigned char *) text,(int) strlen(text));
5238  notify.type=SelectionNotify;
5239  notify.display=request->display;
5240  notify.requestor=request->requestor;
5241  notify.selection=request->selection;
5242  notify.target=request->target;
5243  notify.time=request->time;
5244  if (request->property == None)
5245  notify.property=request->target;
5246  else
5247  notify.property=request->property;
5248  (void) XSendEvent(request->display,request->requestor,False,0,
5249  (XEvent *) &notify);
5250  }
5251  default:
5252  break;
5253  }
5254  if ((state & UpdateConfigurationState) != 0)
5255  {
5256  (void) XPutBackEvent(display,&event);
5257  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5258  break;
5259  }
5260  } while ((state & ExitState) == 0);
5261  } while ((state & ExitState) == 0);
5262  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5263  XSetCursorState(display,windows,MagickFalse);
5264  if ((state & EscapeState) != 0)
5265  return(MagickTrue);
5266  if (mode == CropMode)
5267  if (((int) crop_info.width != windows->image.ximage->width) ||
5268  ((int) crop_info.height != windows->image.ximage->height))
5269  {
5270  /*
5271  Reconfigure Image window as defined by cropping rectangle.
5272  */
5273  XSetCropGeometry(display,windows,&crop_info,image);
5274  windows->image.window_changes.width=(int) crop_info.width;
5275  windows->image.window_changes.height=(int) crop_info.height;
5276  (void) XConfigureImage(display,resource_info,windows,image,exception);
5277  return(MagickTrue);
5278  }
5279  /*
5280  Copy image before applying image transforms.
5281  */
5282  XSetCursorState(display,windows,MagickTrue);
5283  XCheckRefreshWindows(display,windows);
5284  width=(unsigned int) image->columns;
5285  height=(unsigned int) image->rows;
5286  x=0;
5287  y=0;
5288  if (windows->image.crop_geometry != (char *) NULL)
5289  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5290  scale_factor=(double) width/windows->image.ximage->width;
5291  crop_info.x+=x;
5292  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5293  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5294  scale_factor=(double) height/windows->image.ximage->height;
5295  crop_info.y+=y;
5296  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5297  crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5298  crop_image=CropImage(image,&crop_info,exception);
5299  XSetCursorState(display,windows,MagickFalse);
5300  if (crop_image == (Image *) NULL)
5301  return(MagickFalse);
5302  if (resource_info->copy_image != (Image *) NULL)
5303  resource_info->copy_image=DestroyImage(resource_info->copy_image);
5304  resource_info->copy_image=crop_image;
5305  if (mode == CopyMode)
5306  {
5307  (void) XConfigureImage(display,resource_info,windows,image,exception);
5308  return(MagickTrue);
5309  }
5310  /*
5311  Cut image.
5312  */
5313  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5314  return(MagickFalse);
5316  image_view=AcquireAuthenticCacheView(image,exception);
5317  for (y=0; y < (int) crop_info.height; y++)
5318  {
5319  q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5320  crop_info.width,1,exception);
5321  if (q == (Quantum *) NULL)
5322  break;
5323  for (x=0; x < (int) crop_info.width; x++)
5324  {
5326  q+=GetPixelChannels(image);
5327  }
5328  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5329  break;
5330  }
5331  image_view=DestroyCacheView(image_view);
5332  /*
5333  Update image configuration.
5334  */
5335  XConfigureImageColormap(display,resource_info,windows,image,exception);
5336  (void) XConfigureImage(display,resource_info,windows,image,exception);
5337  return(MagickTrue);
5338 }
5339 
5340 /*
5341 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5342 % %
5343 % %
5344 % %
5345 + X D r a w I m a g e %
5346 % %
5347 % %
5348 % %
5349 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5350 %
5351 % XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5352 % the image.
5353 %
5354 % The format of the XDrawEditImage method is:
5355 %
5356 % MagickBooleanType XDrawEditImage(Display *display,
5357 % XResourceInfo *resource_info,XWindows *windows,Image **image,
5358 % ExceptionInfo *exception)
5359 %
5360 % A description of each parameter follows:
5361 %
5362 % o display: Specifies a connection to an X server; returned from
5363 % XOpenDisplay.
5364 %
5365 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5366 %
5367 % o windows: Specifies a pointer to a XWindows structure.
5368 %
5369 % o image: the image.
5370 %
5371 % o exception: return any errors or warnings in this structure.
5372 %
5373 */
5374 static MagickBooleanType XDrawEditImage(Display *display,
5375  XResourceInfo *resource_info,XWindows *windows,Image **image,
5376  ExceptionInfo *exception)
5377 {
5378  const char
5379  *const DrawMenu[] =
5380  {
5381  "Element",
5382  "Color",
5383  "Stipple",
5384  "Width",
5385  "Undo",
5386  "Help",
5387  "Dismiss",
5388  (char *) NULL
5389  };
5390 
5391  static ElementType
5392  element = PointElement;
5393 
5394  static const ModeType
5395  DrawCommands[] =
5396  {
5397  DrawElementCommand,
5398  DrawColorCommand,
5399  DrawStippleCommand,
5400  DrawWidthCommand,
5401  DrawUndoCommand,
5402  DrawHelpCommand,
5403  DrawDismissCommand
5404  };
5405 
5406  static Pixmap
5407  stipple = (Pixmap) NULL;
5408 
5409  static unsigned int
5410  pen_id = 0,
5411  line_width = 1;
5412 
5413  char
5414  command[MagickPathExtent],
5415  text[MagickPathExtent];
5416 
5417  Cursor
5418  cursor;
5419 
5420  int
5421  entry,
5422  id,
5423  number_coordinates,
5424  x,
5425  y;
5426 
5427  double
5428  degrees;
5429 
5431  status;
5432 
5434  rectangle_info;
5435 
5436  register int
5437  i;
5438 
5439  unsigned int
5440  distance,
5441  height,
5442  max_coordinates,
5443  width;
5444 
5445  size_t
5446  state;
5447 
5448  Window
5449  root_window;
5450 
5451  XDrawInfo
5452  draw_info;
5453 
5454  XEvent
5455  event;
5456 
5457  XPoint
5458  *coordinate_info;
5459 
5460  XSegment
5461  line_info;
5462 
5463  /*
5464  Allocate polygon info.
5465  */
5466  max_coordinates=2048;
5467  coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5468  sizeof(*coordinate_info));
5469  if (coordinate_info == (XPoint *) NULL)
5470  {
5471  (void) ThrowMagickException(exception,GetMagickModule(),
5472  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5473  return(MagickFalse);
5474  }
5475  /*
5476  Map Command widget.
5477  */
5478  (void) CloneString(&windows->command.name,"Draw");
5479  windows->command.data=4;
5480  (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5481  (void) XMapRaised(display,windows->command.id);
5482  XClientMessage(display,windows->image.id,windows->im_protocols,
5483  windows->im_update_widget,CurrentTime);
5484  /*
5485  Wait for first button press.
5486  */
5487  root_window=XRootWindow(display,XDefaultScreen(display));
5488  draw_info.stencil=OpaqueStencil;
5489  status=MagickTrue;
5490  cursor=XCreateFontCursor(display,XC_tcross);
5491  for ( ; ; )
5492  {
5493  XQueryPosition(display,windows->image.id,&x,&y);
5494  (void) XSelectInput(display,windows->image.id,
5495  windows->image.attributes.event_mask | PointerMotionMask);
5496  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5497  state=DefaultState;
5498  do
5499  {
5500  if (windows->info.mapped != MagickFalse )
5501  {
5502  /*
5503  Display pointer position.
5504  */
5505  (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
5506  x+windows->image.x,y+windows->image.y);
5507  XInfoWidget(display,windows,text);
5508  }
5509  /*
5510  Wait for next event.
5511  */
5512  XScreenEvent(display,windows,&event,exception);
5513  if (event.xany.window == windows->command.id)
5514  {
5515  /*
5516  Select a command from the Command widget.
5517  */
5518  id=XCommandWidget(display,windows,DrawMenu,&event);
5519  if (id < 0)
5520  continue;
5521  switch (DrawCommands[id])
5522  {
5523  case DrawElementCommand:
5524  {
5525  const char
5526  *const Elements[] =
5527  {
5528  "point",
5529  "line",
5530  "rectangle",
5531  "fill rectangle",
5532  "circle",
5533  "fill circle",
5534  "ellipse",
5535  "fill ellipse",
5536  "polygon",
5537  "fill polygon",
5538  (char *) NULL,
5539  };
5540 
5541  /*
5542  Select a command from the pop-up menu.
5543  */
5544  element=(ElementType) (XMenuWidget(display,windows,
5545  DrawMenu[id],Elements,command)+1);
5546  break;
5547  }
5548  case DrawColorCommand:
5549  {
5550  const char
5551  *ColorMenu[MaxNumberPens+1];
5552 
5553  int
5554  pen_number;
5555 
5557  transparent;
5558 
5559  XColor
5560  color;
5561 
5562  /*
5563  Initialize menu selections.
5564  */
5565  for (i=0; i < (int) (MaxNumberPens-2); i++)
5566  ColorMenu[i]=resource_info->pen_colors[i];
5567  ColorMenu[MaxNumberPens-2]="transparent";
5568  ColorMenu[MaxNumberPens-1]="Browser...";
5569  ColorMenu[MaxNumberPens]=(char *) NULL;
5570  /*
5571  Select a pen color from the pop-up menu.
5572  */
5573  pen_number=XMenuWidget(display,windows,DrawMenu[id],
5574  (const char **) ColorMenu,command);
5575  if (pen_number < 0)
5576  break;
5577  transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5578  MagickFalse;
5579  if (transparent != MagickFalse )
5580  {
5581  draw_info.stencil=TransparentStencil;
5582  break;
5583  }
5584  if (pen_number == (MaxNumberPens-1))
5585  {
5586  static char
5587  color_name[MagickPathExtent] = "gray";
5588 
5589  /*
5590  Select a pen color from a dialog.
5591  */
5592  resource_info->pen_colors[pen_number]=color_name;
5593  XColorBrowserWidget(display,windows,"Select",color_name);
5594  if (*color_name == '\0')
5595  break;
5596  }
5597  /*
5598  Set pen color.
5599  */
5600  (void) XParseColor(display,windows->map_info->colormap,
5601  resource_info->pen_colors[pen_number],&color);
5602  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5603  (unsigned int) MaxColors,&color);
5604  windows->pixel_info->pen_colors[pen_number]=color;
5605  pen_id=(unsigned int) pen_number;
5606  draw_info.stencil=OpaqueStencil;
5607  break;
5608  }
5609  case DrawStippleCommand:
5610  {
5611  const char
5612  *StipplesMenu[] =
5613  {
5614  "Brick",
5615  "Diagonal",
5616  "Scales",
5617  "Vertical",
5618  "Wavy",
5619  "Translucent",
5620  "Opaque",
5621  (char *) NULL,
5622  (char *) NULL,
5623  };
5624 
5625  Image
5626  *stipple_image;
5627 
5628  ImageInfo
5629  *image_info;
5630 
5631  int
5632  status;
5633 
5634  static char
5635  filename[MagickPathExtent] = "\0";
5636 
5637  /*
5638  Select a command from the pop-up menu.
5639  */
5640  StipplesMenu[7]="Open...";
5641  entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5642  command);
5643  if (entry < 0)
5644  break;
5645  if (stipple != (Pixmap) NULL)
5646  (void) XFreePixmap(display,stipple);
5647  stipple=(Pixmap) NULL;
5648  if (entry != 7)
5649  {
5650  switch (entry)
5651  {
5652  case 0:
5653  {
5654  stipple=XCreateBitmapFromData(display,root_window,
5655  (char *) BricksBitmap,BricksWidth,BricksHeight);
5656  break;
5657  }
5658  case 1:
5659  {
5660  stipple=XCreateBitmapFromData(display,root_window,
5661  (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5662  break;
5663  }
5664  case 2:
5665  {
5666  stipple=XCreateBitmapFromData(display,root_window,
5667  (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5668  break;
5669  }
5670  case 3:
5671  {
5672  stipple=XCreateBitmapFromData(display,root_window,
5673  (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5674  break;
5675  }
5676  case 4:
5677  {
5678  stipple=XCreateBitmapFromData(display,root_window,
5679  (char *) WavyBitmap,WavyWidth,WavyHeight);
5680  break;
5681  }
5682  case 5:
5683  {
5684  stipple=XCreateBitmapFromData(display,root_window,
5685  (char *) HighlightBitmap,HighlightWidth,
5686  HighlightHeight);
5687  break;
5688  }
5689  case 6:
5690  default:
5691  {
5692  stipple=XCreateBitmapFromData(display,root_window,
5693  (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5694  break;
5695  }
5696  }
5697  break;
5698  }
5699  XFileBrowserWidget(display,windows,"Stipple",filename);
5700  if (*filename == '\0')
5701  break;
5702  /*
5703  Read image.
5704  */
5705  XSetCursorState(display,windows,MagickTrue);
5706  XCheckRefreshWindows(display,windows);
5707  image_info=AcquireImageInfo();
5708  (void) CopyMagickString(image_info->filename,filename,
5710  stipple_image=ReadImage(image_info,exception);
5711  CatchException(exception);
5712  XSetCursorState(display,windows,MagickFalse);
5713  if (stipple_image == (Image *) NULL)
5714  break;
5715  (void) AcquireUniqueFileResource(filename);
5716  (void) FormatLocaleString(stipple_image->filename,
5717  MagickPathExtent,"xbm:%s",filename);
5718  (void) WriteImage(image_info,stipple_image,exception);
5719  stipple_image=DestroyImage(stipple_image);
5720  image_info=DestroyImageInfo(image_info);
5721  status=XReadBitmapFile(display,root_window,filename,&width,
5722  &height,&stipple,&x,&y);
5723  (void) RelinquishUniqueFileResource(filename);
5724  if ((status != BitmapSuccess) != 0)
5725  XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5726  filename);
5727  break;
5728  }
5729  case DrawWidthCommand:
5730  {
5731  const char
5732  *const WidthsMenu[] =
5733  {
5734  "1",
5735  "2",
5736  "4",
5737  "8",
5738  "16",
5739  "Dialog...",
5740  (char *) NULL,
5741  };
5742 
5743  static char
5744  width[MagickPathExtent] = "0";
5745 
5746  /*
5747  Select a command from the pop-up menu.
5748  */
5749  entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5750  command);
5751  if (entry < 0)
5752  break;
5753  if (entry != 5)
5754  {
5755  line_width=(unsigned int) StringToUnsignedLong(
5756  WidthsMenu[entry]);
5757  break;
5758  }
5759  (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5760  width);
5761  if (*width == '\0')
5762  break;
5763  line_width=(unsigned int) StringToUnsignedLong(width);
5764  break;
5765  }
5766  case DrawUndoCommand:
5767  {
5768  (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5769  image,exception);
5770  break;
5771  }
5772  case DrawHelpCommand:
5773  {
5774  XTextViewHelp(display,resource_info,windows,MagickFalse,
5775  "Help Viewer - Image Rotation",ImageDrawHelp);
5776  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5777  break;
5778  }
5779  case DrawDismissCommand:
5780  {
5781  /*
5782  Prematurely exit.
5783  */
5784  state|=EscapeState;
5785  state|=ExitState;
5786  break;
5787  }
5788  default:
5789  break;
5790  }
5791  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5792  continue;
5793  }
5794  switch (event.type)
5795  {
5796  case ButtonPress:
5797  {
5798  if (event.xbutton.button != Button1)
5799  break;
5800  if (event.xbutton.window != windows->image.id)
5801  break;
5802  /*
5803  exit loop.
5804  */
5805  x=event.xbutton.x;
5806  y=event.xbutton.y;
5807  state|=ExitState;
5808  break;
5809  }
5810  case ButtonRelease:
5811  break;
5812  case Expose:
5813  break;
5814  case KeyPress:
5815  {
5816  KeySym
5817  key_symbol;
5818 
5819  if (event.xkey.window != windows->image.id)
5820  break;
5821  /*
5822  Respond to a user key press.
5823  */
5824  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5825  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5826  switch ((int) key_symbol)
5827  {
5828  case XK_Escape:
5829  case XK_F20:
5830  {
5831  /*
5832  Prematurely exit.
5833  */
5834  state|=EscapeState;
5835  state|=ExitState;
5836  break;
5837  }
5838  case XK_F1:
5839  case XK_Help:
5840  {
5841  XTextViewHelp(display,resource_info,windows,MagickFalse,
5842  "Help Viewer - Image Rotation",ImageDrawHelp);
5843  break;
5844  }
5845  default:
5846  {
5847  (void) XBell(display,0);
5848  break;
5849  }
5850  }
5851  break;
5852  }
5853  case MotionNotify:
5854  {
5855  /*
5856  Map and unmap Info widget as text cursor crosses its boundaries.
5857  */
5858  x=event.xmotion.x;
5859  y=event.xmotion.y;
5860  if (windows->info.mapped != MagickFalse )
5861  {
5862  if ((x < (int) (windows->info.x+windows->info.width)) &&
5863  (y < (int) (windows->info.y+windows->info.height)))
5864  (void) XWithdrawWindow(display,windows->info.id,
5865  windows->info.screen);
5866  }
5867  else
5868  if ((x > (int) (windows->info.x+windows->info.width)) ||
5869  (y > (int) (windows->info.y+windows->info.height)))
5870  (void) XMapWindow(display,windows->info.id);
5871  break;
5872  }
5873  }
5874  } while ((state & ExitState) == 0);
5875  (void) XSelectInput(display,windows->image.id,
5876  windows->image.attributes.event_mask);
5877  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5878  if ((state & EscapeState) != 0)
5879  break;
5880  /*
5881  Draw element as pointer moves until the button is released.
5882  */
5883  distance=0;
5884  degrees=0.0;
5885  line_info.x1=x;
5886  line_info.y1=y;
5887  line_info.x2=x;
5888  line_info.y2=y;
5889  rectangle_info.x=(ssize_t) x;
5890  rectangle_info.y=(ssize_t) y;
5891  rectangle_info.width=0;
5892  rectangle_info.height=0;
5893  number_coordinates=1;
5894  coordinate_info->x=x;
5895  coordinate_info->y=y;
5896  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5897  state=DefaultState;
5898  do
5899  {
5900  switch (element)
5901  {
5902  case PointElement:
5903  default:
5904  {
5905  if (number_coordinates > 1)
5906  {
5907  (void) XDrawLines(display,windows->image.id,
5908  windows->image.highlight_context,coordinate_info,
5909  number_coordinates,CoordModeOrigin);
5910  (void) FormatLocaleString(text,MagickPathExtent," %+d%+d",
5911  coordinate_info[number_coordinates-1].x,
5912  coordinate_info[number_coordinates-1].y);
5913  XInfoWidget(display,windows,text);
5914  }
5915  break;
5916  }
5917  case LineElement:
5918  {
5919  if (distance > 9)
5920  {
5921  /*
5922  Display angle of the line.
5923  */
5924  degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5925  line_info.y1),(double) (line_info.x2-line_info.x1)));
5926  (void) FormatLocaleString(text,MagickPathExtent," %g",
5927  (double) degrees);
5928  XInfoWidget(display,windows,text);
5929  XHighlightLine(display,windows->image.id,
5930  windows->image.highlight_context,&line_info);
5931  }
5932  else
5933  if (windows->info.mapped != MagickFalse )
5934  (void) XWithdrawWindow(display,windows->info.id,
5935  windows->info.screen);
5936  break;
5937  }
5938  case RectangleElement:
5939  case FillRectangleElement:
5940  {
5941  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5942  {
5943  /*
5944  Display info and draw drawing rectangle.
5945  */
5947  " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5948  (double) rectangle_info.height,(double) rectangle_info.x,
5949  (double) rectangle_info.y);
5950  XInfoWidget(display,windows,text);
5951  XHighlightRectangle(display,windows->image.id,
5952  windows->image.highlight_context,&rectangle_info);
5953  }
5954  else
5955  if (windows->info.mapped != MagickFalse )
5956  (void) XWithdrawWindow(display,windows->info.id,
5957  windows->info.screen);
5958  break;
5959  }
5960  case CircleElement:
5961  case FillCircleElement:
5962  case EllipseElement:
5963  case FillEllipseElement:
5964  {
5965  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5966  {
5967  /*
5968  Display info and draw drawing rectangle.
5969  */
5971  " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5972  (double) rectangle_info.height,(double) rectangle_info.x,
5973  (double) rectangle_info.y);
5974  XInfoWidget(display,windows,text);
5975  XHighlightEllipse(display,windows->image.id,
5976  windows->image.highlight_context,&rectangle_info);
5977  }
5978  else
5979  if (windows->info.mapped != MagickFalse )
5980  (void) XWithdrawWindow(display,windows->info.id,
5981  windows->info.screen);
5982  break;
5983  }
5984  case PolygonElement:
5985  case FillPolygonElement:
5986  {
5987  if (number_coordinates > 1)
5988  (void) XDrawLines(display,windows->image.id,
5989  windows->image.highlight_context,coordinate_info,
5990  number_coordinates,CoordModeOrigin);
5991  if (distance > 9)
5992  {
5993  /*
5994  Display angle of the line.
5995  */
5996  degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5997  line_info.y1),(double) (line_info.x2-line_info.x1)));
5998  (void) FormatLocaleString(text,MagickPathExtent," %g",
5999  (double) degrees);
6000  XInfoWidget(display,windows,text);
6001  XHighlightLine(display,windows->image.id,
6002  windows->image.highlight_context,&line_info);
6003  }
6004  else
6005  if (windows->info.mapped != MagickFalse )
6006  (void) XWithdrawWindow(display,windows->info.id,
6007  windows->info.screen);
6008  break;
6009  }
6010  }
6011  /*
6012  Wait for next event.
6013  */
6014  XScreenEvent(display,windows,&event,exception);
6015  switch (element)
6016  {
6017  case PointElement:
6018  default:
6019  {
6020  if (number_coordinates > 1)
6021  (void) XDrawLines(display,windows->image.id,
6022  windows->image.highlight_context,coordinate_info,
6023  number_coordinates,CoordModeOrigin);
6024  break;
6025  }
6026  case LineElement:
6027  {
6028  if (distance > 9)
6029  XHighlightLine(display,windows->image.id,
6030  windows->image.highlight_context,&line_info);
6031  break;
6032  }
6033  case RectangleElement:
6034  case FillRectangleElement:
6035  {
6036  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6037  XHighlightRectangle(display,windows->image.id,
6038  windows->image.highlight_context,&rectangle_info);
6039  break;
6040  }
6041  case CircleElement:
6042  case FillCircleElement:
6043  case EllipseElement:
6044  case FillEllipseElement:
6045  {
6046  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6047  XHighlightEllipse(display,windows->image.id,
6048  windows->image.highlight_context,&rectangle_info);
6049  break;
6050  }
6051  case PolygonElement:
6052  case FillPolygonElement:
6053  {
6054  if (number_coordinates > 1)
6055  (void) XDrawLines(display,windows->image.id,
6056  windows->image.highlight_context,coordinate_info,
6057  number_coordinates,CoordModeOrigin);
6058  if (distance > 9)
6059  XHighlightLine(display,windows->image.id,
6060  windows->image.highlight_context,&line_info);
6061  break;
6062  }
6063  }
6064  switch (event.type)
6065  {
6066  case ButtonPress:
6067  break;
6068  case ButtonRelease:
6069  {
6070  /*
6071  User has committed to element.
6072  */
6073  line_info.x2=event.xbutton.x;
6074  line_info.y2=event.xbutton.y;
6075  rectangle_info.x=(ssize_t) event.xbutton.x;
6076  rectangle_info.y=(ssize_t) event.xbutton.y;
6077  coordinate_info[number_coordinates].x=event.xbutton.x;
6078  coordinate_info[number_coordinates].y=event.xbutton.y;
6079  if (((element != PolygonElement) &&
6080  (element != FillPolygonElement)) || (distance <= 9))
6081  {
6082  state|=ExitState;
6083  break;
6084  }
6085  number_coordinates++;
6086  if (number_coordinates < (int) max_coordinates)
6087  {
6088  line_info.x1=event.xbutton.x;
6089  line_info.y1=event.xbutton.y;
6090  break;
6091  }
6092  max_coordinates<<=1;
6093  coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6094  max_coordinates,sizeof(*coordinate_info));
6095  if (coordinate_info == (XPoint *) NULL)
6096  (void) ThrowMagickException(exception,GetMagickModule(),
6097  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6098  break;
6099  }
6100  case Expose:
6101  break;
6102  case MotionNotify:
6103  {
6104  if (event.xmotion.window != windows->image.id)
6105  break;
6106  if (element != PointElement)
6107  {
6108  line_info.x2=event.xmotion.x;
6109  line_info.y2=event.xmotion.y;
6110  rectangle_info.x=(ssize_t) event.xmotion.x;
6111  rectangle_info.y=(ssize_t) event.xmotion.y;
6112  break;
6113  }
6114  coordinate_info[number_coordinates].x=event.xbutton.x;
6115  coordinate_info[number_coordinates].y=event.xbutton.y;
6116  number_coordinates++;
6117  if (number_coordinates < (int) max_coordinates)
6118  break;
6119  max_coordinates<<=1;
6120  coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6121  max_coordinates,sizeof(*coordinate_info));
6122  if (coordinate_info == (XPoint *) NULL)
6123  (void) ThrowMagickException(exception,GetMagickModule(),
6124  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6125  break;
6126  }
6127  default:
6128  break;
6129  }
6130  /*
6131  Check boundary conditions.
6132  */
6133  if (line_info.x2 < 0)
6134  line_info.x2=0;
6135  else
6136  if (line_info.x2 > (int) windows->image.width)
6137  line_info.x2=(short) windows->image.width;
6138  if (line_info.y2 < 0)
6139  line_info.y2=0;
6140  else
6141  if (line_info.y2 > (int) windows->image.height)
6142  line_info.y2=(short) windows->image.height;
6143  distance=(unsigned int)
6144  (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6145  ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6146  if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6147  ((state & ExitState) != 0))
6148  {
6149  if (rectangle_info.x < 0)
6150  rectangle_info.x=0;
6151  else
6152  if (rectangle_info.x > (ssize_t) windows->image.width)
6153  rectangle_info.x=(ssize_t) windows->image.width;
6154  if ((int) rectangle_info.x < x)
6155  rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6156  else
6157  {
6158  rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6159  rectangle_info.x=(ssize_t) x;
6160  }
6161  if (rectangle_info.y < 0)
6162  rectangle_info.y=0;
6163  else
6164  if (rectangle_info.y > (ssize_t) windows->image.height)
6165  rectangle_info.y=(ssize_t) windows->image.height;
6166  if ((int) rectangle_info.y < y)
6167  rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6168  else
6169  {
6170  rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6171  rectangle_info.y=(ssize_t) y;
6172  }
6173  }
6174  } while ((state & ExitState) == 0);
6175  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6176  if ((element == PointElement) || (element == PolygonElement) ||
6177  (element == FillPolygonElement))
6178  {
6179  /*
6180  Determine polygon bounding box.
6181  */
6182  rectangle_info.x=(ssize_t) coordinate_info->x;
6183  rectangle_info.y=(ssize_t) coordinate_info->y;
6184  x=coordinate_info->x;
6185  y=coordinate_info->y;
6186  for (i=1; i < number_coordinates; i++)
6187  {
6188  if (coordinate_info[i].x > x)
6189  x=coordinate_info[i].x;
6190  if (coordinate_info[i].y > y)
6191  y=coordinate_info[i].y;
6192  if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6193  rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6194  if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6195  rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6196  }
6197  rectangle_info.width=(size_t) (x-rectangle_info.x);
6198  rectangle_info.height=(size_t) (y-rectangle_info.y);
6199  for (i=0; i < number_coordinates; i++)
6200  {
6201  coordinate_info[i].x-=rectangle_info.x;
6202  coordinate_info[i].y-=rectangle_info.y;
6203  }
6204  }
6205  else
6206  if (distance <= 9)
6207  continue;
6208  else
6209  if ((element == RectangleElement) ||
6210  (element == CircleElement) || (element == EllipseElement))
6211  {
6212  rectangle_info.width--;
6213  rectangle_info.height--;
6214  }
6215  /*
6216  Drawing is relative to image configuration.
6217  */
6218  draw_info.x=(int) rectangle_info.x;
6219  draw_info.y=(int) rectangle_info.y;
6220  (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6221  image,exception);
6222  width=(unsigned int) (*image)->columns;
6223  height=(unsigned int) (*image)->rows;
6224  x=0;
6225  y=0;
6226  if (windows->image.crop_geometry != (char *) NULL)
6227  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6228  draw_info.x+=windows->image.x-(line_width/2);
6229  if (draw_info.x < 0)
6230  draw_info.x=0;
6231  draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6232  draw_info.y+=windows->image.y-(line_width/2);
6233  if (draw_info.y < 0)
6234  draw_info.y=0;
6235  draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6236  draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6237  if (draw_info.width > (unsigned int) (*image)->columns)
6238  draw_info.width=(unsigned int) (*image)->columns;
6239  draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6240  if (draw_info.height > (unsigned int) (*image)->rows)
6241  draw_info.height=(unsigned int) (*image)->rows;
6242  (void) FormatLocaleString(draw_info.geometry,MagickPathExtent,"%ux%u%+d%+d",
6243  width*draw_info.width/windows->image.ximage->width,
6244  height*draw_info.height/windows->image.ximage->height,
6245  draw_info.x+x,draw_info.y+y);
6246  /*
6247  Initialize drawing attributes.
6248  */
6249  draw_info.degrees=0.0;
6250  draw_info.element=element;
6251  draw_info.stipple=stipple;
6252  draw_info.line_width=line_width;
6253  draw_info.line_info=line_info;
6254  if (line_info.x1 > (int) (line_width/2))
6255  draw_info.line_info.x1=(short) line_width/2;
6256  if (line_info.y1 > (int) (line_width/2))
6257  draw_info.line_info.y1=(short) line_width/2;
6258  draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6259  draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6260  if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6261  {
6262  draw_info.line_info.x2=(-draw_info.line_info.x2);
6263  draw_info.line_info.y2=(-draw_info.line_info.y2);
6264  }
6265  if (draw_info.line_info.x2 < 0)
6266  {
6267  draw_info.line_info.x2=(-draw_info.line_info.x2);
6268  Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6269  }
6270  if (draw_info.line_info.y2 < 0)
6271  {
6272  draw_info.line_info.y2=(-draw_info.line_info.y2);
6273  Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6274  }
6275  draw_info.rectangle_info=rectangle_info;
6276  if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6277  draw_info.rectangle_info.x=(ssize_t) line_width/2;
6278  if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6279  draw_info.rectangle_info.y=(ssize_t) line_width/2;
6280  draw_info.number_coordinates=(unsigned int) number_coordinates;
6281  draw_info.coordinate_info=coordinate_info;
6282  windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6283  /*
6284  Draw element on image.
6285  */
6286  XSetCursorState(display,windows,MagickTrue);
6287  XCheckRefreshWindows(display,windows);
6288  status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6289  XSetCursorState(display,windows,MagickFalse);
6290  /*
6291  Update image colormap and return to image drawing.
6292  */
6293  XConfigureImageColormap(display,resource_info,windows,*image,exception);
6294  (void) XConfigureImage(display,resource_info,windows,*image,exception);
6295  }
6296  XSetCursorState(display,windows,MagickFalse);
6297  coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6298  return(status != 0 ? MagickTrue : MagickFalse);
6299 }
6300 
6301 /*
6302 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6303 % %
6304 % %
6305 % %
6306 + X D r a w P a n R e c t a n g l e %
6307 % %
6308 % %
6309 % %
6310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6311 %
6312 % XDrawPanRectangle() draws a rectangle in the pan window. The pan window
6313 % displays a zoom image and the rectangle shows which portion of the image is
6314 % displayed in the Image window.
6315 %
6316 % The format of the XDrawPanRectangle method is:
6317 %
6318 % XDrawPanRectangle(Display *display,XWindows *windows)
6319 %
6320 % A description of each parameter follows:
6321 %
6322 % o display: Specifies a connection to an X server; returned from
6323 % XOpenDisplay.
6324 %
6325 % o windows: Specifies a pointer to a XWindows structure.
6326 %
6327 */
6328 static void XDrawPanRectangle(Display *display,XWindows *windows)
6329 {
6330  double
6331  scale_factor;
6332 
6334  highlight_info;
6335 
6336  /*
6337  Determine dimensions of the panning rectangle.
6338  */
6339  scale_factor=(double) windows->pan.width/windows->image.ximage->width;
6340  highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6341  highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6342  scale_factor=(double)
6343  windows->pan.height/windows->image.ximage->height;
6344  highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6345  highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6346  /*
6347  Display the panning rectangle.
6348  */
6349  (void) XClearWindow(display,windows->pan.id);
6350  XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6351  &highlight_info);
6352 }
6353 
6354 /*
6355 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6356 % %
6357 % %
6358 % %
6359 + X I m a g e C a c h e %
6360 % %
6361 % %
6362 % %
6363 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6364 %
6365 % XImageCache() handles the creation, manipulation, and destruction of the
6366 % image cache (undo and redo buffers).
6367 %
6368 % The format of the XImageCache method is:
6369 %
6370 % void XImageCache(Display *display,XResourceInfo *resource_info,
6371 % XWindows *windows,const CommandType command,Image **image,
6372 % ExceptionInfo *exception)
6373 %
6374 % A description of each parameter follows:
6375 %
6376 % o display: Specifies a connection to an X server; returned from
6377 % XOpenDisplay.
6378 %
6379 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6380 %
6381 % o windows: Specifies a pointer to a XWindows structure.
6382 %
6383 % o command: Specifies a command to perform.
6384 %
6385 % o image: the image; XImageCache may transform the image and return a new
6386 % image pointer.
6387 %
6388 % o exception: return any errors or warnings in this structure.
6389 %
6390 */
6391 static void XImageCache(Display *display,XResourceInfo *resource_info,
6392  XWindows *windows,const CommandType command,Image **image,
6393  ExceptionInfo *exception)
6394 {
6395  Image
6396  *cache_image;
6397 
6398  static Image
6399  *redo_image = (Image *) NULL,
6400  *undo_image = (Image *) NULL;
6401 
6402  switch (command)
6403  {
6404  case FreeBuffersCommand:
6405  {
6406  /*
6407  Free memory from the undo and redo cache.
6408  */
6409  while (undo_image != (Image *) NULL)
6410  {
6411  cache_image=undo_image;
6412  undo_image=GetPreviousImageInList(undo_image);
6413  cache_image->list=DestroyImage(cache_image->list);
6414  cache_image=DestroyImage(cache_image);
6415  }
6416  undo_image=NewImageList();
6417  if (redo_image != (Image *) NULL)
6418  redo_image=DestroyImage(redo_image);
6419  redo_image=NewImageList();
6420  return;
6421  }
6422  case UndoCommand:
6423  {
6424  char
6425  image_geometry[MagickPathExtent];
6426 
6427  /*
6428  Undo the last image transformation.
6429  */
6430  if (undo_image == (Image *) NULL)
6431  {
6432  (void) XBell(display,0);
6433  return;
6434  }
6435  cache_image=undo_image;
6436  undo_image=GetPreviousImageInList(undo_image);
6437  windows->image.window_changes.width=(int) cache_image->columns;
6438  windows->image.window_changes.height=(int) cache_image->rows;
6439  (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
6440  windows->image.ximage->width,windows->image.ximage->height);
6441  (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6442  exception);
6443  if (windows->image.crop_geometry != (char *) NULL)
6444  windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6445  windows->image.crop_geometry);
6446  windows->image.crop_geometry=cache_image->geometry;
6447  if (redo_image != (Image *) NULL)
6448  redo_image=DestroyImage(redo_image);
6449  redo_image=(*image);
6450  *image=cache_image->list;
6451  cache_image=DestroyImage(cache_image);
6452  if (windows->image.orphan != MagickFalse )
6453  return;
6454  XConfigureImageColormap(display,resource_info,windows,*image,exception);
6455  (void) XConfigureImage(display,resource_info,windows,*image,exception);
6456  return;
6457  }
6458  case CutCommand:
6459  case PasteCommand:
6460  case ApplyCommand:
6461  case HalfSizeCommand:
6462  case OriginalSizeCommand:
6463  case DoubleSizeCommand:
6464  case ResizeCommand:
6465  case TrimCommand:
6466  case CropCommand:
6467  case ChopCommand:
6468  case FlipCommand:
6469  case FlopCommand:
6470  case RotateRightCommand:
6471  case RotateLeftCommand:
6472  case RotateCommand:
6473  case ShearCommand:
6474  case RollCommand:
6475  case NegateCommand:
6476  case ContrastStretchCommand:
6477  case SigmoidalContrastCommand:
6478  case NormalizeCommand:
6479  case EqualizeCommand:
6480  case HueCommand:
6481  case SaturationCommand:
6482  case BrightnessCommand:
6483  case GammaCommand:
6484  case SpiffCommand:
6485  case DullCommand:
6486  case GrayscaleCommand:
6487  case MapCommand:
6488  case QuantizeCommand:
6489  case DespeckleCommand:
6490  case EmbossCommand:
6491  case ReduceNoiseCommand:
6492  case AddNoiseCommand:
6493  case SharpenCommand:
6494  case BlurCommand:
6495  case ThresholdCommand:
6496  case EdgeDetectCommand:
6497  case SpreadCommand:
6498  case ShadeCommand:
6499  case RaiseCommand:
6500  case SegmentCommand:
6501  case SolarizeCommand:
6502  case SepiaToneCommand:
6503  case SwirlCommand:
6504  case ImplodeCommand:
6505  case VignetteCommand:
6506  case WaveCommand:
6507  case OilPaintCommand:
6508  case CharcoalDrawCommand:
6509  case AnnotateCommand:
6510  case AddBorderCommand:
6511  case AddFrameCommand:
6512  case CompositeCommand:
6513  case CommentCommand:
6514  case LaunchCommand:
6515  case RegionofInterestCommand:
6516  case SaveToUndoBufferCommand:
6517  case RedoCommand:
6518  {
6519  Image
6520  *previous_image;
6521 
6522  ssize_t
6523  bytes;
6524 
6525  bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6526  if (undo_image != (Image *) NULL)
6527  {
6528  /*
6529  Ensure the undo cache has enough memory available.
6530  */
6531  previous_image=undo_image;
6532  while (previous_image != (Image *) NULL)
6533  {
6534  bytes+=previous_image->list->columns*previous_image->list->rows*
6535  sizeof(PixelInfo);
6536  if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6537  {
6538  previous_image=GetPreviousImageInList(previous_image);
6539  continue;
6540  }
6541  bytes-=previous_image->list->columns*previous_image->list->rows*
6542  sizeof(PixelInfo);
6543  if (previous_image == undo_image)
6544  undo_image=NewImageList();
6545  else
6546  previous_image->next->previous=NewImageList();
6547  break;
6548  }
6549  while (previous_image != (Image *) NULL)
6550  {
6551  /*
6552  Delete any excess memory from undo cache.
6553  */
6554  cache_image=previous_image;
6555  previous_image=GetPreviousImageInList(previous_image);
6556  cache_image->list=DestroyImage(cache_image->list);
6557  cache_image=DestroyImage(cache_image);
6558  }
6559  }
6560  if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6561  break;
6562  /*
6563  Save image before transformations are applied.
6564  */
6565  cache_image=AcquireImage((ImageInfo *) NULL,exception);
6566  if (cache_image == (Image *) NULL)
6567  break;
6568  XSetCursorState(display,windows,MagickTrue);
6569  XCheckRefreshWindows(display,windows);
6570  cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6571  XSetCursorState(display,windows,MagickFalse);
6572  if (cache_image->list == (Image *) NULL)
6573  {
6574  cache_image=DestroyImage(cache_image);
6575  break;
6576  }
6577  cache_image->columns=(size_t) windows->image.ximage->width;
6578  cache_image->rows=(size_t) windows->image.ximage->height;
6579  cache_image->geometry=windows->image.crop_geometry;
6580  if (windows->image.crop_geometry != (char *) NULL)
6581  {
6582  cache_image->geometry=AcquireString((char *) NULL);
6583  (void) CopyMagickString(cache_image->geometry,
6584  windows->image.crop_geometry,MagickPathExtent);
6585  }
6586  if (undo_image == (Image *) NULL)
6587  {
6588  undo_image=cache_image;
6589  break;
6590  }
6591  undo_image->next=cache_image;
6592  undo_image->next->previous=undo_image;
6593  undo_image=undo_image->next;
6594  break;
6595  }
6596  default:
6597  break;
6598  }
6599  if (command == RedoCommand)
6600  {
6601  /*
6602  Redo the last image transformation.
6603  */
6604  if (redo_image == (Image *) NULL)
6605  {
6606  (void) XBell(display,0);
6607  return;
6608  }
6609  windows->image.window_changes.width=(int) redo_image->columns;
6610  windows->image.window_changes.height=(int) redo_image->rows;
6611  if (windows->image.crop_geometry != (char *) NULL)
6612  windows->image.crop_geometry=(char *)
6613  RelinquishMagickMemory(windows->image.crop_geometry);
6614  windows->image.crop_geometry=redo_image->geometry;
6615  *image=DestroyImage(*image);
6616  *image=redo_image;
6617  redo_image=NewImageList();
6618  if (windows->image.orphan != MagickFalse )
6619  return;
6620  XConfigureImageColormap(display,resource_info,windows,*image,exception);
6621  (void) XConfigureImage(display,resource_info,windows,*image,exception);
6622  return;
6623  }
6624  if (command != InfoCommand)
6625  return;
6626  /*
6627  Display image info.
6628  */
6629  XSetCursorState(display,windows,MagickTrue);
6630  XCheckRefreshWindows(display,windows);
6631  XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6632  XSetCursorState(display,windows,MagickFalse);
6633 }
6634 
6635 /*
6636 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6637 % %
6638 % %
6639 % %
6640 + X I m a g e W i n d o w C o m m a n d %
6641 % %
6642 % %
6643 % %
6644 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6645 %
6646 % XImageWindowCommand() makes a transform to the image or Image window as
6647 % specified by a user menu button or keyboard command.
6648 %
6649 % The format of the XImageWindowCommand method is:
6650 %
6651 % CommandType XImageWindowCommand(Display *display,
6652 % XResourceInfo *resource_info,XWindows *windows,
6653 % const MagickStatusType state,KeySym key_symbol,Image **image,
6654 % ExceptionInfo *exception)
6655 %
6656 % A description of each parameter follows:
6657 %
6658 % o nexus: Method XImageWindowCommand returns an image when the
6659 % user chooses 'Open Image' from the command menu. Otherwise a null
6660 % image is returned.
6661 %
6662 % o display: Specifies a connection to an X server; returned from
6663 % XOpenDisplay.
6664 %
6665 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6666 %
6667 % o windows: Specifies a pointer to a XWindows structure.
6668 %
6669 % o state: key mask.
6670 %
6671 % o key_symbol: Specifies a command to perform.
6672 %
6673 % o image: the image; XImageWIndowCommand may transform the image and
6674 % return a new image pointer.
6675 %
6676 % o exception: return any errors or warnings in this structure.
6677 %
6678 */
6679 static CommandType XImageWindowCommand(Display *display,
6680  XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6681  KeySym key_symbol,Image **image,ExceptionInfo *exception)
6682 {
6683  static char
6684  delta[MagickPathExtent] = "";
6685 
6686  static const char
6687  Digits[] = "01234567890";
6688 
6689  static KeySym
6690  last_symbol = XK_0;
6691 
6692  if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6693  {
6694  if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6695  {
6696  *delta='\0';
6697  resource_info->quantum=1;
6698  }
6699  last_symbol=key_symbol;
6700  delta[strlen(delta)+1]='\0';
6701  delta[strlen(delta)]=Digits[key_symbol-XK_0];
6702  resource_info->quantum=StringToLong(delta);
6703  return(NullCommand);
6704  }
6705  last_symbol=key_symbol;
6706  if (resource_info->immutable)
670