MagickCore  7.0.9
display.c
Go to the documentation of this file.
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % DDDD IIIII SSSSS PPPP L AAA Y Y %
7 % D D I SS P P L A A Y Y %
8 % D D I SSS PPPP L AAAAA Y %
9 % D D I SS P L A A Y %
10 % DDDD IIIII SSSSS P LLLLL A A Y %
11 % %
12 % %
13 % MagickCore Methods to Interactively Display and Edit an Image %
14 % %
15 % Software Design %
16 % Cristy %
17 % July 1992 %
18 % %
19 % %
20 % Copyright 1999-2020 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  register 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  register char
1847  *p;
1848 
1849  register 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 *) AcquireMagickMemory(
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 *) AcquireMagickMemory(
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  register Quantum
3305  *q;
3306 
3307  register 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  register int
4282  x;
4283 
4284  register 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  register 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_image=CropImage(image,&crop_info,exception);
5300  XSetCursorState(display,windows,MagickFalse);
5301  if (crop_image == (Image *) NULL)
5302  return(MagickFalse);
5303  if (resource_info->copy_image != (Image *) NULL)
5304  resource_info->copy_image=DestroyImage(resource_info->copy_image);
5305  resource_info->copy_image=crop_image;
5306  if (mode == CopyMode)
5307  {
5308  (void) XConfigureImage(display,resource_info,windows,image,exception);
5309  return(MagickTrue);
5310  }
5311  /*
5312  Cut image.
5313  */
5314  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5315  return(MagickFalse);
5317  image_view=AcquireAuthenticCacheView(image,exception);
5318  for (y=0; y < (int) crop_info.height; y++)
5319  {
5320  q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5321  crop_info.width,1,exception);
5322  if (q == (Quantum *) NULL)
5323  break;
5324  for (x=0; x < (int) crop_info.width; x++)
5325  {
5327  q+=GetPixelChannels(image);
5328  }
5329  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5330  break;
5331  }
5332  image_view=DestroyCacheView(image_view);
5333  /*
5334  Update image configuration.
5335  */
5336  XConfigureImageColormap(display,resource_info,windows,image,exception);
5337  (void) XConfigureImage(display,resource_info,windows,image,exception);
5338  return(MagickTrue);
5339 }
5340 
5341 /*
5342 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5343 % %
5344 % %
5345 % %
5346 + X D r a w I m a g e %
5347 % %
5348 % %
5349 % %
5350 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5351 %
5352 % XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5353 % the image.
5354 %
5355 % The format of the XDrawEditImage method is:
5356 %
5357 % MagickBooleanType XDrawEditImage(Display *display,
5358 % XResourceInfo *resource_info,XWindows *windows,Image **image,
5359 % ExceptionInfo *exception)
5360 %
5361 % A description of each parameter follows:
5362 %
5363 % o display: Specifies a connection to an X server; returned from
5364 % XOpenDisplay.
5365 %
5366 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5367 %
5368 % o windows: Specifies a pointer to a XWindows structure.
5369 %
5370 % o image: the image.
5371 %
5372 % o exception: return any errors or warnings in this structure.
5373 %
5374 */
5375 static MagickBooleanType XDrawEditImage(Display *display,
5376  XResourceInfo *resource_info,XWindows *windows,Image **image,
5377  ExceptionInfo *exception)
5378 {
5379  const char
5380  *const DrawMenu[] =
5381  {
5382  "Element",
5383  "Color",
5384  "Stipple",
5385  "Width",
5386  "Undo",
5387  "Help",
5388  "Dismiss",
5389  (char *) NULL
5390  };
5391 
5392  static ElementType
5393  element = PointElement;
5394 
5395  static const ModeType
5396  DrawCommands[] =
5397  {
5398  DrawElementCommand,
5399  DrawColorCommand,
5400  DrawStippleCommand,
5401  DrawWidthCommand,
5402  DrawUndoCommand,
5403  DrawHelpCommand,
5404  DrawDismissCommand
5405  };
5406 
5407  static Pixmap
5408  stipple = (Pixmap) NULL;
5409 
5410  static unsigned int
5411  pen_id = 0,
5412  line_width = 1;
5413 
5414  char
5415  command[MagickPathExtent],
5416  text[MagickPathExtent];
5417 
5418  Cursor
5419  cursor;
5420 
5421  int
5422  entry,
5423  id,
5424  number_coordinates,
5425  x,
5426  y;
5427 
5428  double
5429  degrees;
5430 
5432  status;
5433 
5435  rectangle_info;
5436 
5437  register int
5438  i;
5439 
5440  unsigned int
5441  distance,
5442  height,
5443  max_coordinates,
5444  width;
5445 
5446  size_t
5447  state;
5448 
5449  Window
5450  root_window;
5451 
5452  XDrawInfo
5453  draw_info;
5454 
5455  XEvent
5456  event;
5457 
5458  XPoint
5459  *coordinate_info;
5460 
5461  XSegment
5462  line_info;
5463 
5464  /*
5465  Allocate polygon info.
5466  */
5467  max_coordinates=2048;
5468  coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5469  sizeof(*coordinate_info));
5470  if (coordinate_info == (XPoint *) NULL)
5471  {
5472  (void) ThrowMagickException(exception,GetMagickModule(),
5473  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5474  return(MagickFalse);
5475  }
5476  /*
5477  Map Command widget.
5478  */
5479  (void) CloneString(&windows->command.name,"Draw");
5480  windows->command.data=4;
5481  (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5482  (void) XMapRaised(display,windows->command.id);
5483  XClientMessage(display,windows->image.id,windows->im_protocols,
5484  windows->im_update_widget,CurrentTime);
5485  /*
5486  Wait for first button press.
5487  */
5488  root_window=XRootWindow(display,XDefaultScreen(display));
5489  draw_info.stencil=OpaqueStencil;
5490  status=MagickTrue;
5491  cursor=XCreateFontCursor(display,XC_tcross);
5492  for ( ; ; )
5493  {
5494  XQueryPosition(display,windows->image.id,&x,&y);
5495  (void) XSelectInput(display,windows->image.id,
5496  windows->image.attributes.event_mask | PointerMotionMask);
5497  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5498  state=DefaultState;
5499  do
5500  {
5501  if (windows->info.mapped != MagickFalse )
5502  {
5503  /*
5504  Display pointer position.
5505  */
5506  (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
5507  x+windows->image.x,y+windows->image.y);
5508  XInfoWidget(display,windows,text);
5509  }
5510  /*
5511  Wait for next event.
5512  */
5513  XScreenEvent(display,windows,&event,exception);
5514  if (event.xany.window == windows->command.id)
5515  {
5516  /*
5517  Select a command from the Command widget.
5518  */
5519  id=XCommandWidget(display,windows,DrawMenu,&event);
5520  if (id < 0)
5521  continue;
5522  switch (DrawCommands[id])
5523  {
5524  case DrawElementCommand:
5525  {
5526  const char
5527  *const Elements[] =
5528  {
5529  "point",
5530  "line",
5531  "rectangle",
5532  "fill rectangle",
5533  "circle",
5534  "fill circle",
5535  "ellipse",
5536  "fill ellipse",
5537  "polygon",
5538  "fill polygon",
5539  (char *) NULL,
5540  };
5541 
5542  /*
5543  Select a command from the pop-up menu.
5544  */
5545  element=(ElementType) (XMenuWidget(display,windows,
5546  DrawMenu[id],Elements,command)+1);
5547  break;
5548  }
5549  case DrawColorCommand:
5550  {
5551  const char
5552  *ColorMenu[MaxNumberPens+1];
5553 
5554  int
5555  pen_number;
5556 
5558  transparent;
5559 
5560  XColor
5561  color;
5562 
5563  /*
5564  Initialize menu selections.
5565  */
5566  for (i=0; i < (int) (MaxNumberPens-2); i++)
5567  ColorMenu[i]=resource_info->pen_colors[i];
5568  ColorMenu[MaxNumberPens-2]="transparent";
5569  ColorMenu[MaxNumberPens-1]="Browser...";
5570  ColorMenu[MaxNumberPens]=(char *) NULL;
5571  /*
5572  Select a pen color from the pop-up menu.
5573  */
5574  pen_number=XMenuWidget(display,windows,DrawMenu[id],
5575  (const char **) ColorMenu,command);
5576  if (pen_number < 0)
5577  break;
5578  transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5579  MagickFalse;
5580  if (transparent != MagickFalse )
5581  {
5582  draw_info.stencil=TransparentStencil;
5583  break;
5584  }
5585  if (pen_number == (MaxNumberPens-1))
5586  {
5587  static char
5588  color_name[MagickPathExtent] = "gray";
5589 
5590  /*
5591  Select a pen color from a dialog.
5592  */
5593  resource_info->pen_colors[pen_number]=color_name;
5594  XColorBrowserWidget(display,windows,"Select",color_name);
5595  if (*color_name == '\0')
5596  break;
5597  }
5598  /*
5599  Set pen color.
5600  */
5601  (void) XParseColor(display,windows->map_info->colormap,
5602  resource_info->pen_colors[pen_number],&color);
5603  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5604  (unsigned int) MaxColors,&color);
5605  windows->pixel_info->pen_colors[pen_number]=color;
5606  pen_id=(unsigned int) pen_number;
5607  draw_info.stencil=OpaqueStencil;
5608  break;
5609  }
5610  case DrawStippleCommand:
5611  {
5612  const char
5613  *StipplesMenu[] =
5614  {
5615  "Brick",
5616  "Diagonal",
5617  "Scales",
5618  "Vertical",
5619  "Wavy",
5620  "Translucent",
5621  "Opaque",
5622  (char *) NULL,
5623  (char *) NULL,
5624  };
5625 
5626  Image
5627  *stipple_image;
5628 
5629  ImageInfo
5630  *image_info;
5631 
5632  int
5633  status;
5634 
5635  static char
5636  filename[MagickPathExtent] = "\0";
5637 
5638  /*
5639  Select a command from the pop-up menu.
5640  */
5641  StipplesMenu[7]="Open...";
5642  entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5643  command);
5644  if (entry < 0)
5645  break;
5646  if (stipple != (Pixmap) NULL)
5647  (void) XFreePixmap(display,stipple);
5648  stipple=(Pixmap) NULL;
5649  if (entry != 7)
5650  {
5651  switch (entry)
5652  {
5653  case 0:
5654  {
5655  stipple=XCreateBitmapFromData(display,root_window,
5656  (char *) BricksBitmap,BricksWidth,BricksHeight);
5657  break;
5658  }
5659  case 1:
5660  {
5661  stipple=XCreateBitmapFromData(display,root_window,
5662  (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5663  break;
5664  }
5665  case 2:
5666  {
5667  stipple=XCreateBitmapFromData(display,root_window,
5668  (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5669  break;
5670  }
5671  case 3:
5672  {
5673  stipple=XCreateBitmapFromData(display,root_window,
5674  (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5675  break;
5676  }
5677  case 4:
5678  {
5679  stipple=XCreateBitmapFromData(display,root_window,
5680  (char *) WavyBitmap,WavyWidth,WavyHeight);
5681  break;
5682  }
5683  case 5:
5684  {
5685  stipple=XCreateBitmapFromData(display,root_window,
5686  (char *) HighlightBitmap,HighlightWidth,
5687  HighlightHeight);
5688  break;
5689  }
5690  case 6:
5691  default:
5692  {
5693  stipple=XCreateBitmapFromData(display,root_window,
5694  (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5695  break;
5696  }
5697  }
5698  break;
5699  }
5700  XFileBrowserWidget(display,windows,"Stipple",filename);
5701  if (*filename == '\0')
5702  break;
5703  /*
5704  Read image.
5705  */
5706  XSetCursorState(display,windows,MagickTrue);
5707  XCheckRefreshWindows(display,windows);
5708  image_info=AcquireImageInfo();
5709  (void) CopyMagickString(image_info->filename,filename,
5711  stipple_image=ReadImage(image_info,exception);
5712  CatchException(exception);
5713  XSetCursorState(display,windows,MagickFalse);
5714  if (stipple_image == (Image *) NULL)
5715  break;
5716  (void) AcquireUniqueFileResource(filename);
5717  (void) FormatLocaleString(stipple_image->filename,
5718  MagickPathExtent,"xbm:%s",filename);
5719  (void) WriteImage(image_info,stipple_image,exception);
5720  stipple_image=DestroyImage(stipple_image);
5721  image_info=DestroyImageInfo(image_info);
5722  status=XReadBitmapFile(display,root_window,filename,&width,
5723  &height,&stipple,&x,&y);
5724  (void) RelinquishUniqueFileResource(filename);
5725  if ((status != BitmapSuccess) != 0)
5726  XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5727  filename);
5728  break;
5729  }
5730  case DrawWidthCommand:
5731  {
5732  const char
5733  *const WidthsMenu[] =
5734  {
5735  "1",
5736  "2",
5737  "4",
5738  "8",
5739  "16",
5740  "Dialog...",
5741  (char *) NULL,
5742  };
5743 
5744  static char
5745  width[MagickPathExtent] = "0";
5746 
5747  /*
5748  Select a command from the pop-up menu.
5749  */
5750  entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5751  command);
5752  if (entry < 0)
5753  break;
5754  if (entry != 5)
5755  {
5756  line_width=(unsigned int) StringToUnsignedLong(
5757  WidthsMenu[entry]);
5758  break;
5759  }
5760  (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5761  width);
5762  if (*width == '\0')
5763  break;
5764  line_width=(unsigned int) StringToUnsignedLong(width);
5765  break;
5766  }
5767  case DrawUndoCommand:
5768  {
5769  (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5770  image,exception);
5771  break;
5772  }
5773  case DrawHelpCommand:
5774  {
5775  XTextViewHelp(display,resource_info,windows,MagickFalse,
5776  "Help Viewer - Image Rotation",ImageDrawHelp);
5777  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5778  break;
5779  }
5780  case DrawDismissCommand:
5781  {
5782  /*
5783  Prematurely exit.
5784  */
5785  state|=EscapeState;
5786  state|=ExitState;
5787  break;
5788  }
5789  default:
5790  break;
5791  }
5792  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5793  continue;
5794  }
5795  switch (event.type)
5796  {
5797  case ButtonPress:
5798  {
5799  if (event.xbutton.button != Button1)
5800  break;
5801  if (event.xbutton.window != windows->image.id)
5802  break;
5803  /*
5804  exit loop.
5805  */
5806  x=event.xbutton.x;
5807  y=event.xbutton.y;
5808  state|=ExitState;
5809  break;
5810  }
5811  case ButtonRelease:
5812  break;
5813  case Expose:
5814  break;
5815  case KeyPress:
5816  {
5817  KeySym
5818  key_symbol;
5819 
5820  if (event.xkey.window != windows->image.id)
5821  break;
5822  /*
5823  Respond to a user key press.
5824  */
5825  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5826  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5827  switch ((int) key_symbol)
5828  {
5829  case XK_Escape:
5830  case XK_F20:
5831  {
5832  /*
5833  Prematurely exit.
5834  */
5835  state|=EscapeState;
5836  state|=ExitState;
5837  break;
5838  }
5839  case XK_F1:
5840  case XK_Help:
5841  {
5842  XTextViewHelp(display,resource_info,windows,MagickFalse,
5843  "Help Viewer - Image Rotation",ImageDrawHelp);
5844  break;
5845  }
5846  default:
5847  {
5848  (void) XBell(display,0);
5849  break;
5850  }
5851  }
5852  break;
5853  }
5854  case MotionNotify:
5855  {
5856  /*
5857  Map and unmap Info widget as text cursor crosses its boundaries.
5858  */
5859  x=event.xmotion.x;
5860  y=event.xmotion.y;
5861  if (windows->info.mapped != MagickFalse )
5862  {
5863  if ((x < (int) (windows->info.x+windows->info.width)) &&
5864  (y < (int) (windows->info.y+windows->info.height)))
5865  (void) XWithdrawWindow(display,windows->info.id,
5866  windows->info.screen);
5867  }
5868  else
5869  if ((x > (int) (windows->info.x+windows->info.width)) ||
5870  (y > (int) (windows->info.y+windows->info.height)))
5871  (void) XMapWindow(display,windows->info.id);
5872  break;
5873  }
5874  }
5875  } while ((state & ExitState) == 0);
5876  (void) XSelectInput(display,windows->image.id,
5877  windows->image.attributes.event_mask);
5878  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5879  if ((state & EscapeState) != 0)
5880  break;
5881  /*
5882  Draw element as pointer moves until the button is released.
5883  */
5884  distance=0;
5885  degrees=0.0;
5886  line_info.x1=x;
5887  line_info.y1=y;
5888  line_info.x2=x;
5889  line_info.y2=y;
5890  rectangle_info.x=(ssize_t) x;
5891  rectangle_info.y=(ssize_t) y;
5892  rectangle_info.width=0;
5893  rectangle_info.height=0;
5894  number_coordinates=1;
5895  coordinate_info->x=x;
5896  coordinate_info->y=y;
5897  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5898  state=DefaultState;
5899  do
5900  {
5901  switch (element)
5902  {
5903  case PointElement:
5904  default:
5905  {
5906  if (number_coordinates > 1)
5907  {
5908  (void) XDrawLines(display,windows->image.id,
5909  windows->image.highlight_context,coordinate_info,
5910  number_coordinates,CoordModeOrigin);
5911  (void) FormatLocaleString(text,MagickPathExtent," %+d%+d",
5912  coordinate_info[number_coordinates-1].x,
5913  coordinate_info[number_coordinates-1].y);
5914  XInfoWidget(display,windows,text);
5915  }
5916  break;
5917  }
5918  case LineElement:
5919  {
5920  if (distance > 9)
5921  {
5922  /*
5923  Display angle of the line.
5924  */
5925  degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5926  line_info.y1),(double) (line_info.x2-line_info.x1)));
5927  (void) FormatLocaleString(text,MagickPathExtent," %g",
5928  (double) degrees);
5929  XInfoWidget(display,windows,text);
5930  XHighlightLine(display,windows->image.id,
5931  windows->image.highlight_context,&line_info);
5932  }
5933  else
5934  if (windows->info.mapped != MagickFalse )
5935  (void) XWithdrawWindow(display,windows->info.id,
5936  windows->info.screen);
5937  break;
5938  }
5939  case RectangleElement:
5940  case FillRectangleElement:
5941  {
5942  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5943  {
5944  /*
5945  Display info and draw drawing rectangle.
5946  */
5948  " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5949  (double) rectangle_info.height,(double) rectangle_info.x,
5950  (double) rectangle_info.y);
5951  XInfoWidget(display,windows,text);
5952  XHighlightRectangle(display,windows->image.id,
5953  windows->image.highlight_context,&rectangle_info);
5954  }
5955  else
5956  if (windows->info.mapped != MagickFalse )
5957  (void) XWithdrawWindow(display,windows->info.id,
5958  windows->info.screen);
5959  break;
5960  }
5961  case CircleElement:
5962  case FillCircleElement:
5963  case EllipseElement:
5964  case FillEllipseElement:
5965  {
5966  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5967  {
5968  /*
5969  Display info and draw drawing rectangle.
5970  */
5972  " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5973  (double) rectangle_info.height,(double) rectangle_info.x,
5974  (double) rectangle_info.y);
5975  XInfoWidget(display,windows,text);
5976  XHighlightEllipse(display,windows->image.id,
5977  windows->image.highlight_context,&rectangle_info);
5978  }
5979  else
5980  if (windows->info.mapped != MagickFalse )
5981  (void) XWithdrawWindow(display,windows->info.id,
5982  windows->info.screen);
5983  break;
5984  }
5985  case PolygonElement:
5986  case FillPolygonElement:
5987  {
5988  if (number_coordinates > 1)
5989  (void) XDrawLines(display,windows->image.id,
5990  windows->image.highlight_context,coordinate_info,
5991  number_coordinates,CoordModeOrigin);
5992  if (distance > 9)
5993  {
5994  /*
5995  Display angle of the line.
5996  */
5997  degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5998  line_info.y1),(double) (line_info.x2-line_info.x1)));
5999  (void) FormatLocaleString(text,MagickPathExtent," %g",
6000  (double) degrees);
6001  XInfoWidget(display,windows,text);
6002  XHighlightLine(display,windows->image.id,
6003  windows->image.highlight_context,&line_info);
6004  }
6005  else
6006  if (windows->info.mapped != MagickFalse )
6007  (void) XWithdrawWindow(display,windows->info.id,
6008  windows->info.screen);
6009  break;
6010  }
6011  }
6012  /*
6013  Wait for next event.
6014  */
6015  XScreenEvent(display,windows,&event,exception);
6016  switch (element)
6017  {
6018  case PointElement:
6019  default:
6020  {
6021  if (number_coordinates > 1)
6022  (void) XDrawLines(display,windows->image.id,
6023  windows->image.highlight_context,coordinate_info,
6024  number_coordinates,CoordModeOrigin);
6025  break;
6026  }
6027  case LineElement:
6028  {
6029  if (distance > 9)
6030  XHighlightLine(display,windows->image.id,
6031  windows->image.highlight_context,&line_info);
6032  break;
6033  }
6034  case RectangleElement:
6035  case FillRectangleElement:
6036  {
6037  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6038  XHighlightRectangle(display,windows->image.id,
6039  windows->image.highlight_context,&rectangle_info);
6040  break;
6041  }
6042  case CircleElement:
6043  case FillCircleElement:
6044  case EllipseElement:
6045  case FillEllipseElement:
6046  {
6047  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6048  XHighlightEllipse(display,windows->image.id,
6049  windows->image.highlight_context,&rectangle_info);
6050  break;
6051  }
6052  case PolygonElement:
6053  case FillPolygonElement:
6054  {
6055  if (number_coordinates > 1)
6056  (void) XDrawLines(display,windows->image.id,
6057  windows->image.highlight_context,coordinate_info,
6058  number_coordinates,CoordModeOrigin);
6059  if (distance > 9)
6060  XHighlightLine(display,windows->image.id,
6061  windows->image.highlight_context,&line_info);
6062  break;
6063  }
6064  }
6065  switch (event.type)
6066  {
6067  case ButtonPress:
6068  break;
6069  case ButtonRelease:
6070  {
6071  /*
6072  User has committed to element.
6073  */
6074  line_info.x2=event.xbutton.x;
6075  line_info.y2=event.xbutton.y;
6076  rectangle_info.x=(ssize_t) event.xbutton.x;
6077  rectangle_info.y=(ssize_t) event.xbutton.y;
6078  coordinate_info[number_coordinates].x=event.xbutton.x;
6079  coordinate_info[number_coordinates].y=event.xbutton.y;
6080  if (((element != PolygonElement) &&
6081  (element != FillPolygonElement)) || (distance <= 9))
6082  {
6083  state|=ExitState;
6084  break;
6085  }
6086  number_coordinates++;
6087  if (number_coordinates < (int) max_coordinates)
6088  {
6089  line_info.x1=event.xbutton.x;
6090  line_info.y1=event.xbutton.y;
6091  break;
6092  }
6093  max_coordinates<<=1;
6094  coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6095  max_coordinates,sizeof(*coordinate_info));
6096  if (coordinate_info == (XPoint *) NULL)
6097  (void) ThrowMagickException(exception,GetMagickModule(),
6098  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6099  break;
6100  }
6101  case Expose:
6102  break;
6103  case MotionNotify:
6104  {
6105  if (event.xmotion.window != windows->image.id)
6106  break;
6107  if (element != PointElement)
6108  {
6109  line_info.x2=event.xmotion.x;
6110  line_info.y2=event.xmotion.y;
6111  rectangle_info.x=(ssize_t) event.xmotion.x;
6112  rectangle_info.y=(ssize_t) event.xmotion.y;
6113  break;
6114  }
6115  coordinate_info[number_coordinates].x=event.xbutton.x;
6116  coordinate_info[number_coordinates].y=event.xbutton.y;
6117  number_coordinates++;
6118  if (number_coordinates < (int) max_coordinates)
6119  break;
6120  max_coordinates<<=1;
6121  coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6122  max_coordinates,sizeof(*coordinate_info));
6123  if (coordinate_info == (XPoint *) NULL)
6124  (void) ThrowMagickException(exception,GetMagickModule(),
6125  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6126  break;
6127  }
6128  default:
6129  break;
6130  }
6131  /*
6132  Check boundary conditions.
6133  */
6134  if (line_info.x2 < 0)
6135  line_info.x2=0;
6136  else
6137  if (line_info.x2 > (int) windows->image.width)
6138  line_info.x2=(short) windows->image.width;
6139  if (line_info.y2 < 0)
6140  line_info.y2=0;
6141  else
6142  if (line_info.y2 > (int) windows->image.height)
6143  line_info.y2=(short) windows->image.height;
6144  distance=(unsigned int)
6145  (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6146  ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6147  if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6148  ((state & ExitState) != 0))
6149  {
6150  if (rectangle_info.x < 0)
6151  rectangle_info.x=0;
6152  else
6153  if (rectangle_info.x > (ssize_t) windows->image.width)
6154  rectangle_info.x=(ssize_t) windows->image.width;
6155  if ((int) rectangle_info.x < x)
6156  rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6157  else
6158  {
6159  rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6160  rectangle_info.x=(ssize_t) x;
6161  }
6162  if (rectangle_info.y < 0)
6163  rectangle_info.y=0;
6164  else
6165  if (rectangle_info.y > (ssize_t) windows->image.height)
6166  rectangle_info.y=(ssize_t) windows->image.height;
6167  if ((int) rectangle_info.y < y)
6168  rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6169  else
6170  {
6171  rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6172  rectangle_info.y=(ssize_t) y;
6173  }
6174  }
6175  } while ((state & ExitState) == 0);
6176  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6177  if ((element == PointElement) || (element == PolygonElement) ||
6178  (element == FillPolygonElement))
6179  {
6180  /*
6181  Determine polygon bounding box.
6182  */
6183  rectangle_info.x=(ssize_t) coordinate_info->x;
6184  rectangle_info.y=(ssize_t) coordinate_info->y;
6185  x=coordinate_info->x;
6186  y=coordinate_info->y;
6187  for (i=1; i < number_coordinates; i++)
6188  {
6189  if (coordinate_info[i].x > x)
6190  x=coordinate_info[i].x;
6191  if (coordinate_info[i].y > y)
6192  y=coordinate_info[i].y;
6193  if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6194  rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6195  if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6196  rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6197  }
6198  rectangle_info.width=(size_t) (x-rectangle_info.x);
6199  rectangle_info.height=(size_t) (y-rectangle_info.y);
6200  for (i=0; i < number_coordinates; i++)
6201  {
6202  coordinate_info[i].x-=rectangle_info.x;
6203  coordinate_info[i].y-=rectangle_info.y;
6204  }
6205  }
6206  else
6207  if (distance <= 9)
6208  continue;
6209  else
6210  if ((element == RectangleElement) ||
6211  (element == CircleElement) || (element == EllipseElement))
6212  {
6213  rectangle_info.width--;
6214  rectangle_info.height--;
6215  }
6216  /*
6217  Drawing is relative to image configuration.
6218  */
6219  draw_info.x=(int) rectangle_info.x;
6220  draw_info.y=(int) rectangle_info.y;
6221  (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6222  image,exception);
6223  width=(unsigned int) (*image)->columns;
6224  height=(unsigned int) (*image)->rows;
6225  x=0;
6226  y=0;
6227  if (windows->image.crop_geometry != (char *) NULL)
6228  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6229  draw_info.x+=windows->image.x-(line_width/2);
6230  if (draw_info.x < 0)
6231  draw_info.x=0;
6232  draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6233  draw_info.y+=windows->image.y-(line_width/2);
6234  if (draw_info.y < 0)
6235  draw_info.y=0;
6236  draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6237  draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6238  if (draw_info.width > (unsigned int) (*image)->columns)
6239  draw_info.width=(unsigned int) (*image)->columns;
6240  draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6241  if (draw_info.height > (unsigned int) (*image)->rows)
6242  draw_info.height=(unsigned int) (*image)->rows;
6243  (void) FormatLocaleString(draw_info.geometry,MagickPathExtent,"%ux%u%+d%+d",
6244  width*draw_info.width/windows->image.ximage->width,
6245  height*draw_info.height/windows->image.ximage->height,
6246  draw_info.x+x,draw_info.y+y);
6247  /*
6248  Initialize drawing attributes.
6249  */
6250  draw_info.degrees=0.0;
6251  draw_info.element=element;
6252  draw_info.stipple=stipple;
6253  draw_info.line_width=line_width;
6254  draw_info.line_info=line_info;
6255  if (line_info.x1 > (int) (line_width/2))
6256  draw_info.line_info.x1=(short) line_width/2;
6257  if (line_info.y1 > (int) (line_width/2))
6258  draw_info.line_info.y1=(short) line_width/2;
6259  draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6260  draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6261  if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6262  {
6263  draw_info.line_info.x2=(-draw_info.line_info.x2);
6264  draw_info.line_info.y2=(-draw_info.line_info.y2);
6265  }
6266  if (draw_info.line_info.x2 < 0)
6267  {
6268  draw_info.line_info.x2=(-draw_info.line_info.x2);
6269  Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6270  }
6271  if (draw_info.line_info.y2 < 0)
6272  {
6273  draw_info.line_info.y2=(-draw_info.line_info.y2);
6274  Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6275  }
6276  draw_info.rectangle_info=rectangle_info;
6277  if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6278  draw_info.rectangle_info.x=(ssize_t) line_width/2;
6279  if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6280  draw_info.rectangle_info.y=(ssize_t) line_width/2;
6281  draw_info.number_coordinates=(unsigned int) number_coordinates;
6282  draw_info.coordinate_info=coordinate_info;
6283  windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6284  /*
6285  Draw element on image.
6286  */
6287  XSetCursorState(display,windows,MagickTrue);
6288  XCheckRefreshWindows(display,windows);
6289  status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6290  XSetCursorState(display,windows,MagickFalse);
6291  /*
6292  Update image colormap and return to image drawing.
6293  */
6294  XConfigureImageColormap(display,resource_info,windows,*image,exception);
6295  (void) XConfigureImage(display,resource_info,windows,*image,exception);
6296  }
6297  XSetCursorState(display,windows,MagickFalse);
6298  coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6299  return(status != 0 ? MagickTrue : MagickFalse);
6300 }
6301 
6302 /*
6303 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6304 % %
6305 % %
6306 % %
6307 + X D r a w P a n R e c t a n g l e %
6308 % %
6309 % %
6310 % %
6311 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6312 %
6313 % XDrawPanRectangle() draws a rectangle in the pan window. The pan window
6314 % displays a zoom image and the rectangle shows which portion of the image is
6315 % displayed in the Image window.
6316 %
6317 % The format of the XDrawPanRectangle method is:
6318 %
6319 % XDrawPanRectangle(Display *display,XWindows *windows)
6320 %
6321 % A description of each parameter follows:
6322 %
6323 % o display: Specifies a connection to an X server; returned from
6324 % XOpenDisplay.
6325 %
6326 % o windows: Specifies a pointer to a XWindows structure.
6327 %
6328 */
6329 static void XDrawPanRectangle(Display *display,XWindows *windows)
6330 {
6331  double
6332  scale_factor;
6333 
6335  highlight_info;
6336 
6337  /*
6338  Determine dimensions of the panning rectangle.
6339  */
6340  scale_factor=(double) windows->pan.width/windows->image.ximage->width;
6341  highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6342  highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6343  scale_factor=(double)
6344  windows->pan.height/windows->image.ximage->height;
6345  highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6346  highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6347  /*
6348  Display the panning rectangle.
6349  */
6350  (void) XClearWindow(display,windows->pan.id);
6351  XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6352  &highlight_info);
6353 }
6354 
6355 /*
6356 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6357 % %
6358 % %
6359 % %
6360 + X I m a g e C a c h e %
6361 % %
6362 % %
6363 % %
6364 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6365 %
6366 % XImageCache() handles the creation, manipulation, and destruction of the
6367 % image cache (undo and redo buffers).
6368 %
6369 % The format of the XImageCache method is:
6370 %
6371 % void XImageCache(Display *display,XResourceInfo *resource_info,
6372 % XWindows *windows,const CommandType command,Image **image,
6373 % ExceptionInfo *exception)
6374 %
6375 % A description of each parameter follows:
6376 %
6377 % o display: Specifies a connection to an X server; returned from
6378 % XOpenDisplay.
6379 %
6380 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6381 %
6382 % o windows: Specifies a pointer to a XWindows structure.
6383 %
6384 % o command: Specifies a command to perform.
6385 %
6386 % o image: the image; XImageCache may transform the image and return a new
6387 % image pointer.
6388 %
6389 % o exception: return any errors or warnings in this structure.
6390 %
6391 */
6392 static void XImageCache(Display *display,XResourceInfo *resource_info,
6393  XWindows *windows,const CommandType command,Image **image,
6394  ExceptionInfo *exception)
6395 {
6396  Image
6397  *cache_image;
6398 
6399  static Image
6400  *redo_image = (Image *) NULL,
6401  *undo_image = (Image *) NULL;
6402 
6403  switch (command)
6404  {
6405  case FreeBuffersCommand:
6406  {
6407  /*
6408  Free memory from the undo and redo cache.
6409  */
6410  while (undo_image != (Image *) NULL)
6411  {
6412  cache_image=undo_image;
6413  undo_image=GetPreviousImageInList(undo_image);
6414  cache_image->list=DestroyImage(cache_image->list);
6415  cache_image=DestroyImage(cache_image);
6416  }
6417  undo_image=NewImageList();
6418  if (redo_image != (Image *) NULL)
6419  redo_image=DestroyImage(redo_image);
6420  redo_image=NewImageList();
6421  return;
6422  }
6423  case UndoCommand:
6424  {
6425  char
6426  image_geometry[MagickPathExtent];
6427 
6428  /*
6429  Undo the last image transformation.
6430  */
6431  if (undo_image == (Image *) NULL)
6432  {
6433  (void) XBell(display,0);
6434  return;
6435  }
6436  cache_image=undo_image;
6437  undo_image=GetPreviousImageInList(undo_image);
6438  windows->image.window_changes.width=(int) cache_image->columns;
6439  windows->image.window_changes.height=(int) cache_image->rows;
6440  (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
6441  windows->image.ximage->width,windows->image.ximage->height);
6442  (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6443  exception);
6444  if (windows->image.crop_geometry != (char *) NULL)
6445  windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6446  windows->image.crop_geometry);
6447  windows->image.crop_geometry=cache_image->geometry;
6448  if (redo_image != (Image *) NULL)
6449  redo_image=DestroyImage(redo_image);
6450  redo_image=(*image);
6451  *image=cache_image->list;
6452  cache_image=DestroyImage(cache_image);
6453  if (windows->image.orphan != MagickFalse )
6454  return;
6455  XConfigureImageColormap(display,resource_info,windows,*image,exception);
6456  (void) XConfigureImage(display,resource_info,windows,*image,exception);
6457  return;
6458  }
6459  case CutCommand:
6460  case PasteCommand:
6461  case ApplyCommand:
6462  case HalfSizeCommand:
6463  case OriginalSizeCommand:
6464  case DoubleSizeCommand:
6465  case ResizeCommand:
6466  case TrimCommand:
6467  case CropCommand:
6468  case ChopCommand:
6469  case FlipCommand:
6470  case FlopCommand:
6471  case RotateRightCommand:
6472  case RotateLeftCommand:
6473  case RotateCommand:
6474  case ShearCommand:
6475  case RollCommand:
6476  case NegateCommand:
6477  case ContrastStretchCommand:
6478  case SigmoidalContrastCommand:
6479  case NormalizeCommand:
6480  case EqualizeCommand:
6481  case HueCommand:
6482  case SaturationCommand:
6483  case BrightnessCommand:
6484  case GammaCommand:
6485  case SpiffCommand:
6486  case DullCommand:
6487  case GrayscaleCommand:
6488  case MapCommand:
6489  case QuantizeCommand:
6490  case DespeckleCommand:
6491  case EmbossCommand:
6492  case ReduceNoiseCommand:
6493  case AddNoiseCommand:
6494  case SharpenCommand:
6495  case BlurCommand:
6496  case ThresholdCommand:
6497  case EdgeDetectCommand:
6498  case SpreadCommand:
6499  case ShadeCommand:
6500  case RaiseCommand:
6501  case SegmentCommand:
6502  case SolarizeCommand:
6503  case SepiaToneCommand:
6504  case SwirlCommand:
6505  case ImplodeCommand:
6506  case VignetteCommand:
6507  case WaveCommand:
6508  case OilPaintCommand:
6509  case CharcoalDrawCommand:
6510  case AnnotateCommand:
6511  case AddBorderCommand:
6512  case AddFrameCommand:
6513  case CompositeCommand:
6514  case CommentCommand:
6515  case LaunchCommand:
6516  case RegionofInterestCommand:
6517  case SaveToUndoBufferCommand:
6518  case RedoCommand:
6519  {
6520  Image
6521  *previous_image;
6522 
6523  ssize_t
6524  bytes;
6525 
6526  bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6527  if (undo_image != (Image *) NULL)
6528  {
6529  /*
6530  Ensure the undo cache has enough memory available.
6531  */
6532  previous_image=undo_image;
6533  while (previous_image != (Image *) NULL)
6534  {
6535  bytes+=previous_image->list->columns*previous_image->list->rows*
6536  sizeof(PixelInfo);
6537  if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6538  {
6539  previous_image=GetPreviousImageInList(previous_image);
6540  continue;
6541  }
6542  bytes-=previous_image->list->columns*previous_image->list->rows*
6543  sizeof(PixelInfo);
6544  if (previous_image == undo_image)
6545  undo_image=NewImageList();
6546  else
6547  previous_image->next->previous=NewImageList();
6548  break;
6549  }
6550  while (previous_image != (Image *) NULL)
6551  {
6552  /*
6553  Delete any excess memory from undo cache.
6554  */
6555  cache_image=previous_image;
6556  previous_image=GetPreviousImageInList(previous_image);
6557  cache_image->list=DestroyImage(cache_image->list);
6558  cache_image=DestroyImage(cache_image);
6559  }
6560  }
6561  if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6562  break;
6563  /*
6564  Save image before transformations are applied.
6565  */
6566  cache_image=AcquireImage((ImageInfo *) NULL,exception);
6567  if (cache_image == (Image *) NULL)
6568  break;
6569  XSetCursorState(display,windows,MagickTrue);
6570  XCheckRefreshWindows(display,windows);
6571  cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6572  XSetCursorState(display,windows,MagickFalse);
6573  if (cache_image->list == (Image *) NULL)
6574  {
6575  cache_image=DestroyImage(cache_image);
6576  break;
6577  }
6578  cache_image->columns=(size_t) windows->image.ximage->width;
6579  cache_image->rows=(size_t) windows->image.ximage->height;
6580  cache_image->geometry=windows->image.crop_geometry;
6581  if (windows->image.crop_geometry != (char *) NULL)
6582  {
6583  cache_image->geometry=AcquireString((char *) NULL);
6584  (void) CopyMagickString(cache_image->geometry,
6585  windows->image.crop_geometry,MagickPathExtent);
6586  }
6587  if (undo_image == (Image *) NULL)
6588  {
6589  undo_image=cache_image;
6590  break;
6591  }
6592  undo_image->next=cache_image;
6593  undo_image->next->previous=undo_image;
6594  undo_image=undo_image->next;
6595  break;
6596  }
6597  default:
6598  break;
6599  }
6600  if (command == RedoCommand)
6601  {
6602  /*
6603  Redo the last image transformation.
6604  */
6605  if (redo_image == (Image *) NULL)
6606  {
6607  (void) XBell(display,0);
6608  return;
6609  }
6610  windows->image.window_changes.width=(int) redo_image->columns;
6611  windows->image.window_changes.height=(int) redo_image->rows;
6612  if (windows->image.crop_geometry != (char *) NULL)
6613  windows->image.crop_geometry=(char *)
6614  RelinquishMagickMemory(windows->image.crop_geometry);
6615  windows->image.crop_geometry=redo_image->geometry;
6616  *image=DestroyImage(*image);
6617  *image=redo_image;
6618  redo_image=NewImageList();
6619  if (windows->image.orphan != MagickFalse )
6620  return;
6621  XConfigureImageColormap(display,resource_info,windows,*image,exception);
6622  (void) XConfigureImage(display,resource_info,windows,*image,exception);
6623  return;
6624  }
6625  if (command != InfoCommand)
6626  return;
6627  /*
6628  Display image info.
6629  */
6630  XSetCursorState(display,windows,MagickTrue);
6631  XCheckRefreshWindows(display,windows);
6632  XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6633  XSetCursorState(display,windows,MagickFalse);
6634 }
6635 
6636 /*
6637 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6638 % %
6639 % %
6640 % %
6641 + X I m a g e W i n d o w C o m m a n d %
6642 % %
6643 % %
6644 % %
6645 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6646 %
6647 % XImageWindowCommand() makes a transform to the image or Image window as
6648 % specified by a user menu button or keyboard command.
6649 %
6650 % The format of the XImageWindowCommand method is:
6651 %
6652 % CommandType XImageWindowCommand(Display *display,
6653 % XResourceInfo *resource_info,XWindows *windows,
6654 % const MagickStatusType state,KeySym key_symbol,Image **image,
6655 % ExceptionInfo *exception)
6656 %
6657 % A description of each parameter follows:
6658 %
6659 % o nexus: Method XImageWindowCommand returns an image when the
6660 % user chooses 'Open Image' from the command menu. Otherwise a null
6661 % image is returned.
6662 %
6663 % o display: Specifies a connection to an X server; returned from
6664 % XOpenDisplay.
6665 %
6666 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6667 %
6668 % o windows: Specifies a pointer to a XWindows structure.
6669 %
6670 % o state: key mask.
6671 %
6672 % o key_symbol: Specifies a command to perform.
6673 %
6674 % o image: the image; XImageWIndowCommand may transform the image and
6675 % return a new image pointer.
6676 %
6677 % o exception: return any errors or warnings in this structure.
6678 %
6679 */
6680 static CommandType XImageWindowCommand(Display *display,
6681  XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6682  KeySym key_symbol,Image **image,ExceptionInfo *exception)
6683 {
6684  static char
6685  delta[MagickPathExtent] = "";
6686 
6687  static const char
6688  Digits[] = "01234567890";
6689 
6690  static KeySym
6691  last_symbol = XK_0;
6692 
6693  if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6694  {
6695  if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6696  {
6697  *delta='\0';
6698  resource_info->quantum=1;
6699  }
6700  last_symbol=key_symbol;
6701  delta[strlen(delta)+1]='\0';
6702  delta[strlen(delta)]=Digits[key_symbol-XK_0];
6703  resource_info->quantum=StringToLong(delta);
6704  return(NullCommand);
6705  }
6706  last_symbol=key_symbol;