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