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