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