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