blob: 7bc1af7f538b214a2775b98cf9a3fcb5e36420c7 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
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% John Cristy %
17% July 1992 %
18% %
19% %
cristy16af1cb2009-12-11 21:38:29 +000020% Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% 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% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
42#include "magick/studio.h"
43#include "magick/artifact.h"
44#include "magick/blob.h"
45#include "magick/cache.h"
46#include "magick/client.h"
47#include "magick/color.h"
48#include "magick/colorspace.h"
49#include "magick/composite.h"
50#include "magick/constitute.h"
51#include "magick/decorate.h"
52#include "magick/delegate.h"
53#include "magick/display.h"
54#include "magick/display-private.h"
55#include "magick/draw.h"
56#include "magick/effect.h"
57#include "magick/enhance.h"
58#include "magick/exception.h"
59#include "magick/exception-private.h"
60#include "magick/fx.h"
61#include "magick/geometry.h"
62#include "magick/image.h"
63#include "magick/image-private.h"
64#include "magick/list.h"
65#include "magick/log.h"
66#include "magick/magick.h"
67#include "magick/memory_.h"
68#include "magick/monitor.h"
69#include "magick/monitor-private.h"
70#include "magick/montage.h"
71#include "magick/option.h"
72#include "magick/paint.h"
73#include "magick/pixel.h"
74#include "magick/pixel-private.h"
75#include "magick/PreRvIcccm.h"
76#include "magick/property.h"
77#include "magick/quantum.h"
78#include "magick/resize.h"
79#include "magick/resource_.h"
80#include "magick/shear.h"
81#include "magick/segment.h"
82#include "magick/string_.h"
83#include "magick/transform.h"
84#include "magick/threshold.h"
85#include "magick/utility.h"
86#include "magick/version.h"
87#include "magick/widget.h"
88#include "magick/xwindow-private.h"
89
90#if defined(MAGICKCORE_X11_DELEGATE)
91/*
92 Define declarations.
93*/
94#define MaxColors MagickMin(windows->visual_info->colormap_size,256L)
95
96/*
97 Constant declarations.
98*/
99static const unsigned char
100 HighlightBitmap[8] =
101 {
102 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
103 },
104 ShadowBitmap[8] =
105 {
106 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
107 };
108
109static const char
110 *PageSizes[] =
111 {
112 "Letter",
113 "Tabloid",
114 "Ledger",
115 "Legal",
116 "Statement",
117 "Executive",
118 "A3",
119 "A4",
120 "A5",
121 "B4",
122 "B5",
123 "Folio",
124 "Quarto",
125 "10x14",
126 (char *) NULL
127 };
128
129/*
130 Help widget declarations.
131*/
132static const char
133 *ImageAnnotateHelp[] =
134 {
135 "In annotate mode, the Command widget has these options:",
136 "",
137 " Font Name",
138 " fixed",
139 " variable",
140 " 5x8",
141 " 6x10",
142 " 7x13bold",
143 " 8x13bold",
144 " 9x15bold",
145 " 10x20",
146 " 12x24",
147 " Browser...",
148 " Font Color",
149 " black",
150 " blue",
151 " cyan",
152 " green",
153 " gray",
154 " red",
155 " magenta",
156 " yellow",
157 " white",
158 " transparent",
159 " Browser...",
160 " Font Color",
161 " black",
162 " blue",
163 " cyan",
164 " green",
165 " gray",
166 " red",
167 " magenta",
168 " yellow",
169 " white",
170 " transparent",
171 " Browser...",
172 " Rotate Text",
173 " -90",
174 " -45",
175 " -30",
176 " 0",
177 " 30",
178 " 45",
179 " 90",
180 " 180",
181 " Dialog...",
182 " Help",
183 " Dismiss",
184 "",
185 "Choose a font name from the Font Name sub-menu. Additional",
186 "font names can be specified with the font browser. You can",
187 "change the menu names by setting the X resources font1",
188 "through font9.",
189 "",
190 "Choose a font color from the Font Color sub-menu.",
191 "Additional font colors can be specified with the color",
192 "browser. You can change the menu colors by setting the X",
193 "resources pen1 through pen9.",
194 "",
195 "If you select the color browser and press Grab, you can",
196 "choose the font color by moving the pointer to the desired",
197 "color on the screen and press any button.",
198 "",
199 "If you choose to rotate the text, choose Rotate Text from the",
200 "menu and select an angle. Typically you will only want to",
201 "rotate one line of text at a time. Depending on the angle you",
202 "choose, subsequent lines may end up overwriting each other.",
203 "",
204 "Choosing a font and its color is optional. The default font",
205 "is fixed and the default color is black. However, you must",
206 "choose a location to begin entering text and press button 1.",
207 "An underscore character will appear at the location of the",
208 "pointer. The cursor changes to a pencil to indicate you are",
209 "in text mode. To exit immediately, press Dismiss.",
210 "",
211 "In text mode, any key presses will display the character at",
212 "the location of the underscore and advance the underscore",
213 "cursor. Enter your text and once completed press Apply to",
214 "finish your image annotation. To correct errors press BACK",
215 "SPACE. To delete an entire line of text, press DELETE. Any",
216 "text that exceeds the boundaries of the image window is",
217 "automagically continued onto the next line.",
218 "",
219 "The actual color you request for the font is saved in the",
220 "image. However, the color that appears in your image window",
221 "may be different. For example, on a monochrome screen the",
222 "text will appear black or white even if you choose the color",
223 "red as the font color. However, the image saved to a file",
224 "with -write is written with red lettering. To assure the",
225 "correct color text in the final image, any PseudoClass image",
226 "is promoted to DirectClass (see miff(5)). To force a",
227 "PseudoClass image to remain PseudoClass, use -colors.",
228 (char *) NULL,
229 },
230 *ImageChopHelp[] =
231 {
232 "In chop mode, the Command widget has these options:",
233 "",
234 " Direction",
235 " horizontal",
236 " vertical",
237 " Help",
238 " Dismiss",
239 "",
240 "If the you choose the horizontal direction (this the",
241 "default), the area of the image between the two horizontal",
242 "endpoints of the chop line is removed. Otherwise, the area",
243 "of the image between the two vertical endpoints of the chop",
244 "line is removed.",
245 "",
246 "Select a location within the image window to begin your chop,",
247 "press and hold any button. Next, move the pointer to",
248 "another location in the image. As you move a line will",
249 "connect the initial location and the pointer. When you",
250 "release the button, the area within the image to chop is",
251 "determined by which direction you choose from the Command",
252 "widget.",
253 "",
254 "To cancel the image chopping, move the pointer back to the",
255 "starting point of the line and release the button.",
256 (char *) NULL,
257 },
258 *ImageColorEditHelp[] =
259 {
260 "In color edit mode, the Command widget has these options:",
261 "",
262 " Method",
263 " point",
264 " replace",
265 " floodfill",
266 " filltoborder",
267 " reset",
268 " Pixel Color",
269 " black",
270 " blue",
271 " cyan",
272 " green",
273 " gray",
274 " red",
275 " magenta",
276 " yellow",
277 " white",
278 " Browser...",
279 " Border Color",
280 " black",
281 " blue",
282 " cyan",
283 " green",
284 " gray",
285 " red",
286 " magenta",
287 " yellow",
288 " white",
289 " Browser...",
290 " Fuzz",
291 " 0%",
292 " 2%",
293 " 5%",
294 " 10%",
295 " 15%",
296 " Dialog...",
297 " Undo",
298 " Help",
299 " Dismiss",
300 "",
301 "Choose a color editing method from the Method sub-menu",
302 "of the Command widget. The point method recolors any pixel",
303 "selected with the pointer until the button is released. The",
304 "replace method recolors any pixel that matches the color of",
305 "the pixel you select with a button press. Floodfill recolors",
306 "any pixel that matches the color of the pixel you select with",
307 "a button press and is a neighbor. Whereas filltoborder recolors",
308 "any neighbor pixel that is not the border color. Finally reset",
309 "changes the entire image to the designated color.",
310 "",
311 "Next, choose a pixel color from the Pixel Color sub-menu.",
312 "Additional pixel colors can be specified with the color",
313 "browser. You can change the menu colors by setting the X",
314 "resources pen1 through pen9.",
315 "",
316 "Now press button 1 to select a pixel within the image window",
317 "to change its color. Additional pixels may be recolored as",
318 "prescribed by the method you choose.",
319 "",
320 "If the Magnify widget is mapped, it can be helpful in positioning",
321 "your pointer within the image (refer to button 2).",
322 "",
323 "The actual color you request for the pixels is saved in the",
324 "image. However, the color that appears in your image window",
325 "may be different. For example, on a monochrome screen the",
326 "pixel will appear black or white even if you choose the",
327 "color red as the pixel color. However, the image saved to a",
328 "file with -write is written with red pixels. To assure the",
329 "correct color text in the final image, any PseudoClass image",
330 "is promoted to DirectClass (see miff(5)). To force a",
331 "PseudoClass image to remain PseudoClass, use -colors.",
332 (char *) NULL,
333 },
334 *ImageCompositeHelp[] =
335 {
336 "First a widget window is displayed requesting you to enter an",
337 "image name. Press Composite, Grab or type a file name.",
338 "Press Cancel if you choose not to create a composite image.",
339 "When you choose Grab, move the pointer to the desired window",
340 "and press any button.",
341 "",
342 "If the Composite image does not have any matte information,",
343 "you are informed and the file browser is displayed again.",
344 "Enter the name of a mask image. The image is typically",
345 "grayscale and the same size as the composite image. If the",
346 "image is not grayscale, it is converted to grayscale and the",
347 "resulting intensities are used as matte information.",
348 "",
349 "A small window appears showing the location of the cursor in",
350 "the image window. You are now in composite mode. To exit",
351 "immediately, press Dismiss. In composite mode, the Command",
352 "widget has these options:",
353 "",
354 " Operators",
355 " Over",
356 " In",
357 " Out",
358 " Atop",
359 " Xor",
360 " Plus",
361 " Minus",
362 " Add",
363 " Subtract",
364 " Difference",
365 " Multiply",
366 " Bumpmap",
367 " Copy",
368 " CopyRed",
369 " CopyGreen",
370 " CopyBlue",
371 " CopyOpacity",
372 " Clear",
373 " Dissolve",
374 " Displace",
375 " Help",
376 " Dismiss",
377 "",
378 "Choose a composite operation from the Operators sub-menu of",
379 "the Command widget. How each operator behaves is described",
380 "below. Image window is the image currently displayed on",
381 "your X server and image is the image obtained with the File",
382 "Browser widget.",
383 "",
384 "Over The result is the union of the two image shapes,",
385 " with image obscuring image window in the region of",
386 " overlap.",
387 "",
388 "In The result is simply image cut by the shape of",
389 " image window. None of the image data of image",
390 " window is in the result.",
391 "",
392 "Out The resulting image is image with the shape of",
393 " image window cut out.",
394 "",
395 "Atop The result is the same shape as image image window,",
396 " with image obscuring image window where the image",
397 " shapes overlap. Note this differs from over",
398 " because the portion of image outside image window's",
399 " shape does not appear in the result.",
400 "",
401 "Xor The result is the image data from both image and",
402 " image window that is outside the overlap region.",
403 " The overlap region is blank.",
404 "",
405 "Plus The result is just the sum of the image data.",
406 " Output values are cropped to QuantumRange (no overflow).",
407 "",
408 "Minus The result of image - image window, with underflow",
409 " cropped to zero.",
410 "",
411 "Add The result of image + image window, with overflow",
412 " wrapping around (mod 256).",
413 "",
414 "Subtract The result of image - image window, with underflow",
415 " wrapping around (mod 256). The add and subtract",
416 " operators can be used to perform reversible",
417 " transformations.",
418 "",
419 "Difference",
420 " The result of abs(image - image window). This",
421 " useful for comparing two very similar images.",
422 "",
423 "Multiply",
424 " The result of image * image window. This",
425 " useful for the creation of drop-shadows.",
426 "",
427 "Bumpmap The result of surface normals from image * image",
428 " window.",
429 "",
430 "Copy The resulting image is image window replaced with",
431 " image. Here the matte information is ignored.",
432 "",
433 "CopyRed The red layer of the image window is replace with",
434 " the red layer of the image. The other layers are",
435 " untouched.",
436 "",
437 "CopyGreen",
438 " The green layer of the image window is replace with",
439 " the green layer of the image. The other layers are",
440 " untouched.",
441 "",
442 "CopyBlue The blue layer of the image window is replace with",
443 " the blue layer of the image. The other layers are",
444 " untouched.",
445 "",
446 "CopyOpacity",
447 " The matte layer of the image window is replace with",
448 " the matte layer of the image. The other layers are",
449 " untouched.",
450 "",
451 "The image compositor requires a matte, or alpha channel in",
452 "the image for some operations. This extra channel usually",
453 "defines a mask which represents a sort of a cookie-cutter",
454 "for the image. This the case when matte is opaque (full",
455 "coverage) for pixels inside the shape, zero outside, and",
456 "between 0 and QuantumRange on the boundary. If image does not",
457 "have a matte channel, it is initialized with 0 for any pixel",
458 "matching in color to pixel location (0,0), otherwise QuantumRange.",
459 "",
460 "If you choose Dissolve, the composite operator becomes Over. The",
461 "image matte channel percent transparency is initialized to factor.",
462 "The image window is initialized to (100-factor). Where factor is the",
463 "value you specify in the Dialog widget.",
464 "",
465 "Displace shifts the image pixels as defined by a displacement",
466 "map. With this option, image is used as a displacement map.",
467 "Black, within the displacement map, is a maximum positive",
468 "displacement. White is a maximum negative displacement and",
469 "middle gray is neutral. The displacement is scaled to determine",
470 "the pixel shift. By default, the displacement applies in both the",
471 "horizontal and vertical directions. However, if you specify a mask,",
472 "image is the horizontal X displacement and mask the vertical Y",
473 "displacement.",
474 "",
475 "Note that matte information for image window is not retained",
476 "for colormapped X server visuals (e.g. StaticColor,",
477 "StaticColor, GrayScale, PseudoColor). Correct compositing",
478 "behavior may require a TrueColor or DirectColor visual or a",
479 "Standard Colormap.",
480 "",
481 "Choosing a composite operator is optional. The default",
482 "operator is replace. However, you must choose a location to",
483 "composite your image and press button 1. Press and hold the",
484 "button before releasing and an outline of the image will",
485 "appear to help you identify your location.",
486 "",
487 "The actual colors of the composite image is saved. However,",
488 "the color that appears in image window may be different.",
489 "For example, on a monochrome screen image window will appear",
490 "black or white even though your composited image may have",
491 "many colors. If the image is saved to a file it is written",
492 "with the correct colors. To assure the correct colors are",
493 "saved in the final image, any PseudoClass image is promoted",
494 "to DirectClass (see miff(5)). To force a PseudoClass image",
495 "to remain PseudoClass, use -colors.",
496 (char *) NULL,
497 },
498 *ImageCutHelp[] =
499 {
500 "In cut mode, the Command widget has these options:",
501 "",
502 " Help",
503 " Dismiss",
504 "",
505 "To define a cut region, press button 1 and drag. The",
506 "cut region is defined by a highlighted rectangle that",
507 "expands or contracts as it follows the pointer. Once you",
508 "are satisfied with the cut region, release the button.",
509 "You are now in rectify mode. In rectify mode, the Command",
510 "widget has these options:",
511 "",
512 " Cut",
513 " Help",
514 " Dismiss",
515 "",
516 "You can make adjustments by moving the pointer to one of the",
517 "cut rectangle corners, pressing a button, and dragging.",
518 "Finally, press Cut to commit your copy region. To",
519 "exit without cutting the image, press Dismiss.",
520 (char *) NULL,
521 },
522 *ImageCopyHelp[] =
523 {
524 "In copy mode, the Command widget has these options:",
525 "",
526 " Help",
527 " Dismiss",
528 "",
529 "To define a copy region, press button 1 and drag. The",
530 "copy region is defined by a highlighted rectangle that",
531 "expands or contracts as it follows the pointer. Once you",
532 "are satisfied with the copy region, release the button.",
533 "You are now in rectify mode. In rectify mode, the Command",
534 "widget has these options:",
535 "",
536 " Copy",
537 " Help",
538 " Dismiss",
539 "",
540 "You can make adjustments by moving the pointer to one of the",
541 "copy rectangle corners, pressing a button, and dragging.",
542 "Finally, press Copy to commit your copy region. To",
543 "exit without copying the image, press Dismiss.",
544 (char *) NULL,
545 },
546 *ImageCropHelp[] =
547 {
548 "In crop mode, the Command widget has these options:",
549 "",
550 " Help",
551 " Dismiss",
552 "",
553 "To define a cropping region, press button 1 and drag. The",
554 "cropping region is defined by a highlighted rectangle that",
555 "expands or contracts as it follows the pointer. Once you",
556 "are satisfied with the cropping region, release the button.",
557 "You are now in rectify mode. In rectify mode, the Command",
558 "widget has these options:",
559 "",
560 " Crop",
561 " Help",
562 " Dismiss",
563 "",
564 "You can make adjustments by moving the pointer to one of the",
565 "cropping rectangle corners, pressing a button, and dragging.",
566 "Finally, press Crop to commit your cropping region. To",
567 "exit without cropping the image, press Dismiss.",
568 (char *) NULL,
569 },
570 *ImageDrawHelp[] =
571 {
572 "The cursor changes to a crosshair to indicate you are in",
573 "draw mode. To exit immediately, press Dismiss. In draw mode,",
574 "the Command widget has these options:",
575 "",
576 " Element",
577 " point",
578 " line",
579 " rectangle",
580 " fill rectangle",
581 " circle",
582 " fill circle",
583 " ellipse",
584 " fill ellipse",
585 " polygon",
586 " fill polygon",
587 " Color",
588 " black",
589 " blue",
590 " cyan",
591 " green",
592 " gray",
593 " red",
594 " magenta",
595 " yellow",
596 " white",
597 " transparent",
598 " Browser...",
599 " Stipple",
600 " Brick",
601 " Diagonal",
602 " Scales",
603 " Vertical",
604 " Wavy",
605 " Translucent",
606 " Opaque",
607 " Open...",
608 " Width",
609 " 1",
610 " 2",
611 " 4",
612 " 8",
613 " 16",
614 " Dialog...",
615 " Undo",
616 " Help",
617 " Dismiss",
618 "",
619 "Choose a drawing primitive from the Element sub-menu.",
620 "",
621 "Choose a color from the Color sub-menu. Additional",
622 "colors can be specified with the color browser.",
623 "",
624 "If you choose the color browser and press Grab, you can",
625 "select the color by moving the pointer to the desired",
626 "color on the screen and press any button. The transparent",
627 "color updates the image matte channel and is useful for",
628 "image compositing.",
629 "",
630 "Choose a stipple, if appropriate, from the Stipple sub-menu.",
631 "Additional stipples can be specified with the file browser.",
632 "Stipples obtained from the file browser must be on disk in the",
633 "X11 bitmap format.",
634 "",
635 "Choose a width, if appropriate, from the Width sub-menu. To",
636 "choose a specific width select the Dialog widget.",
637 "",
638 "Choose a point in the Image window and press button 1 and",
639 "hold. Next, move the pointer to another location in the",
640 "image. As you move, a line connects the initial location and",
641 "the pointer. When you release the button, the image is",
642 "updated with the primitive you just drew. For polygons, the",
643 "image is updated when you press and release the button without",
644 "moving the pointer.",
645 "",
646 "To cancel image drawing, move the pointer back to the",
647 "starting point of the line and release the button.",
648 (char *) NULL,
649 },
650 *DisplayHelp[] =
651 {
652 "BUTTONS",
653 " The effects of each button press is described below. Three",
654 " buttons are required. If you have a two button mouse,",
655 " button 1 and 3 are returned. Press ALT and button 3 to",
656 " simulate button 2.",
657 "",
658 " 1 Press this button to map or unmap the Command widget.",
659 "",
660 " 2 Press and drag to define a region of the image to",
661 " magnify.",
662 "",
663 " 3 Press and drag to choose from a select set of commands.",
664 " This button behaves differently if the image being",
665 " displayed is a visual image directory. Here, choose a",
666 " particular tile of the directory and press this button and",
667 " drag to select a command from a pop-up menu. Choose from",
668 " these menu items:",
669 "",
670 " Open",
671 " Next",
672 " Former",
673 " Delete",
674 " Update",
675 "",
676 " If you choose Open, the image represented by the tile is",
677 " displayed. To return to the visual image directory, choose",
678 " Next from the Command widget. Next and Former moves to the",
679 " next or former image respectively. Choose Delete to delete",
680 " a particular image tile. Finally, choose Update to",
681 " synchronize all the image tiles with their respective",
682 " images.",
683 "",
684 "COMMAND WIDGET",
685 " The Command widget lists a number of sub-menus and commands.",
686 " They are",
687 "",
688 " File",
689 " Open...",
690 " Next",
691 " Former",
692 " Select...",
693 " Save...",
694 " Print...",
695 " Delete...",
696 " New...",
697 " Visual Directory...",
698 " Quit",
699 " Edit",
700 " Undo",
701 " Redo",
702 " Cut",
703 " Copy",
704 " Paste",
705 " View",
706 " Half Size",
707 " Original Size",
708 " Double Size",
709 " Resize...",
710 " Apply",
711 " Refresh",
712 " Restore",
713 " Transform",
714 " Crop",
715 " Chop",
716 " Flop",
717 " Flip",
718 " Rotate Right",
719 " Rotate Left",
720 " Rotate...",
721 " Shear...",
722 " Roll...",
723 " Trim Edges",
724 " Enhance",
725 " Brightness...",
726 " Saturation...",
727 " Hue...",
728 " Gamma...",
729 " Sharpen...",
730 " Dull",
731 " Contrast Stretch...",
732 " Sigmoidal Contrast...",
733 " Normalize",
734 " Equalize",
735 " Negate",
736 " Grayscale",
737 " Map...",
738 " Quantize...",
739 " Effects",
740 " Despeckle",
741 " Emboss",
742 " Reduce Noise",
743 " Add Noise",
744 " Sharpen...",
745 " Blur...",
746 " Threshold...",
747 " Edge Detect...",
748 " Spread...",
749 " Shade...",
750 " Painting...",
751 " Segment...",
752 " F/X",
753 " Solarize...",
754 " Sepia Tone...",
755 " Swirl...",
756 " Implode...",
757 " Vignette...",
758 " Wave...",
759 " Oil Painting...",
760 " Charcoal Drawing...",
761 " Image Edit",
762 " Annotate...",
763 " Draw...",
764 " Color...",
765 " Matte...",
766 " Composite...",
767 " Add Border...",
768 " Add Frame...",
769 " Comment...",
770 " Launch...",
771 " Region of Interest...",
772 " Miscellany",
773 " Image Info",
774 " Zoom Image",
775 " Show Preview...",
776 " Show Histogram",
777 " Show Matte",
778 " Background...",
779 " Slide Show",
780 " Preferences...",
781 " Help",
782 " Overview",
783 " Browse Documentation",
784 " About Display",
785 "",
786 " Menu items with a indented triangle have a sub-menu. They",
787 " are represented above as the indented items. To access a",
788 " sub-menu item, move the pointer to the appropriate menu and",
789 " press a button and drag. When you find the desired sub-menu",
790 " item, release the button and the command is executed. Move",
791 " the pointer away from the sub-menu if you decide not to",
792 " execute a particular command.",
793 "",
794 "KEYBOARD ACCELERATORS",
795 " Accelerators are one or two key presses that effect a",
796 " particular command. The keyboard accelerators that",
797 " display(1) understands is:",
798 "",
799 " Ctl+O Press to open an image from a file.",
800 "",
801 " space Press to display the next image.",
802 "",
803 " If the image is a multi-paged document such as a Postscript",
804 " document, you can skip ahead several pages by preceding",
805 " this command with a number. For example to display the",
806 " third page beyond the current page, press 3<space>.",
807 "",
808 " backspace Press to display the former image.",
809 "",
810 " If the image is a multi-paged document such as a Postscript",
811 " document, you can skip behind several pages by preceding",
812 " this command with a number. For example to display the",
813 " third page preceding the current page, press 3<backspace>.",
814 "",
815 " Ctl+S Press to write the image to a file.",
816 "",
817 " Ctl+P Press to print the image to a Postscript printer.",
818 "",
819 " Ctl+D Press to delete an image file.",
820 "",
821 " Ctl+N Press to create a blank canvas.",
822 "",
823 " Ctl+Q Press to discard all images and exit program.",
824 "",
825 " Ctl+Z Press to undo last image transformation.",
826 "",
827 " Ctl+R Press to redo last image transformation.",
828 "",
829 " Ctl+X Press to cut a region of the image.",
830 "",
831 " Ctl+C Press to copy a region of the image.",
832 "",
833 " Ctl+V Press to paste a region to the image.",
834 "",
835 " < Press to half the image size.",
836 "",
837 " - Press to return to the original image size.",
838 "",
839 " > Press to double the image size.",
840 "",
841 " % Press to resize the image to a width and height you",
842 " specify.",
843 "",
844 "Cmd-A Press to make any image transformations permanent."
845 "",
846 " By default, any image size transformations are applied",
847 " to the original image to create the image displayed on",
848 " the X server. However, the transformations are not",
849 " permanent (i.e. the original image does not change",
850 " size only the X image does). For example, if you",
851 " press > the X image will appear to double in size,",
852 " but the original image will in fact remain the same size.",
853 " To force the original image to double in size, press >",
854 " followed by Cmd-A.",
855 "",
856 " @ Press to refresh the image window.",
857 "",
858 " C Press to cut out a rectangular region of the image.",
859 "",
860 " [ Press to chop the image.",
861 "",
862 " H Press to flop image in the horizontal direction.",
863 "",
864 " V Press to flip image in the vertical direction.",
865 "",
866 " / Press to rotate the image 90 degrees clockwise.",
867 "",
868 " \\ Press to rotate the image 90 degrees counter-clockwise.",
869 "",
870 " * Press to rotate the image the number of degrees you",
871 " specify.",
872 "",
873 " S Press to shear the image the number of degrees you",
874 " specify.",
875 "",
876 " R Press to roll the image.",
877 "",
878 " T Press to trim the image edges.",
879 "",
880 " Shft-H Press to vary the image hue.",
881 "",
882 " Shft-S Press to vary the color saturation.",
883 "",
884 " Shft-L Press to vary the color brightness.",
885 "",
886 " Shft-G Press to gamma correct the image.",
887 "",
888 " Shft-C Press to sharpen the image contrast.",
889 "",
890 " Shft-Z Press to dull the image contrast.",
891 "",
892 " = Press to perform histogram equalization on the image.",
893 "",
894 " Shft-N Press to perform histogram normalization on the image.",
895 "",
896 " Shft-~ Press to negate the colors of the image.",
897 "",
898 " . Press to convert the image colors to gray.",
899 "",
900 " Shft-# Press to set the maximum number of unique colors in the",
901 " image.",
902 "",
903 " F2 Press to reduce the speckles in an image.",
904 "",
905 " F3 Press to eliminate peak noise from an image.",
906 "",
907 " F4 Press to add noise to an image.",
908 "",
909 " F5 Press to sharpen an image.",
910 "",
911 " F6 Press to delete an image file.",
912 "",
913 " F7 Press to threshold the image.",
914 "",
915 " F8 Press to detect edges within an image.",
916 "",
917 " F9 Press to emboss an image.",
918 "",
919 " F10 Press to displace pixels by a random amount.",
920 "",
921 " F11 Press to negate all pixels above the threshold level.",
922 "",
923 " F12 Press to shade the image using a distant light source.",
924 "",
925 " F13 Press to lighten or darken image edges to create a 3-D effect.",
926 "",
927 " F14 Press to segment the image by color.",
928 "",
929 " Meta-S Press to swirl image pixels about the center.",
930 "",
931 " Meta-I Press to implode image pixels about the center.",
932 "",
933 " Meta-W Press to alter an image along a sine wave.",
934 "",
935 " Meta-P Press to simulate an oil painting.",
936 "",
937 " Meta-C Press to simulate a charcoal drawing.",
938 "",
939 " Alt-A Press to annotate the image with text.",
940 "",
941 " Alt-D Press to draw on an image.",
942 "",
943 " Alt-P Press to edit an image pixel color.",
944 "",
945 " Alt-M Press to edit the image matte information.",
946 "",
947 " Alt-V Press to composite the image with another.",
948 "",
949 " Alt-B Press to add a border to the image.",
950 "",
951 " Alt-F Press to add an ornamental border to the image.",
952 "",
953 " Alt-Shft-!",
954 " Press to add an image comment.",
955 "",
956 " Ctl-A Press to apply image processing techniques to a region",
957 " of interest.",
958 "",
959 " Shft-? Press to display information about the image.",
960 "",
961 " Shft-+ Press to map the zoom image window.",
962 "",
963 " Shft-P Press to preview an image enhancement, effect, or f/x.",
964 "",
965 " F1 Press to display helpful information about display(1).",
966 "",
967 " Find Press to browse documentation about ImageMagick.",
968 "",
969 " 1-9 Press to change the level of magnification.",
970 "",
971 " Use the arrow keys to move the image one pixel up, down,",
972 " left, or right within the magnify window. Be sure to first",
973 " map the magnify window by pressing button 2.",
974 "",
975 " Press ALT and one of the arrow keys to trim off one pixel",
976 " from any side of the image.",
977 (char *) NULL,
978 },
979 *ImageMatteEditHelp[] =
980 {
981 "Matte information within an image is useful for some",
982 "operations such as image compositing (See IMAGE",
983 "COMPOSITING). This extra channel usually defines a mask",
984 "which represents a sort of a cookie-cutter for the image.",
985 "This the case when matte is opaque (full coverage) for",
986 "pixels inside the shape, zero outside, and between 0 and",
987 "QuantumRange on the boundary.",
988 "",
989 "A small window appears showing the location of the cursor in",
990 "the image window. You are now in matte edit mode. To exit",
991 "immediately, press Dismiss. In matte edit mode, the Command",
992 "widget has these options:",
993 "",
994 " Method",
995 " point",
996 " replace",
997 " floodfill",
998 " filltoborder",
999 " reset",
1000 " Border Color",
1001 " black",
1002 " blue",
1003 " cyan",
1004 " green",
1005 " gray",
1006 " red",
1007 " magenta",
1008 " yellow",
1009 " white",
1010 " Browser...",
1011 " Fuzz",
1012 " 0%",
1013 " 2%",
1014 " 5%",
1015 " 10%",
1016 " 15%",
1017 " Dialog...",
1018 " Matte",
1019 " Opaque",
1020 " Transparent",
1021 " Dialog...",
1022 " Undo",
1023 " Help",
1024 " Dismiss",
1025 "",
1026 "Choose a matte editing method from the Method sub-menu of",
1027 "the Command widget. The point method changes the matte value",
1028 "of any pixel selected with the pointer until the button is",
1029 "is released. The replace method changes the matte value of",
1030 "any pixel that matches the color of the pixel you select with",
1031 "a button press. Floodfill changes the matte value of any pixel",
1032 "that matches the color of the pixel you select with a button",
1033 "press and is a neighbor. Whereas filltoborder changes the matte",
1034 "value any neighbor pixel that is not the border color. Finally",
1035 "reset changes the entire image to the designated matte value.",
1036 "",
1037 "Choose Matte Value and pick Opaque or Transarent. For other values",
1038 "select the Dialog entry. Here a dialog appears requesting a matte",
1039 "value. The value you select is assigned as the opacity value of the",
1040 "selected pixel or pixels.",
1041 "",
1042 "Now, press any button to select a pixel within the image",
1043 "window to change its matte value.",
1044 "",
1045 "If the Magnify widget is mapped, it can be helpful in positioning",
1046 "your pointer within the image (refer to button 2).",
1047 "",
1048 "Matte information is only valid in a DirectClass image.",
1049 "Therefore, any PseudoClass image is promoted to DirectClass",
1050 "(see miff(5)). Note that matte information for PseudoClass",
1051 "is not retained for colormapped X server visuals (e.g.",
1052 "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1053 "immediately save your image to a file (refer to Write).",
1054 "Correct matte editing behavior may require a TrueColor or",
1055 "DirectColor visual or a Standard Colormap.",
1056 (char *) NULL,
1057 },
1058 *ImagePanHelp[] =
1059 {
1060 "When an image exceeds the width or height of the X server",
1061 "screen, display maps a small panning icon. The rectangle",
1062 "within the panning icon shows the area that is currently",
1063 "displayed in the image window. To pan about the image,",
1064 "press any button and drag the pointer within the panning",
1065 "icon. The pan rectangle moves with the pointer and the",
1066 "image window is updated to reflect the location of the",
1067 "rectangle within the panning icon. When you have selected",
1068 "the area of the image you wish to view, release the button.",
1069 "",
1070 "Use the arrow keys to pan the image one pixel up, down,",
1071 "left, or right within the image window.",
1072 "",
1073 "The panning icon is withdrawn if the image becomes smaller",
1074 "than the dimensions of the X server screen.",
1075 (char *) NULL,
1076 },
1077 *ImagePasteHelp[] =
1078 {
1079 "A small window appears showing the location of the cursor in",
1080 "the image window. You are now in paste mode. To exit",
1081 "immediately, press Dismiss. In paste mode, the Command",
1082 "widget has these options:",
1083 "",
1084 " Operators",
1085 " over",
1086 " in",
1087 " out",
1088 " atop",
1089 " xor",
1090 " plus",
1091 " minus",
1092 " add",
1093 " subtract",
1094 " difference",
1095 " replace",
1096 " Help",
1097 " Dismiss",
1098 "",
1099 "Choose a composite operation from the Operators sub-menu of",
1100 "the Command widget. How each operator behaves is described",
1101 "below. Image window is the image currently displayed on",
1102 "your X server and image is the image obtained with the File",
1103 "Browser widget.",
1104 "",
1105 "Over The result is the union of the two image shapes,",
1106 " with image obscuring image window in the region of",
1107 " overlap.",
1108 "",
1109 "In The result is simply image cut by the shape of",
1110 " image window. None of the image data of image",
1111 " window is in the result.",
1112 "",
1113 "Out The resulting image is image with the shape of",
1114 " image window cut out.",
1115 "",
1116 "Atop The result is the same shape as image image window,",
1117 " with image obscuring image window where the image",
1118 " shapes overlap. Note this differs from over",
1119 " because the portion of image outside image window's",
1120 " shape does not appear in the result.",
1121 "",
1122 "Xor The result is the image data from both image and",
1123 " image window that is outside the overlap region.",
1124 " The overlap region is blank.",
1125 "",
1126 "Plus The result is just the sum of the image data.",
1127 " Output values are cropped to QuantumRange (no overflow).",
1128 " This operation is independent of the matte",
1129 " channels.",
1130 "",
1131 "Minus The result of image - image window, with underflow",
1132 " cropped to zero.",
1133 "",
1134 "Add The result of image + image window, with overflow",
1135 " wrapping around (mod 256).",
1136 "",
1137 "Subtract The result of image - image window, with underflow",
1138 " wrapping around (mod 256). The add and subtract",
1139 " operators can be used to perform reversible",
1140 " transformations.",
1141 "",
1142 "Difference",
1143 " The result of abs(image - image window). This",
1144 " useful for comparing two very similar images.",
1145 "",
1146 "Copy The resulting image is image window replaced with",
1147 " image. Here the matte information is ignored.",
1148 "",
1149 "CopyRed The red layer of the image window is replace with",
1150 " the red layer of the image. The other layers are",
1151 " untouched.",
1152 "",
1153 "CopyGreen",
1154 " The green layer of the image window is replace with",
1155 " the green layer of the image. The other layers are",
1156 " untouched.",
1157 "",
1158 "CopyBlue The blue layer of the image window is replace with",
1159 " the blue layer of the image. The other layers are",
1160 " untouched.",
1161 "",
1162 "CopyOpacity",
1163 " The matte layer of the image window is replace with",
1164 " the matte layer of the image. The other layers are",
1165 " untouched.",
1166 "",
1167 "The image compositor requires a matte, or alpha channel in",
1168 "the image for some operations. This extra channel usually",
1169 "defines a mask which represents a sort of a cookie-cutter",
1170 "for the image. This the case when matte is opaque (full",
1171 "coverage) for pixels inside the shape, zero outside, and",
1172 "between 0 and QuantumRange on the boundary. If image does not",
1173 "have a matte channel, it is initialized with 0 for any pixel",
1174 "matching in color to pixel location (0,0), otherwise QuantumRange.",
1175 "",
1176 "Note that matte information for image window is not retained",
1177 "for colormapped X server visuals (e.g. StaticColor,",
1178 "StaticColor, GrayScale, PseudoColor). Correct compositing",
1179 "behavior may require a TrueColor or DirectColor visual or a",
1180 "Standard Colormap.",
1181 "",
1182 "Choosing a composite operator is optional. The default",
1183 "operator is replace. However, you must choose a location to",
1184 "paste your image and press button 1. Press and hold the",
1185 "button before releasing and an outline of the image will",
1186 "appear to help you identify your location.",
1187 "",
1188 "The actual colors of the pasted image is saved. However,",
1189 "the color that appears in image window may be different.",
1190 "For example, on a monochrome screen image window will appear",
1191 "black or white even though your pasted image may have",
1192 "many colors. If the image is saved to a file it is written",
1193 "with the correct colors. To assure the correct colors are",
1194 "saved in the final image, any PseudoClass image is promoted",
1195 "to DirectClass (see miff(5)). To force a PseudoClass image",
1196 "to remain PseudoClass, use -colors.",
1197 (char *) NULL,
1198 },
1199 *ImageROIHelp[] =
1200 {
1201 "In region of interest mode, the Command widget has these",
1202 "options:",
1203 "",
1204 " Help",
1205 " Dismiss",
1206 "",
1207 "To define a region of interest, press button 1 and drag.",
1208 "The region of interest is defined by a highlighted rectangle",
1209 "that expands or contracts as it follows the pointer. Once",
1210 "you are satisfied with the region of interest, release the",
1211 "button. You are now in apply mode. In apply mode the",
1212 "Command widget has these options:",
1213 "",
1214 " File",
1215 " Save...",
1216 " Print...",
1217 " Edit",
1218 " Undo",
1219 " Redo",
1220 " Transform",
1221 " Flop",
1222 " Flip",
1223 " Rotate Right",
1224 " Rotate Left",
1225 " Enhance",
1226 " Hue...",
1227 " Saturation...",
1228 " Brightness...",
1229 " Gamma...",
1230 " Spiff",
1231 " Dull",
1232 " Contrast Stretch",
1233 " Sigmoidal Contrast...",
1234 " Normalize",
1235 " Equalize",
1236 " Negate",
1237 " Grayscale",
1238 " Map...",
1239 " Quantize...",
1240 " Effects",
1241 " Despeckle",
1242 " Emboss",
1243 " Reduce Noise",
1244 " Sharpen...",
1245 " Blur...",
1246 " Threshold...",
1247 " Edge Detect...",
1248 " Spread...",
1249 " Shade...",
1250 " Raise...",
1251 " Segment...",
1252 " F/X",
1253 " Solarize...",
1254 " Sepia Tone...",
1255 " Swirl...",
1256 " Implode...",
1257 " Vignette...",
1258 " Wave...",
1259 " Oil Painting...",
1260 " Charcoal Drawing...",
1261 " Miscellany",
1262 " Image Info",
1263 " Zoom Image",
1264 " Show Preview...",
1265 " Show Histogram",
1266 " Show Matte",
1267 " Help",
1268 " Dismiss",
1269 "",
1270 "You can make adjustments to the region of interest by moving",
1271 "the pointer to one of the rectangle corners, pressing a",
1272 "button, and dragging. Finally, choose an image processing",
1273 "technique from the Command widget. You can choose more than",
1274 "one image processing technique to apply to an area.",
1275 "Alternatively, you can move the region of interest before",
1276 "applying another image processing technique. To exit, press",
1277 "Dismiss.",
1278 (char *) NULL,
1279 },
1280 *ImageRotateHelp[] =
1281 {
1282 "In rotate mode, the Command widget has these options:",
1283 "",
1284 " Pixel Color",
1285 " black",
1286 " blue",
1287 " cyan",
1288 " green",
1289 " gray",
1290 " red",
1291 " magenta",
1292 " yellow",
1293 " white",
1294 " Browser...",
1295 " Direction",
1296 " horizontal",
1297 " vertical",
1298 " Help",
1299 " Dismiss",
1300 "",
1301 "Choose a background color from the Pixel Color sub-menu.",
1302 "Additional background colors can be specified with the color",
1303 "browser. You can change the menu colors by setting the X",
1304 "resources pen1 through pen9.",
1305 "",
1306 "If you choose the color browser and press Grab, you can",
1307 "select the background color by moving the pointer to the",
1308 "desired color on the screen and press any button.",
1309 "",
1310 "Choose a point in the image window and press this button and",
1311 "hold. Next, move the pointer to another location in the",
1312 "image. As you move a line connects the initial location and",
1313 "the pointer. When you release the button, the degree of",
1314 "image rotation is determined by the slope of the line you",
1315 "just drew. The slope is relative to the direction you",
1316 "choose from the Direction sub-menu of the Command widget.",
1317 "",
1318 "To cancel the image rotation, move the pointer back to the",
1319 "starting point of the line and release the button.",
1320 (char *) NULL,
1321 };
1322
1323/*
1324 Enumeration declarations.
1325*/
1326typedef enum
1327{
1328 CopyMode,
1329 CropMode,
1330 CutMode
1331} ClipboardMode;
1332
1333typedef enum
1334{
1335 OpenCommand,
1336 NextCommand,
1337 FormerCommand,
1338 SelectCommand,
1339 SaveCommand,
1340 PrintCommand,
1341 DeleteCommand,
1342 NewCommand,
1343 VisualDirectoryCommand,
1344 QuitCommand,
1345 UndoCommand,
1346 RedoCommand,
1347 CutCommand,
1348 CopyCommand,
1349 PasteCommand,
1350 HalfSizeCommand,
1351 OriginalSizeCommand,
1352 DoubleSizeCommand,
1353 ResizeCommand,
1354 ApplyCommand,
1355 RefreshCommand,
1356 RestoreCommand,
1357 CropCommand,
1358 ChopCommand,
1359 FlopCommand,
1360 FlipCommand,
1361 RotateRightCommand,
1362 RotateLeftCommand,
1363 RotateCommand,
1364 ShearCommand,
1365 RollCommand,
1366 TrimCommand,
1367 HueCommand,
1368 SaturationCommand,
1369 BrightnessCommand,
1370 GammaCommand,
1371 SpiffCommand,
1372 DullCommand,
1373 ContrastStretchCommand,
1374 SigmoidalContrastCommand,
1375 NormalizeCommand,
1376 EqualizeCommand,
1377 NegateCommand,
1378 GrayscaleCommand,
1379 MapCommand,
1380 QuantizeCommand,
1381 DespeckleCommand,
1382 EmbossCommand,
1383 ReduceNoiseCommand,
1384 AddNoiseCommand,
1385 SharpenCommand,
1386 BlurCommand,
1387 ThresholdCommand,
1388 EdgeDetectCommand,
1389 SpreadCommand,
1390 ShadeCommand,
1391 RaiseCommand,
1392 SegmentCommand,
1393 SolarizeCommand,
1394 SepiaToneCommand,
1395 SwirlCommand,
1396 ImplodeCommand,
1397 VignetteCommand,
1398 WaveCommand,
1399 OilPaintCommand,
1400 CharcoalDrawCommand,
1401 AnnotateCommand,
1402 DrawCommand,
1403 ColorCommand,
1404 MatteCommand,
1405 CompositeCommand,
1406 AddBorderCommand,
1407 AddFrameCommand,
1408 CommentCommand,
1409 LaunchCommand,
1410 RegionofInterestCommand,
1411 ROIHelpCommand,
1412 ROIDismissCommand,
1413 InfoCommand,
1414 ZoomCommand,
1415 ShowPreviewCommand,
1416 ShowHistogramCommand,
1417 ShowMatteCommand,
1418 BackgroundCommand,
1419 SlideShowCommand,
1420 PreferencesCommand,
1421 HelpCommand,
1422 BrowseDocumentationCommand,
1423 VersionCommand,
1424 SaveToUndoBufferCommand,
1425 FreeBuffersCommand,
1426 NullCommand
1427} CommandType;
1428
1429typedef enum
1430{
1431 AnnotateNameCommand,
1432 AnnotateFontColorCommand,
1433 AnnotateBackgroundColorCommand,
1434 AnnotateRotateCommand,
1435 AnnotateHelpCommand,
1436 AnnotateDismissCommand,
1437 TextHelpCommand,
1438 TextApplyCommand,
1439 ChopDirectionCommand,
1440 ChopHelpCommand,
1441 ChopDismissCommand,
1442 HorizontalChopCommand,
1443 VerticalChopCommand,
1444 ColorEditMethodCommand,
1445 ColorEditColorCommand,
1446 ColorEditBorderCommand,
1447 ColorEditFuzzCommand,
1448 ColorEditUndoCommand,
1449 ColorEditHelpCommand,
1450 ColorEditDismissCommand,
1451 CompositeOperatorsCommand,
1452 CompositeDissolveCommand,
1453 CompositeDisplaceCommand,
1454 CompositeHelpCommand,
1455 CompositeDismissCommand,
1456 CropHelpCommand,
1457 CropDismissCommand,
1458 RectifyCopyCommand,
1459 RectifyHelpCommand,
1460 RectifyDismissCommand,
1461 DrawElementCommand,
1462 DrawColorCommand,
1463 DrawStippleCommand,
1464 DrawWidthCommand,
1465 DrawUndoCommand,
1466 DrawHelpCommand,
1467 DrawDismissCommand,
1468 MatteEditMethod,
1469 MatteEditBorderCommand,
1470 MatteEditFuzzCommand,
1471 MatteEditValueCommand,
1472 MatteEditUndoCommand,
1473 MatteEditHelpCommand,
1474 MatteEditDismissCommand,
1475 PasteOperatorsCommand,
1476 PasteHelpCommand,
1477 PasteDismissCommand,
1478 RotateColorCommand,
1479 RotateDirectionCommand,
1480 RotateCropCommand,
1481 RotateSharpenCommand,
1482 RotateHelpCommand,
1483 RotateDismissCommand,
1484 HorizontalRotateCommand,
1485 VerticalRotateCommand,
1486 TileLoadCommand,
1487 TileNextCommand,
1488 TileFormerCommand,
1489 TileDeleteCommand,
1490 TileUpdateCommand
1491} ModeType;
1492
1493/*
1494 Stipples.
1495*/
1496#define BricksWidth 20
1497#define BricksHeight 20
1498#define DiagonalWidth 16
1499#define DiagonalHeight 16
1500#define HighlightWidth 8
1501#define HighlightHeight 8
1502#define ScalesWidth 16
1503#define ScalesHeight 16
1504#define ShadowWidth 8
1505#define ShadowHeight 8
1506#define VerticalWidth 16
1507#define VerticalHeight 16
1508#define WavyWidth 16
1509#define WavyHeight 16
1510
1511/*
1512 Constant declaration.
1513*/
1514static const int
1515 RoiDelta = 8;
1516
1517static const unsigned char
1518 BricksBitmap[] =
1519 {
1520 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1521 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1522 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1523 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1524 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1525 },
1526 DiagonalBitmap[] =
1527 {
1528 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1529 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1530 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1531 },
1532 ScalesBitmap[] =
1533 {
1534 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1535 0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1536 0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1537 },
1538 VerticalBitmap[] =
1539 {
1540 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1541 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1542 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1543 },
1544 WavyBitmap[] =
1545 {
1546 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1547 0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1548 0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1549 };
1550
1551/*
1552 Function prototypes.
1553*/
1554static CommandType
1555 XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1556 const MagickStatusType,KeySym,Image **);
1557
1558static Image
1559 *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1560 Image **),
1561 *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1562 *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *),
1563 *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *);
1564
1565static MagickBooleanType
1566 XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *),
1567 XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1568 XChopImage(Display *,XResourceInfo *,XWindows *,Image **),
1569 XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode),
1570 XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **),
1571 XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1572 XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *),
1573 XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *),
1574 XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1575 XPasteImage(Display *,XResourceInfo *,XWindows *,Image *),
1576 XPrintImage(Display *,XResourceInfo *,XWindows *,Image *),
1577 XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **),
1578 XROIImage(Display *,XResourceInfo *,XWindows *,Image **),
1579 XSaveImage(Display *,XResourceInfo *,XWindows *,Image *),
1580 XTrimImage(Display *,XResourceInfo *,XWindows *,Image *);
1581
1582static void
1583 XDrawPanRectangle(Display *,XWindows *),
1584 XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **),
1585 XMagnifyImage(Display *,XWindows *,XEvent *),
1586 XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *),
1587 XPanImage(Display *,XWindows *,XEvent *),
1588 XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1589 const KeySym),
1590 XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1591 XScreenEvent(Display *,XWindows *,XEvent *),
1592 XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1593
1594/*
1595%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1596% %
1597% %
1598% %
1599% D i s p l a y I m a g e s %
1600% %
1601% %
1602% %
1603%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1604%
1605% DisplayImages() displays an image sequence to any X window screen. It
1606% returns a value other than 0 if successful. Check the exception member
1607% of image to determine the reason for any failure.
1608%
1609% The format of the DisplayImages method is:
1610%
1611% MagickBooleanType DisplayImages(const ImageInfo *image_info,
1612% Image *images)
1613%
1614% A description of each parameter follows:
1615%
1616% o image_info: the image info.
1617%
1618% o image: the image.
1619%
1620*/
1621MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1622 Image *images)
1623{
1624 char
1625 *argv[1];
1626
1627 Display
1628 *display;
1629
1630 Image
1631 *image;
1632
1633 register long
1634 i;
1635
1636 unsigned long
1637 state;
1638
1639 XrmDatabase
1640 resource_database;
1641
1642 XResourceInfo
1643 resource_info;
1644
1645 assert(image_info != (const ImageInfo *) NULL);
1646 assert(image_info->signature == MagickSignature);
1647 assert(images != (Image *) NULL);
1648 assert(images->signature == MagickSignature);
1649 if (images->debug != MagickFalse)
1650 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1651 display=XOpenDisplay(image_info->server_name);
1652 if (display == (Display *) NULL)
1653 {
1654 (void) ThrowMagickException(&images->exception,GetMagickModule(),
1655 XServerError,"UnableToOpenXServer","`%s'",XDisplayName(
1656 image_info->server_name));
1657 return(MagickFalse);
1658 }
1659 if (images->exception.severity != UndefinedException)
1660 CatchException(&images->exception);
1661 (void) XSetErrorHandler(XError);
1662 resource_database=XGetResourceDatabase(display,GetClientName());
1663 (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1664 XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1665 if (image_info->page != (char *) NULL)
1666 resource_info.image_geometry=AcquireString(image_info->page);
1667 resource_info.immutable=MagickTrue;
1668 argv[0]=AcquireString(GetClientName());
1669 state=DefaultState;
1670 for (i=0; (state & ExitState) == 0; i++)
1671 {
1672 if ((images->iterations != 0) && (i >= (long) images->iterations))
1673 break;
1674 image=GetImageFromList(images,i % GetImageListLength(images));
1675 (void) XDisplayImage(display,&resource_info,argv,1,&image,&state);
1676 }
1677 argv[0]=DestroyString(argv[0]);
1678 (void) XCloseDisplay(display);
1679 XDestroyResourceInfo(&resource_info);
1680 if (images->exception.severity != UndefinedException)
1681 return(MagickFalse);
1682 return(MagickTrue);
1683}
1684
1685/*
1686%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1687% %
1688% %
1689% %
1690% R e m o t e D i s p l a y C o m m a n d %
1691% %
1692% %
1693% %
1694%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1695%
1696% RemoteDisplayCommand() encourages a remote display program to display the
1697% specified image filename.
1698%
1699% The format of the RemoteDisplayCommand method is:
1700%
1701% MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1702% const char *window,const char *filename,ExceptionInfo *exception)
1703%
1704% A description of each parameter follows:
1705%
1706% o image_info: the image info.
1707%
1708% o window: Specifies the name or id of an X window.
1709%
1710% o filename: the name of the image filename to display.
1711%
1712% o exception: return any errors or warnings in this structure.
1713%
1714*/
1715MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1716 const char *window,const char *filename,ExceptionInfo *exception)
1717{
1718 Display
1719 *display;
1720
1721 MagickStatusType
1722 status;
1723
1724 assert(image_info != (const ImageInfo *) NULL);
1725 assert(image_info->signature == MagickSignature);
1726 assert(filename != (char *) NULL);
1727 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1728 display=XOpenDisplay(image_info->server_name);
1729 if (display == (Display *) NULL)
1730 {
1731 (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1732 "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1733 return(MagickFalse);
1734 }
1735 (void) XSetErrorHandler(XError);
1736 status=XRemoteCommand(display,window,filename);
1737 (void) XCloseDisplay(display);
1738 return(status != 0 ? MagickTrue : MagickFalse);
1739}
1740
1741/*
1742%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1743% %
1744% %
1745% %
1746+ X A n n o t a t e E d i t I m a g e %
1747% %
1748% %
1749% %
1750%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1751%
1752% XAnnotateEditImage() annotates the image with text.
1753%
1754% The format of the XAnnotateEditImage method is:
1755%
1756% MagickBooleanType XAnnotateEditImage(Display *display,
1757% XResourceInfo *resource_info,XWindows *windows,Image *image)
1758%
1759% A description of each parameter follows:
1760%
1761% o display: Specifies a connection to an X server; returned from
1762% XOpenDisplay.
1763%
1764% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1765%
1766% o windows: Specifies a pointer to a XWindows structure.
1767%
1768% o image: the image; returned from ReadImage.
1769%
1770*/
1771
1772static inline long MagickMax(const long x,const long y)
1773{
1774 if (x > y)
1775 return(x);
1776 return(y);
1777}
1778
1779static inline long MagickMin(const long x,const long y)
1780{
1781 if (x < y)
1782 return(x);
1783 return(y);
1784}
1785
1786static MagickBooleanType XAnnotateEditImage(Display *display,
1787 XResourceInfo *resource_info,XWindows *windows,Image *image)
1788{
1789 static const char
1790 *AnnotateMenu[] =
1791 {
1792 "Font Name",
1793 "Font Color",
1794 "Box Color",
1795 "Rotate Text",
1796 "Help",
1797 "Dismiss",
1798 (char *) NULL
1799 },
1800 *TextMenu[] =
1801 {
1802 "Help",
1803 "Apply",
1804 (char *) NULL
1805 };
1806
1807 static const ModeType
1808 AnnotateCommands[] =
1809 {
1810 AnnotateNameCommand,
1811 AnnotateFontColorCommand,
1812 AnnotateBackgroundColorCommand,
1813 AnnotateRotateCommand,
1814 AnnotateHelpCommand,
1815 AnnotateDismissCommand
1816 },
1817 TextCommands[] =
1818 {
1819 TextHelpCommand,
1820 TextApplyCommand
1821 };
1822
1823 static MagickBooleanType
1824 transparent_box = MagickTrue,
1825 transparent_pen = MagickFalse;
1826
1827 static MagickRealType
1828 degrees = 0.0;
1829
1830 static unsigned int
1831 box_id = MaxNumberPens-2,
1832 font_id = 0,
1833 pen_id = 0;
1834
1835 char
1836 command[MaxTextExtent],
1837 text[MaxTextExtent];
1838
1839 const char
1840 *ColorMenu[MaxNumberPens+1];
1841
1842 Cursor
1843 cursor;
1844
1845 GC
1846 annotate_context;
1847
1848 int
1849 id,
1850 pen_number,
1851 status,
1852 x,
1853 y;
1854
1855 KeySym
1856 key_symbol;
1857
1858 register char
1859 *p;
1860
1861 register long
1862 i;
1863
1864 unsigned int
1865 height,
1866 width;
1867
1868 unsigned long
1869 state;
1870
1871 XAnnotateInfo
1872 *annotate_info,
1873 *previous_info;
1874
1875 XColor
1876 color;
1877
1878 XFontStruct
1879 *font_info;
1880
1881 XEvent
1882 event,
1883 text_event;
1884
1885 /*
1886 Map Command widget.
1887 */
1888 (void) CloneString(&windows->command.name,"Annotate");
1889 windows->command.data=4;
1890 (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1891 (void) XMapRaised(display,windows->command.id);
1892 XClientMessage(display,windows->image.id,windows->im_protocols,
1893 windows->im_update_widget,CurrentTime);
1894 /*
1895 Track pointer until button 1 is pressed.
1896 */
1897 XQueryPosition(display,windows->image.id,&x,&y);
1898 (void) XSelectInput(display,windows->image.id,
1899 windows->image.attributes.event_mask | PointerMotionMask);
1900 cursor=XCreateFontCursor(display,XC_left_side);
1901 (void) XCheckDefineCursor(display,windows->image.id,cursor);
1902 state=DefaultState;
1903 do
1904 {
1905 if (windows->info.mapped != MagickFalse)
1906 {
1907 /*
1908 Display pointer position.
1909 */
1910 (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
1911 x+windows->image.x,y+windows->image.y);
1912 XInfoWidget(display,windows,text);
1913 }
1914 /*
1915 Wait for next event.
1916 */
1917 XScreenEvent(display,windows,&event);
1918 if (event.xany.window == windows->command.id)
1919 {
1920 /*
1921 Select a command from the Command widget.
1922 */
1923 id=XCommandWidget(display,windows,AnnotateMenu,&event);
1924 (void) XCheckDefineCursor(display,windows->image.id,cursor);
1925 if (id < 0)
1926 continue;
1927 switch (AnnotateCommands[id])
1928 {
1929 case AnnotateNameCommand:
1930 {
1931 const char
1932 *FontMenu[MaxNumberFonts];
1933
1934 int
1935 font_number;
1936
1937 /*
1938 Initialize menu selections.
1939 */
1940 for (i=0; i < MaxNumberFonts; i++)
1941 FontMenu[i]=resource_info->font_name[i];
1942 FontMenu[MaxNumberFonts-2]="Browser...";
1943 FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1944 /*
1945 Select a font name from the pop-up menu.
1946 */
1947 font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1948 (const char **) FontMenu,command);
1949 if (font_number < 0)
1950 break;
1951 if (font_number == (MaxNumberFonts-2))
1952 {
1953 static char
1954 font_name[MaxTextExtent] = "fixed";
1955
1956 /*
1957 Select a font name from a browser.
1958 */
1959 resource_info->font_name[font_number]=font_name;
1960 XFontBrowserWidget(display,windows,"Select",font_name);
1961 if (*font_name == '\0')
1962 break;
1963 }
1964 /*
1965 Initialize font info.
1966 */
1967 font_info=XLoadQueryFont(display,resource_info->font_name[
1968 font_number]);
1969 if (font_info == (XFontStruct *) NULL)
1970 {
1971 XNoticeWidget(display,windows,"Unable to load font:",
1972 resource_info->font_name[font_number]);
1973 break;
1974 }
1975 font_id=(unsigned int) font_number;
1976 (void) XFreeFont(display,font_info);
1977 break;
1978 }
1979 case AnnotateFontColorCommand:
1980 {
1981 /*
1982 Initialize menu selections.
1983 */
1984 for (i=0; i < (int) (MaxNumberPens-2); i++)
1985 ColorMenu[i]=resource_info->pen_colors[i];
1986 ColorMenu[MaxNumberPens-2]="transparent";
1987 ColorMenu[MaxNumberPens-1]="Browser...";
1988 ColorMenu[MaxNumberPens]=(const char *) NULL;
1989 /*
1990 Select a pen color from the pop-up menu.
1991 */
1992 pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
1993 (const char **) ColorMenu,command);
1994 if (pen_number < 0)
1995 break;
1996 transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
1997 MagickFalse;
1998 if (transparent_pen != MagickFalse)
1999 break;
2000 if (pen_number == (MaxNumberPens-1))
2001 {
2002 static char
2003 color_name[MaxTextExtent] = "gray";
2004
2005 /*
2006 Select a pen color from a dialog.
2007 */
2008 resource_info->pen_colors[pen_number]=color_name;
2009 XColorBrowserWidget(display,windows,"Select",color_name);
2010 if (*color_name == '\0')
2011 break;
2012 }
2013 /*
2014 Set pen color.
2015 */
2016 (void) XParseColor(display,windows->map_info->colormap,
2017 resource_info->pen_colors[pen_number],&color);
2018 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2019 (unsigned int) MaxColors,&color);
2020 windows->pixel_info->pen_colors[pen_number]=color;
2021 pen_id=(unsigned int) pen_number;
2022 break;
2023 }
2024 case AnnotateBackgroundColorCommand:
2025 {
2026 /*
2027 Initialize menu selections.
2028 */
2029 for (i=0; i < (int) (MaxNumberPens-2); i++)
2030 ColorMenu[i]=resource_info->pen_colors[i];
2031 ColorMenu[MaxNumberPens-2]="transparent";
2032 ColorMenu[MaxNumberPens-1]="Browser...";
2033 ColorMenu[MaxNumberPens]=(const char *) NULL;
2034 /*
2035 Select a pen color from the pop-up menu.
2036 */
2037 pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2038 (const char **) ColorMenu,command);
2039 if (pen_number < 0)
2040 break;
2041 transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2042 MagickFalse;
2043 if (transparent_box != MagickFalse)
2044 break;
2045 if (pen_number == (MaxNumberPens-1))
2046 {
2047 static char
2048 color_name[MaxTextExtent] = "gray";
2049
2050 /*
2051 Select a pen color from a dialog.
2052 */
2053 resource_info->pen_colors[pen_number]=color_name;
2054 XColorBrowserWidget(display,windows,"Select",color_name);
2055 if (*color_name == '\0')
2056 break;
2057 }
2058 /*
2059 Set pen color.
2060 */
2061 (void) XParseColor(display,windows->map_info->colormap,
2062 resource_info->pen_colors[pen_number],&color);
2063 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2064 (unsigned int) MaxColors,&color);
2065 windows->pixel_info->pen_colors[pen_number]=color;
2066 box_id=(unsigned int) pen_number;
2067 break;
2068 }
2069 case AnnotateRotateCommand:
2070 {
2071 int
2072 entry;
2073
2074 static char
2075 angle[MaxTextExtent] = "30.0";
2076
2077 static const char
2078 *RotateMenu[] =
2079 {
2080 "-90",
2081 "-45",
2082 "-30",
2083 "0",
2084 "30",
2085 "45",
2086 "90",
2087 "180",
2088 "Dialog...",
2089 (char *) NULL,
2090 };
2091
2092 /*
2093 Select a command from the pop-up menu.
2094 */
2095 entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2096 command);
2097 if (entry < 0)
2098 break;
2099 if (entry != 8)
2100 {
2101 degrees=atof(RotateMenu[entry]);
2102 break;
2103 }
2104 (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2105 angle);
2106 if (*angle == '\0')
2107 break;
2108 degrees=atof(angle);
2109 break;
2110 }
2111 case AnnotateHelpCommand:
2112 {
2113 XTextViewWidget(display,resource_info,windows,MagickFalse,
2114 "Help Viewer - Image Annotation",ImageAnnotateHelp);
2115 break;
2116 }
2117 case AnnotateDismissCommand:
2118 {
2119 /*
2120 Prematurely exit.
2121 */
2122 state|=EscapeState;
2123 state|=ExitState;
2124 break;
2125 }
2126 default:
2127 break;
2128 }
2129 continue;
2130 }
2131 switch (event.type)
2132 {
2133 case ButtonPress:
2134 {
2135 if (event.xbutton.button != Button1)
2136 break;
2137 if (event.xbutton.window != windows->image.id)
2138 break;
2139 /*
2140 Change to text entering mode.
2141 */
2142 x=event.xbutton.x;
2143 y=event.xbutton.y;
2144 state|=ExitState;
2145 break;
2146 }
2147 case ButtonRelease:
2148 break;
2149 case Expose:
2150 break;
2151 case KeyPress:
2152 {
2153 if (event.xkey.window != windows->image.id)
2154 break;
2155 /*
2156 Respond to a user key press.
2157 */
2158 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2159 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2160 switch ((int) key_symbol)
2161 {
2162 case XK_Escape:
2163 case XK_F20:
2164 {
2165 /*
2166 Prematurely exit.
2167 */
2168 state|=EscapeState;
2169 state|=ExitState;
2170 break;
2171 }
2172 case XK_F1:
2173 case XK_Help:
2174 {
2175 XTextViewWidget(display,resource_info,windows,MagickFalse,
2176 "Help Viewer - Image Annotation",ImageAnnotateHelp);
2177 break;
2178 }
2179 default:
2180 {
2181 (void) XBell(display,0);
2182 break;
2183 }
2184 }
2185 break;
2186 }
2187 case MotionNotify:
2188 {
2189 /*
2190 Map and unmap Info widget as cursor crosses its boundaries.
2191 */
2192 x=event.xmotion.x;
2193 y=event.xmotion.y;
2194 if (windows->info.mapped != MagickFalse)
2195 {
2196 if ((x < (int) (windows->info.x+windows->info.width)) &&
2197 (y < (int) (windows->info.y+windows->info.height)))
2198 (void) XWithdrawWindow(display,windows->info.id,
2199 windows->info.screen);
2200 }
2201 else
2202 if ((x > (int) (windows->info.x+windows->info.width)) ||
2203 (y > (int) (windows->info.y+windows->info.height)))
2204 (void) XMapWindow(display,windows->info.id);
2205 break;
2206 }
2207 default:
2208 break;
2209 }
2210 } while ((state & ExitState) == 0);
2211 (void) XSelectInput(display,windows->image.id,
2212 windows->image.attributes.event_mask);
2213 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2214 if ((state & EscapeState) != 0)
2215 return(MagickTrue);
2216 /*
2217 Set font info and check boundary conditions.
2218 */
2219 font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2220 if (font_info == (XFontStruct *) NULL)
2221 {
2222 XNoticeWidget(display,windows,"Unable to load font:",
2223 resource_info->font_name[font_id]);
2224 font_info=windows->font_info;
2225 }
2226 if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2227 x=(int) windows->image.width-font_info->max_bounds.width;
2228 if (y < (int) (font_info->ascent+font_info->descent))
2229 y=(int) font_info->ascent+font_info->descent;
2230 if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2231 ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2232 return(MagickFalse);
2233 /*
2234 Initialize annotate structure.
2235 */
2236 annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2237 if (annotate_info == (XAnnotateInfo *) NULL)
2238 return(MagickFalse);
2239 XGetAnnotateInfo(annotate_info);
2240 annotate_info->x=x;
2241 annotate_info->y=y;
2242 if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2243 annotate_info->stencil=OpaqueStencil;
2244 else
2245 if (transparent_box == MagickFalse)
2246 annotate_info->stencil=BackgroundStencil;
2247 else
2248 annotate_info->stencil=ForegroundStencil;
2249 annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2250 annotate_info->degrees=degrees;
2251 annotate_info->font_info=font_info;
2252 annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2253 windows->image.width/MagickMax(font_info->min_bounds.width,1)+2UL,
2254 sizeof(*annotate_info->text));
2255 if (annotate_info->text == (char *) NULL)
2256 return(MagickFalse);
2257 /*
2258 Create cursor and set graphic context.
2259 */
2260 cursor=XCreateFontCursor(display,XC_pencil);
2261 (void) XCheckDefineCursor(display,windows->image.id,cursor);
2262 annotate_context=windows->image.annotate_context;
2263 (void) XSetFont(display,annotate_context,font_info->fid);
2264 (void) XSetBackground(display,annotate_context,
2265 windows->pixel_info->pen_colors[box_id].pixel);
2266 (void) XSetForeground(display,annotate_context,
2267 windows->pixel_info->pen_colors[pen_id].pixel);
2268 /*
2269 Begin annotating the image with text.
2270 */
2271 (void) CloneString(&windows->command.name,"Text");
2272 windows->command.data=0;
2273 (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2274 state=DefaultState;
2275 (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2276 text_event.xexpose.width=(int) font_info->max_bounds.width;
2277 text_event.xexpose.height=font_info->max_bounds.ascent+
2278 font_info->max_bounds.descent;
2279 p=annotate_info->text;
2280 do
2281 {
2282 /*
2283 Display text cursor.
2284 */
2285 *p='\0';
2286 (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2287 /*
2288 Wait for next event.
2289 */
2290 XScreenEvent(display,windows,&event);
2291 if (event.xany.window == windows->command.id)
2292 {
2293 /*
2294 Select a command from the Command widget.
2295 */
2296 (void) XSetBackground(display,annotate_context,
2297 windows->pixel_info->background_color.pixel);
2298 (void) XSetForeground(display,annotate_context,
2299 windows->pixel_info->foreground_color.pixel);
2300 id=XCommandWidget(display,windows,AnnotateMenu,&event);
2301 (void) XSetBackground(display,annotate_context,
2302 windows->pixel_info->pen_colors[box_id].pixel);
2303 (void) XSetForeground(display,annotate_context,
2304 windows->pixel_info->pen_colors[pen_id].pixel);
2305 if (id < 0)
2306 continue;
2307 switch (TextCommands[id])
2308 {
2309 case TextHelpCommand:
2310 {
2311 XTextViewWidget(display,resource_info,windows,MagickFalse,
2312 "Help Viewer - Image Annotation",ImageAnnotateHelp);
2313 (void) XCheckDefineCursor(display,windows->image.id,cursor);
2314 break;
2315 }
2316 case TextApplyCommand:
2317 {
2318 /*
2319 Finished annotating.
2320 */
2321 annotate_info->width=(unsigned int) XTextWidth(font_info,
2322 annotate_info->text,(int) strlen(annotate_info->text));
2323 XRefreshWindow(display,&windows->image,&text_event);
2324 state|=ExitState;
2325 break;
2326 }
2327 default:
2328 break;
2329 }
2330 continue;
2331 }
2332 /*
2333 Erase text cursor.
2334 */
2335 text_event.xexpose.x=x;
2336 text_event.xexpose.y=y-font_info->max_bounds.ascent;
2337 (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2338 (unsigned int) text_event.xexpose.width,(unsigned int)
2339 text_event.xexpose.height,MagickFalse);
2340 XRefreshWindow(display,&windows->image,&text_event);
2341 switch (event.type)
2342 {
2343 case ButtonPress:
2344 {
2345 if (event.xbutton.window != windows->image.id)
2346 break;
2347 if (event.xbutton.button == Button2)
2348 {
2349 /*
2350 Request primary selection.
2351 */
2352 (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2353 windows->image.id,CurrentTime);
2354 break;
2355 }
2356 break;
2357 }
2358 case Expose:
2359 {
2360 if (event.xexpose.count == 0)
2361 {
2362 XAnnotateInfo
2363 *text_info;
2364
2365 /*
2366 Refresh Image window.
2367 */
2368 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2369 text_info=annotate_info;
2370 while (text_info != (XAnnotateInfo *) NULL)
2371 {
2372 if (annotate_info->stencil == ForegroundStencil)
2373 (void) XDrawString(display,windows->image.id,annotate_context,
2374 text_info->x,text_info->y,text_info->text,
2375 (int) strlen(text_info->text));
2376 else
2377 (void) XDrawImageString(display,windows->image.id,
2378 annotate_context,text_info->x,text_info->y,text_info->text,
2379 (int) strlen(text_info->text));
2380 text_info=text_info->previous;
2381 }
2382 (void) XDrawString(display,windows->image.id,annotate_context,
2383 x,y,"_",1);
2384 }
2385 break;
2386 }
2387 case KeyPress:
2388 {
2389 int
2390 length;
2391
2392 if (event.xkey.window != windows->image.id)
2393 break;
2394 /*
2395 Respond to a user key press.
2396 */
2397 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2398 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2399 *(command+length)='\0';
2400 if (((event.xkey.state & ControlMask) != 0) ||
2401 ((event.xkey.state & Mod1Mask) != 0))
2402 state|=ModifierState;
2403 if ((state & ModifierState) != 0)
2404 switch ((int) key_symbol)
2405 {
2406 case XK_u:
2407 case XK_U:
2408 {
2409 key_symbol=DeleteCommand;
2410 break;
2411 }
2412 default:
2413 break;
2414 }
2415 switch ((int) key_symbol)
2416 {
2417 case XK_BackSpace:
2418 {
2419 /*
2420 Erase one character.
2421 */
2422 if (p == annotate_info->text)
2423 {
2424 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2425 break;
2426 else
2427 {
2428 /*
2429 Go to end of the previous line of text.
2430 */
2431 annotate_info=annotate_info->previous;
2432 p=annotate_info->text;
2433 x=annotate_info->x+annotate_info->width;
2434 y=annotate_info->y;
2435 if (annotate_info->width != 0)
2436 p+=strlen(annotate_info->text);
2437 break;
2438 }
2439 }
2440 p--;
2441 x-=XTextWidth(font_info,p,1);
2442 text_event.xexpose.x=x;
2443 text_event.xexpose.y=y-font_info->max_bounds.ascent;
2444 XRefreshWindow(display,&windows->image,&text_event);
2445 break;
2446 }
2447 case XK_bracketleft:
2448 {
2449 key_symbol=XK_Escape;
2450 break;
2451 }
2452 case DeleteCommand:
2453 {
2454 /*
2455 Erase the entire line of text.
2456 */
2457 while (p != annotate_info->text)
2458 {
2459 p--;
2460 x-=XTextWidth(font_info,p,1);
2461 text_event.xexpose.x=x;
2462 XRefreshWindow(display,&windows->image,&text_event);
2463 }
2464 break;
2465 }
2466 case XK_Escape:
2467 case XK_F20:
2468 {
2469 /*
2470 Finished annotating.
2471 */
2472 annotate_info->width=(unsigned int) XTextWidth(font_info,
2473 annotate_info->text,(int) strlen(annotate_info->text));
2474 XRefreshWindow(display,&windows->image,&text_event);
2475 state|=ExitState;
2476 break;
2477 }
2478 default:
2479 {
2480 /*
2481 Draw a single character on the Image window.
2482 */
2483 if ((state & ModifierState) != 0)
2484 break;
2485 if (*command == '\0')
2486 break;
2487 *p=(*command);
2488 if (annotate_info->stencil == ForegroundStencil)
2489 (void) XDrawString(display,windows->image.id,annotate_context,
2490 x,y,p,1);
2491 else
2492 (void) XDrawImageString(display,windows->image.id,
2493 annotate_context,x,y,p,1);
2494 x+=XTextWidth(font_info,p,1);
2495 p++;
2496 if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2497 break;
2498 }
2499 case XK_Return:
2500 case XK_KP_Enter:
2501 {
2502 /*
2503 Advance to the next line of text.
2504 */
2505 *p='\0';
2506 annotate_info->width=(unsigned int) XTextWidth(font_info,
2507 annotate_info->text,(int) strlen(annotate_info->text));
2508 if (annotate_info->next != (XAnnotateInfo *) NULL)
2509 {
2510 /*
2511 Line of text already exists.
2512 */
2513 annotate_info=annotate_info->next;
2514 x=annotate_info->x;
2515 y=annotate_info->y;
2516 p=annotate_info->text;
2517 break;
2518 }
2519 annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2520 sizeof(*annotate_info->next));
2521 if (annotate_info->next == (XAnnotateInfo *) NULL)
2522 return(MagickFalse);
2523 *annotate_info->next=(*annotate_info);
2524 annotate_info->next->previous=annotate_info;
2525 annotate_info=annotate_info->next;
2526 annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2527 windows->image.width/MagickMax(font_info->min_bounds.width,1)+2UL,
2528 sizeof(*annotate_info->text));
2529 if (annotate_info->text == (char *) NULL)
2530 return(MagickFalse);
2531 annotate_info->y+=annotate_info->height;
2532 if (annotate_info->y > (int) windows->image.height)
2533 annotate_info->y=(int) annotate_info->height;
2534 annotate_info->next=(XAnnotateInfo *) NULL;
2535 x=annotate_info->x;
2536 y=annotate_info->y;
2537 p=annotate_info->text;
2538 break;
2539 }
2540 }
2541 break;
2542 }
2543 case KeyRelease:
2544 {
2545 /*
2546 Respond to a user key release.
2547 */
2548 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2549 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2550 state&=(~ModifierState);
2551 break;
2552 }
2553 case SelectionNotify:
2554 {
2555 Atom
2556 type;
2557
2558 int
2559 format;
2560
2561 unsigned char
2562 *data;
2563
2564 unsigned long
2565 after,
2566 length;
2567
2568 /*
2569 Obtain response from primary selection.
2570 */
2571 if (event.xselection.property == (Atom) None)
2572 break;
2573 status=XGetWindowProperty(display,event.xselection.requestor,
2574 event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2575 &type,&format,&length,&after,&data);
2576 if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2577 (length == 0))
2578 break;
2579 /*
2580 Annotate Image window with primary selection.
2581 */
2582 for (i=0; i < (long) length; i++)
2583 {
2584 if ((char) data[i] != '\n')
2585 {
2586 /*
2587 Draw a single character on the Image window.
2588 */
2589 *p=(char) data[i];
2590 (void) XDrawString(display,windows->image.id,annotate_context,
2591 x,y,p,1);
2592 x+=XTextWidth(font_info,p,1);
2593 p++;
2594 if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2595 continue;
2596 }
2597 /*
2598 Advance to the next line of text.
2599 */
2600 *p='\0';
2601 annotate_info->width=(unsigned int) XTextWidth(font_info,
2602 annotate_info->text,(int) strlen(annotate_info->text));
2603 if (annotate_info->next != (XAnnotateInfo *) NULL)
2604 {
2605 /*
2606 Line of text already exists.
2607 */
2608 annotate_info=annotate_info->next;
2609 x=annotate_info->x;
2610 y=annotate_info->y;
2611 p=annotate_info->text;
2612 continue;
2613 }
2614 annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2615 sizeof(*annotate_info->next));
2616 if (annotate_info->next == (XAnnotateInfo *) NULL)
2617 return(MagickFalse);
2618 *annotate_info->next=(*annotate_info);
2619 annotate_info->next->previous=annotate_info;
2620 annotate_info=annotate_info->next;
2621 annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2622 windows->image.width/MagickMax(font_info->min_bounds.width,1)+2UL,
2623 sizeof(*annotate_info->text));
2624 if (annotate_info->text == (char *) NULL)
2625 return(MagickFalse);
2626 annotate_info->y+=annotate_info->height;
2627 if (annotate_info->y > (int) windows->image.height)
2628 annotate_info->y=(int) annotate_info->height;
2629 annotate_info->next=(XAnnotateInfo *) NULL;
2630 x=annotate_info->x;
2631 y=annotate_info->y;
2632 p=annotate_info->text;
2633 }
2634 (void) XFree((void *) data);
2635 break;
2636 }
2637 default:
2638 break;
2639 }
2640 } while ((state & ExitState) == 0);
2641 (void) XFreeCursor(display,cursor);
2642 /*
2643 Annotation is relative to image configuration.
2644 */
2645 width=(unsigned int) image->columns;
2646 height=(unsigned int) image->rows;
2647 x=0;
2648 y=0;
2649 if (windows->image.crop_geometry != (char *) NULL)
2650 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2651 /*
2652 Initialize annotated image.
2653 */
2654 XSetCursorState(display,windows,MagickTrue);
2655 XCheckRefreshWindows(display,windows);
2656 while (annotate_info != (XAnnotateInfo *) NULL)
2657 {
2658 if (annotate_info->width == 0)
2659 {
2660 /*
2661 No text on this line-- go to the next line of text.
2662 */
2663 previous_info=annotate_info->previous;
2664 annotate_info->text=(char *)
2665 RelinquishMagickMemory(annotate_info->text);
2666 annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2667 annotate_info=previous_info;
2668 continue;
2669 }
2670 /*
2671 Determine pixel index for box and pen color.
2672 */
2673 windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2674 if (windows->pixel_info->colors != 0)
2675 for (i=0; i < (long) windows->pixel_info->colors; i++)
2676 if (windows->pixel_info->pixels[i] ==
2677 windows->pixel_info->pen_colors[box_id].pixel)
2678 {
2679 windows->pixel_info->box_index=(unsigned short) i;
2680 break;
2681 }
2682 windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2683 if (windows->pixel_info->colors != 0)
2684 for (i=0; i < (long) windows->pixel_info->colors; i++)
2685 if (windows->pixel_info->pixels[i] ==
2686 windows->pixel_info->pen_colors[pen_id].pixel)
2687 {
2688 windows->pixel_info->pen_index=(unsigned short) i;
2689 break;
2690 }
2691 /*
2692 Define the annotate geometry string.
2693 */
2694 annotate_info->x=(int)
2695 width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2696 annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2697 windows->image.y)/windows->image.ximage->height;
2698 (void) FormatMagickString(annotate_info->geometry,MaxTextExtent,
2699 "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2700 height*annotate_info->height/windows->image.ximage->height,
2701 annotate_info->x+x,annotate_info->y+y);
2702 /*
2703 Annotate image with text.
2704 */
2705 status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
2706 if (status == 0)
2707 return(MagickFalse);
2708 /*
2709 Free up memory.
2710 */
2711 previous_info=annotate_info->previous;
2712 annotate_info->text=DestroyString(annotate_info->text);
2713 annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2714 annotate_info=previous_info;
2715 }
2716 (void) XSetForeground(display,annotate_context,
2717 windows->pixel_info->foreground_color.pixel);
2718 (void) XSetBackground(display,annotate_context,
2719 windows->pixel_info->background_color.pixel);
2720 (void) XSetFont(display,annotate_context,windows->font_info->fid);
2721 XSetCursorState(display,windows,MagickFalse);
2722 (void) XFreeFont(display,font_info);
2723 /*
2724 Update image configuration.
2725 */
2726 XConfigureImageColormap(display,resource_info,windows,image);
2727 (void) XConfigureImage(display,resource_info,windows,image);
2728 return(MagickTrue);
2729}
2730
2731/*
2732%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2733% %
2734% %
2735% %
2736+ X B a c k g r o u n d I m a g e %
2737% %
2738% %
2739% %
2740%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2741%
2742% XBackgroundImage() displays the image in the background of a window.
2743%
2744% The format of the XBackgroundImage method is:
2745%
2746% MagickBooleanType XBackgroundImage(Display *display,
2747% XResourceInfo *resource_info,XWindows *windows,Image **image)
2748%
2749% A description of each parameter follows:
2750%
2751% o display: Specifies a connection to an X server; returned from
2752% XOpenDisplay.
2753%
2754% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2755%
2756% o windows: Specifies a pointer to a XWindows structure.
2757%
2758% o image: the image.
2759%
2760*/
2761static MagickBooleanType XBackgroundImage(Display *display,
2762 XResourceInfo *resource_info,XWindows *windows,Image **image)
2763{
2764#define BackgroundImageTag "Background/Image"
2765
2766 int
2767 status;
2768
2769 static char
2770 window_id[MaxTextExtent] = "root";
2771
2772 XResourceInfo
2773 background_resources;
2774
2775 /*
2776 Put image in background.
2777 */
2778 status=XDialogWidget(display,windows,"Background",
2779 "Enter window id (id 0x00 selects window with pointer):",window_id);
2780 if (*window_id == '\0')
2781 return(MagickFalse);
2782 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
2783 XInfoWidget(display,windows,BackgroundImageTag);
2784 XSetCursorState(display,windows,MagickTrue);
2785 XCheckRefreshWindows(display,windows);
2786 background_resources=(*resource_info);
2787 background_resources.window_id=window_id;
2788 background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2789 status=XDisplayBackgroundImage(display,&background_resources,*image);
2790 if (status != MagickFalse)
2791 XClientMessage(display,windows->image.id,windows->im_protocols,
2792 windows->im_retain_colors,CurrentTime);
2793 XSetCursorState(display,windows,MagickFalse);
2794 (void) XMagickCommand(display,resource_info,windows,UndoCommand,image);
2795 return(MagickTrue);
2796}
2797
2798/*
2799%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2800% %
2801% %
2802% %
2803+ X C h o p I m a g e %
2804% %
2805% %
2806% %
2807%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2808%
2809% XChopImage() chops the X image.
2810%
2811% The format of the XChopImage method is:
2812%
2813% MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2814% XWindows *windows,Image **image)
2815%
2816% A description of each parameter follows:
2817%
2818% o display: Specifies a connection to an X server; returned from
2819% XOpenDisplay.
2820%
2821% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2822%
2823% o windows: Specifies a pointer to a XWindows structure.
2824%
2825% o image: the image.
2826%
2827*/
2828static MagickBooleanType XChopImage(Display *display,
2829 XResourceInfo *resource_info,XWindows *windows,Image **image)
2830{
2831 static const char
2832 *ChopMenu[] =
2833 {
2834 "Direction",
2835 "Help",
2836 "Dismiss",
2837 (char *) NULL
2838 };
2839
2840 static ModeType
2841 direction = HorizontalChopCommand;
2842
2843 static const ModeType
2844 ChopCommands[] =
2845 {
2846 ChopDirectionCommand,
2847 ChopHelpCommand,
2848 ChopDismissCommand
2849 },
2850 DirectionCommands[] =
2851 {
2852 HorizontalChopCommand,
2853 VerticalChopCommand
2854 };
2855
2856 char
2857 text[MaxTextExtent];
2858
2859 Image
2860 *chop_image;
2861
2862 int
2863 id,
2864 x,
2865 y;
2866
2867 MagickRealType
2868 scale_factor;
2869
2870 RectangleInfo
2871 chop_info;
2872
2873 unsigned int
2874 distance,
2875 height,
2876 width;
2877
2878 unsigned long
2879 state;
2880
2881 XEvent
2882 event;
2883
2884 XSegment
2885 segment_info;
2886
2887 /*
2888 Map Command widget.
2889 */
2890 (void) CloneString(&windows->command.name,"Chop");
2891 windows->command.data=1;
2892 (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2893 (void) XMapRaised(display,windows->command.id);
2894 XClientMessage(display,windows->image.id,windows->im_protocols,
2895 windows->im_update_widget,CurrentTime);
2896 /*
2897 Track pointer until button 1 is pressed.
2898 */
2899 XQueryPosition(display,windows->image.id,&x,&y);
2900 (void) XSelectInput(display,windows->image.id,
2901 windows->image.attributes.event_mask | PointerMotionMask);
2902 state=DefaultState;
2903 do
2904 {
2905 if (windows->info.mapped != MagickFalse)
2906 {
2907 /*
2908 Display pointer position.
2909 */
2910 (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
2911 x+windows->image.x,y+windows->image.y);
2912 XInfoWidget(display,windows,text);
2913 }
2914 /*
2915 Wait for next event.
2916 */
2917 XScreenEvent(display,windows,&event);
2918 if (event.xany.window == windows->command.id)
2919 {
2920 /*
2921 Select a command from the Command widget.
2922 */
2923 id=XCommandWidget(display,windows,ChopMenu,&event);
2924 if (id < 0)
2925 continue;
2926 switch (ChopCommands[id])
2927 {
2928 case ChopDirectionCommand:
2929 {
2930 char
2931 command[MaxTextExtent];
2932
2933 static const char
2934 *Directions[] =
2935 {
2936 "horizontal",
2937 "vertical",
2938 (char *) NULL,
2939 };
2940
2941 /*
2942 Select a command from the pop-up menu.
2943 */
2944 id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2945 if (id >= 0)
2946 direction=DirectionCommands[id];
2947 break;
2948 }
2949 case ChopHelpCommand:
2950 {
2951 XTextViewWidget(display,resource_info,windows,MagickFalse,
2952 "Help Viewer - Image Chop",ImageChopHelp);
2953 break;
2954 }
2955 case ChopDismissCommand:
2956 {
2957 /*
2958 Prematurely exit.
2959 */
2960 state|=EscapeState;
2961 state|=ExitState;
2962 break;
2963 }
2964 default:
2965 break;
2966 }
2967 continue;
2968 }
2969 switch (event.type)
2970 {
2971 case ButtonPress:
2972 {
2973 if (event.xbutton.button != Button1)
2974 break;
2975 if (event.xbutton.window != windows->image.id)
2976 break;
2977 /*
2978 User has committed to start point of chopping line.
2979 */
2980 segment_info.x1=(short int) event.xbutton.x;
2981 segment_info.x2=(short int) event.xbutton.x;
2982 segment_info.y1=(short int) event.xbutton.y;
2983 segment_info.y2=(short int) event.xbutton.y;
2984 state|=ExitState;
2985 break;
2986 }
2987 case ButtonRelease:
2988 break;
2989 case Expose:
2990 break;
2991 case KeyPress:
2992 {
2993 char
2994 command[MaxTextExtent];
2995
2996 KeySym
2997 key_symbol;
2998
2999 if (event.xkey.window != windows->image.id)
3000 break;
3001 /*
3002 Respond to a user key press.
3003 */
3004 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3005 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3006 switch ((int) key_symbol)
3007 {
3008 case XK_Escape:
3009 case XK_F20:
3010 {
3011 /*
3012 Prematurely exit.
3013 */
3014 state|=EscapeState;
3015 state|=ExitState;
3016 break;
3017 }
3018 case XK_F1:
3019 case XK_Help:
3020 {
3021 (void) XSetFunction(display,windows->image.highlight_context,
3022 GXcopy);
3023 XTextViewWidget(display,resource_info,windows,MagickFalse,
3024 "Help Viewer - Image Chop",ImageChopHelp);
3025 (void) XSetFunction(display,windows->image.highlight_context,
3026 GXinvert);
3027 break;
3028 }
3029 default:
3030 {
3031 (void) XBell(display,0);
3032 break;
3033 }
3034 }
3035 break;
3036 }
3037 case MotionNotify:
3038 {
3039 /*
3040 Map and unmap Info widget as text cursor crosses its boundaries.
3041 */
3042 x=event.xmotion.x;
3043 y=event.xmotion.y;
3044 if (windows->info.mapped != MagickFalse)
3045 {
3046 if ((x < (int) (windows->info.x+windows->info.width)) &&
3047 (y < (int) (windows->info.y+windows->info.height)))
3048 (void) XWithdrawWindow(display,windows->info.id,
3049 windows->info.screen);
3050 }
3051 else
3052 if ((x > (int) (windows->info.x+windows->info.width)) ||
3053 (y > (int) (windows->info.y+windows->info.height)))
3054 (void) XMapWindow(display,windows->info.id);
3055 }
3056 }
3057 } while ((state & ExitState) == 0);
3058 (void) XSelectInput(display,windows->image.id,
3059 windows->image.attributes.event_mask);
3060 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3061 if ((state & EscapeState) != 0)
3062 return(MagickTrue);
3063 /*
3064 Draw line as pointer moves until the mouse button is released.
3065 */
3066 chop_info.width=0;
3067 chop_info.height=0;
3068 chop_info.x=0;
3069 chop_info.y=0;
3070 distance=0;
3071 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3072 state=DefaultState;
3073 do
3074 {
3075 if (distance > 9)
3076 {
3077 /*
3078 Display info and draw chopping line.
3079 */
3080 if (windows->info.mapped == MagickFalse)
3081 (void) XMapWindow(display,windows->info.id);
3082 (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
3083 chop_info.width,chop_info.height,chop_info.x,chop_info.y);
3084 XInfoWidget(display,windows,text);
3085 XHighlightLine(display,windows->image.id,
3086 windows->image.highlight_context,&segment_info);
3087 }
3088 else
3089 if (windows->info.mapped != MagickFalse)
3090 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3091 /*
3092 Wait for next event.
3093 */
3094 XScreenEvent(display,windows,&event);
3095 if (distance > 9)
3096 XHighlightLine(display,windows->image.id,
3097 windows->image.highlight_context,&segment_info);
3098 switch (event.type)
3099 {
3100 case ButtonPress:
3101 {
3102 segment_info.x2=(short int) event.xmotion.x;
3103 segment_info.y2=(short int) event.xmotion.y;
3104 break;
3105 }
3106 case ButtonRelease:
3107 {
3108 /*
3109 User has committed to chopping line.
3110 */
3111 segment_info.x2=(short int) event.xbutton.x;
3112 segment_info.y2=(short int) event.xbutton.y;
3113 state|=ExitState;
3114 break;
3115 }
3116 case Expose:
3117 break;
3118 case MotionNotify:
3119 {
3120 segment_info.x2=(short int) event.xmotion.x;
3121 segment_info.y2=(short int) event.xmotion.y;
3122 }
3123 default:
3124 break;
3125 }
3126 /*
3127 Check boundary conditions.
3128 */
3129 if (segment_info.x2 < 0)
3130 segment_info.x2=0;
3131 else
3132 if (segment_info.x2 > windows->image.ximage->width)
3133 segment_info.x2=windows->image.ximage->width;
3134 if (segment_info.y2 < 0)
3135 segment_info.y2=0;
3136 else
3137 if (segment_info.y2 > windows->image.ximage->height)
3138 segment_info.y2=windows->image.ximage->height;
3139 distance=(unsigned int)
3140 (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3141 ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3142 /*
3143 Compute chopping geometry.
3144 */
3145 if (direction == HorizontalChopCommand)
3146 {
3147 chop_info.width=(unsigned long) (segment_info.x2-segment_info.x1+1);
3148 chop_info.x=windows->image.x+segment_info.x1;
3149 chop_info.height=0;
3150 chop_info.y=0;
3151 if (segment_info.x1 > (int) segment_info.x2)
3152 {
3153 chop_info.width=(unsigned long) (segment_info.x1-segment_info.x2+1);
3154 chop_info.x=windows->image.x+segment_info.x2;
3155 }
3156 }
3157 else
3158 {
3159 chop_info.width=0;
3160 chop_info.height=(unsigned long) (segment_info.y2-segment_info.y1+1);
3161 chop_info.x=0;
3162 chop_info.y=windows->image.y+segment_info.y1;
3163 if (segment_info.y1 > segment_info.y2)
3164 {
3165 chop_info.height=(unsigned long)
3166 (segment_info.y1-segment_info.y2+1);
3167 chop_info.y=windows->image.y+segment_info.y2;
3168 }
3169 }
3170 } while ((state & ExitState) == 0);
3171 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3172 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3173 if (distance <= 9)
3174 return(MagickTrue);
3175 /*
3176 Image chopping is relative to image configuration.
3177 */
3178 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
3179 XSetCursorState(display,windows,MagickTrue);
3180 XCheckRefreshWindows(display,windows);
3181 windows->image.window_changes.width=windows->image.ximage->width-
3182 (unsigned int) chop_info.width;
3183 windows->image.window_changes.height=windows->image.ximage->height-
3184 (unsigned int) chop_info.height;
3185 width=(unsigned int) (*image)->columns;
3186 height=(unsigned int) (*image)->rows;
3187 x=0;
3188 y=0;
3189 if (windows->image.crop_geometry != (char *) NULL)
3190 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3191 scale_factor=(MagickRealType) width/windows->image.ximage->width;
3192 chop_info.x+=x;
3193 chop_info.x=(int) (scale_factor*chop_info.x+0.5);
3194 chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3195 scale_factor=(MagickRealType) height/windows->image.ximage->height;
3196 chop_info.y+=y;
3197 chop_info.y=(int) (scale_factor*chop_info.y+0.5);
3198 chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3199 /*
3200 Chop image.
3201 */
3202 chop_image=ChopImage(*image,&chop_info,&(*image)->exception);
3203 XSetCursorState(display,windows,MagickFalse);
3204 if (chop_image == (Image *) NULL)
3205 return(MagickFalse);
3206 *image=DestroyImage(*image);
3207 *image=chop_image;
3208 /*
3209 Update image configuration.
3210 */
3211 XConfigureImageColormap(display,resource_info,windows,*image);
3212 (void) XConfigureImage(display,resource_info,windows,*image);
3213 return(MagickTrue);
3214}
3215
3216/*
3217%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3218% %
3219% %
3220% %
3221+ X C o l o r E d i t I m a g e %
3222% %
3223% %
3224% %
3225%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3226%
3227% XColorEditImage() allows the user to interactively change the color of one
3228% pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3229%
3230% The format of the XColorEditImage method is:
3231%
3232% MagickBooleanType XColorEditImage(Display *display,
3233% XResourceInfo *resource_info,XWindows *windows,Image **image)
3234%
3235% A description of each parameter follows:
3236%
3237% o display: Specifies a connection to an X server; returned from
3238% XOpenDisplay.
3239%
3240% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3241%
3242% o windows: Specifies a pointer to a XWindows structure.
3243%
3244% o image: the image; returned from ReadImage.
3245%
3246*/
3247
3248
3249static MagickBooleanType XColorEditImage(Display *display,
3250 XResourceInfo *resource_info,XWindows *windows,Image **image)
3251{
3252 static const char
3253 *ColorEditMenu[] =
3254 {
3255 "Method",
3256 "Pixel Color",
3257 "Border Color",
3258 "Fuzz",
3259 "Undo",
3260 "Help",
3261 "Dismiss",
3262 (char *) NULL
3263 };
3264
3265 static const ModeType
3266 ColorEditCommands[] =
3267 {
3268 ColorEditMethodCommand,
3269 ColorEditColorCommand,
3270 ColorEditBorderCommand,
3271 ColorEditFuzzCommand,
3272 ColorEditUndoCommand,
3273 ColorEditHelpCommand,
3274 ColorEditDismissCommand
3275 };
3276
3277 static PaintMethod
3278 method = PointMethod;
3279
3280 static unsigned int
3281 pen_id = 0;
3282
3283 static XColor
3284 border_color = { 0, 0, 0, 0, 0, 0 };
3285
3286 char
3287 command[MaxTextExtent],
3288 text[MaxTextExtent];
3289
3290 Cursor
3291 cursor;
3292
3293 ExceptionInfo
3294 *exception;
3295
3296 int
3297 entry,
3298 id,
3299 x,
3300 x_offset,
3301 y,
3302 y_offset;
3303
3304 register PixelPacket
3305 *q;
3306
3307 register long
3308 i;
3309
3310 unsigned int
3311 height,
3312 width;
3313
3314 unsigned long
3315 state;
3316
3317 XColor
3318 color;
3319
3320 XEvent
3321 event;
3322
3323 /*
3324 Map Command widget.
3325 */
3326 (void) CloneString(&windows->command.name,"Color Edit");
3327 windows->command.data=4;
3328 (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3329 (void) XMapRaised(display,windows->command.id);
3330 XClientMessage(display,windows->image.id,windows->im_protocols,
3331 windows->im_update_widget,CurrentTime);
3332 /*
3333 Make cursor.
3334 */
3335 cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3336 resource_info->background_color,resource_info->foreground_color);
3337 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3338 /*
3339 Track pointer until button 1 is pressed.
3340 */
3341 XQueryPosition(display,windows->image.id,&x,&y);
3342 (void) XSelectInput(display,windows->image.id,
3343 windows->image.attributes.event_mask | PointerMotionMask);
3344 state=DefaultState;
3345 do
3346 {
3347 if (windows->info.mapped != MagickFalse)
3348 {
3349 /*
3350 Display pointer position.
3351 */
3352 (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
3353 x+windows->image.x,y+windows->image.y);
3354 XInfoWidget(display,windows,text);
3355 }
3356 /*
3357 Wait for next event.
3358 */
3359 XScreenEvent(display,windows,&event);
3360 if (event.xany.window == windows->command.id)
3361 {
3362 /*
3363 Select a command from the Command widget.
3364 */
3365 id=XCommandWidget(display,windows,ColorEditMenu,&event);
3366 if (id < 0)
3367 {
3368 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3369 continue;
3370 }
3371 switch (ColorEditCommands[id])
3372 {
3373 case ColorEditMethodCommand:
3374 {
3375 char
3376 **methods;
3377
3378 /*
3379 Select a method from the pop-up menu.
3380 */
3381 methods=(char **) GetMagickOptions(MagickMethodOptions);
3382 if (methods == (char **) NULL)
3383 break;
3384 entry=XMenuWidget(display,windows,ColorEditMenu[id],
3385 (const char **) methods,command);
3386 if (entry >= 0)
3387 method=(PaintMethod) ParseMagickOption(MagickMethodOptions,
3388 MagickFalse,methods[entry]);
3389 methods=DestroyStringList(methods);
3390 break;
3391 }
3392 case ColorEditColorCommand:
3393 {
3394 const char
3395 *ColorMenu[MaxNumberPens];
3396
3397 int
3398 pen_number;
3399
3400 /*
3401 Initialize menu selections.
3402 */
3403 for (i=0; i < (int) (MaxNumberPens-2); i++)
3404 ColorMenu[i]=resource_info->pen_colors[i];
3405 ColorMenu[MaxNumberPens-2]="Browser...";
3406 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3407 /*
3408 Select a pen color from the pop-up menu.
3409 */
3410 pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3411 (const char **) ColorMenu,command);
3412 if (pen_number < 0)
3413 break;
3414 if (pen_number == (MaxNumberPens-2))
3415 {
3416 static char
3417 color_name[MaxTextExtent] = "gray";
3418
3419 /*
3420 Select a pen color from a dialog.
3421 */
3422 resource_info->pen_colors[pen_number]=color_name;
3423 XColorBrowserWidget(display,windows,"Select",color_name);
3424 if (*color_name == '\0')
3425 break;
3426 }
3427 /*
3428 Set pen color.
3429 */
3430 (void) XParseColor(display,windows->map_info->colormap,
3431 resource_info->pen_colors[pen_number],&color);
3432 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3433 (unsigned int) MaxColors,&color);
3434 windows->pixel_info->pen_colors[pen_number]=color;
3435 pen_id=(unsigned int) pen_number;
3436 break;
3437 }
3438 case ColorEditBorderCommand:
3439 {
3440 const char
3441 *ColorMenu[MaxNumberPens];
3442
3443 int
3444 pen_number;
3445
3446 /*
3447 Initialize menu selections.
3448 */
3449 for (i=0; i < (int) (MaxNumberPens-2); i++)
3450 ColorMenu[i]=resource_info->pen_colors[i];
3451 ColorMenu[MaxNumberPens-2]="Browser...";
3452 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3453 /*
3454 Select a pen color from the pop-up menu.
3455 */
3456 pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3457 (const char **) ColorMenu,command);
3458 if (pen_number < 0)
3459 break;
3460 if (pen_number == (MaxNumberPens-2))
3461 {
3462 static char
3463 color_name[MaxTextExtent] = "gray";
3464
3465 /*
3466 Select a pen color from a dialog.
3467 */
3468 resource_info->pen_colors[pen_number]=color_name;
3469 XColorBrowserWidget(display,windows,"Select",color_name);
3470 if (*color_name == '\0')
3471 break;
3472 }
3473 /*
3474 Set border color.
3475 */
3476 (void) XParseColor(display,windows->map_info->colormap,
3477 resource_info->pen_colors[pen_number],&border_color);
3478 break;
3479 }
3480 case ColorEditFuzzCommand:
3481 {
3482 static char
3483 fuzz[MaxTextExtent];
3484
3485 static const char
3486 *FuzzMenu[] =
3487 {
3488 "0%",
3489 "2%",
3490 "5%",
3491 "10%",
3492 "15%",
3493 "Dialog...",
3494 (char *) NULL,
3495 };
3496
3497 /*
3498 Select a command from the pop-up menu.
3499 */
3500 entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3501 command);
3502 if (entry < 0)
3503 break;
3504 if (entry != 5)
3505 {
3506 (*image)->fuzz=StringToDouble(FuzzMenu[entry],1.0*QuantumRange+
3507 1.0);
3508 break;
3509 }
3510 (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3511 (void) XDialogWidget(display,windows,"Ok",
3512 "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3513 if (*fuzz == '\0')
3514 break;
3515 (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3516 (*image)->fuzz=StringToDouble(fuzz,1.0*QuantumRange+1.0);
3517 break;
3518 }
3519 case ColorEditUndoCommand:
3520 {
3521 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3522 image);
3523 break;
3524 }
3525 case ColorEditHelpCommand:
3526 default:
3527 {
3528 XTextViewWidget(display,resource_info,windows,MagickFalse,
3529 "Help Viewer - Image Annotation",ImageColorEditHelp);
3530 break;
3531 }
3532 case ColorEditDismissCommand:
3533 {
3534 /*
3535 Prematurely exit.
3536 */
3537 state|=EscapeState;
3538 state|=ExitState;
3539 break;
3540 }
3541 }
3542 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3543 continue;
3544 }
3545 switch (event.type)
3546 {
3547 case ButtonPress:
3548 {
3549 if (event.xbutton.button != Button1)
3550 break;
3551 if ((event.xbutton.window != windows->image.id) &&
3552 (event.xbutton.window != windows->magnify.id))
3553 break;
3554 /*
3555 exit loop.
3556 */
3557 x=event.xbutton.x;
3558 y=event.xbutton.y;
3559 (void) XMagickCommand(display,resource_info,windows,
3560 SaveToUndoBufferCommand,image);
3561 state|=UpdateConfigurationState;
3562 break;
3563 }
3564 case ButtonRelease:
3565 {
3566 if (event.xbutton.button != Button1)
3567 break;
3568 if ((event.xbutton.window != windows->image.id) &&
3569 (event.xbutton.window != windows->magnify.id))
3570 break;
3571 /*
3572 Update colormap information.
3573 */
3574 x=event.xbutton.x;
3575 y=event.xbutton.y;
3576 XConfigureImageColormap(display,resource_info,windows,*image);
3577 (void) XConfigureImage(display,resource_info,windows,*image);
3578 XInfoWidget(display,windows,text);
3579 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3580 state&=(~UpdateConfigurationState);
3581 break;
3582 }
3583 case Expose:
3584 break;
3585 case KeyPress:
3586 {
3587 KeySym
3588 key_symbol;
3589
3590 if (event.xkey.window == windows->magnify.id)
3591 {
3592 Window
3593 window;
3594
3595 window=windows->magnify.id;
3596 while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3597 }
3598 if (event.xkey.window != windows->image.id)
3599 break;
3600 /*
3601 Respond to a user key press.
3602 */
3603 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3604 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3605 switch ((int) key_symbol)
3606 {
3607 case XK_Escape:
3608 case XK_F20:
3609 {
3610 /*
3611 Prematurely exit.
3612 */
3613 state|=ExitState;
3614 break;
3615 }
3616 case XK_F1:
3617 case XK_Help:
3618 {
3619 XTextViewWidget(display,resource_info,windows,MagickFalse,
3620 "Help Viewer - Image Annotation",ImageColorEditHelp);
3621 break;
3622 }
3623 default:
3624 {
3625 (void) XBell(display,0);
3626 break;
3627 }
3628 }
3629 break;
3630 }
3631 case MotionNotify:
3632 {
3633 /*
3634 Map and unmap Info widget as cursor crosses its boundaries.
3635 */
3636 x=event.xmotion.x;
3637 y=event.xmotion.y;
3638 if (windows->info.mapped != MagickFalse)
3639 {
3640 if ((x < (int) (windows->info.x+windows->info.width)) &&
3641 (y < (int) (windows->info.y+windows->info.height)))
3642 (void) XWithdrawWindow(display,windows->info.id,
3643 windows->info.screen);
3644 }
3645 else
3646 if ((x > (int) (windows->info.x+windows->info.width)) ||
3647 (y > (int) (windows->info.y+windows->info.height)))
3648 (void) XMapWindow(display,windows->info.id);
3649 break;
3650 }
3651 default:
3652 break;
3653 }
3654 if (event.xany.window == windows->magnify.id)
3655 {
3656 x=windows->magnify.x-windows->image.x;
3657 y=windows->magnify.y-windows->image.y;
3658 }
3659 x_offset=x;
3660 y_offset=y;
3661 if ((state & UpdateConfigurationState) != 0)
3662 {
3663 int
3664 x,
3665 y;
3666
3667 /*
3668 Pixel edit is relative to image configuration.
3669 */
3670 (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3671 MagickTrue);
3672 color=windows->pixel_info->pen_colors[pen_id];
3673 XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3674 width=(unsigned int) (*image)->columns;
3675 height=(unsigned int) (*image)->rows;
3676 x=0;
3677 y=0;
3678 if (windows->image.crop_geometry != (char *) NULL)
3679 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3680 &width,&height);
3681 x_offset=(int)
3682 (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3683 y_offset=(int)
3684 (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3685 if ((x_offset < 0) || (y_offset < 0))
3686 continue;
3687 if ((x_offset >= (long) (*image)->columns) ||
3688 (y_offset >= (long) (*image)->rows))
3689 continue;
3690 exception=(&(*image)->exception);
3691 switch (method)
3692 {
3693 case PointMethod:
3694 default:
3695 {
3696 /*
3697 Update color information using point algorithm.
3698 */
3699 if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3700 return(MagickFalse);
3701 q=GetAuthenticPixels(*image,x_offset,y_offset,1,1,exception);
3702 if (q == (PixelPacket *) NULL)
3703 break;
3704 q->red=ScaleShortToQuantum(color.red);
3705 q->green=ScaleShortToQuantum(color.green);
3706 q->blue=ScaleShortToQuantum(color.blue);
3707 (void) SyncAuthenticPixels(*image,&(*image)->exception);
3708 break;
3709 }
3710 case ReplaceMethod:
3711 {
3712 PixelPacket
3713 target;
3714
3715 /*
3716 Update color information using replace algorithm.
3717 */
3718 (void) GetOneVirtualPixel(*image,x_offset,y_offset,&target,
3719 &(*image)->exception);
3720 if ((*image)->storage_class == DirectClass)
3721 {
3722 for (y=0; y < (long) (*image)->rows; y++)
3723 {
3724 q=GetAuthenticPixels(*image,0,y,(*image)->columns,1,exception);
3725 if (q == (PixelPacket *) NULL)
3726 break;
3727 for (x=0; x < (int) (*image)->columns; x++)
3728 {
3729 if (IsColorSimilar(*image,q,&target))
3730 {
3731 q->red=ScaleShortToQuantum(color.red);
3732 q->green=ScaleShortToQuantum(color.green);
3733 q->blue=ScaleShortToQuantum(color.blue);
3734 }
3735 q++;
3736 }
3737 if (SyncAuthenticPixels(*image,exception) == MagickFalse)
3738 break;
3739 }
3740 }
3741 else
3742 {
3743 for (i=0; i < (int) (*image)->colors; i++)
3744 if (IsColorSimilar(*image,(*image)->colormap+i,&target))
3745 {
3746 (*image)->colormap[i].red=ScaleShortToQuantum(color.red);
3747 (*image)->colormap[i].green=ScaleShortToQuantum(
3748 color.green);
3749 (*image)->colormap[i].blue=ScaleShortToQuantum(
3750 color.blue);
3751 }
3752 (void) SyncImage(*image);
3753 }
3754 break;
3755 }
3756 case FloodfillMethod:
3757 case FillToBorderMethod:
3758 {
3759 DrawInfo
3760 *draw_info;
3761
3762 MagickPixelPacket
3763 target;
3764
3765 /*
3766 Update color information using floodfill algorithm.
3767 */
3768 (void) GetOneVirtualMagickPixel(*image,x_offset,y_offset,&target,
3769 exception);
3770 if (method == FillToBorderMethod)
3771 {
3772 target.red=(MagickRealType)
3773 ScaleShortToQuantum(border_color.red);
3774 target.green=(MagickRealType)
3775 ScaleShortToQuantum(border_color.green);
3776 target.blue=(MagickRealType)
3777 ScaleShortToQuantum(border_color.blue);
3778 }
3779 draw_info=CloneDrawInfo(resource_info->image_info,
3780 (DrawInfo *) NULL);
3781 (void) QueryColorDatabase(resource_info->pen_colors[pen_id],
3782 &draw_info->fill,exception);
3783 (void) FloodfillPaintImage(*image,DefaultChannels,draw_info,&target,
3784 x_offset,y_offset,method == FloodfillMethod ? MagickFalse :
3785 MagickTrue);
3786 draw_info=DestroyDrawInfo(draw_info);
3787 break;
3788 }
3789 case ResetMethod:
3790 {
3791 /*
3792 Update color information using reset algorithm.
3793 */
3794 if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3795 return(MagickFalse);
3796 for (y=0; y < (long) (*image)->rows; y++)
3797 {
3798 q=QueueAuthenticPixels(*image,0,y,(*image)->columns,1,exception);
3799 if (q == (PixelPacket *) NULL)
3800 break;
3801 for (x=0; x < (int) (*image)->columns; x++)
3802 {
3803 q->red=ScaleShortToQuantum(color.red);
3804 q->green=ScaleShortToQuantum(color.green);
3805 q->blue=ScaleShortToQuantum(color.blue);
3806 q++;
3807 }
3808 if (SyncAuthenticPixels(*image,exception) == MagickFalse)
3809 break;
3810 }
3811 break;
3812 }
3813 }
3814 state&=(~UpdateConfigurationState);
3815 }
3816 } while ((state & ExitState) == 0);
3817 (void) XSelectInput(display,windows->image.id,
3818 windows->image.attributes.event_mask);
3819 XSetCursorState(display,windows,MagickFalse);
3820 (void) XFreeCursor(display,cursor);
3821 return(MagickTrue);
3822}
3823
3824/*
3825%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3826% %
3827% %
3828% %
3829+ X C o m p o s i t e I m a g e %
3830% %
3831% %
3832% %
3833%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3834%
3835% XCompositeImage() requests an image name from the user, reads the image and
3836% composites it with the X window image at a location the user chooses with
3837% the pointer.
3838%
3839% The format of the XCompositeImage method is:
3840%
3841% MagickBooleanType XCompositeImage(Display *display,
3842% XResourceInfo *resource_info,XWindows *windows,Image *image)
3843%
3844% A description of each parameter follows:
3845%
3846% o display: Specifies a connection to an X server; returned from
3847% XOpenDisplay.
3848%
3849% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3850%
3851% o windows: Specifies a pointer to a XWindows structure.
3852%
3853% o image: the image; returned from ReadImage.
3854%
3855*/
3856static MagickBooleanType XCompositeImage(Display *display,
3857 XResourceInfo *resource_info,XWindows *windows,Image *image)
3858{
3859 static char
3860 displacement_geometry[MaxTextExtent] = "30x30",
3861 filename[MaxTextExtent] = "\0";
3862
3863 static const char
3864 *CompositeMenu[] =
3865 {
3866 "Operators",
3867 "Dissolve",
3868 "Displace",
3869 "Help",
3870 "Dismiss",
3871 (char *) NULL
3872 };
3873
3874 static CompositeOperator
3875 compose = CopyCompositeOp;
3876
3877 static const ModeType
3878 CompositeCommands[] =
3879 {
3880 CompositeOperatorsCommand,
3881 CompositeDissolveCommand,
3882 CompositeDisplaceCommand,
3883 CompositeHelpCommand,
3884 CompositeDismissCommand
3885 };
3886
3887 char
3888 text[MaxTextExtent];
3889
3890 Cursor
3891 cursor;
3892
3893 Image
3894 *composite_image;
3895
3896 int
3897 entry,
3898 id,
3899 x,
3900 y;
3901
3902 MagickRealType
3903 blend,
3904 scale_factor;
3905
3906 RectangleInfo
3907 highlight_info,
3908 composite_info;
3909
3910 unsigned int
3911 height,
3912 width;
3913
3914 unsigned long
3915 state;
3916
3917 XEvent
3918 event;
3919
3920 /*
3921 Request image file name from user.
3922 */
3923 XFileBrowserWidget(display,windows,"Composite",filename);
3924 if (*filename == '\0')
3925 return(MagickTrue);
3926 /*
3927 Read image.
3928 */
3929 XSetCursorState(display,windows,MagickTrue);
3930 XCheckRefreshWindows(display,windows);
3931 (void) CopyMagickString(resource_info->image_info->filename,filename,
3932 MaxTextExtent);
3933 composite_image=ReadImage(resource_info->image_info,&image->exception);
3934 CatchException(&image->exception);
3935 XSetCursorState(display,windows,MagickFalse);
3936 if (composite_image == (Image *) NULL)
3937 return(MagickFalse);
3938 /*
3939 Map Command widget.
3940 */
3941 (void) CloneString(&windows->command.name,"Composite");
3942 windows->command.data=1;
3943 (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3944 (void) XMapRaised(display,windows->command.id);
3945 XClientMessage(display,windows->image.id,windows->im_protocols,
3946 windows->im_update_widget,CurrentTime);
3947 /*
3948 Track pointer until button 1 is pressed.
3949 */
3950 XQueryPosition(display,windows->image.id,&x,&y);
3951 (void) XSelectInput(display,windows->image.id,
3952 windows->image.attributes.event_mask | PointerMotionMask);
3953 composite_info.x=windows->image.x+x;
3954 composite_info.y=windows->image.y+y;
3955 composite_info.width=0;
3956 composite_info.height=0;
3957 cursor=XCreateFontCursor(display,XC_ul_angle);
3958 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3959 blend=0.0;
3960 state=DefaultState;
3961 do
3962 {
3963 if (windows->info.mapped != MagickFalse)
3964 {
3965 /*
3966 Display pointer position.
3967 */
3968 (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
3969 composite_info.x,composite_info.y);
3970 XInfoWidget(display,windows,text);
3971 }
3972 highlight_info=composite_info;
3973 highlight_info.x=composite_info.x-windows->image.x;
3974 highlight_info.y=composite_info.y-windows->image.y;
3975 XHighlightRectangle(display,windows->image.id,
3976 windows->image.highlight_context,&highlight_info);
3977 /*
3978 Wait for next event.
3979 */
3980 XScreenEvent(display,windows,&event);
3981 XHighlightRectangle(display,windows->image.id,
3982 windows->image.highlight_context,&highlight_info);
3983 if (event.xany.window == windows->command.id)
3984 {
3985 /*
3986 Select a command from the Command widget.
3987 */
3988 id=XCommandWidget(display,windows,CompositeMenu,&event);
3989 if (id < 0)
3990 continue;
3991 switch (CompositeCommands[id])
3992 {
3993 case CompositeOperatorsCommand:
3994 {
3995 char
3996 command[MaxTextExtent],
3997 **operators;
3998
3999 /*
4000 Select a command from the pop-up menu.
4001 */
4002 operators=GetMagickOptions(MagickComposeOptions);
4003 if (operators == (char **) NULL)
4004 break;
4005 entry=XMenuWidget(display,windows,CompositeMenu[id],
4006 (const char **) operators,command);
4007 if (entry >= 0)
4008 compose=(CompositeOperator) ParseMagickOption(
4009 MagickComposeOptions,MagickFalse,operators[entry]);
4010 operators=DestroyStringList(operators);
4011 break;
4012 }
4013 case CompositeDissolveCommand:
4014 {
4015 static char
4016 factor[MaxTextExtent] = "20.0";
4017
4018 /*
4019 Dissolve the two images a given percent.
4020 */
4021 (void) XSetFunction(display,windows->image.highlight_context,
4022 GXcopy);
4023 (void) XDialogWidget(display,windows,"Dissolve",
4024 "Enter the blend factor (0.0 - 99.9%):",factor);
4025 (void) XSetFunction(display,windows->image.highlight_context,
4026 GXinvert);
4027 if (*factor == '\0')
4028 break;
4029 blend=atof(factor);
4030 compose=DissolveCompositeOp;
4031 break;
4032 }
4033 case CompositeDisplaceCommand:
4034 {
4035 /*
4036 Get horizontal and vertical scale displacement geometry.
4037 */
4038 (void) XSetFunction(display,windows->image.highlight_context,
4039 GXcopy);
4040 (void) XDialogWidget(display,windows,"Displace",
4041 "Enter the horizontal and vertical scale:",displacement_geometry);
4042 (void) XSetFunction(display,windows->image.highlight_context,
4043 GXinvert);
4044 if (*displacement_geometry == '\0')
4045 break;
4046 compose=DisplaceCompositeOp;
4047 break;
4048 }
4049 case CompositeHelpCommand:
4050 {
4051 (void) XSetFunction(display,windows->image.highlight_context,
4052 GXcopy);
4053 XTextViewWidget(display,resource_info,windows,MagickFalse,
4054 "Help Viewer - Image Composite",ImageCompositeHelp);
4055 (void) XSetFunction(display,windows->image.highlight_context,
4056 GXinvert);
4057 break;
4058 }
4059 case CompositeDismissCommand:
4060 {
4061 /*
4062 Prematurely exit.
4063 */
4064 state|=EscapeState;
4065 state|=ExitState;
4066 break;
4067 }
4068 default:
4069 break;
4070 }
4071 continue;
4072 }
4073 switch (event.type)
4074 {
4075 case ButtonPress:
4076 {
4077 if (image->debug != MagickFalse)
4078 (void) LogMagickEvent(X11Event,GetMagickModule(),
4079 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4080 event.xbutton.button,event.xbutton.x,event.xbutton.y);
4081 if (event.xbutton.button != Button1)
4082 break;
4083 if (event.xbutton.window != windows->image.id)
4084 break;
4085 /*
4086 Change cursor.
4087 */
4088 composite_info.width=composite_image->columns;
4089 composite_info.height=composite_image->rows;
4090 (void) XCheckDefineCursor(display,windows->image.id,cursor);
4091 composite_info.x=windows->image.x+event.xbutton.x;
4092 composite_info.y=windows->image.y+event.xbutton.y;
4093 break;
4094 }
4095 case ButtonRelease:
4096 {
4097 if (image->debug != MagickFalse)
4098 (void) LogMagickEvent(X11Event,GetMagickModule(),
4099 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4100 event.xbutton.button,event.xbutton.x,event.xbutton.y);
4101 if (event.xbutton.button != Button1)
4102 break;
4103 if (event.xbutton.window != windows->image.id)
4104 break;
4105 if ((composite_info.width != 0) && (composite_info.height != 0))
4106 {
4107 /*
4108 User has selected the location of the composite image.
4109 */
4110 composite_info.x=windows->image.x+event.xbutton.x;
4111 composite_info.y=windows->image.y+event.xbutton.y;
4112 state|=ExitState;
4113 }
4114 break;
4115 }
4116 case Expose:
4117 break;
4118 case KeyPress:
4119 {
4120 char
4121 command[MaxTextExtent];
4122
4123 KeySym
4124 key_symbol;
4125
4126 int
4127 length;
4128
4129 if (event.xkey.window != windows->image.id)
4130 break;
4131 /*
4132 Respond to a user key press.
4133 */
4134 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4135 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4136 *(command+length)='\0';
4137 if (image->debug != MagickFalse)
4138 (void) LogMagickEvent(X11Event,GetMagickModule(),
4139 "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4140 switch ((int) key_symbol)
4141 {
4142 case XK_Escape:
4143 case XK_F20:
4144 {
4145 /*
4146 Prematurely exit.
4147 */
4148 composite_image=DestroyImage(composite_image);
4149 state|=EscapeState;
4150 state|=ExitState;
4151 break;
4152 }
4153 case XK_F1:
4154 case XK_Help:
4155 {
4156 (void) XSetFunction(display,windows->image.highlight_context,
4157 GXcopy);
4158 XTextViewWidget(display,resource_info,windows,MagickFalse,
4159 "Help Viewer - Image Composite",ImageCompositeHelp);
4160 (void) XSetFunction(display,windows->image.highlight_context,
4161 GXinvert);
4162 break;
4163 }
4164 default:
4165 {
4166 (void) XBell(display,0);
4167 break;
4168 }
4169 }
4170 break;
4171 }
4172 case MotionNotify:
4173 {
4174 /*
4175 Map and unmap Info widget as text cursor crosses its boundaries.
4176 */
4177 x=event.xmotion.x;
4178 y=event.xmotion.y;
4179 if (windows->info.mapped != MagickFalse)
4180 {
4181 if ((x < (int) (windows->info.x+windows->info.width)) &&
4182 (y < (int) (windows->info.y+windows->info.height)))
4183 (void) XWithdrawWindow(display,windows->info.id,
4184 windows->info.screen);
4185 }
4186 else
4187 if ((x > (int) (windows->info.x+windows->info.width)) ||
4188 (y > (int) (windows->info.y+windows->info.height)))
4189 (void) XMapWindow(display,windows->info.id);
4190 composite_info.x=windows->image.x+x;
4191 composite_info.y=windows->image.y+y;
4192 break;
4193 }
4194 default:
4195 {
4196 if (image->debug != MagickFalse)
4197 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4198 event.type);
4199 break;
4200 }
4201 }
4202 } while ((state & ExitState) == 0);
4203 (void) XSelectInput(display,windows->image.id,
4204 windows->image.attributes.event_mask);
4205 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4206 XSetCursorState(display,windows,MagickFalse);
4207 (void) XFreeCursor(display,cursor);
4208 if ((state & EscapeState) != 0)
4209 return(MagickTrue);
4210 /*
4211 Image compositing is relative to image configuration.
4212 */
4213 XSetCursorState(display,windows,MagickTrue);
4214 XCheckRefreshWindows(display,windows);
4215 width=(unsigned int) image->columns;
4216 height=(unsigned int) image->rows;
4217 x=0;
4218 y=0;
4219 if (windows->image.crop_geometry != (char *) NULL)
4220 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4221 scale_factor=(MagickRealType) width/windows->image.ximage->width;
4222 composite_info.x+=x;
4223 composite_info.x=(int) (scale_factor*composite_info.x+0.5);
4224 composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4225 scale_factor=(MagickRealType) height/windows->image.ximage->height;
4226 composite_info.y+=y;
4227 composite_info.y=(int) (scale_factor*composite_info.y+0.5);
4228 composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4229 if ((composite_info.width != composite_image->columns) ||
4230 (composite_info.height != composite_image->rows))
4231 {
4232 Image
4233 *resize_image;
4234
4235 /*
4236 Scale composite image.
4237 */
4238 resize_image=ZoomImage(composite_image,composite_info.width,
4239 composite_info.height,&image->exception);
4240 composite_image=DestroyImage(composite_image);
4241 if (resize_image == (Image *) NULL)
4242 {
4243 XSetCursorState(display,windows,MagickFalse);
4244 return(MagickFalse);
4245 }
4246 composite_image=resize_image;
4247 }
4248 if (compose == DisplaceCompositeOp)
4249 (void) SetImageArtifact(composite_image,"compose:args",
4250 displacement_geometry);
4251 if (blend != 0.0)
4252 {
4253 ExceptionInfo
4254 *exception;
4255
4256 int
4257 y;
4258
4259 Quantum
4260 opacity;
4261
4262 register int
4263 x;
4264
4265 register PixelPacket
4266 *q;
4267
4268 /*
4269 Create mattes for blending.
4270 */
4271 (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel);
4272 opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
4273 ((long) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
4274 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
4275 return(MagickFalse);
4276 image->matte=MagickTrue;
4277 exception=(&image->exception);
4278 for (y=0; y < (long) image->rows; y++)
4279 {
4280 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4281 if (q == (PixelPacket *) NULL)
4282 break;
4283 for (x=0; x < (int) image->columns; x++)
4284 {
4285 q->opacity=opacity;
4286 q++;
4287 }
4288 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4289 break;
4290 }
4291 }
4292 /*
4293 Composite image with X Image window.
4294 */
4295 (void) CompositeImage(image,compose,composite_image,composite_info.x,
4296 composite_info.y);
4297 composite_image=DestroyImage(composite_image);
4298 XSetCursorState(display,windows,MagickFalse);
4299 /*
4300 Update image configuration.
4301 */
4302 XConfigureImageColormap(display,resource_info,windows,image);
4303 (void) XConfigureImage(display,resource_info,windows,image);
4304 return(MagickTrue);
4305}
4306
4307/*
4308%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4309% %
4310% %
4311% %
4312+ X C o n f i g u r e I m a g e %
4313% %
4314% %
4315% %
4316%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4317%
4318% XConfigureImage() creates a new X image. It also notifies the window
4319% manager of the new image size and configures the transient widows.
4320%
4321% The format of the XConfigureImage method is:
4322%
4323% MagickBooleanType XConfigureImage(Display *display,
4324% XResourceInfo *resource_info,XWindows *windows,Image *image)
4325%
4326% A description of each parameter follows:
4327%
4328% o display: Specifies a connection to an X server; returned from
4329% XOpenDisplay.
4330%
4331% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4332%
4333% o windows: Specifies a pointer to a XWindows structure.
4334%
4335% o image: the image.
4336%
4337%
4338*/
4339static MagickBooleanType XConfigureImage(Display *display,
4340 XResourceInfo *resource_info,XWindows *windows,Image *image)
4341{
4342 char
4343 geometry[MaxTextExtent];
4344
4345 long
4346 x,
4347 y;
4348
4349 MagickStatusType
4350 status;
4351
4352 unsigned long
4353 mask,
4354 height,
4355 width;
4356
4357 XSizeHints
4358 *size_hints;
4359
4360 XWindowChanges
4361 window_changes;
4362
4363 /*
4364 Dismiss if window dimensions are zero.
4365 */
4366 width=(unsigned int) windows->image.window_changes.width;
4367 height=(unsigned int) windows->image.window_changes.height;
4368 if (image->debug != MagickFalse)
4369 (void) LogMagickEvent(X11Event,GetMagickModule(),
4370 "Configure Image: %dx%d=>%lux%lu",windows->image.ximage->width,
4371 windows->image.ximage->height,width,height);
4372 if ((width*height) == 0)
4373 return(MagickTrue);
4374 x=0;
4375 y=0;
4376 /*
4377 Resize image to fit Image window dimensions.
4378 */
4379 XSetCursorState(display,windows,MagickTrue);
4380 (void) XFlush(display);
4381 if (((int) width != windows->image.ximage->width) ||
4382 ((int) height != windows->image.ximage->height))
4383 image->taint=MagickTrue;
4384 windows->magnify.x=(int)
4385 width*windows->magnify.x/windows->image.ximage->width;
4386 windows->magnify.y=(int)
4387 height*windows->magnify.y/windows->image.ximage->height;
4388 windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4389 windows->image.y=(int)
4390 (height*windows->image.y/windows->image.ximage->height);
4391 status=XMakeImage(display,resource_info,&windows->image,image,
4392 (unsigned int) width,(unsigned int) height);
4393 if (status == MagickFalse)
4394 XNoticeWidget(display,windows,"Unable to configure X image:",
4395 windows->image.name);
4396 /*
4397 Notify window manager of the new configuration.
4398 */
4399 if (resource_info->image_geometry != (char *) NULL)
4400 (void) FormatMagickString(geometry,MaxTextExtent,"%s>!",
4401 resource_info->image_geometry);
4402 else
4403 (void) FormatMagickString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4404 XDisplayWidth(display,windows->image.screen),
4405 XDisplayHeight(display,windows->image.screen));
4406 (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4407 window_changes.width=(int) width;
4408 if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4409 window_changes.width=XDisplayWidth(display,windows->image.screen);
4410 window_changes.height=(int) height;
4411 if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4412 window_changes.height=XDisplayHeight(display,windows->image.screen);
4413 mask=(unsigned long) (CWWidth | CWHeight);
4414 if (resource_info->backdrop)
4415 {
4416 mask|=CWX | CWY;
4417 window_changes.x=(int)
4418 ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4419 window_changes.y=(int)
4420 ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4421 }
4422 (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4423 (unsigned int) mask,&window_changes);
4424 (void) XClearWindow(display,windows->image.id);
4425 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4426 /*
4427 Update Magnify window configuration.
4428 */
4429 if (windows->magnify.mapped != MagickFalse)
4430 XMakeMagnifyImage(display,windows);
4431 windows->pan.crop_geometry=windows->image.crop_geometry;
4432 XBestIconSize(display,&windows->pan,image);
4433 while (((windows->pan.width << 1) < MaxIconSize) &&
4434 ((windows->pan.height << 1) < MaxIconSize))
4435 {
4436 windows->pan.width<<=1;
4437 windows->pan.height<<=1;
4438 }
4439 if (windows->pan.geometry != (char *) NULL)
4440 (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4441 &windows->pan.width,&windows->pan.height);
4442 window_changes.width=(int) windows->pan.width;
4443 window_changes.height=(int) windows->pan.height;
4444 size_hints=XAllocSizeHints();
4445 if (size_hints != (XSizeHints *) NULL)
4446 {
4447 /*
4448 Set new size hints.
4449 */
4450 size_hints->flags=PSize | PMinSize | PMaxSize;
4451 size_hints->width=window_changes.width;
4452 size_hints->height=window_changes.height;
4453 size_hints->min_width=size_hints->width;
4454 size_hints->min_height=size_hints->height;
4455 size_hints->max_width=size_hints->width;
4456 size_hints->max_height=size_hints->height;
4457 (void) XSetNormalHints(display,windows->pan.id,size_hints);
4458 (void) XFree((void *) size_hints);
4459 }
4460 (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4461 (unsigned int) (CWWidth | CWHeight),&window_changes);
4462 /*
4463 Update icon window configuration.
4464 */
4465 windows->icon.crop_geometry=windows->image.crop_geometry;
4466 XBestIconSize(display,&windows->icon,image);
4467 window_changes.width=(int) windows->icon.width;
4468 window_changes.height=(int) windows->icon.height;
4469 (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4470 (unsigned int) (CWWidth | CWHeight),&window_changes);
4471 XSetCursorState(display,windows,MagickFalse);
4472 return(status != 0 ? MagickTrue : MagickFalse);
4473}
4474
4475/*
4476%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4477% %
4478% %
4479% %
4480+ X C r o p I m a g e %
4481% %
4482% %
4483% %
4484%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4485%
4486% XCropImage() allows the user to select a region of the image and crop, copy,
4487% or cut it. For copy or cut, the image can subsequently be composited onto
4488% the image with XPasteImage.
4489%
4490% The format of the XCropImage method is:
4491%
4492% MagickBooleanType XCropImage(Display *display,
4493% XResourceInfo *resource_info,XWindows *windows,Image *image,
4494% const ClipboardMode mode)
4495%
4496% A description of each parameter follows:
4497%
4498% o display: Specifies a connection to an X server; returned from
4499% XOpenDisplay.
4500%
4501% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4502%
4503% o windows: Specifies a pointer to a XWindows structure.
4504%
4505% o image: the image; returned from ReadImage.
4506%
4507% o mode: This unsigned value specified whether the image should be
4508% cropped, copied, or cut.
4509%
4510*/
4511static MagickBooleanType XCropImage(Display *display,
4512 XResourceInfo *resource_info,XWindows *windows,Image *image,
4513 const ClipboardMode mode)
4514{
4515 static const char
4516 *CropModeMenu[] =
4517 {
4518 "Help",
4519 "Dismiss",
4520 (char *) NULL
4521 },
4522 *RectifyModeMenu[] =
4523 {
4524 "Crop",
4525 "Help",
4526 "Dismiss",
4527 (char *) NULL
4528 };
4529
4530 static const ModeType
4531 CropCommands[] =
4532 {
4533 CropHelpCommand,
4534 CropDismissCommand
4535 },
4536 RectifyCommands[] =
4537 {
4538 RectifyCopyCommand,
4539 RectifyHelpCommand,
4540 RectifyDismissCommand
4541 };
4542
4543 char
4544 command[MaxTextExtent],
4545 text[MaxTextExtent];
4546
4547 Cursor
4548 cursor;
4549
4550 ExceptionInfo
4551 *exception;
4552
4553 int
4554 id,
4555 x,
4556 y;
4557
4558 KeySym
4559 key_symbol;
4560
4561 Image
4562 *crop_image;
4563
4564 MagickRealType
4565 scale_factor;
4566
4567 RectangleInfo
4568 crop_info,
4569 highlight_info;
4570
4571 register PixelPacket
4572 *q;
4573
4574 unsigned int
4575 height,
4576 width;
4577
4578 unsigned long
4579 state;
4580
4581 XEvent
4582 event;
4583
4584 /*
4585 Map Command widget.
4586 */
4587 switch (mode)
4588 {
4589 case CopyMode:
4590 {
4591 (void) CloneString(&windows->command.name,"Copy");
4592 break;
4593 }
4594 case CropMode:
4595 {
4596 (void) CloneString(&windows->command.name,"Crop");
4597 break;
4598 }
4599 case CutMode:
4600 {
4601 (void) CloneString(&windows->command.name,"Cut");
4602 break;
4603 }
4604 }
4605 RectifyModeMenu[0]=windows->command.name;
4606 windows->command.data=0;
4607 (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4608 (void) XMapRaised(display,windows->command.id);
4609 XClientMessage(display,windows->image.id,windows->im_protocols,
4610 windows->im_update_widget,CurrentTime);
4611 /*
4612 Track pointer until button 1 is pressed.
4613 */
4614 XQueryPosition(display,windows->image.id,&x,&y);
4615 (void) XSelectInput(display,windows->image.id,
4616 windows->image.attributes.event_mask | PointerMotionMask);
4617 crop_info.x=windows->image.x+x;
4618 crop_info.y=windows->image.y+y;
4619 crop_info.width=0;
4620 crop_info.height=0;
4621 cursor=XCreateFontCursor(display,XC_fleur);
4622 state=DefaultState;
4623 do
4624 {
4625 if (windows->info.mapped != MagickFalse)
4626 {
4627 /*
4628 Display pointer position.
4629 */
4630 (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
4631 crop_info.x,crop_info.y);
4632 XInfoWidget(display,windows,text);
4633 }
4634 /*
4635 Wait for next event.
4636 */
4637 XScreenEvent(display,windows,&event);
4638 if (event.xany.window == windows->command.id)
4639 {
4640 /*
4641 Select a command from the Command widget.
4642 */
4643 id=XCommandWidget(display,windows,CropModeMenu,&event);
4644 if (id < 0)
4645 continue;
4646 switch (CropCommands[id])
4647 {
4648 case CropHelpCommand:
4649 {
4650 switch (mode)
4651 {
4652 case CopyMode:
4653 {
4654 XTextViewWidget(display,resource_info,windows,MagickFalse,
4655 "Help Viewer - Image Copy",ImageCopyHelp);
4656 break;
4657 }
4658 case CropMode:
4659 {
4660 XTextViewWidget(display,resource_info,windows,MagickFalse,
4661 "Help Viewer - Image Crop",ImageCropHelp);
4662 break;
4663 }
4664 case CutMode:
4665 {
4666 XTextViewWidget(display,resource_info,windows,MagickFalse,
4667 "Help Viewer - Image Cut",ImageCutHelp);
4668 break;
4669 }
4670 }
4671 break;
4672 }
4673 case CropDismissCommand:
4674 {
4675 /*
4676 Prematurely exit.
4677 */
4678 state|=EscapeState;
4679 state|=ExitState;
4680 break;
4681 }
4682 default:
4683 break;
4684 }
4685 continue;
4686 }
4687 switch (event.type)
4688 {
4689 case ButtonPress:
4690 {
4691 if (event.xbutton.button != Button1)
4692 break;
4693 if (event.xbutton.window != windows->image.id)
4694 break;
4695 /*
4696 Note first corner of cropping rectangle-- exit loop.
4697 */
4698 (void) XCheckDefineCursor(display,windows->image.id,cursor);
4699 crop_info.x=windows->image.x+event.xbutton.x;
4700 crop_info.y=windows->image.y+event.xbutton.y;
4701 state|=ExitState;
4702 break;
4703 }
4704 case ButtonRelease:
4705 break;
4706 case Expose:
4707 break;
4708 case KeyPress:
4709 {
4710 if (event.xkey.window != windows->image.id)
4711 break;
4712 /*
4713 Respond to a user key press.
4714 */
4715 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4716 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4717 switch ((int) key_symbol)
4718 {
4719 case XK_Escape:
4720 case XK_F20:
4721 {
4722 /*
4723 Prematurely exit.
4724 */
4725 state|=EscapeState;
4726 state|=ExitState;
4727 break;
4728 }
4729 case XK_F1:
4730 case XK_Help:
4731 {
4732 switch (mode)
4733 {
4734 case CopyMode:
4735 {
4736 XTextViewWidget(display,resource_info,windows,MagickFalse,
4737 "Help Viewer - Image Copy",ImageCopyHelp);
4738 break;
4739 }
4740 case CropMode:
4741 {
4742 XTextViewWidget(display,resource_info,windows,MagickFalse,
4743 "Help Viewer - Image Crop",ImageCropHelp);
4744 break;
4745 }
4746 case CutMode:
4747 {
4748 XTextViewWidget(display,resource_info,windows,MagickFalse,
4749 "Help Viewer - Image Cut",ImageCutHelp);
4750 break;
4751 }
4752 }
4753 break;
4754 }
4755 default:
4756 {
4757 (void) XBell(display,0);
4758 break;
4759 }
4760 }
4761 break;
4762 }
4763 case MotionNotify:
4764 {
4765 if (event.xmotion.window != windows->image.id)
4766 break;
4767 /*
4768 Map and unmap Info widget as text cursor crosses its boundaries.
4769 */
4770 x=event.xmotion.x;
4771 y=event.xmotion.y;
4772 if (windows->info.mapped != MagickFalse)
4773 {
4774 if ((x < (int) (windows->info.x+windows->info.width)) &&
4775 (y < (int) (windows->info.y+windows->info.height)))
4776 (void) XWithdrawWindow(display,windows->info.id,
4777 windows->info.screen);
4778 }
4779 else
4780 if ((x > (int) (windows->info.x+windows->info.width)) ||
4781 (y > (int) (windows->info.y+windows->info.height)))
4782 (void) XMapWindow(display,windows->info.id);
4783 crop_info.x=windows->image.x+x;
4784 crop_info.y=windows->image.y+y;
4785 break;
4786 }
4787 default:
4788 break;
4789 }
4790 } while ((state & ExitState) == 0);
4791 (void) XSelectInput(display,windows->image.id,
4792 windows->image.attributes.event_mask);
4793 if ((state & EscapeState) != 0)
4794 {
4795 /*
4796 User want to exit without cropping.
4797 */
4798 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4799 (void) XFreeCursor(display,cursor);
4800 return(MagickTrue);
4801 }
4802 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4803 do
4804 {
4805 /*
4806 Size rectangle as pointer moves until the mouse button is released.
4807 */
4808 x=(int) crop_info.x;
4809 y=(int) crop_info.y;
4810 crop_info.width=0;
4811 crop_info.height=0;
4812 state=DefaultState;
4813 do
4814 {
4815 highlight_info=crop_info;
4816 highlight_info.x=crop_info.x-windows->image.x;
4817 highlight_info.y=crop_info.y-windows->image.y;
4818 if ((highlight_info.width > 3) && (highlight_info.height > 3))
4819 {
4820 /*
4821 Display info and draw cropping rectangle.
4822 */
4823 if (windows->info.mapped == MagickFalse)
4824 (void) XMapWindow(display,windows->info.id);
4825 (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
4826 crop_info.width,crop_info.height,crop_info.x,crop_info.y);
4827 XInfoWidget(display,windows,text);
4828 XHighlightRectangle(display,windows->image.id,
4829 windows->image.highlight_context,&highlight_info);
4830 }
4831 else
4832 if (windows->info.mapped != MagickFalse)
4833 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4834 /*
4835 Wait for next event.
4836 */
4837 XScreenEvent(display,windows,&event);
4838 if ((highlight_info.width > 3) && (highlight_info.height > 3))
4839 XHighlightRectangle(display,windows->image.id,
4840 windows->image.highlight_context,&highlight_info);
4841 switch (event.type)
4842 {
4843 case ButtonPress:
4844 {
4845 crop_info.x=windows->image.x+event.xbutton.x;
4846 crop_info.y=windows->image.y+event.xbutton.y;
4847 break;
4848 }
4849 case ButtonRelease:
4850 {
4851 /*
4852 User has committed to cropping rectangle.
4853 */
4854 crop_info.x=windows->image.x+event.xbutton.x;
4855 crop_info.y=windows->image.y+event.xbutton.y;
4856 XSetCursorState(display,windows,MagickFalse);
4857 state|=ExitState;
4858 windows->command.data=0;
4859 (void) XCommandWidget(display,windows,RectifyModeMenu,
4860 (XEvent *) NULL);
4861 break;
4862 }
4863 case Expose:
4864 break;
4865 case MotionNotify:
4866 {
4867 crop_info.x=windows->image.x+event.xmotion.x;
4868 crop_info.y=windows->image.y+event.xmotion.y;
4869 }
4870 default:
4871 break;
4872 }
4873 if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4874 ((state & ExitState) != 0))
4875 {
4876 /*
4877 Check boundary conditions.
4878 */
4879 if (crop_info.x < 0)
4880 crop_info.x=0;
4881 else
4882 if (crop_info.x > (int) windows->image.ximage->width)
4883 crop_info.x=windows->image.ximage->width;
4884 if ((int) crop_info.x < x)
4885 crop_info.width=(unsigned int) (x-crop_info.x);
4886 else
4887 {
4888 crop_info.width=(unsigned int) (crop_info.x-x);
4889 crop_info.x=x;
4890 }
4891 if (crop_info.y < 0)
4892 crop_info.y=0;
4893 else
4894 if (crop_info.y > (int) windows->image.ximage->height)
4895 crop_info.y=windows->image.ximage->height;
4896 if ((int) crop_info.y < y)
4897 crop_info.height=(unsigned int) (y-crop_info.y);
4898 else
4899 {
4900 crop_info.height=(unsigned int) (crop_info.y-y);
4901 crop_info.y=y;
4902 }
4903 }
4904 } while ((state & ExitState) == 0);
4905 /*
4906 Wait for user to grab a corner of the rectangle or press return.
4907 */
4908 state=DefaultState;
4909 (void) XMapWindow(display,windows->info.id);
4910 do
4911 {
4912 if (windows->info.mapped != MagickFalse)
4913 {
4914 /*
4915 Display pointer position.
4916 */
4917 (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
4918 crop_info.width,crop_info.height,crop_info.x,crop_info.y);
4919 XInfoWidget(display,windows,text);
4920 }
4921 highlight_info=crop_info;
4922 highlight_info.x=crop_info.x-windows->image.x;
4923 highlight_info.y=crop_info.y-windows->image.y;
4924 if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4925 {
4926 state|=EscapeState;
4927 state|=ExitState;
4928 break;
4929 }
4930 XHighlightRectangle(display,windows->image.id,
4931 windows->image.highlight_context,&highlight_info);
4932 XScreenEvent(display,windows,&event);
4933 if (event.xany.window == windows->command.id)
4934 {
4935 /*
4936 Select a command from the Command widget.
4937 */
4938 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4939 id=XCommandWidget(display,windows,RectifyModeMenu,&event);
4940 (void) XSetFunction(display,windows->image.highlight_context,
4941 GXinvert);
4942 XHighlightRectangle(display,windows->image.id,
4943 windows->image.highlight_context,&highlight_info);
4944 if (id >= 0)
4945 switch (RectifyCommands[id])
4946 {
4947 case RectifyCopyCommand:
4948 {
4949 state|=ExitState;
4950 break;
4951 }
4952 case RectifyHelpCommand:
4953 {
4954 (void) XSetFunction(display,windows->image.highlight_context,
4955 GXcopy);
4956 switch (mode)
4957 {
4958 case CopyMode:
4959 {
4960 XTextViewWidget(display,resource_info,windows,MagickFalse,
4961 "Help Viewer - Image Copy",ImageCopyHelp);
4962 break;
4963 }
4964 case CropMode:
4965 {
4966 XTextViewWidget(display,resource_info,windows,MagickFalse,
4967 "Help Viewer - Image Crop",ImageCropHelp);
4968 break;
4969 }
4970 case CutMode:
4971 {
4972 XTextViewWidget(display,resource_info,windows,MagickFalse,
4973 "Help Viewer - Image Cut",ImageCutHelp);
4974 break;
4975 }
4976 }
4977 (void) XSetFunction(display,windows->image.highlight_context,
4978 GXinvert);
4979 break;
4980 }
4981 case RectifyDismissCommand:
4982 {
4983 /*
4984 Prematurely exit.
4985 */
4986 state|=EscapeState;
4987 state|=ExitState;
4988 break;
4989 }
4990 default:
4991 break;
4992 }
4993 continue;
4994 }
4995 XHighlightRectangle(display,windows->image.id,
4996 windows->image.highlight_context,&highlight_info);
4997 switch (event.type)
4998 {
4999 case ButtonPress:
5000 {
5001 if (event.xbutton.button != Button1)
5002 break;
5003 if (event.xbutton.window != windows->image.id)
5004 break;
5005 x=windows->image.x+event.xbutton.x;
5006 y=windows->image.y+event.xbutton.y;
5007 if ((x < (int) (crop_info.x+RoiDelta)) &&
5008 (x > (int) (crop_info.x-RoiDelta)) &&
5009 (y < (int) (crop_info.y+RoiDelta)) &&
5010 (y > (int) (crop_info.y-RoiDelta)))
5011 {
5012 crop_info.x=(long) (crop_info.x+crop_info.width);
5013 crop_info.y=(long) (crop_info.y+crop_info.height);
5014 state|=UpdateConfigurationState;
5015 break;
5016 }
5017 if ((x < (int) (crop_info.x+RoiDelta)) &&
5018 (x > (int) (crop_info.x-RoiDelta)) &&
5019 (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5020 (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5021 {
5022 crop_info.x=(long) (crop_info.x+crop_info.width);
5023 state|=UpdateConfigurationState;
5024 break;
5025 }
5026 if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5027 (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5028 (y < (int) (crop_info.y+RoiDelta)) &&
5029 (y > (int) (crop_info.y-RoiDelta)))
5030 {
5031 crop_info.y=(long) (crop_info.y+crop_info.height);
5032 state|=UpdateConfigurationState;
5033 break;
5034 }
5035 if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5036 (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5037 (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5038 (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5039 {
5040 state|=UpdateConfigurationState;
5041 break;
5042 }
5043 }
5044 case ButtonRelease:
5045 {
5046 if (event.xbutton.window == windows->pan.id)
5047 if ((highlight_info.x != crop_info.x-windows->image.x) ||
5048 (highlight_info.y != crop_info.y-windows->image.y))
5049 XHighlightRectangle(display,windows->image.id,
5050 windows->image.highlight_context,&highlight_info);
5051 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5052 event.xbutton.time);
5053 break;
5054 }
5055 case Expose:
5056 {
5057 if (event.xexpose.window == windows->image.id)
5058 if (event.xexpose.count == 0)
5059 {
5060 event.xexpose.x=(int) highlight_info.x;
5061 event.xexpose.y=(int) highlight_info.y;
5062 event.xexpose.width=(int) highlight_info.width;
5063 event.xexpose.height=(int) highlight_info.height;
5064 XRefreshWindow(display,&windows->image,&event);
5065 }
5066 if (event.xexpose.window == windows->info.id)
5067 if (event.xexpose.count == 0)
5068 XInfoWidget(display,windows,text);
5069 break;
5070 }
5071 case KeyPress:
5072 {
5073 if (event.xkey.window != windows->image.id)
5074 break;
5075 /*
5076 Respond to a user key press.
5077 */
5078 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5079 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5080 switch ((int) key_symbol)
5081 {
5082 case XK_Escape:
5083 case XK_F20:
5084 state|=EscapeState;
5085 case XK_Return:
5086 {
5087 state|=ExitState;
5088 break;
5089 }
5090 case XK_Home:
5091 case XK_KP_Home:
5092 {
5093 crop_info.x=(long) (windows->image.width/2L-crop_info.width/2L);
5094 crop_info.y=(long) (windows->image.height/2L-crop_info.height/2L);
5095 break;
5096 }
5097 case XK_Left:
5098 case XK_KP_Left:
5099 {
5100 crop_info.x--;
5101 break;
5102 }
5103 case XK_Up:
5104 case XK_KP_Up:
5105 case XK_Next:
5106 {
5107 crop_info.y--;
5108 break;
5109 }
5110 case XK_Right:
5111 case XK_KP_Right:
5112 {
5113 crop_info.x++;
5114 break;
5115 }
5116 case XK_Prior:
5117 case XK_Down:
5118 case XK_KP_Down:
5119 {
5120 crop_info.y++;
5121 break;
5122 }
5123 case XK_F1:
5124 case XK_Help:
5125 {
5126 (void) XSetFunction(display,windows->image.highlight_context,
5127 GXcopy);
5128 switch (mode)
5129 {
5130 case CopyMode:
5131 {
5132 XTextViewWidget(display,resource_info,windows,MagickFalse,
5133 "Help Viewer - Image Copy",ImageCopyHelp);
5134 break;
5135 }
5136 case CropMode:
5137 {
5138 XTextViewWidget(display,resource_info,windows,MagickFalse,
5139 "Help Viewer - Image Cropg",ImageCropHelp);
5140 break;
5141 }
5142 case CutMode:
5143 {
5144 XTextViewWidget(display,resource_info,windows,MagickFalse,
5145 "Help Viewer - Image Cutg",ImageCutHelp);
5146 break;
5147 }
5148 }
5149 (void) XSetFunction(display,windows->image.highlight_context,
5150 GXinvert);
5151 break;
5152 }
5153 default:
5154 {
5155 (void) XBell(display,0);
5156 break;
5157 }
5158 }
5159 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5160 event.xkey.time);
5161 break;
5162 }
5163 case KeyRelease:
5164 break;
5165 case MotionNotify:
5166 {
5167 if (event.xmotion.window != windows->image.id)
5168 break;
5169 /*
5170 Map and unmap Info widget as text cursor crosses its boundaries.
5171 */
5172 x=event.xmotion.x;
5173 y=event.xmotion.y;
5174 if (windows->info.mapped != MagickFalse)
5175 {
5176 if ((x < (int) (windows->info.x+windows->info.width)) &&
5177 (y < (int) (windows->info.y+windows->info.height)))
5178 (void) XWithdrawWindow(display,windows->info.id,
5179 windows->info.screen);
5180 }
5181 else
5182 if ((x > (int) (windows->info.x+windows->info.width)) ||
5183 (y > (int) (windows->info.y+windows->info.height)))
5184 (void) XMapWindow(display,windows->info.id);
5185 crop_info.x=windows->image.x+event.xmotion.x;
5186 crop_info.y=windows->image.y+event.xmotion.y;
5187 break;
5188 }
5189 case SelectionRequest:
5190 {
5191 XSelectionEvent
5192 notify;
5193
5194 XSelectionRequestEvent
5195 *request;
5196
5197 /*
5198 Set primary selection.
5199 */
5200 (void) FormatMagickString(text,MaxTextExtent,"%lux%lu%+ld%+ld",
5201 crop_info.width,crop_info.height,crop_info.x,crop_info.y);
5202 request=(&(event.xselectionrequest));
5203 (void) XChangeProperty(request->display,request->requestor,
5204 request->property,request->target,8,PropModeReplace,
5205 (unsigned char *) text,(int) strlen(text));
5206 notify.type=SelectionNotify;
5207 notify.display=request->display;
5208 notify.requestor=request->requestor;
5209 notify.selection=request->selection;
5210 notify.target=request->target;
5211 notify.time=request->time;
5212 if (request->property == None)
5213 notify.property=request->target;
5214 else
5215 notify.property=request->property;
5216 (void) XSendEvent(request->display,request->requestor,False,0,
5217 (XEvent *) &notify);
5218 }
5219 default:
5220 break;
5221 }
5222 if ((state & UpdateConfigurationState) != 0)
5223 {
5224 (void) XPutBackEvent(display,&event);
5225 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5226 break;
5227 }
5228 } while ((state & ExitState) == 0);
5229 } while ((state & ExitState) == 0);
5230 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5231 XSetCursorState(display,windows,MagickFalse);
5232 if ((state & EscapeState) != 0)
5233 return(MagickTrue);
5234 if (mode == CropMode)
5235 if (((int) crop_info.width != windows->image.ximage->width) ||
5236 ((int) crop_info.height != windows->image.ximage->height))
5237 {
5238 /*
5239 Reconfigure Image window as defined by cropping rectangle.
5240 */
5241 XSetCropGeometry(display,windows,&crop_info,image);
5242 windows->image.window_changes.width=(int) crop_info.width;
5243 windows->image.window_changes.height=(int) crop_info.height;
5244 (void) XConfigureImage(display,resource_info,windows,image);
5245 return(MagickTrue);
5246 }
5247 /*
5248 Copy image before applying image transforms.
5249 */
5250 XSetCursorState(display,windows,MagickTrue);
5251 XCheckRefreshWindows(display,windows);
5252 width=(unsigned int) image->columns;
5253 height=(unsigned int) image->rows;
5254 x=0;
5255 y=0;
5256 if (windows->image.crop_geometry != (char *) NULL)
5257 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5258 scale_factor=(MagickRealType) width/windows->image.ximage->width;
5259 crop_info.x+=x;
5260 crop_info.x=(int) (scale_factor*crop_info.x+0.5);
5261 crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5262 scale_factor=(MagickRealType) height/windows->image.ximage->height;
5263 crop_info.y+=y;
5264 crop_info.y=(int) (scale_factor*crop_info.y+0.5);
5265 crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5266 crop_image=CropImage(image,&crop_info,&image->exception);
5267 XSetCursorState(display,windows,MagickFalse);
5268 if (crop_image == (Image *) NULL)
5269 return(MagickFalse);
5270 if (resource_info->copy_image != (Image *) NULL)
5271 resource_info->copy_image=DestroyImage(resource_info->copy_image);
5272 resource_info->copy_image=crop_image;
5273 if (mode == CopyMode)
5274 {
5275 (void) XConfigureImage(display,resource_info,windows,image);
5276 return(MagickTrue);
5277 }
5278 /*
5279 Cut image.
5280 */
5281 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
5282 return(MagickFalse);
5283 image->matte=MagickTrue;
5284 exception=(&image->exception);
5285 for (y=0; y < (long) crop_info.height; y++)
5286 {
5287 q=GetAuthenticPixels(image,crop_info.x,y+crop_info.y,crop_info.width,1,
5288 exception);
5289 if (q == (PixelPacket *) NULL)
5290 break;
5291 for (x=0; x < (int) crop_info.width; x++)
5292 {
5293 q->opacity=(Quantum) TransparentOpacity;
5294 q++;
5295 }
5296 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5297 break;
5298 }
5299 /*
5300 Update image configuration.
5301 */
5302 XConfigureImageColormap(display,resource_info,windows,image);
5303 (void) XConfigureImage(display,resource_info,windows,image);
5304 return(MagickTrue);
5305}
5306
5307/*
5308%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5309% %
5310% %
5311% %
5312+ X D r a w I m a g e %
5313% %
5314% %
5315% %
5316%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5317%
5318% XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5319% the image.
5320%
5321% The format of the XDrawEditImage method is:
5322%
5323% MagickBooleanType XDrawEditImage(Display *display,
5324% XResourceInfo *resource_info,XWindows *windows,Image **image)
5325%
5326% A description of each parameter follows:
5327%
5328% o display: Specifies a connection to an X server; returned from
5329% XOpenDisplay.
5330%
5331% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5332%
5333% o windows: Specifies a pointer to a XWindows structure.
5334%
5335% o image: the image.
5336%
5337*/
5338static MagickBooleanType XDrawEditImage(Display *display,
5339 XResourceInfo *resource_info,XWindows *windows,Image **image)
5340{
5341 static const char
5342 *DrawMenu[] =
5343 {
5344 "Element",
5345 "Color",
5346 "Stipple",
5347 "Width",
5348 "Undo",
5349 "Help",
5350 "Dismiss",
5351 (char *) NULL
5352 };
5353
5354 static ElementType
5355 element = PointElement;
5356
5357 static const ModeType
5358 DrawCommands[] =
5359 {
5360 DrawElementCommand,
5361 DrawColorCommand,
5362 DrawStippleCommand,
5363 DrawWidthCommand,
5364 DrawUndoCommand,
5365 DrawHelpCommand,
5366 DrawDismissCommand
5367 };
5368
5369 static Pixmap
5370 stipple = (Pixmap) NULL;
5371
5372 static unsigned int
5373 pen_id = 0,
5374 line_width = 1;
5375
5376 char
5377 command[MaxTextExtent],
5378 text[MaxTextExtent];
5379
5380 Cursor
5381 cursor;
5382
5383 int
5384 entry,
5385 id,
5386 number_coordinates,
5387 x,
5388 y;
5389
5390 MagickRealType
5391 degrees;
5392
5393 MagickStatusType
5394 status;
5395
5396 RectangleInfo
5397 rectangle_info;
5398
5399 register int
5400 i;
5401
5402 unsigned int
5403 distance,
5404 height,
5405 max_coordinates,
5406 width;
5407
5408 unsigned long
5409 state;
5410
5411 Window
5412 root_window;
5413
5414 XDrawInfo
5415 draw_info;
5416
5417 XEvent
5418 event;
5419
5420 XPoint
5421 *coordinate_info;
5422
5423 XSegment
5424 line_info;
5425
5426 /*
5427 Allocate polygon info.
5428 */
5429 max_coordinates=2048;
5430 coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5431 sizeof(*coordinate_info));
5432 if (coordinate_info == (XPoint *) NULL)
5433 {
5434 (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
5435 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5436 return(MagickFalse);
5437 }
5438 /*
5439 Map Command widget.
5440 */
5441 (void) CloneString(&windows->command.name,"Draw");
5442 windows->command.data=4;
5443 (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5444 (void) XMapRaised(display,windows->command.id);
5445 XClientMessage(display,windows->image.id,windows->im_protocols,
5446 windows->im_update_widget,CurrentTime);
5447 /*
5448 Wait for first button press.
5449 */
5450 root_window=XRootWindow(display,XDefaultScreen(display));
5451 draw_info.stencil=OpaqueStencil;
5452 status=MagickTrue;
5453 cursor=XCreateFontCursor(display,XC_tcross);
5454 for ( ; ; )
5455 {
5456 XQueryPosition(display,windows->image.id,&x,&y);
5457 (void) XSelectInput(display,windows->image.id,
5458 windows->image.attributes.event_mask | PointerMotionMask);
5459 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5460 state=DefaultState;
5461 do
5462 {
5463 if (windows->info.mapped != MagickFalse)
5464 {
5465 /*
5466 Display pointer position.
5467 */
5468 (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
5469 x+windows->image.x,y+windows->image.y);
5470 XInfoWidget(display,windows,text);
5471 }
5472 /*
5473 Wait for next event.
5474 */
5475 XScreenEvent(display,windows,&event);
5476 if (event.xany.window == windows->command.id)
5477 {
5478 /*
5479 Select a command from the Command widget.
5480 */
5481 id=XCommandWidget(display,windows,DrawMenu,&event);
5482 if (id < 0)
5483 continue;
5484 switch (DrawCommands[id])
5485 {
5486 case DrawElementCommand:
5487 {
5488 static const char
5489 *Elements[] =
5490 {
5491 "point",
5492 "line",
5493 "rectangle",
5494 "fill rectangle",
5495 "circle",
5496 "fill circle",
5497 "ellipse",
5498 "fill ellipse",
5499 "polygon",
5500 "fill polygon",
5501 (char *) NULL,
5502 };
5503
5504 /*
5505 Select a command from the pop-up menu.
5506 */
5507 element=(ElementType) (XMenuWidget(display,windows,
5508 DrawMenu[id],Elements,command)+1);
5509 break;
5510 }
5511 case DrawColorCommand:
5512 {
5513 const char
5514 *ColorMenu[MaxNumberPens+1];
5515
5516 int
5517 pen_number;
5518
5519 MagickBooleanType
5520 transparent;
5521
5522 XColor
5523 color;
5524
5525 /*
5526 Initialize menu selections.
5527 */
5528 for (i=0; i < (int) (MaxNumberPens-2); i++)
5529 ColorMenu[i]=resource_info->pen_colors[i];
5530 ColorMenu[MaxNumberPens-2]="transparent";
5531 ColorMenu[MaxNumberPens-1]="Browser...";
5532 ColorMenu[MaxNumberPens]=(char *) NULL;
5533 /*
5534 Select a pen color from the pop-up menu.
5535 */
5536 pen_number=XMenuWidget(display,windows,DrawMenu[id],
5537 (const char **) ColorMenu,command);
5538 if (pen_number < 0)
5539 break;
5540 transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5541 MagickFalse;
5542 if (transparent != MagickFalse)
5543 {
5544 draw_info.stencil=TransparentStencil;
5545 break;
5546 }
5547 if (pen_number == (MaxNumberPens-1))
5548 {
5549 static char
5550 color_name[MaxTextExtent] = "gray";
5551
5552 /*
5553 Select a pen color from a dialog.
5554 */
5555 resource_info->pen_colors[pen_number]=color_name;
5556 XColorBrowserWidget(display,windows,"Select",color_name);
5557 if (*color_name == '\0')
5558 break;
5559 }
5560 /*
5561 Set pen color.
5562 */
5563 (void) XParseColor(display,windows->map_info->colormap,
5564 resource_info->pen_colors[pen_number],&color);
5565 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5566 (unsigned int) MaxColors,&color);
5567 windows->pixel_info->pen_colors[pen_number]=color;
5568 pen_id=(unsigned int) pen_number;
5569 draw_info.stencil=OpaqueStencil;
5570 break;
5571 }
5572 case DrawStippleCommand:
5573 {
5574 Image
5575 *stipple_image;
5576
5577 ImageInfo
5578 *image_info;
5579
5580 int
5581 status;
5582
5583 static char
5584 filename[MaxTextExtent] = "\0";
5585
5586 static const char
5587 *StipplesMenu[] =
5588 {
5589 "Brick",
5590 "Diagonal",
5591 "Scales",
5592 "Vertical",
5593 "Wavy",
5594 "Translucent",
5595 "Opaque",
5596 (char *) NULL,
5597 (char *) NULL,
5598 };
5599
5600 /*
5601 Select a command from the pop-up menu.
5602 */
5603 StipplesMenu[7]="Open...";
5604 entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5605 command);
5606 if (entry < 0)
5607 break;
5608 if (stipple != (Pixmap) NULL)
5609 (void) XFreePixmap(display,stipple);
5610 stipple=(Pixmap) NULL;
5611 if (entry == 6)
5612 break;
5613 if (entry != 7)
5614 {
5615 switch (entry)
5616 {
5617 case 0:
5618 {
5619 stipple=XCreateBitmapFromData(display,root_window,
5620 (char *) BricksBitmap,BricksWidth,BricksHeight);
5621 break;
5622 }
5623 case 1:
5624 {
5625 stipple=XCreateBitmapFromData(display,root_window,
5626 (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5627 break;
5628 }
5629 case 2:
5630 {
5631 stipple=XCreateBitmapFromData(display,root_window,
5632 (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5633 break;
5634 }
5635 case 3:
5636 {
5637 stipple=XCreateBitmapFromData(display,root_window,
5638 (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5639 break;
5640 }
5641 case 4:
5642 {
5643 stipple=XCreateBitmapFromData(display,root_window,
5644 (char *) WavyBitmap,WavyWidth,WavyHeight);
5645 break;
5646 }
5647 case 5:
5648 default:
5649 {
5650 stipple=XCreateBitmapFromData(display,root_window,
5651 (char *) HighlightBitmap,HighlightWidth,
5652 HighlightHeight);
5653 break;
5654 }
5655 }
5656 break;
5657 }
5658 XFileBrowserWidget(display,windows,"Stipple",filename);
5659 if (*filename == '\0')
5660 break;
5661 /*
5662 Read image.
5663 */
5664 XSetCursorState(display,windows,MagickTrue);
5665 XCheckRefreshWindows(display,windows);
5666 image_info=AcquireImageInfo();
5667 (void) CopyMagickString(image_info->filename,filename,
5668 MaxTextExtent);
5669 stipple_image=ReadImage(image_info,&(*image)->exception);
5670 CatchException(&(*image)->exception);
5671 XSetCursorState(display,windows,MagickFalse);
5672 if (stipple_image == (Image *) NULL)
5673 break;
5674 (void) AcquireUniqueFileResource(filename);
5675 (void) FormatMagickString(stipple_image->filename,MaxTextExtent,
5676 "xbm:%s",filename);
5677 (void) WriteImage(image_info,stipple_image);
5678 stipple_image=DestroyImage(stipple_image);
5679 image_info=DestroyImageInfo(image_info);
5680 status=XReadBitmapFile(display,root_window,filename,&width,
5681 &height,&stipple,&x,&y);
5682 (void) RelinquishUniqueFileResource(filename);
5683 if ((status != BitmapSuccess) != 0)
5684 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5685 filename);
5686 break;
5687 }
5688 case DrawWidthCommand:
5689 {
5690 static char
5691 width[MaxTextExtent] = "0";
5692
5693 static const char
5694 *WidthsMenu[] =
5695 {
5696 "1",
5697 "2",
5698 "4",
5699 "8",
5700 "16",
5701 "Dialog...",
5702 (char *) NULL,
5703 };
5704
5705 /*
5706 Select a command from the pop-up menu.
5707 */
5708 entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5709 command);
5710 if (entry < 0)
5711 break;
5712 if (entry != 5)
5713 {
5714 line_width=(unsigned int) atoi(WidthsMenu[entry]);
5715 break;
5716 }
5717 (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5718 width);
5719 if (*width == '\0')
5720 break;
5721 line_width=(unsigned int) atoi(width);
5722 break;
5723 }
5724 case DrawUndoCommand:
5725 {
5726 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5727 image);
5728 break;
5729 }
5730 case DrawHelpCommand:
5731 {
5732 XTextViewWidget(display,resource_info,windows,MagickFalse,
5733 "Help Viewer - Image Rotation",ImageDrawHelp);
5734 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5735 break;
5736 }
5737 case DrawDismissCommand:
5738 {
5739 /*
5740 Prematurely exit.
5741 */
5742 state|=EscapeState;
5743 state|=ExitState;
5744 break;
5745 }
5746 default:
5747 break;
5748 }
5749 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5750 continue;
5751 }
5752 switch (event.type)
5753 {
5754 case ButtonPress:
5755 {
5756 if (event.xbutton.button != Button1)
5757 break;
5758 if (event.xbutton.window != windows->image.id)
5759 break;
5760 /*
5761 exit loop.
5762 */
5763 x=event.xbutton.x;
5764 y=event.xbutton.y;
5765 state|=ExitState;
5766 break;
5767 }
5768 case ButtonRelease:
5769 break;
5770 case Expose:
5771 break;
5772 case KeyPress:
5773 {
5774 KeySym
5775 key_symbol;
5776
5777 if (event.xkey.window != windows->image.id)
5778 break;
5779 /*
5780 Respond to a user key press.
5781 */
5782 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5783 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5784 switch ((int) key_symbol)
5785 {
5786 case XK_Escape:
5787 case XK_F20:
5788 {
5789 /*
5790 Prematurely exit.
5791 */
5792 state|=EscapeState;
5793 state|=ExitState;
5794 break;
5795 }
5796 case XK_F1:
5797 case XK_Help:
5798 {
5799 XTextViewWidget(display,resource_info,windows,MagickFalse,
5800 "Help Viewer - Image Rotation",ImageDrawHelp);
5801 break;
5802 }
5803 default:
5804 {
5805 (void) XBell(display,0);
5806 break;
5807 }
5808 }
5809 break;
5810 }
5811 case MotionNotify:
5812 {
5813 /*
5814 Map and unmap Info widget as text cursor crosses its boundaries.
5815 */
5816 x=event.xmotion.x;
5817 y=event.xmotion.y;
5818 if (windows->info.mapped != MagickFalse)
5819 {
5820 if ((x < (int) (windows->info.x+windows->info.width)) &&
5821 (y < (int) (windows->info.y+windows->info.height)))
5822 (void) XWithdrawWindow(display,windows->info.id,
5823 windows->info.screen);
5824 }
5825 else
5826 if ((x > (int) (windows->info.x+windows->info.width)) ||
5827 (y > (int) (windows->info.y+windows->info.height)))
5828 (void) XMapWindow(display,windows->info.id);
5829 break;
5830 }
5831 }
5832 } while ((state & ExitState) == 0);
5833 (void) XSelectInput(display,windows->image.id,
5834 windows->image.attributes.event_mask);
5835 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5836 if ((state & EscapeState) != 0)
5837 break;
5838 /*
5839 Draw element as pointer moves until the button is released.
5840 */
5841 distance=0;
5842 degrees=0.0;
5843 line_info.x1=x;
5844 line_info.y1=y;
5845 line_info.x2=x;
5846 line_info.y2=y;
5847 rectangle_info.x=x;
5848 rectangle_info.y=y;
5849 rectangle_info.width=0;
5850 rectangle_info.height=0;
5851 number_coordinates=1;
5852 coordinate_info->x=x;
5853 coordinate_info->y=y;
5854 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5855 state=DefaultState;
5856 do
5857 {
5858 switch (element)
5859 {
5860 case PointElement:
5861 default:
5862 {
5863 if (number_coordinates > 1)
5864 {
5865 (void) XDrawLines(display,windows->image.id,
5866 windows->image.highlight_context,coordinate_info,
5867 number_coordinates,CoordModeOrigin);
5868 (void) FormatMagickString(text,MaxTextExtent," %+d%+d",
5869 coordinate_info[number_coordinates-1].x,
5870 coordinate_info[number_coordinates-1].y);
5871 XInfoWidget(display,windows,text);
5872 }
5873 break;
5874 }
5875 case LineElement:
5876 {
5877 if (distance > 9)
5878 {
5879 /*
5880 Display angle of the line.
5881 */
5882 degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5883 line_info.y1),(double) (line_info.x2-line_info.x1)));
5884 (void) FormatMagickString(text,MaxTextExtent," %g",
5885 (double) degrees);
5886 XInfoWidget(display,windows,text);
5887 XHighlightLine(display,windows->image.id,
5888 windows->image.highlight_context,&line_info);
5889 }
5890 else
5891 if (windows->info.mapped != MagickFalse)
5892 (void) XWithdrawWindow(display,windows->info.id,
5893 windows->info.screen);
5894 break;
5895 }
5896 case RectangleElement:
5897 case FillRectangleElement:
5898 {
5899 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5900 {
5901 /*
5902 Display info and draw drawing rectangle.
5903 */
5904 (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
5905 rectangle_info.width,rectangle_info.height,rectangle_info.x,
5906 rectangle_info.y);
5907 XInfoWidget(display,windows,text);
5908 XHighlightRectangle(display,windows->image.id,
5909 windows->image.highlight_context,&rectangle_info);
5910 }
5911 else
5912 if (windows->info.mapped != MagickFalse)
5913 (void) XWithdrawWindow(display,windows->info.id,
5914 windows->info.screen);
5915 break;
5916 }
5917 case CircleElement:
5918 case FillCircleElement:
5919 case EllipseElement:
5920 case FillEllipseElement:
5921 {
5922 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5923 {
5924 /*
5925 Display info and draw drawing rectangle.
5926 */
5927 (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
5928 rectangle_info.width,rectangle_info.height,rectangle_info.x,
5929 rectangle_info.y);
5930 XInfoWidget(display,windows,text);
5931 XHighlightEllipse(display,windows->image.id,
5932 windows->image.highlight_context,&rectangle_info);
5933 }
5934 else
5935 if (windows->info.mapped != MagickFalse)
5936 (void) XWithdrawWindow(display,windows->info.id,
5937 windows->info.screen);
5938 break;
5939 }
5940 case PolygonElement:
5941 case FillPolygonElement:
5942 {
5943 if (number_coordinates > 1)
5944 (void) XDrawLines(display,windows->image.id,
5945 windows->image.highlight_context,coordinate_info,
5946 number_coordinates,CoordModeOrigin);
5947 if (distance > 9)
5948 {
5949 /*
5950 Display angle of the line.
5951 */
5952 degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5953 line_info.y1),(double) (line_info.x2-line_info.x1)));
5954 (void) FormatMagickString(text,MaxTextExtent," %g",
5955 (double) degrees);
5956 XInfoWidget(display,windows,text);
5957 XHighlightLine(display,windows->image.id,
5958 windows->image.highlight_context,&line_info);
5959 }
5960 else
5961 if (windows->info.mapped != MagickFalse)
5962 (void) XWithdrawWindow(display,windows->info.id,
5963 windows->info.screen);
5964 break;
5965 }
5966 }
5967 /*
5968 Wait for next event.
5969 */
5970 XScreenEvent(display,windows,&event);
5971 switch (element)
5972 {
5973 case PointElement:
5974 default:
5975 {
5976 if (number_coordinates > 1)
5977 (void) XDrawLines(display,windows->image.id,
5978 windows->image.highlight_context,coordinate_info,
5979 number_coordinates,CoordModeOrigin);
5980 break;
5981 }
5982 case LineElement:
5983 {
5984 if (distance > 9)
5985 XHighlightLine(display,windows->image.id,
5986 windows->image.highlight_context,&line_info);
5987 break;
5988 }
5989 case RectangleElement:
5990 case FillRectangleElement:
5991 {
5992 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5993 XHighlightRectangle(display,windows->image.id,
5994 windows->image.highlight_context,&rectangle_info);
5995 break;
5996 }
5997 case CircleElement:
5998 case FillCircleElement:
5999 case EllipseElement:
6000 case FillEllipseElement:
6001 {
6002 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6003 XHighlightEllipse(display,windows->image.id,
6004 windows->image.highlight_context,&rectangle_info);
6005 break;
6006 }
6007 case PolygonElement:
6008 case FillPolygonElement:
6009 {
6010 if (number_coordinates > 1)
6011 (void) XDrawLines(display,windows->image.id,
6012 windows->image.highlight_context,coordinate_info,
6013 number_coordinates,CoordModeOrigin);
6014 if (distance > 9)
6015 XHighlightLine(display,windows->image.id,
6016 windows->image.highlight_context,&line_info);
6017 break;
6018 }
6019 }
6020 switch (event.type)
6021 {
6022 case ButtonPress:
6023 break;
6024 case ButtonRelease:
6025 {
6026 /*
6027 User has committed to element.
6028 */
6029 line_info.x2=event.xbutton.x;
6030 line_info.y2=event.xbutton.y;
6031 rectangle_info.x=event.xbutton.x;
6032 rectangle_info.y=event.xbutton.y;
6033 coordinate_info[number_coordinates].x=event.xbutton.x;
6034 coordinate_info[number_coordinates].y=event.xbutton.y;
6035 if (((element != PolygonElement) &&
6036 (element != FillPolygonElement)) || (distance <= 9))
6037 {
6038 state|=ExitState;
6039 break;
6040 }
6041 number_coordinates++;
6042 if (number_coordinates < (int) max_coordinates)
6043 {
6044 line_info.x1=event.xbutton.x;
6045 line_info.y1=event.xbutton.y;
6046 break;
6047 }
6048 max_coordinates<<=1;
6049 coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6050 max_coordinates,sizeof(*coordinate_info));
6051 if (coordinate_info == (XPoint *) NULL)
6052 (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6053 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6054 break;
6055 }
6056 case Expose:
6057 break;
6058 case MotionNotify:
6059 {
6060 if (event.xmotion.window != windows->image.id)
6061 break;
6062 if (element != PointElement)
6063 {
6064 line_info.x2=event.xmotion.x;
6065 line_info.y2=event.xmotion.y;
6066 rectangle_info.x=event.xmotion.x;
6067 rectangle_info.y=event.xmotion.y;
6068 break;
6069 }
6070 coordinate_info[number_coordinates].x=event.xbutton.x;
6071 coordinate_info[number_coordinates].y=event.xbutton.y;
6072 number_coordinates++;
6073 if (number_coordinates < (int) max_coordinates)
6074 break;
6075 max_coordinates<<=1;
6076 coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6077 max_coordinates,sizeof(*coordinate_info));
6078 if (coordinate_info == (XPoint *) NULL)
6079 (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6080 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6081 break;
6082 }
6083 default:
6084 break;
6085 }
6086 /*
6087 Check boundary conditions.
6088 */
6089 if (line_info.x2 < 0)
6090 line_info.x2=0;
6091 else
6092 if (line_info.x2 > (int) windows->image.width)
6093 line_info.x2=(short) windows->image.width;
6094 if (line_info.y2 < 0)
6095 line_info.y2=0;
6096 else
6097 if (line_info.y2 > (int) windows->image.height)
6098 line_info.y2=(short) windows->image.height;
6099 distance=(unsigned int)
6100 (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6101 ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6102 if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6103 ((state & ExitState) != 0))
6104 {
6105 if (rectangle_info.x < 0)
6106 rectangle_info.x=0;
6107 else
6108 if (rectangle_info.x > (int) windows->image.width)
6109 rectangle_info.x=(long) windows->image.width;
6110 if ((int) rectangle_info.x < x)
6111 rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6112 else
6113 {
6114 rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6115 rectangle_info.x=x;
6116 }
6117 if (rectangle_info.y < 0)
6118 rectangle_info.y=0;
6119 else
6120 if (rectangle_info.y > (int) windows->image.height)
6121 rectangle_info.y=(long) windows->image.height;
6122 if ((int) rectangle_info.y < y)
6123 rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6124 else
6125 {
6126 rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6127 rectangle_info.y=y;
6128 }
6129 }
6130 } while ((state & ExitState) == 0);
6131 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6132 if ((element == PointElement) || (element == PolygonElement) ||
6133 (element == FillPolygonElement))
6134 {
6135 /*
6136 Determine polygon bounding box.
6137 */
6138 rectangle_info.x=coordinate_info->x;
6139 rectangle_info.y=coordinate_info->y;
6140 x=coordinate_info->x;
6141 y=coordinate_info->y;
6142 for (i=1; i < number_coordinates; i++)
6143 {
6144 if (coordinate_info[i].x > x)
6145 x=coordinate_info[i].x;
6146 if (coordinate_info[i].y > y)
6147 y=coordinate_info[i].y;
6148 if (coordinate_info[i].x < rectangle_info.x)
6149 rectangle_info.x=MagickMax(coordinate_info[i].x,0);
6150 if (coordinate_info[i].y < rectangle_info.y)
6151 rectangle_info.y=MagickMax(coordinate_info[i].y,0);
6152 }
6153 rectangle_info.width=(unsigned long) (x-rectangle_info.x);
6154 rectangle_info.height=(unsigned long) (y-rectangle_info.y);
6155 for (i=0; i < number_coordinates; i++)
6156 {
6157 coordinate_info[i].x-=rectangle_info.x;
6158 coordinate_info[i].y-=rectangle_info.y;
6159 }
6160 }
6161 else
6162 if (distance <= 9)
6163 continue;
6164 else
6165 if ((element == RectangleElement) ||
6166 (element == CircleElement) || (element == EllipseElement))
6167 {
6168 rectangle_info.width--;
6169 rectangle_info.height--;
6170 }
6171 /*
6172 Drawing is relative to image configuration.
6173 */
6174 draw_info.x=(int) rectangle_info.x;
6175 draw_info.y=(int) rectangle_info.y;
6176 (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6177 image);
6178 width=(unsigned int) (*image)->columns;
6179 height=(unsigned int) (*image)->rows;
6180 x=0;
6181 y=0;
6182 if (windows->image.crop_geometry != (char *) NULL)
6183 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6184 draw_info.x+=windows->image.x-(line_width/2);
6185 if (draw_info.x < 0)
6186 draw_info.x=0;
6187 draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6188 draw_info.y+=windows->image.y-(line_width/2);
6189 if (draw_info.y < 0)
6190 draw_info.y=0;
6191 draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6192 draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6193 if (draw_info.width > (unsigned int) (*image)->columns)
6194 draw_info.width=(unsigned int) (*image)->columns;
6195 draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6196 if (draw_info.height > (unsigned int) (*image)->rows)
6197 draw_info.height=(unsigned int) (*image)->rows;
6198 (void) FormatMagickString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6199 width*draw_info.width/windows->image.ximage->width,
6200 height*draw_info.height/windows->image.ximage->height,
6201 draw_info.x+x,draw_info.y+y);
6202 /*
6203 Initialize drawing attributes.
6204 */
6205 draw_info.degrees=0.0;
6206 draw_info.element=element;
6207 draw_info.stipple=stipple;
6208 draw_info.line_width=line_width;
6209 draw_info.line_info=line_info;
6210 if (line_info.x1 > (int) (line_width/2))
6211 draw_info.line_info.x1=(short) line_width/2;
6212 if (line_info.y1 > (int) (line_width/2))
6213 draw_info.line_info.y1=(short) line_width/2;
6214 draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6215 draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6216 if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6217 {
6218 draw_info.line_info.x2=(-draw_info.line_info.x2);
6219 draw_info.line_info.y2=(-draw_info.line_info.y2);
6220 }
6221 if (draw_info.line_info.x2 < 0)
6222 {
6223 draw_info.line_info.x2=(-draw_info.line_info.x2);
6224 Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6225 }
6226 if (draw_info.line_info.y2 < 0)
6227 {
6228 draw_info.line_info.y2=(-draw_info.line_info.y2);
6229 Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6230 }
6231 draw_info.rectangle_info=rectangle_info;
6232 if (draw_info.rectangle_info.x > (int) (line_width/2))
6233 draw_info.rectangle_info.x=(long) line_width/2;
6234 if (draw_info.rectangle_info.y > (int) (line_width/2))
6235 draw_info.rectangle_info.y=(long) line_width/2;
6236 draw_info.number_coordinates=(unsigned int) number_coordinates;
6237 draw_info.coordinate_info=coordinate_info;
6238 windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6239 /*
6240 Draw element on image.
6241 */
6242 XSetCursorState(display,windows,MagickTrue);
6243 XCheckRefreshWindows(display,windows);
6244 status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6245 XSetCursorState(display,windows,MagickFalse);
6246 /*
6247 Update image colormap and return to image drawing.
6248 */
6249 XConfigureImageColormap(display,resource_info,windows,*image);
6250 (void) XConfigureImage(display,resource_info,windows,*image);
6251 }
6252 XSetCursorState(display,windows,MagickFalse);
6253 coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6254 return(status != 0 ? MagickTrue : MagickFalse);
6255}
6256
6257/*
6258%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6259% %
6260% %
6261% %
6262+ X D r a w P a n R e c t a n g l e %
6263% %
6264% %
6265% %
6266%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6267%
6268% XDrawPanRectangle() draws a rectangle in the pan window. The pan window
6269% displays a zoom image and the rectangle shows which portion of the image is
6270% displayed in the Image window.
6271%
6272% The format of the XDrawPanRectangle method is:
6273%
6274% XDrawPanRectangle(Display *display,XWindows *windows)
6275%
6276% A description of each parameter follows:
6277%
6278% o display: Specifies a connection to an X server; returned from
6279% XOpenDisplay.
6280%
6281% o windows: Specifies a pointer to a XWindows structure.
6282%
6283*/
6284static void XDrawPanRectangle(Display *display,XWindows *windows)
6285{
6286 MagickRealType
6287 scale_factor;
6288
6289 RectangleInfo
6290 highlight_info;
6291
6292 /*
6293 Determine dimensions of the panning rectangle.
6294 */
6295 scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6296 highlight_info.x=(int) (scale_factor*windows->image.x+0.5);
6297 highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6298 scale_factor=(MagickRealType)
6299 windows->pan.height/windows->image.ximage->height;
6300 highlight_info.y=(int) (scale_factor*windows->image.y+0.5);
6301 highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6302 /*
6303 Display the panning rectangle.
6304 */
6305 (void) XClearWindow(display,windows->pan.id);
6306 XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6307 &highlight_info);
6308}
6309
6310/*
6311%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6312% %
6313% %
6314% %
6315+ X I m a g e C a c h e %
6316% %
6317% %
6318% %
6319%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6320%
6321% XImageCache() handles the creation, manipulation, and destruction of the
6322% image cache (undo and redo buffers).
6323%
6324% The format of the XImageCache method is:
6325%
6326% void XImageCache(Display *display,XResourceInfo *resource_info,
6327% XWindows *windows,const CommandType command,Image **image)
6328%
6329% A description of each parameter follows:
6330%
6331% o display: Specifies a connection to an X server; returned from
6332% XOpenDisplay.
6333%
6334% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6335%
6336% o windows: Specifies a pointer to a XWindows structure.
6337%
6338% o command: Specifies a command to perform.
6339%
6340% o image: the image; XImageCache
6341% may transform the image and return a new image pointer.
6342%
6343*/
6344static void XImageCache(Display *display,XResourceInfo *resource_info,
6345 XWindows *windows,const CommandType command,Image **image)
6346{
6347 Image
6348 *cache_image;
6349
6350 static Image
6351 *redo_image = (Image *) NULL,
6352 *undo_image = (Image *) NULL;
6353
6354 switch (command)
6355 {
6356 case FreeBuffersCommand:
6357 {
6358 /*
6359 Free memory from the undo and redo cache.
6360 */
6361 while (undo_image != (Image *) NULL)
6362 {
6363 cache_image=undo_image;
6364 undo_image=GetPreviousImageInList(undo_image);
6365 cache_image->list=DestroyImage(cache_image->list);
6366 cache_image=DestroyImage(cache_image);
6367 }
6368 undo_image=NewImageList();
6369 if (redo_image != (Image *) NULL)
6370 redo_image=DestroyImage(redo_image);
6371 redo_image=NewImageList();
6372 return;
6373 }
6374 case UndoCommand:
6375 {
6376 /*
6377 Undo the last image transformation.
6378 */
6379 if (undo_image == (Image *) NULL)
6380 {
6381 (void) XBell(display,0);
6382 return;
6383 }
6384 cache_image=undo_image;
6385 undo_image=GetPreviousImageInList(undo_image);
6386 windows->image.window_changes.width=(int) cache_image->columns;
6387 windows->image.window_changes.height=(int) cache_image->rows;
6388 if (windows->image.crop_geometry != (char *) NULL)
6389 windows->image.crop_geometry=(char *)
6390 RelinquishMagickMemory(windows->image.crop_geometry);
6391 windows->image.crop_geometry=cache_image->geometry;
6392 if (redo_image != (Image *) NULL)
6393 redo_image=DestroyImage(redo_image);
6394 redo_image=(*image);
6395 *image=cache_image->list;
6396 cache_image=DestroyImage(cache_image);
6397 if (windows->image.orphan != MagickFalse)
6398 return;
6399 XConfigureImageColormap(display,resource_info,windows,*image);
6400 (void) XConfigureImage(display,resource_info,windows,*image);
6401 return;
6402 }
6403 case CutCommand:
6404 case PasteCommand:
6405 case ApplyCommand:
6406 case HalfSizeCommand:
6407 case OriginalSizeCommand:
6408 case DoubleSizeCommand:
6409 case ResizeCommand:
6410 case TrimCommand:
6411 case CropCommand:
6412 case ChopCommand:
6413 case FlipCommand:
6414 case FlopCommand:
6415 case RotateRightCommand:
6416 case RotateLeftCommand:
6417 case RotateCommand:
6418 case ShearCommand:
6419 case RollCommand:
6420 case NegateCommand:
6421 case ContrastStretchCommand:
6422 case SigmoidalContrastCommand:
6423 case NormalizeCommand:
6424 case EqualizeCommand:
6425 case HueCommand:
6426 case SaturationCommand:
6427 case BrightnessCommand:
6428 case GammaCommand:
6429 case SpiffCommand:
6430 case DullCommand:
6431 case GrayscaleCommand:
6432 case MapCommand:
6433 case QuantizeCommand:
6434 case DespeckleCommand:
6435 case EmbossCommand:
6436 case ReduceNoiseCommand:
6437 case AddNoiseCommand:
6438 case SharpenCommand:
6439 case BlurCommand:
6440 case ThresholdCommand:
6441 case EdgeDetectCommand:
6442 case SpreadCommand:
6443 case ShadeCommand:
6444 case RaiseCommand:
6445 case SegmentCommand:
6446 case SolarizeCommand:
6447 case SepiaToneCommand:
6448 case SwirlCommand:
6449 case ImplodeCommand:
6450 case VignetteCommand:
6451 case WaveCommand:
6452 case OilPaintCommand:
6453 case CharcoalDrawCommand:
6454 case AnnotateCommand:
6455 case AddBorderCommand:
6456 case AddFrameCommand:
6457 case CompositeCommand:
6458 case CommentCommand:
6459 case LaunchCommand:
6460 case RegionofInterestCommand:
6461 case SaveToUndoBufferCommand:
6462 case RedoCommand:
6463 {
6464 Image
6465 *previous_image;
6466
6467 long
6468 bytes;
6469
6470 bytes=(long) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
6471 if (undo_image != (Image *) NULL)
6472 {
6473 /*
6474 Ensure the undo stash.has enough memory available.
6475 */
6476 previous_image=undo_image;
6477 while (previous_image != (Image *) NULL)
6478 {
6479 bytes+=previous_image->list->columns*previous_image->list->rows*
6480 sizeof(PixelPacket);
6481 if (bytes <= (long) (resource_info->undo_cache << 20))
6482 {
6483 previous_image=GetPreviousImageInList(previous_image);
6484 continue;
6485 }
6486 bytes-=previous_image->list->columns*previous_image->list->rows*
6487 sizeof(PixelPacket);
6488 if (previous_image == undo_image)
6489 undo_image=NewImageList();
6490 else
6491 previous_image->next->previous=NewImageList();
6492 break;
6493 }
6494 while (previous_image != (Image *) NULL)
6495 {
6496 /*
6497 Delete any excess memory from undo cache.
6498 */
6499 cache_image=previous_image;
6500 previous_image=GetPreviousImageInList(previous_image);
6501 cache_image->list=DestroyImage(cache_image->list);
6502 cache_image=DestroyImage(cache_image);
6503 }
6504 }
6505 if (bytes > (long) (resource_info->undo_cache << 20))
6506 break;
6507 /*
6508 Save image before transformations are applied.
6509 */
6510 cache_image=AcquireImage((ImageInfo *) NULL);
6511 if (cache_image == (Image *) NULL)
6512 break;
6513 XSetCursorState(display,windows,MagickTrue);
6514 XCheckRefreshWindows(display,windows);
6515 cache_image->list=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
6516 XSetCursorState(display,windows,MagickFalse);
6517 if (cache_image->list == (Image *) NULL)
6518 {
6519 cache_image=DestroyImage(cache_image);
6520 break;
6521 }
6522 cache_image->columns=(unsigned long) windows->image.ximage->width;
6523 cache_image->rows=(unsigned long) windows->image.ximage->height;
6524 cache_image->geometry=windows->image.crop_geometry;
6525 if (windows->image.crop_geometry != (char *) NULL)
6526 {
6527 cache_image->geometry=AcquireString((char *) NULL);
6528 (void) CopyMagickString(cache_image->geometry,
6529 windows->image.crop_geometry,MaxTextExtent);
6530 }
6531 if (undo_image == (Image *) NULL)
6532 {
6533 undo_image=cache_image;
6534 break;
6535 }
6536 undo_image->next=cache_image;
6537 undo_image->next->previous=undo_image;
6538 undo_image=undo_image->next;
6539 break;
6540 }
6541 default:
6542 break;
6543 }
6544 if (command == RedoCommand)
6545 {
6546 /*
6547 Redo the last image transformation.
6548 */
6549 if (redo_image == (Image *) NULL)
6550 {
6551 (void) XBell(display,0);
6552 return;
6553 }
6554 windows->image.window_changes.width=(int) redo_image->columns;
6555 windows->image.window_changes.height=(int) redo_image->rows;
6556 if (windows->image.crop_geometry != (char *) NULL)
6557 windows->image.crop_geometry=(char *)
6558 RelinquishMagickMemory(windows->image.crop_geometry);
6559 windows->image.crop_geometry=redo_image->geometry;
6560 *image=DestroyImage(*image);
6561 *image=redo_image;
6562 redo_image=NewImageList();
6563 if (windows->image.orphan != MagickFalse)
6564 return;
6565 XConfigureImageColormap(display,resource_info,windows,*image);
6566 (void) XConfigureImage(display,resource_info,windows,*image);
6567 return;
6568 }
6569 if (command != InfoCommand)
6570 return;
6571 /*
6572 Display image info.
6573 */
6574 XSetCursorState(display,windows,MagickTrue);
6575 XCheckRefreshWindows(display,windows);
6576 XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6577 XSetCursorState(display,windows,MagickFalse);
6578}
6579
6580/*
6581%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6582% %
6583% %
6584% %
6585+ X I m a g e W i n d o w C o m m a n d %
6586% %
6587% %
6588% %
6589%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6590%
6591% XImageWindowCommand() makes a transform to the image or Image window as
6592% specified by a user menu button or keyboard command.
6593%
6594% The format of the XMagickCommand method is:
6595%
6596% CommandType XImageWindowCommand(Display *display,
6597% XResourceInfo *resource_info,XWindows *windows,
6598% const MagickStatusType state,KeySym key_symbol,Image **image)
6599%
6600% A description of each parameter follows:
6601%
6602% o nexus: Method XImageWindowCommand returns an image when the
6603% user chooses 'Open Image' from the command menu. Otherwise a null
6604% image is returned.
6605%
6606% o display: Specifies a connection to an X server; returned from
6607% XOpenDisplay.
6608%
6609% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6610%
6611% o windows: Specifies a pointer to a XWindows structure.
6612%
6613% o state: key mask.
6614%
6615% o key_symbol: Specifies a command to perform.
6616%
6617% o image: the image; XImageWIndowCommand
6618% may transform the image and return a new image pointer.
6619%
6620*/
6621static CommandType XImageWindowCommand(Display *display,
6622 XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6623 KeySym key_symbol,Image **image)
6624{
6625 static char
6626 delta[MaxTextExtent] = "";
6627
6628 static const char
6629 Digits[] = "01234567890";
6630
6631 static KeySym
6632 last_symbol = XK_0;
6633
6634 if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6635 {
6636 if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6637 {
6638 *delta='\0';
6639 resource_info->quantum=1;
6640 }
6641 last_symbol=key_symbol;
6642 delta[strlen(delta)+1]='\0';
6643 delta[strlen(delta)]=Digits[key_symbol-XK_0];
6644 resource_info->quantum=atoi(delta);
6645 return(NullCommand);
6646 }
6647 last_symbol=key_symbol;
6648 if (resource_info->immutable)
6649 {
6650 /*
6651 Virtual image window has a restricted command set.
6652 */
6653 switch (key_symbol)
6654 {
6655 case XK_question:
6656 return(InfoCommand);
6657 case XK_p:
6658 case XK_Print:
6659 return(PrintCommand);
6660 case XK_space:
6661 return(NextCommand);
6662 case XK_q:
6663 case XK_Escape:
6664 return(QuitCommand);
6665 default:
6666 break;
6667 }
6668 return(NullCommand);
6669 }
6670 switch ((int) key_symbol)
6671 {
6672 case XK_o:
6673 {
6674 if ((state & ControlMask) == 0)
6675 break;
6676 return(OpenCommand);
6677 }
6678 case XK_space:
6679 return(NextCommand);
6680 case XK_BackSpace:
6681 return(FormerCommand);
6682 case XK_s:
6683 {
6684 if ((state & Mod1Mask) != 0)
6685 return(SwirlCommand);
6686 if ((state & ControlMask) == 0)
6687 return(ShearCommand);
6688 return(SaveCommand);
6689 }
6690 case XK_p:
6691 case XK_Print:
6692 {
6693 if ((state & Mod1Mask) != 0)
6694 return(OilPaintCommand);
6695 if ((state & Mod4Mask) != 0)
6696 return(ColorCommand);
6697 if ((state & ControlMask) == 0)
6698 return(NullCommand);
6699 return(PrintCommand);
6700 }
6701 case XK_d:
6702 {
6703 if ((state & Mod4Mask) != 0)
6704 return(DrawCommand);
6705 if ((state & ControlMask) == 0)
6706 return(NullCommand);
6707 return(DeleteCommand);
6708 }
6709 case XK_Select:
6710 {
6711 if ((state & ControlMask) == 0)
6712 return(NullCommand);
6713 return(SelectCommand);
6714 }
6715 case XK_n:
6716 {
6717 if ((state & ControlMask) == 0)
6718 return(NullCommand);
6719 return(NewCommand);
6720 }
6721 case XK_q:
6722 case XK_Escape:
6723 return(QuitCommand);
6724 case XK_z:
6725 case XK_Undo:
6726 {
6727 if ((state & ControlMask) == 0)
6728 return(NullCommand);
6729 return(UndoCommand);
6730 }
6731 case XK_r:
6732 case XK_Redo:
6733 {
6734 if ((state & ControlMask) == 0)
6735 return(RollCommand);
6736 return(RedoCommand);
6737 }
6738 case XK_x:
6739 {
6740 if ((state & ControlMask) == 0)
6741 return(NullCommand);
6742 return(CutCommand);
6743 }
6744 case XK_c:
6745 {
6746 if ((state & Mod1Mask) != 0)
6747 return(CharcoalDrawCommand);
6748 if ((state & ControlMask) == 0)
6749 return(CropCommand);
6750 return(CopyCommand);
6751 }
6752 case XK_v:
6753 case XK_Insert:
6754 {
6755 if ((state & Mod4Mask) != 0)
6756 return(CompositeCommand);
6757 if ((state & ControlMask) == 0)
6758 return(FlipCommand);
6759 return(PasteCommand);
6760 }
6761 case XK_less:
6762 return(HalfSizeCommand);
6763 case XK_minus:
6764 return(OriginalSizeCommand);
6765 case XK_greater:
6766 return(DoubleSizeCommand);
6767 case XK_percent:
6768 return(ResizeCommand);
6769 case XK_at:
6770 return(RefreshCommand);
6771 case XK_bracketleft:
6772 return(ChopCommand);
6773 case XK_h:
6774 return(FlopCommand);
6775 case XK_slash:
6776 return(RotateRightCommand);
6777 case XK_backslash:
6778 return(RotateLeftCommand);
6779 case XK_asterisk:
6780 return(RotateCommand);
6781 case XK_t:
6782 return(TrimCommand);
6783 case XK_H:
6784 return(HueCommand);
6785 case XK_S:
6786 return(SaturationCommand);
6787 case XK_L:
6788 return(BrightnessCommand);
6789 case XK_G:
6790 return(GammaCommand);
6791 case XK_C:
6792 return(SpiffCommand);
6793 case XK_Z:
6794 return(DullCommand);
6795 case XK_N:
6796 return(NormalizeCommand);
6797 case XK_equal:
6798 return(EqualizeCommand);
6799 case XK_asciitilde:
6800 return(NegateCommand);
6801 case XK_period:
6802 return(GrayscaleCommand);
6803 case XK_numbersign:
6804 return(QuantizeCommand);
6805 case XK_F2:
6806 return(DespeckleCommand);
6807 case XK_F3:
6808 return(EmbossCommand);
6809 case XK_F4:
6810 return(ReduceNoiseCommand);
6811 case XK_F5:
6812 return(AddNoiseCommand);
6813 case XK_F6:
6814 return(SharpenCommand);
6815 case XK_F7:
6816 return(BlurCommand);
6817 case XK_F8:
6818 return(ThresholdCommand);
6819 case XK_F9:
6820 return(EdgeDetectCommand);
6821 case XK_F10:
6822 return(SpreadCommand);
6823 case XK_F11:
6824 return(ShadeCommand);
6825 case XK_F12:
6826 return(RaiseCommand);
6827 case XK_F13:
6828 return(SegmentCommand);
6829 case XK_i:
6830 {
6831 if ((state & Mod1Mask) == 0)
6832 return(NullCommand);
6833 return(ImplodeCommand);
6834 }
6835 case XK_w:
6836 {
6837 if ((state & Mod1Mask) == 0)
6838 return(NullCommand);
6839 return(WaveCommand);
6840 }
6841 case XK_m:
6842 {
6843 if ((state & Mod4Mask) == 0)
6844 return(NullCommand);
6845 return(MatteCommand);
6846 }
6847 case XK_b:
6848 {
6849 if ((state & Mod4Mask) == 0)
6850 return(NullCommand);
6851 return(AddBorderCommand);
6852 }
6853 case XK_f:
6854 {
6855 if ((state & Mod4Mask) == 0)
6856 return(NullCommand);
6857 return(AddFrameCommand);
6858 }
6859 case XK_exclam:
6860 {
6861 if ((state & Mod4Mask) == 0)
6862 return(NullCommand);
6863 return(CommentCommand);
6864 }
6865 case XK_a:
6866 {
6867 if ((state & Mod1Mask) != 0)
6868 return(ApplyCommand);
6869 if ((state & Mod4Mask) != 0)
6870 return(AnnotateCommand);
6871 if ((state & ControlMask) == 0)
6872 return(NullCommand);
6873 return(RegionofInterestCommand);
6874 }
6875 case XK_question:
6876 return(InfoCommand);
6877 case XK_plus:
6878 return(ZoomCommand);
6879 case XK_P:
6880 {
6881 if ((state & ShiftMask) == 0)
6882 return(NullCommand);
6883 return(ShowPreviewCommand);
6884 }
6885 case XK_Execute:
6886 return(LaunchCommand);
6887 case XK_F1:
6888 return(HelpCommand);
6889 case XK_Find:
6890 return(BrowseDocumentationCommand);
6891 case XK_Menu:
6892 {
6893 (void) XMapRaised(display,windows->command.id);
6894 return(NullCommand);
6895 }
6896 case XK_Next:
6897 case XK_Prior:
6898 case XK_Home:
6899 case XK_KP_Home:
6900 {
6901 XTranslateImage(display,windows,*image,key_symbol);
6902 return(NullCommand);
6903 }
6904 case XK_Up:
6905 case XK_KP_Up:
6906 case XK_Down:
6907 case XK_KP_Down:
6908 case XK_Left:
6909 case XK_KP_Left:
6910 case XK_Right:
6911 case XK_KP_Right:
6912 {
6913 if ((state & Mod1Mask) != 0)
6914 {
6915 RectangleInfo
6916 crop_info;
6917
6918 /*
6919 Trim one pixel from edge of image.
6920 */
6921 crop_info.x=0;
6922 crop_info.y=0;
6923 crop_info.width=(unsigned long) windows->image.ximage->width;
6924 crop_info.height=(unsigned long) windows->image.ximage->height;
6925 if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
6926 {
6927 if (resource_info->quantum >= (int) crop_info.height)
6928 resource_info->quantum=(int) crop_info.height-1;
6929 crop_info.height-=resource_info->quantum;
6930 }
6931 if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
6932 {
6933 if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
6934 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
6935 crop_info.y+=resource_info->quantum;
6936 crop_info.height-=resource_info->quantum;
6937 }
6938 if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
6939 {
6940 if (resource_info->quantum >= (int) crop_info.width)
6941 resource_info->quantum=(int) crop_info.width-1;
6942 crop_info.width-=resource_info->quantum;
6943 }
6944 if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
6945 {
6946 if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
6947 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
6948 crop_info.x+=resource_info->quantum;
6949 crop_info.width-=resource_info->quantum;
6950 }
6951 if ((int) (windows->image.x+windows->image.width) >
6952 (int) crop_info.width)
6953 windows->image.x=(int) (crop_info.width-windows->image.width);
6954 if ((int) (windows->image.y+windows->image.height) >
6955 (int) crop_info.height)
6956 windows->image.y=(int) (crop_info.height-windows->image.height);
6957 XSetCropGeometry(display,windows,&crop_info,*image);
6958 windows->image.window_changes.width=(int) crop_info.width;
6959 windows->image.window_changes.height=(int) crop_info.height;
6960 (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
6961 (void) XConfigureImage(display,resource_info,windows,*image);
6962 return(NullCommand);
6963 }
6964 XTranslateImage(display,windows,*image,key_symbol);
6965 return(NullCommand);
6966 }
6967 default:
6968 return(NullCommand);
6969 }
6970 return(NullCommand);
6971}
6972
6973/*
6974%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6975% %
6976% %
6977% %
6978+ X M a g i c k C o m m a n d %
6979% %
6980% %
6981% %
6982%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6983%
6984% XMagickCommand() makes a transform to the image or Image window as
6985% specified by a user menu button or keyboard command.
6986%
6987% The format of the XMagickCommand method is:
6988%
6989% Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
6990% XWindows *windows,const CommandType command,Image **image)
6991%
6992% A description of each parameter follows:
6993%
6994% o nexus: Method XMagickCommand returns an image when the
6995% user chooses 'Load Image' from the command menu. Otherwise a null
6996% image is returned.
6997%
6998% o display: Specifies a connection to an X server; returned from
6999% XOpenDisplay.
7000%
7001% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7002%
7003% o windows: Specifies a pointer to a XWindows structure.
7004%
7005% o command: Specifies a command to perform.
7006%
7007% o image: the image; XMagickCommand
7008% may transform the image and return a new image pointer.
7009%
7010*/
7011static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7012 XWindows *windows,const CommandType command,Image **image)
7013{
7014 char
7015 filename[MaxTextExtent],
7016 geometry[MaxTextExtent],
7017 modulate_factors[MaxTextExtent];
7018
7019 GeometryInfo
7020 geometry_info;
7021
7022 Image
7023 *nexus;
7024
7025 ImageInfo
7026 *image_info;
7027
7028 int
7029 x,
7030 y;
7031
7032 MagickStatusType
7033 flags,
7034 status;
7035
7036 QuantizeInfo
7037 quantize_info;
7038
7039 RectangleInfo
7040 page_geometry;
7041
7042 register int
7043 i;
7044
7045 static char
7046 color[MaxTextExtent] = "gray";
7047
7048 unsigned int
7049 height,
7050 width;
7051
7052 /*
7053 Process user command.
7054 */
7055 XCheckRefreshWindows(display,windows);
7056 XImageCache(display,resource_info,windows,command,image);
7057 nexus=NewImageList();
7058 windows->image.window_changes.width=windows->image.ximage->width;
7059 windows->image.window_changes.height=windows->image.ximage->height;
7060 image_info=CloneImageInfo(resource_info->image_info);
7061 SetGeometryInfo(&geometry_info);
7062 GetQuantizeInfo(&quantize_info);
7063 switch (command)
7064 {
7065 case OpenCommand:
7066 {
7067 /*
7068 Load image.
7069 */
7070 nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7071 break;
7072 }
7073 case NextCommand:
7074 {
7075 /*
7076 Display next image.
7077 */
7078 for (i=0; i < resource_info->quantum; i++)
7079 XClientMessage(display,windows->image.id,windows->im_protocols,
7080 windows->im_next_image,CurrentTime);
7081 break;
7082 }
7083 case FormerCommand:
7084 {
7085 /*
7086 Display former image.
7087 */
7088 for (i=0; i < resource_info->quantum; i++)
7089 XClientMessage(display,windows->image.id,windows->im_protocols,
7090 windows->im_former_image,CurrentTime);
7091 break;
7092 }
7093 case SelectCommand:
7094 {
7095 int
7096 status;
7097
7098 /*
7099 Select image.
7100 */
7101 status=chdir(resource_info->home_directory);
7102 if (status == -1)
7103 (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
7104 FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
7105 nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7106 break;
7107 }
7108 case SaveCommand:
7109 {
7110 /*
7111 Save image.
7112 */
7113 status=XSaveImage(display,resource_info,windows,*image);
7114 if (status == MagickFalse)
7115 {
7116 XNoticeWidget(display,windows,"Unable to write X image:",
7117 (*image)->filename);
7118 break;
7119 }
7120 break;
7121 }
7122 case PrintCommand:
7123 {
7124 /*
7125 Print image.
7126 */
7127 status=XPrintImage(display,resource_info,windows,*image);
7128 if (status == MagickFalse)
7129 {
7130 XNoticeWidget(display,windows,"Unable to print X image:",
7131 (*image)->filename);
7132 break;
7133 }
7134 break;
7135 }
7136 case DeleteCommand:
7137 {
7138 static char
7139 filename[MaxTextExtent] = "\0";
7140
7141 /*
7142 Delete image file.
7143 */
7144 XFileBrowserWidget(display,windows,"Delete",filename);
7145 if (*filename == '\0')
7146 break;
7147 status=remove(filename) != 0 ? MagickTrue : MagickFalse;
7148 if (status != MagickFalse)
7149 XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7150 break;
7151 }
7152 case NewCommand:
7153 {
7154 int
7155 status;
7156
7157 static char
7158 color[MaxTextExtent] = "gray",
7159 geometry[MaxTextExtent] = "640x480";
7160
7161 static const char
7162 *format = "gradient";
7163
7164 /*
7165 Query user for canvas geometry.
7166 */
7167 status=XDialogWidget(display,windows,"New","Enter image geometry:",
7168 geometry);
7169 if (*geometry == '\0')
7170 break;
7171 if (status == 0)
7172 format="xc";
7173 XColorBrowserWidget(display,windows,"Select",color);
7174 if (*color == '\0')
7175 break;
7176 /*
7177 Create canvas.
7178 */
7179 (void) FormatMagickString(image_info->filename,MaxTextExtent,
7180 "%s:%s",format,color);
7181 (void) CloneString(&image_info->size,geometry);
7182 nexus=ReadImage(image_info,&(*image)->exception);
7183 CatchException(&(*image)->exception);
7184 XClientMessage(display,windows->image.id,windows->im_protocols,
7185 windows->im_next_image,CurrentTime);
7186 break;
7187 }
7188 case VisualDirectoryCommand:
7189 {
7190 /*
7191 Visual Image directory.
7192 */
7193 nexus=XVisualDirectoryImage(display,resource_info,windows);
7194 break;
7195 }
7196 case QuitCommand:
7197 {
7198 /*
7199 exit program.
7200 */
7201 if (resource_info->confirm_exit == MagickFalse)
7202 XClientMessage(display,windows->image.id,windows->im_protocols,
7203 windows->im_exit,CurrentTime);
7204 else
7205 {
7206 int
7207 status;
7208
7209 /*
7210 Confirm program exit.
7211 */
7212 status=XConfirmWidget(display,windows,"Do you really want to exit",
7213 resource_info->client_name);
7214 if (status > 0)
7215 XClientMessage(display,windows->image.id,windows->im_protocols,
7216 windows->im_exit,CurrentTime);
7217 }
7218 break;
7219 }
7220 case CutCommand:
7221 {
7222 /*
7223 Cut image.
7224 */
7225 (void) XCropImage(display,resource_info,windows,*image,CutMode);
7226 break;
7227 }
7228 case CopyCommand:
7229 {
7230 /*
7231 Copy image.
7232 */
7233 (void) XCropImage(display,resource_info,windows,*image,CopyMode);
7234 break;
7235 }
7236 case PasteCommand:
7237 {
7238 /*
7239 Paste image.
7240 */
7241 status=XPasteImage(display,resource_info,windows,*image);
7242 if (status == MagickFalse)
7243 {
7244 XNoticeWidget(display,windows,"Unable to paste X image",
7245 (*image)->filename);
7246 break;
7247 }
7248 break;
7249 }
7250 case HalfSizeCommand:
7251 {
7252 /*
7253 Half image size.
7254 */
7255 windows->image.window_changes.width=windows->image.ximage->width/2;
7256 windows->image.window_changes.height=windows->image.ximage->height/2;
7257 (void) XConfigureImage(display,resource_info,windows,*image);
7258 break;
7259 }
7260 case OriginalSizeCommand:
7261 {
7262 /*
7263 Original image size.
7264 */
7265 windows->image.window_changes.width=(int) (*image)->columns;
7266 windows->image.window_changes.height=(int) (*image)->rows;
7267 (void) XConfigureImage(display,resource_info,windows,*image);
7268 break;
7269 }
7270 case DoubleSizeCommand:
7271 {
7272 /*
7273 Double the image size.
7274 */
7275 windows->image.window_changes.width=windows->image.ximage->width << 1;
7276 windows->image.window_changes.height=windows->image.ximage->height << 1;
7277 (void) XConfigureImage(display,resource_info,windows,*image);
7278 break;
7279 }
7280 case ResizeCommand:
7281 {
7282 int
7283 status;
7284
7285 long
7286 x,
7287 y;
7288
7289 unsigned long
7290 height,
7291 width;
7292
7293 /*
7294 Resize image.
7295 */
7296 width=(unsigned long) windows->image.ximage->width;
7297 height=(unsigned long) windows->image.ximage->height;
7298 x=0;
7299 y=0;
7300 (void) FormatMagickString(geometry,MaxTextExtent,"%lux%lu+0+0",
7301 width,height);
7302 status=XDialogWidget(display,windows,"Resize",
7303 "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7304 if (*geometry == '\0')
7305 break;
7306 if (status == 0)
7307 (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7308 (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7309 windows->image.window_changes.width=(int) width;
7310 windows->image.window_changes.height=(int) height;
7311 (void) XConfigureImage(display,resource_info,windows,*image);
7312 break;
7313 }
7314 case ApplyCommand:
7315 {
7316 char
7317 image_geometry[MaxTextExtent];
7318
7319 if ((windows->image.crop_geometry == (char *) NULL) &&
7320 ((int) (*image)->columns == windows->image.ximage->width) &&
7321 ((int) (*image)->rows == windows->image.ximage->height))
7322 break;
7323 /*
7324 Apply size transforms to image.
7325 */
7326 XSetCursorState(display,windows,MagickTrue);
7327 XCheckRefreshWindows(display,windows);
7328 /*
7329 Crop and/or scale displayed image.
7330 */
7331 (void) FormatMagickString(image_geometry,MaxTextExtent,"%dx%d!",
7332 windows->image.ximage->width,windows->image.ximage->height);
7333 (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7334 if (windows->image.crop_geometry != (char *) NULL)
7335 windows->image.crop_geometry=(char *)
7336 RelinquishMagickMemory(windows->image.crop_geometry);
7337 windows->image.x=0;
7338 windows->image.y=0;
7339 XConfigureImageColormap(display,resource_info,windows,*image);
7340 (void) XConfigureImage(display,resource_info,windows,*image);
7341 break;
7342 }
7343 case RefreshCommand:
7344 {
7345 (void) XConfigureImage(display,resource_info,windows,*image);
7346 break;
7347 }
7348 case RestoreCommand:
7349 {
7350 /*
7351 Restore Image window to its original size.
7352 */
7353 if ((windows->image.width == (unsigned int) (*image)->columns) &&
7354 (windows->image.height == (unsigned int) (*image)->rows) &&
7355 (windows->image.crop_geometry == (char *) NULL))
7356 {
7357 (void) XBell(display,0);
7358 break;
7359 }
7360 windows->image.window_changes.width=(int) (*image)->columns;
7361 windows->image.window_changes.height=(int) (*image)->rows;
7362 if (windows->image.crop_geometry != (char *) NULL)
7363 {
7364 windows->image.crop_geometry=(char *)
7365 RelinquishMagickMemory(windows->image.crop_geometry);
7366 windows->image.crop_geometry=(char *) NULL;
7367 windows->image.x=0;
7368 windows->image.y=0;
7369 }
7370 XConfigureImageColormap(display,resource_info,windows,*image);
7371 (void) XConfigureImage(display,resource_info,windows,*image);
7372 break;
7373 }
7374 case CropCommand:
7375 {
7376 /*
7377 Crop image.
7378 */
7379 (void) XCropImage(display,resource_info,windows,*image,CropMode);
7380 break;
7381 }
7382 case ChopCommand:
7383 {
7384 /*
7385 Chop image.
7386 */
7387 status=XChopImage(display,resource_info,windows,image);
7388 if (status == MagickFalse)
7389 {
7390 XNoticeWidget(display,windows,"Unable to cut X image",
7391 (*image)->filename);
7392 break;
7393 }
7394 break;
7395 }
7396 case FlopCommand:
7397 {
7398 Image
7399 *flop_image;
7400
7401 /*
7402 Flop image scanlines.
7403 */
7404 XSetCursorState(display,windows,MagickTrue);
7405 XCheckRefreshWindows(display,windows);
7406 flop_image=FlopImage(*image,&(*image)->exception);
7407 if (flop_image != (Image *) NULL)
7408 {
7409 *image=DestroyImage(*image);
7410 *image=flop_image;
7411 }
7412 CatchException(&(*image)->exception);
7413 XSetCursorState(display,windows,MagickFalse);
7414 if (windows->image.crop_geometry != (char *) NULL)
7415 {
7416 /*
7417 Flop crop geometry.
7418 */
7419 width=(unsigned int) (*image)->columns;
7420 height=(unsigned int) (*image)->rows;
7421 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7422 &width,&height);
7423 (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
7424 "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7425 }
7426 if (windows->image.orphan != MagickFalse)
7427 break;
7428 (void) XConfigureImage(display,resource_info,windows,*image);
7429 break;
7430 }
7431 case FlipCommand:
7432 {
7433 Image
7434 *flip_image;
7435
7436 /*
7437 Flip image scanlines.
7438 */
7439 XSetCursorState(display,windows,MagickTrue);
7440 XCheckRefreshWindows(display,windows);
7441 flip_image=FlipImage(*image,&(*image)->exception);
7442 if (flip_image != (Image *) NULL)
7443 {
7444 *image=DestroyImage(*image);
7445 *image=flip_image;
7446 }
7447 CatchException(&(*image)->exception);
7448 XSetCursorState(display,windows,MagickFalse);
7449 if (windows->image.crop_geometry != (char *) NULL)
7450 {
7451 /*
7452 Flip crop geometry.
7453 */
7454 width=(unsigned int) (*image)->columns;
7455 height=(unsigned int) (*image)->rows;
7456 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7457 &width,&height);
7458 (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
7459 "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7460 }
7461 if (windows->image.orphan != MagickFalse)
7462 break;
7463 (void) XConfigureImage(display,resource_info,windows,*image);
7464 break;
7465 }
7466 case RotateRightCommand:
7467 {
7468 /*
7469 Rotate image 90 degrees clockwise.
7470 */
7471 status=XRotateImage(display,resource_info,windows,90.0,image);
7472 if (status == MagickFalse)
7473 {
7474 XNoticeWidget(display,windows,"Unable to rotate X image",
7475 (*image)->filename);
7476 break;
7477 }
7478 break;
7479 }
7480 case RotateLeftCommand:
7481 {
7482 /*
7483 Rotate image 90 degrees counter-clockwise.
7484 */
7485 status=XRotateImage(display,resource_info,windows,-90.0,image);
7486 if (status == MagickFalse)
7487 {
7488 XNoticeWidget(display,windows,"Unable to rotate X image",
7489 (*image)->filename);
7490 break;
7491 }
7492 break;
7493 }
7494 case RotateCommand:
7495 {
7496 /*
7497 Rotate image.
7498 */
7499 status=XRotateImage(display,resource_info,windows,0.0,image);
7500 if (status == MagickFalse)
7501 {
7502 XNoticeWidget(display,windows,"Unable to rotate X image",
7503 (*image)->filename);
7504 break;
7505 }
7506 break;
7507 }
7508 case ShearCommand:
7509 {
7510 Image
7511 *shear_image;
7512
7513 static char
7514 geometry[MaxTextExtent] = "45.0x45.0";
7515
7516 /*
7517 Query user for shear color and geometry.
7518 */
7519 XColorBrowserWidget(display,windows,"Select",color);
7520 if (*color == '\0')
7521 break;
7522 (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7523 geometry);
7524 if (*geometry == '\0')
7525 break;
7526 /*
7527 Shear image.
7528 */
7529 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7530 XSetCursorState(display,windows,MagickTrue);
7531 XCheckRefreshWindows(display,windows);
7532 (void) QueryColorDatabase(color,&(*image)->background_color,
7533 &(*image)->exception);
7534 flags=ParseGeometry(geometry,&geometry_info);
7535 if ((flags & SigmaValue) == 0)
7536 geometry_info.sigma=geometry_info.rho;
7537 shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7538 &(*image)->exception);
7539 if (shear_image != (Image *) NULL)
7540 {
7541 *image=DestroyImage(*image);
7542 *image=shear_image;
7543 }
7544 CatchException(&(*image)->exception);
7545 XSetCursorState(display,windows,MagickFalse);
7546 if (windows->image.orphan != MagickFalse)
7547 break;
7548 windows->image.window_changes.width=(int) (*image)->columns;
7549 windows->image.window_changes.height=(int) (*image)->rows;
7550 XConfigureImageColormap(display,resource_info,windows,*image);
7551 (void) XConfigureImage(display,resource_info,windows,*image);
7552 break;
7553 }
7554 case RollCommand:
7555 {
7556 Image
7557 *roll_image;
7558
7559 static char
7560 geometry[MaxTextExtent] = "+2+2";
7561
7562 /*
7563 Query user for the roll geometry.
7564 */
7565 (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7566 geometry);
7567 if (*geometry == '\0')
7568 break;
7569 /*
7570 Roll image.
7571 */
7572 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7573 XSetCursorState(display,windows,MagickTrue);
7574 XCheckRefreshWindows(display,windows);
7575 (void) ParsePageGeometry(*image,geometry,&page_geometry,
7576 &(*image)->exception);
7577 roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7578 &(*image)->exception);
7579 if (roll_image != (Image *) NULL)
7580 {
7581 *image=DestroyImage(*image);
7582 *image=roll_image;
7583 }
7584 CatchException(&(*image)->exception);
7585 XSetCursorState(display,windows,MagickFalse);
7586 if (windows->image.orphan != MagickFalse)
7587 break;
7588 windows->image.window_changes.width=(int) (*image)->columns;
7589 windows->image.window_changes.height=(int) (*image)->rows;
7590 XConfigureImageColormap(display,resource_info,windows,*image);
7591 (void) XConfigureImage(display,resource_info,windows,*image);
7592 break;
7593 }
7594 case TrimCommand:
7595 {
7596 static char
7597 fuzz[MaxTextExtent];
7598
7599 /*
7600 Query user for the fuzz factor.
7601 */
7602 (void) FormatMagickString(fuzz,MaxTextExtent,"%g%%",100.0*(*image)->fuzz/
7603 (QuantumRange+1.0));
7604 (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7605 if (*fuzz == '\0')
7606 break;
7607 (*image)->fuzz=StringToDouble(fuzz,(double) QuantumRange+1.0);
7608 /*
7609 Trim image.
7610 */
7611 status=XTrimImage(display,resource_info,windows,*image);
7612 if (status == MagickFalse)
7613 {
7614 XNoticeWidget(display,windows,"Unable to trim X image",
7615 (*image)->filename);
7616 break;
7617 }
7618 break;
7619 }
7620 case HueCommand:
7621 {
7622 static char
7623 hue_percent[MaxTextExtent] = "110";
7624
7625 /*
7626 Query user for percent hue change.
7627 */
7628 (void) XDialogWidget(display,windows,"Apply",
7629 "Enter percent change in image hue (0-200):",hue_percent);
7630 if (*hue_percent == '\0')
7631 break;
7632 /*
7633 Vary the image hue.
7634 */
7635 XSetCursorState(display,windows,MagickTrue);
7636 XCheckRefreshWindows(display,windows);
7637 (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7638 (void) ConcatenateMagickString(modulate_factors,hue_percent,
7639 MaxTextExtent);
7640 (void) ModulateImage(*image,modulate_factors);
7641 XSetCursorState(display,windows,MagickFalse);
7642 if (windows->image.orphan != MagickFalse)
7643 break;
7644 XConfigureImageColormap(display,resource_info,windows,*image);
7645 (void) XConfigureImage(display,resource_info,windows,*image);
7646 break;
7647 }
7648 case SaturationCommand:
7649 {
7650 static char
7651 saturation_percent[MaxTextExtent] = "110";
7652
7653 /*
7654 Query user for percent saturation change.
7655 */
7656 (void) XDialogWidget(display,windows,"Apply",
7657 "Enter percent change in color saturation (0-200):",saturation_percent);
7658 if (*saturation_percent == '\0')
7659 break;
7660 /*
7661 Vary color saturation.
7662 */
7663 XSetCursorState(display,windows,MagickTrue);
7664 XCheckRefreshWindows(display,windows);
7665 (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7666 (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7667 MaxTextExtent);
7668 (void) ModulateImage(*image,modulate_factors);
7669 XSetCursorState(display,windows,MagickFalse);
7670 if (windows->image.orphan != MagickFalse)
7671 break;
7672 XConfigureImageColormap(display,resource_info,windows,*image);
7673 (void) XConfigureImage(display,resource_info,windows,*image);
7674 break;
7675 }
7676 case BrightnessCommand:
7677 {
7678 static char
7679 brightness_percent[MaxTextExtent] = "110";
7680
7681 /*
7682 Query user for percent brightness change.
7683 */
7684 (void) XDialogWidget(display,windows,"Apply",
7685 "Enter percent change in color brightness (0-200):",brightness_percent);
7686 if (*brightness_percent == '\0')
7687 break;
7688 /*
7689 Vary the color brightness.
7690 */
7691 XSetCursorState(display,windows,MagickTrue);
7692 XCheckRefreshWindows(display,windows);
7693 (void) CopyMagickString(modulate_factors,brightness_percent,
7694 MaxTextExtent);
7695 (void) ModulateImage(*image,modulate_factors);
7696 XSetCursorState(display,windows,MagickFalse);
7697 if (windows->image.orphan != MagickFalse)
7698 break;
7699 XConfigureImageColormap(display,resource_info,windows,*image);
7700 (void) XConfigureImage(display,resource_info,windows,*image);
7701 break;
7702 }
7703 case GammaCommand:
7704 {
7705 static char
7706 factor[MaxTextExtent] = "1.6";
7707
7708 /*
7709 Query user for gamma value.
7710 */
7711 (void) XDialogWidget(display,windows,"Gamma",
7712 "Enter gamma value (e.g. 1.0,1.0,1.6):",factor);
7713 if (*factor == '\0')
7714 break;
7715 /*
7716 Gamma correct image.
7717 */
7718 XSetCursorState(display,windows,MagickTrue);
7719 XCheckRefreshWindows(display,windows);
7720 (void) GammaImage(*image,factor);
7721 XSetCursorState(display,windows,MagickFalse);
7722 if (windows->image.orphan != MagickFalse)
7723 break;
7724 XConfigureImageColormap(display,resource_info,windows,*image);
7725 (void) XConfigureImage(display,resource_info,windows,*image);
7726 break;
7727 }
7728 case SpiffCommand:
7729 {
7730 /*
7731 Sharpen the image contrast.
7732 */
7733 XSetCursorState(display,windows,MagickTrue);
7734 XCheckRefreshWindows(display,windows);
7735 (void) ContrastImage(*image,MagickTrue);
7736 XSetCursorState(display,windows,MagickFalse);
7737 if (windows->image.orphan != MagickFalse)
7738 break;
7739 XConfigureImageColormap(display,resource_info,windows,*image);
7740 (void) XConfigureImage(display,resource_info,windows,*image);
7741 break;
7742 }
7743 case DullCommand:
7744 {
7745 /*
7746 Dull the image contrast.
7747 */
7748 XSetCursorState(display,windows,MagickTrue);
7749 XCheckRefreshWindows(display,windows);
7750 (void) ContrastImage(*image,MagickFalse);
7751 XSetCursorState(display,windows,MagickFalse);
7752 if (windows->image.orphan != MagickFalse)
7753 break;
7754 XConfigureImageColormap(display,resource_info,windows,*image);
7755 (void) XConfigureImage(display,resource_info,windows,*image);
7756 break;
7757 }
7758 case ContrastStretchCommand:
7759 {
7760 double
7761 black_point,
7762 white_point;
7763
7764 static char
7765 levels[MaxTextExtent] = "1%";
7766
7767 /*
7768 Query user for gamma value.
7769 */
7770 (void) XDialogWidget(display,windows,"Contrast Stretch",
7771 "Enter black and white points:",levels);
7772 if (*levels == '\0')
7773 break;
7774 /*
7775 Contrast stretch image.
7776 */
7777 XSetCursorState(display,windows,MagickTrue);
7778 XCheckRefreshWindows(display,windows);
7779 flags=ParseGeometry(levels,&geometry_info);
7780 black_point=geometry_info.rho;
7781 white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7782 if ((flags & PercentValue) != 0)
7783 {
7784 black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7785 white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7786 }
7787 white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7788 (void) ContrastStretchImageChannel(*image,DefaultChannels,black_point,
7789 white_point);
7790 XSetCursorState(display,windows,MagickFalse);
7791 if (windows->image.orphan != MagickFalse)
7792 break;
7793 XConfigureImageColormap(display,resource_info,windows,*image);
7794 (void) XConfigureImage(display,resource_info,windows,*image);
7795 break;
7796 }
7797 case SigmoidalContrastCommand:
7798 {
7799 static char
7800 levels[MaxTextExtent] = "3x50%";
7801
7802 /*
7803 Query user for gamma value.
7804 */
7805 (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7806 "Enter contrast and midpoint:",levels);
7807 if (*levels == '\0')
7808 break;
7809 /*
7810 Contrast stretch image.
7811 */
7812 XSetCursorState(display,windows,MagickTrue);
7813 XCheckRefreshWindows(display,windows);
7814 (void) SigmoidalContrastImage(*image,MagickTrue,levels);
7815 XSetCursorState(display,windows,MagickFalse);
7816 if (windows->image.orphan != MagickFalse)
7817 break;
7818 XConfigureImageColormap(display,resource_info,windows,*image);
7819 (void) XConfigureImage(display,resource_info,windows,*image);
7820 break;
7821 }
7822 case NormalizeCommand:
7823 {
7824 /*
7825 Perform histogram normalization on the image.
7826 */
7827 XSetCursorState(display,windows,MagickTrue);
7828 XCheckRefreshWindows(display,windows);
7829 (void) NormalizeImage(*image);
7830 XSetCursorState(display,windows,MagickFalse);
7831 if (windows->image.orphan != MagickFalse)
7832 break;
7833 XConfigureImageColormap(display,resource_info,windows,*image);
7834 (void) XConfigureImage(display,resource_info,windows,*image);
7835 break;
7836 }
7837 case EqualizeCommand:
7838 {
7839 /*
7840 Perform histogram equalization on the image.
7841 */
7842 XSetCursorState(display,windows,MagickTrue);
7843 XCheckRefreshWindows(display,windows);
7844 (void) EqualizeImage(*image);
7845 XSetCursorState(display,windows,MagickFalse);
7846 if (windows->image.orphan != MagickFalse)
7847 break;
7848 XConfigureImageColormap(display,resource_info,windows,*image);
7849 (void) XConfigureImage(display,resource_info,windows,*image);
7850 break;
7851 }
7852 case NegateCommand:
7853 {
7854 /*
7855 Negate colors in image.
7856 */
7857 XSetCursorState(display,windows,MagickTrue);
7858 XCheckRefreshWindows(display,windows);
7859 (void) NegateImage(*image,MagickFalse);
7860 XSetCursorState(display,windows,MagickFalse);
7861 if (windows->image.orphan != MagickFalse)
7862 break;
7863 XConfigureImageColormap(display,resource_info,windows,*image);
7864 (void) XConfigureImage(display,resource_info,windows,*image);
7865 break;
7866 }
7867 case GrayscaleCommand:
7868 {
7869 /*
7870 Convert image to grayscale.
7871 */
7872 XSetCursorState(display,windows,MagickTrue);
7873 XCheckRefreshWindows(display,windows);
7874 (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7875 GrayscaleType : GrayscaleMatteType);
7876 XSetCursorState(display,windows,MagickFalse);
7877 if (windows->image.orphan != MagickFalse)
7878 break;
7879 XConfigureImageColormap(display,resource_info,windows,*image);
7880 (void) XConfigureImage(display,resource_info,windows,*image);
7881 break;
7882 }
7883 case MapCommand:
7884 {
7885 Image
7886 *affinity_image;
7887
7888 static char
7889 filename[MaxTextExtent] = "\0";
7890
7891 /*
7892 Request image file name from user.
7893 */
7894 XFileBrowserWidget(display,windows,"Map",filename);
7895 if (*filename == '\0')
7896 break;
7897 /*
7898 Map image.
7899 */
7900 XSetCursorState(display,windows,MagickTrue);
7901 XCheckRefreshWindows(display,windows);
7902 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
7903 affinity_image=ReadImage(image_info,&(*image)->exception);
7904 if (affinity_image != (Image *) NULL)
7905 {
7906 (void) RemapImage(&quantize_info,*image,affinity_image);
7907 affinity_image=DestroyImage(affinity_image);
7908 }
7909 CatchException(&(*image)->exception);
7910 XSetCursorState(display,windows,MagickFalse);
7911 if (windows->image.orphan != MagickFalse)
7912 break;
7913 XConfigureImageColormap(display,resource_info,windows,*image);
7914 (void) XConfigureImage(display,resource_info,windows,*image);
7915 break;
7916 }
7917 case QuantizeCommand:
7918 {
7919 int
7920 status;
7921
7922 static char
7923 colors[MaxTextExtent] = "256";
7924
7925 /*
7926 Query user for maximum number of colors.
7927 */
7928 status=XDialogWidget(display,windows,"Quantize",
7929 "Maximum number of colors:",colors);
7930 if (*colors == '\0')
7931 break;
7932 /*
7933 Color reduce the image.
7934 */
7935 XSetCursorState(display,windows,MagickTrue);
7936 XCheckRefreshWindows(display,windows);
7937 quantize_info.number_colors=(unsigned long) atol(colors);
7938 quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
7939 (void) QuantizeImage(&quantize_info,*image);
7940 XSetCursorState(display,windows,MagickFalse);
7941 if (windows->image.orphan != MagickFalse)
7942 break;
7943 XConfigureImageColormap(display,resource_info,windows,*image);
7944 (void) XConfigureImage(display,resource_info,windows,*image);
7945 break;
7946 }
7947 case DespeckleCommand:
7948 {
7949 Image
7950 *despeckle_image;
7951
7952 /*
7953 Despeckle image.
7954 */
7955 XSetCursorState(display,windows,MagickTrue);
7956 XCheckRefreshWindows(display,windows);
7957 despeckle_image=DespeckleImage(*image,&(*image)->exception);
7958 if (despeckle_image != (Image *) NULL)
7959 {
7960 *image=DestroyImage(*image);
7961 *image=despeckle_image;
7962 }
7963 CatchException(&(*image)->exception);
7964 XSetCursorState(display,windows,MagickFalse);
7965 if (windows->image.orphan != MagickFalse)
7966 break;
7967 XConfigureImageColormap(display,resource_info,windows,*image);
7968 (void) XConfigureImage(display,resource_info,windows,*image);
7969 break;
7970 }
7971 case EmbossCommand:
7972 {
7973 Image
7974 *emboss_image;
7975
7976 static char
7977 radius[MaxTextExtent] = "0.0x1.0";
7978
7979 /*
7980 Query user for emboss radius.
7981 */
7982 (void) XDialogWidget(display,windows,"Emboss",
7983 "Enter the emboss radius and standard deviation:",radius);
7984 if (*radius == '\0')
7985 break;
7986 /*
7987 Reduce noise in the image.
7988 */
7989 XSetCursorState(display,windows,MagickTrue);
7990 XCheckRefreshWindows(display,windows);
7991 flags=ParseGeometry(radius,&geometry_info);
7992 if ((flags & SigmaValue) == 0)
7993 geometry_info.sigma=1.0;
7994 emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
7995 &(*image)->exception);
7996 if (emboss_image != (Image *) NULL)
7997 {
7998 *image=DestroyImage(*image);
7999 *image=emboss_image;
8000 }
8001 CatchException(&(*image)->exception);
8002 XSetCursorState(display,windows,MagickFalse);
8003 if (windows->image.orphan != MagickFalse)
8004 break;
8005 XConfigureImageColormap(display,resource_info,windows,*image);
8006 (void) XConfigureImage(display,resource_info,windows,*image);
8007 break;
8008 }
8009 case ReduceNoiseCommand:
8010 {
8011 Image
8012 *noise_image;
8013
8014 static char
8015 radius[MaxTextExtent] = "0";
8016
8017 /*
8018 Query user for noise radius.
8019 */
8020 (void) XDialogWidget(display,windows,"Reduce Noise",
8021 "Enter the noise radius:",radius);
8022 if (*radius == '\0')
8023 break;
8024 /*
8025 Reduce noise in the image.
8026 */
8027 XSetCursorState(display,windows,MagickTrue);
8028 XCheckRefreshWindows(display,windows);
8029 flags=ParseGeometry(radius,&geometry_info);
8030 noise_image=ReduceNoiseImage(*image,geometry_info.rho,
8031 &(*image)->exception);
8032 if (noise_image != (Image *) NULL)
8033 {
8034 *image=DestroyImage(*image);
8035 *image=noise_image;
8036 }
8037 CatchException(&(*image)->exception);
8038 XSetCursorState(display,windows,MagickFalse);
8039 if (windows->image.orphan != MagickFalse)
8040 break;
8041 XConfigureImageColormap(display,resource_info,windows,*image);
8042 (void) XConfigureImage(display,resource_info,windows,*image);
8043 break;
8044 }
8045 case AddNoiseCommand:
8046 {
8047 char
8048 **noises;
8049
8050 Image
8051 *noise_image;
8052
8053 static char
8054 noise_type[MaxTextExtent] = "Gaussian";
8055
8056 /*
8057 Add noise to the image.
8058 */
8059 noises=GetMagickOptions(MagickNoiseOptions);
8060 if (noises == (char **) NULL)
8061 break;
8062 XListBrowserWidget(display,windows,&windows->widget,
8063 (const char **) noises,"Add Noise",
8064 "Select a type of noise to add to your image:",noise_type);
8065 noises=DestroyStringList(noises);
8066 if (*noise_type == '\0')
8067 break;
8068 XSetCursorState(display,windows,MagickTrue);
8069 XCheckRefreshWindows(display,windows);
8070 noise_image=AddNoiseImage(*image,(NoiseType) ParseMagickOption(
8071 MagickNoiseOptions,MagickFalse,noise_type),&(*image)->exception);
8072 if (noise_image != (Image *) NULL)
8073 {
8074 *image=DestroyImage(*image);
8075 *image=noise_image;
8076 }
8077 CatchException(&(*image)->exception);
8078 XSetCursorState(display,windows,MagickFalse);
8079 if (windows->image.orphan != MagickFalse)
8080 break;
8081 XConfigureImageColormap(display,resource_info,windows,*image);
8082 (void) XConfigureImage(display,resource_info,windows,*image);
8083 break;
8084 }
8085 case SharpenCommand:
8086 {
8087 Image
8088 *sharp_image;
8089
8090 static char
8091 radius[MaxTextExtent] = "0.0x1.0";
8092
8093 /*
8094 Query user for sharpen radius.
8095 */
8096 (void) XDialogWidget(display,windows,"Sharpen",
8097 "Enter the sharpen radius and standard deviation:",radius);
8098 if (*radius == '\0')
8099 break;
8100 /*
8101 Sharpen image scanlines.
8102 */
8103 XSetCursorState(display,windows,MagickTrue);
8104 XCheckRefreshWindows(display,windows);
8105 flags=ParseGeometry(radius,&geometry_info);
8106 sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8107 &(*image)->exception);
8108 if (sharp_image != (Image *) NULL)
8109 {
8110 *image=DestroyImage(*image);
8111 *image=sharp_image;
8112 }
8113 CatchException(&(*image)->exception);
8114 XSetCursorState(display,windows,MagickFalse);
8115 if (windows->image.orphan != MagickFalse)
8116 break;
8117 XConfigureImageColormap(display,resource_info,windows,*image);
8118 (void) XConfigureImage(display,resource_info,windows,*image);
8119 break;
8120 }
8121 case BlurCommand:
8122 {
8123 Image
8124 *blur_image;
8125
8126 static char
8127 radius[MaxTextExtent] = "0.0x1.0";
8128
8129 /*
8130 Query user for blur radius.
8131 */
8132 (void) XDialogWidget(display,windows,"Blur",
8133 "Enter the blur radius and standard deviation:",radius);
8134 if (*radius == '\0')
8135 break;
8136 /*
8137 Blur an image.
8138 */
8139 XSetCursorState(display,windows,MagickTrue);
8140 XCheckRefreshWindows(display,windows);
8141 flags=ParseGeometry(radius,&geometry_info);
8142 blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8143 &(*image)->exception);
8144 if (blur_image != (Image *) NULL)
8145 {
8146 *image=DestroyImage(*image);
8147 *image=blur_image;
8148 }
8149 CatchException(&(*image)->exception);
8150 XSetCursorState(display,windows,MagickFalse);
8151 if (windows->image.orphan != MagickFalse)
8152 break;
8153 XConfigureImageColormap(display,resource_info,windows,*image);
8154 (void) XConfigureImage(display,resource_info,windows,*image);
8155 break;
8156 }
8157 case ThresholdCommand:
8158 {
8159 double
8160 threshold;
8161
8162 static char
8163 factor[MaxTextExtent] = "128";
8164
8165 /*
8166 Query user for threshold value.
8167 */
8168 (void) XDialogWidget(display,windows,"Threshold",
8169 "Enter threshold value:",factor);
8170 if (*factor == '\0')
8171 break;
8172 /*
8173 Gamma correct image.
8174 */
8175 XSetCursorState(display,windows,MagickTrue);
8176 XCheckRefreshWindows(display,windows);
8177 threshold=StringToDouble(factor,QuantumRange);
8178 (void) BilevelImage(*image,threshold);
8179 XSetCursorState(display,windows,MagickFalse);
8180 if (windows->image.orphan != MagickFalse)
8181 break;
8182 XConfigureImageColormap(display,resource_info,windows,*image);
8183 (void) XConfigureImage(display,resource_info,windows,*image);
8184 break;
8185 }
8186 case EdgeDetectCommand:
8187 {
8188 Image
8189 *edge_image;
8190
8191 static char
8192 radius[MaxTextExtent] = "0";
8193
8194 /*
8195 Query user for edge factor.
8196 */
8197 (void) XDialogWidget(display,windows,"Detect Edges",
8198 "Enter the edge detect radius:",radius);
8199 if (*radius == '\0')
8200 break;
8201 /*
8202 Detect edge in image.
8203 */
8204 XSetCursorState(display,windows,MagickTrue);
8205 XCheckRefreshWindows(display,windows);
8206 flags=ParseGeometry(radius,&geometry_info);
8207 edge_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8208 if (edge_image != (Image *) NULL)
8209 {
8210 *image=DestroyImage(*image);
8211 *image=edge_image;
8212 }
8213 CatchException(&(*image)->exception);
8214 XSetCursorState(display,windows,MagickFalse);
8215 if (windows->image.orphan != MagickFalse)
8216 break;
8217 XConfigureImageColormap(display,resource_info,windows,*image);
8218 (void) XConfigureImage(display,resource_info,windows,*image);
8219 break;
8220 }
8221 case SpreadCommand:
8222 {
8223 Image
8224 *spread_image;
8225
8226 static char
8227 amount[MaxTextExtent] = "2";
8228
8229 /*
8230 Query user for spread amount.
8231 */
8232 (void) XDialogWidget(display,windows,"Spread",
8233 "Enter the displacement amount:",amount);
8234 if (*amount == '\0')
8235 break;
8236 /*
8237 Displace image pixels by a random amount.
8238 */
8239 XSetCursorState(display,windows,MagickTrue);
8240 XCheckRefreshWindows(display,windows);
8241 flags=ParseGeometry(amount,&geometry_info);
8242 spread_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8243 if (spread_image != (Image *) NULL)
8244 {
8245 *image=DestroyImage(*image);
8246 *image=spread_image;
8247 }
8248 CatchException(&(*image)->exception);
8249 XSetCursorState(display,windows,MagickFalse);
8250 if (windows->image.orphan != MagickFalse)
8251 break;
8252 XConfigureImageColormap(display,resource_info,windows,*image);
8253 (void) XConfigureImage(display,resource_info,windows,*image);
8254 break;
8255 }
8256 case ShadeCommand:
8257 {
8258 Image
8259 *shade_image;
8260
8261 int
8262 status;
8263
8264 static char
8265 geometry[MaxTextExtent] = "30x30";
8266
8267 /*
8268 Query user for the shade geometry.
8269 */
8270 status=XDialogWidget(display,windows,"Shade",
8271 "Enter the azimuth and elevation of the light source:",geometry);
8272 if (*geometry == '\0')
8273 break;
8274 /*
8275 Shade image pixels.
8276 */
8277 XSetCursorState(display,windows,MagickTrue);
8278 XCheckRefreshWindows(display,windows);
8279 flags=ParseGeometry(geometry,&geometry_info);
8280 if ((flags & SigmaValue) == 0)
8281 geometry_info.sigma=1.0;
8282 shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8283 geometry_info.rho,geometry_info.sigma,&(*image)->exception);
8284 if (shade_image != (Image *) NULL)
8285 {
8286 *image=DestroyImage(*image);
8287 *image=shade_image;
8288 }
8289 CatchException(&(*image)->exception);
8290 XSetCursorState(display,windows,MagickFalse);
8291 if (windows->image.orphan != MagickFalse)
8292 break;
8293 XConfigureImageColormap(display,resource_info,windows,*image);
8294 (void) XConfigureImage(display,resource_info,windows,*image);
8295 break;
8296 }
8297 case RaiseCommand:
8298 {
8299 static char
8300 bevel_width[MaxTextExtent] = "10";
8301
8302 /*
8303 Query user for bevel width.
8304 */
8305 (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8306 if (*bevel_width == '\0')
8307 break;
8308 /*
8309 Raise an image.
8310 */
8311 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8312 XSetCursorState(display,windows,MagickTrue);
8313 XCheckRefreshWindows(display,windows);
8314 (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8315 &(*image)->exception);
8316 (void) RaiseImage(*image,&page_geometry,MagickTrue);
8317 XSetCursorState(display,windows,MagickFalse);
8318 if (windows->image.orphan != MagickFalse)
8319 break;
8320 XConfigureImageColormap(display,resource_info,windows,*image);
8321 (void) XConfigureImage(display,resource_info,windows,*image);
8322 break;
8323 }
8324 case SegmentCommand:
8325 {
8326 static char
8327 threshold[MaxTextExtent] = "1.0x1.5";
8328
8329 /*
8330 Query user for smoothing threshold.
8331 */
8332 (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8333 threshold);
8334 if (*threshold == '\0')
8335 break;
8336 /*
8337 Segment an image.
8338 */
8339 XSetCursorState(display,windows,MagickTrue);
8340 XCheckRefreshWindows(display,windows);
8341 flags=ParseGeometry(threshold,&geometry_info);
8342 if ((flags & SigmaValue) == 0)
8343 geometry_info.sigma=1.0;
8344 (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8345 geometry_info.sigma);
8346 XSetCursorState(display,windows,MagickFalse);
8347 if (windows->image.orphan != MagickFalse)
8348 break;
8349 XConfigureImageColormap(display,resource_info,windows,*image);
8350 (void) XConfigureImage(display,resource_info,windows,*image);
8351 break;
8352 }
8353 case SepiaToneCommand:
8354 {
8355 double
8356 threshold;
8357
8358 Image
8359 *sepia_image;
8360
8361 static char
8362 factor[MaxTextExtent] = "80%";
8363
8364 /*
8365 Query user for sepia-tone factor.
8366 */
8367 (void) XDialogWidget(display,windows,"Sepia Tone",
8368 "Enter the sepia tone factor (0 - 99.9%):",factor);
8369 if (*factor == '\0')
8370 break;
8371 /*
8372 Sepia tone image pixels.
8373 */
8374 XSetCursorState(display,windows,MagickTrue);
8375 XCheckRefreshWindows(display,windows);
8376 threshold=StringToDouble(factor,QuantumRange);
8377 sepia_image=SepiaToneImage(*image,threshold,&(*image)->exception);
8378 if (sepia_image != (Image *) NULL)
8379 {
8380 *image=DestroyImage(*image);
8381 *image=sepia_image;
8382 }
8383 CatchException(&(*image)->exception);
8384 XSetCursorState(display,windows,MagickFalse);
8385 if (windows->image.orphan != MagickFalse)
8386 break;
8387 XConfigureImageColormap(display,resource_info,windows,*image);
8388 (void) XConfigureImage(display,resource_info,windows,*image);
8389 break;
8390 }
8391 case SolarizeCommand:
8392 {
8393 double
8394 threshold;
8395
8396 static char
8397 factor[MaxTextExtent] = "60%";
8398
8399 /*
8400 Query user for solarize factor.
8401 */
8402 (void) XDialogWidget(display,windows,"Solarize",
8403 "Enter the solarize factor (0 - 99.9%):",factor);
8404 if (*factor == '\0')
8405 break;
8406 /*
8407 Solarize image pixels.
8408 */
8409 XSetCursorState(display,windows,MagickTrue);
8410 XCheckRefreshWindows(display,windows);
8411 threshold=StringToDouble(factor,QuantumRange);
8412 (void) SolarizeImage(*image,threshold);
8413 XSetCursorState(display,windows,MagickFalse);
8414 if (windows->image.orphan != MagickFalse)
8415 break;
8416 XConfigureImageColormap(display,resource_info,windows,*image);
8417 (void) XConfigureImage(display,resource_info,windows,*image);
8418 break;
8419 }
8420 case SwirlCommand:
8421 {
8422 Image
8423 *swirl_image;
8424
8425 static char
8426 degrees[MaxTextExtent] = "60";
8427
8428 /*
8429 Query user for swirl angle.
8430 */
8431 (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8432 degrees);
8433 if (*degrees == '\0')
8434 break;
8435 /*
8436 Swirl image pixels about the center.
8437 */
8438 XSetCursorState(display,windows,MagickTrue);
8439 XCheckRefreshWindows(display,windows);
8440 flags=ParseGeometry(degrees,&geometry_info);
8441 swirl_image=SwirlImage(*image,geometry_info.rho,&(*image)->exception);
8442 if (swirl_image != (Image *) NULL)
8443 {
8444 *image=DestroyImage(*image);
8445 *image=swirl_image;
8446 }
8447 CatchException(&(*image)->exception);
8448 XSetCursorState(display,windows,MagickFalse);
8449 if (windows->image.orphan != MagickFalse)
8450 break;
8451 XConfigureImageColormap(display,resource_info,windows,*image);
8452 (void) XConfigureImage(display,resource_info,windows,*image);
8453 break;
8454 }
8455 case ImplodeCommand:
8456 {
8457 Image
8458 *implode_image;
8459
8460 static char
8461 factor[MaxTextExtent] = "0.3";
8462
8463 /*
8464 Query user for implode factor.
8465 */
8466 (void) XDialogWidget(display,windows,"Implode",
8467 "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8468 if (*factor == '\0')
8469 break;
8470 /*
8471 Implode image pixels about the center.
8472 */
8473 XSetCursorState(display,windows,MagickTrue);
8474 XCheckRefreshWindows(display,windows);
8475 flags=ParseGeometry(factor,&geometry_info);
8476 implode_image=ImplodeImage(*image,geometry_info.rho,&(*image)->exception);
8477 if (implode_image != (Image *) NULL)
8478 {
8479 *image=DestroyImage(*image);
8480 *image=implode_image;
8481 }
8482 CatchException(&(*image)->exception);
8483 XSetCursorState(display,windows,MagickFalse);
8484 if (windows->image.orphan != MagickFalse)
8485 break;
8486 XConfigureImageColormap(display,resource_info,windows,*image);
8487 (void) XConfigureImage(display,resource_info,windows,*image);
8488 break;
8489 }
8490 case VignetteCommand:
8491 {
8492 Image
8493 *vignette_image;
8494
8495 static char
8496 geometry[MaxTextExtent] = "0x20";
8497
8498 /*
8499 Query user for the vignette geometry.
8500 */
8501 (void) XDialogWidget(display,windows,"Vignette",
8502 "Enter the radius, sigma, and x and y offsets:",geometry);
8503 if (*geometry == '\0')
8504 break;
8505 /*
8506 Soften the edges of the image in vignette style
8507 */
8508 XSetCursorState(display,windows,MagickTrue);
8509 XCheckRefreshWindows(display,windows);
8510 flags=ParseGeometry(geometry,&geometry_info);
8511 if ((flags & SigmaValue) == 0)
8512 geometry_info.sigma=1.0;
8513 if ((flags & XiValue) == 0)
8514 geometry_info.xi=0.1*(*image)->columns;
8515 if ((flags & PsiValue) == 0)
8516 geometry_info.psi=0.1*(*image)->rows;
8517 vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8518 (long) (geometry_info.xi+0.5),(long) (geometry_info.psi+0.5),
8519 &(*image)->exception);
8520 if (vignette_image != (Image *) NULL)
8521 {
8522 *image=DestroyImage(*image);
8523 *image=vignette_image;
8524 }
8525 CatchException(&(*image)->exception);
8526 XSetCursorState(display,windows,MagickFalse);
8527 if (windows->image.orphan != MagickFalse)
8528 break;
8529 XConfigureImageColormap(display,resource_info,windows,*image);
8530 (void) XConfigureImage(display,resource_info,windows,*image);
8531 break;
8532 }
8533 case WaveCommand:
8534 {
8535 Image
8536 *wave_image;
8537
8538 static char
8539 geometry[MaxTextExtent] = "25x150";
8540
8541 /*
8542 Query user for the wave geometry.
8543 */
8544 (void) XDialogWidget(display,windows,"Wave",
8545 "Enter the amplitude and length of the wave:",geometry);
8546 if (*geometry == '\0')
8547 break;
8548 /*
8549 Alter an image along a sine wave.
8550 */
8551 XSetCursorState(display,windows,MagickTrue);
8552 XCheckRefreshWindows(display,windows);
8553 flags=ParseGeometry(geometry,&geometry_info);
8554 if ((flags & SigmaValue) == 0)
8555 geometry_info.sigma=1.0;
8556 wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8557 &(*image)->exception);
8558 if (wave_image != (Image *) NULL)
8559 {
8560 *image=DestroyImage(*image);
8561 *image=wave_image;
8562 }
8563 CatchException(&(*image)->exception);
8564 XSetCursorState(display,windows,MagickFalse);
8565 if (windows->image.orphan != MagickFalse)
8566 break;
8567 XConfigureImageColormap(display,resource_info,windows,*image);
8568 (void) XConfigureImage(display,resource_info,windows,*image);
8569 break;
8570 }
8571 case OilPaintCommand:
8572 {
8573 Image
8574 *paint_image;
8575
8576 static char
8577 radius[MaxTextExtent] = "0";
8578
8579 /*
8580 Query user for circular neighborhood radius.
8581 */
8582 (void) XDialogWidget(display,windows,"Oil Paint",
8583 "Enter the mask radius:",radius);
8584 if (*radius == '\0')
8585 break;
8586 /*
8587 OilPaint image scanlines.
8588 */
8589 XSetCursorState(display,windows,MagickTrue);
8590 XCheckRefreshWindows(display,windows);
8591 flags=ParseGeometry(radius,&geometry_info);
8592 paint_image=OilPaintImage(*image,geometry_info.rho,&(*image)->exception);
8593 if (paint_image != (Image *) NULL)
8594 {
8595 *image=DestroyImage(*image);
8596 *image=paint_image;
8597 }
8598 CatchException(&(*image)->exception);
8599 XSetCursorState(display,windows,MagickFalse);
8600 if (windows->image.orphan != MagickFalse)
8601 break;
8602 XConfigureImageColormap(display,resource_info,windows,*image);
8603 (void) XConfigureImage(display,resource_info,windows,*image);
8604 break;
8605 }
8606 case CharcoalDrawCommand:
8607 {
8608 Image
8609 *charcoal_image;
8610
8611 static char
8612 radius[MaxTextExtent] = "0x1";
8613
8614 /*
8615 Query user for charcoal radius.
8616 */
8617 (void) XDialogWidget(display,windows,"Charcoal Draw",
8618 "Enter the charcoal radius and sigma:",radius);
8619 if (*radius == '\0')
8620 break;
8621 /*
8622 Charcoal the image.
8623 */
8624 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8625 XSetCursorState(display,windows,MagickTrue);
8626 XCheckRefreshWindows(display,windows);
8627 flags=ParseGeometry(radius,&geometry_info);
8628 if ((flags & SigmaValue) == 0)
8629 geometry_info.sigma=geometry_info.rho;
8630 charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8631 &(*image)->exception);
8632 if (charcoal_image != (Image *) NULL)
8633 {
8634 *image=DestroyImage(*image);
8635 *image=charcoal_image;
8636 }
8637 CatchException(&(*image)->exception);
8638 XSetCursorState(display,windows,MagickFalse);
8639 if (windows->image.orphan != MagickFalse)
8640 break;
8641 XConfigureImageColormap(display,resource_info,windows,*image);
8642 (void) XConfigureImage(display,resource_info,windows,*image);
8643 break;
8644 }
8645 case AnnotateCommand:
8646 {
8647 /*
8648 Annotate the image with text.
8649 */
8650 status=XAnnotateEditImage(display,resource_info,windows,*image);
8651 if (status == MagickFalse)
8652 {
8653 XNoticeWidget(display,windows,"Unable to annotate X image",
8654 (*image)->filename);
8655 break;
8656 }
8657 break;
8658 }
8659 case DrawCommand:
8660 {
8661 /*
8662 Draw image.
8663 */
8664 status=XDrawEditImage(display,resource_info,windows,image);
8665 if (status == MagickFalse)
8666 {
8667 XNoticeWidget(display,windows,"Unable to draw on the X image",
8668 (*image)->filename);
8669 break;
8670 }
8671 break;
8672 }
8673 case ColorCommand:
8674 {
8675 /*
8676 Color edit.
8677 */
8678 status=XColorEditImage(display,resource_info,windows,image);
8679 if (status == MagickFalse)
8680 {
8681 XNoticeWidget(display,windows,"Unable to pixel edit X image",
8682 (*image)->filename);
8683 break;
8684 }
8685 break;
8686 }
8687 case MatteCommand:
8688 {
8689 /*
8690 Matte edit.
8691 */
8692 status=XMatteEditImage(display,resource_info,windows,image);
8693 if (status == MagickFalse)
8694 {
8695 XNoticeWidget(display,windows,"Unable to matte edit X image",
8696 (*image)->filename);
8697 break;
8698 }
8699 break;
8700 }
8701 case CompositeCommand:
8702 {
8703 /*
8704 Composite image.
8705 */
8706 status=XCompositeImage(display,resource_info,windows,*image);
8707 if (status == MagickFalse)
8708 {
8709 XNoticeWidget(display,windows,"Unable to composite X image",
8710 (*image)->filename);
8711 break;
8712 }
8713 break;
8714 }
8715 case AddBorderCommand:
8716 {
8717 Image
8718 *border_image;
8719
8720 static char
8721 geometry[MaxTextExtent] = "6x6";
8722
8723 /*
8724 Query user for border color and geometry.
8725 */
8726 XColorBrowserWidget(display,windows,"Select",color);
8727 if (*color == '\0')
8728 break;
8729 (void) XDialogWidget(display,windows,"Add Border",
8730 "Enter border geometry:",geometry);
8731 if (*geometry == '\0')
8732 break;
8733 /*
8734 Add a border to the image.
8735 */
8736 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8737 XSetCursorState(display,windows,MagickTrue);
8738 XCheckRefreshWindows(display,windows);
8739 (void) QueryColorDatabase(color,&(*image)->border_color,
8740 &(*image)->exception);
8741 (void) ParsePageGeometry(*image,geometry,&page_geometry,
8742 &(*image)->exception);
8743 border_image=BorderImage(*image,&page_geometry,&(*image)->exception);
8744 if (border_image != (Image *) NULL)
8745 {
8746 *image=DestroyImage(*image);
8747 *image=border_image;
8748 }
8749 CatchException(&(*image)->exception);
8750 XSetCursorState(display,windows,MagickFalse);
8751 if (windows->image.orphan != MagickFalse)
8752 break;
8753 windows->image.window_changes.width=(int) (*image)->columns;
8754 windows->image.window_changes.height=(int) (*image)->rows;
8755 XConfigureImageColormap(display,resource_info,windows,*image);
8756 (void) XConfigureImage(display,resource_info,windows,*image);
8757 break;
8758 }
8759 case AddFrameCommand:
8760 {
8761 FrameInfo
8762 frame_info;
8763
8764 Image
8765 *frame_image;
8766
8767 static char
8768 geometry[MaxTextExtent] = "6x6";
8769
8770 /*
8771 Query user for frame color and geometry.
8772 */
8773 XColorBrowserWidget(display,windows,"Select",color);
8774 if (*color == '\0')
8775 break;
8776 (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8777 geometry);
8778 if (*geometry == '\0')
8779 break;
8780 /*
8781 Surround image with an ornamental border.
8782 */
8783 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8784 XSetCursorState(display,windows,MagickTrue);
8785 XCheckRefreshWindows(display,windows);
8786 (void) QueryColorDatabase(color,&(*image)->matte_color,
8787 &(*image)->exception);
8788 (void) ParsePageGeometry(*image,geometry,&page_geometry,
8789 &(*image)->exception);
8790 frame_info.width=page_geometry.width;
8791 frame_info.height=page_geometry.height;
8792 frame_info.outer_bevel=page_geometry.x;
8793 frame_info.inner_bevel=page_geometry.y;
8794 frame_info.x=(long) frame_info.width;
8795 frame_info.y=(long) frame_info.height;
8796 frame_info.width=(*image)->columns+2*frame_info.width;
8797 frame_info.height=(*image)->rows+2*frame_info.height;
8798 frame_image=FrameImage(*image,&frame_info,&(*image)->exception);
8799 if (frame_image != (Image *) NULL)
8800 {
8801 *image=DestroyImage(*image);
8802 *image=frame_image;
8803 }
8804 CatchException(&(*image)->exception);
8805 XSetCursorState(display,windows,MagickFalse);
8806 if (windows->image.orphan != MagickFalse)
8807 break;
8808 windows->image.window_changes.width=(int) (*image)->columns;
8809 windows->image.window_changes.height=(int) (*image)->rows;
8810 XConfigureImageColormap(display,resource_info,windows,*image);
8811 (void) XConfigureImage(display,resource_info,windows,*image);
8812 break;
8813 }
8814 case CommentCommand:
8815 {
8816 const char
8817 *value;
8818
8819 FILE
8820 *file;
8821
8822 int
8823 unique_file;
8824
8825 /*
8826 Edit image comment.
8827 */
8828 unique_file=AcquireUniqueFileResource(image_info->filename);
8829 if (unique_file == -1)
8830 XNoticeWidget(display,windows,"Unable to edit image comment",
8831 image_info->filename);
8832 value=GetImageProperty(*image,"comment");
8833 if (value == (char *) NULL)
8834 unique_file=close(unique_file)-1;
8835 else
8836 {
8837 register const char
8838 *p;
8839
8840 file=fdopen(unique_file,"w");
8841 if (file == (FILE *) NULL)
8842 {
8843 XNoticeWidget(display,windows,"Unable to edit image comment",
8844 image_info->filename);
8845 break;
8846 }
8847 for (p=value; *p != '\0'; p++)
8848 (void) fputc((int) *p,file);
8849 (void) fputc('\n',file);
8850 (void) fclose(file);
8851 }
8852 XSetCursorState(display,windows,MagickTrue);
8853 XCheckRefreshWindows(display,windows);
8854 status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8855 &(*image)->exception);
8856 if (status == MagickFalse)
8857 XNoticeWidget(display,windows,"Unable to edit image comment",
8858 (char *) NULL);
8859 else
8860 {
8861 char
8862 *comment;
8863
8864 comment=FileToString(image_info->filename,~0UL,&(*image)->exception);
8865 if (comment != (char *) NULL)
8866 {
8867 (void) SetImageProperty(*image,"comment",comment);
8868 (*image)->taint=MagickTrue;
8869 }
8870 }
8871 (void) RelinquishUniqueFileResource(image_info->filename);
8872 XSetCursorState(display,windows,MagickFalse);
8873 break;
8874 }
8875 case LaunchCommand:
8876 {
8877 /*
8878 Launch program.
8879 */
8880 XSetCursorState(display,windows,MagickTrue);
8881 XCheckRefreshWindows(display,windows);
8882 (void) AcquireUniqueFilename(filename);
8883 (void) FormatMagickString((*image)->filename,MaxTextExtent,"launch:%s",
8884 filename);
8885 status=WriteImage(image_info,*image);
8886 if (status == MagickFalse)
8887 XNoticeWidget(display,windows,"Unable to launch image editor",
8888 (char *) NULL);
8889 else
8890 {
8891 nexus=ReadImage(resource_info->image_info,&(*image)->exception);
8892 CatchException(&(*image)->exception);
8893 XClientMessage(display,windows->image.id,windows->im_protocols,
8894 windows->im_next_image,CurrentTime);
8895 }
8896 (void) RelinquishUniqueFileResource(filename);
8897 XSetCursorState(display,windows,MagickFalse);
8898 break;
8899 }
8900 case RegionofInterestCommand:
8901 {
8902 /*
8903 Apply an image processing technique to a region of interest.
8904 */
8905 (void) XROIImage(display,resource_info,windows,image);
8906 break;
8907 }
8908 case InfoCommand:
8909 break;
8910 case ZoomCommand:
8911 {
8912 /*
8913 Zoom image.
8914 */
8915 if (windows->magnify.mapped != MagickFalse)
8916 (void) XRaiseWindow(display,windows->magnify.id);
8917 else
8918 {
8919 /*
8920 Make magnify image.
8921 */
8922 XSetCursorState(display,windows,MagickTrue);
8923 (void) XMapRaised(display,windows->magnify.id);
8924 XSetCursorState(display,windows,MagickFalse);
8925 }
8926 break;
8927 }
8928 case ShowPreviewCommand:
8929 {
8930 char
8931 **previews;
8932
8933 Image
8934 *preview_image;
8935
8936 static char
8937 preview_type[MaxTextExtent] = "Gamma";
8938
8939 /*
8940 Select preview type from menu.
8941 */
8942 previews=GetMagickOptions(MagickPreviewOptions);
8943 if (previews == (char **) NULL)
8944 break;
8945 XListBrowserWidget(display,windows,&windows->widget,
8946 (const char **) previews,"Preview",
8947 "Select an enhancement, effect, or F/X:",preview_type);
8948 previews=DestroyStringList(previews);
8949 if (*preview_type == '\0')
8950 break;
8951 /*
8952 Show image preview.
8953 */
8954 XSetCursorState(display,windows,MagickTrue);
8955 XCheckRefreshWindows(display,windows);
8956 image_info->preview_type=(PreviewType)
8957 ParseMagickOption(MagickPreviewOptions,MagickFalse,preview_type);
8958 image_info->group=(long) windows->image.id;
8959 (void) DeleteImageProperty(*image,"label");
8960 (void) SetImageProperty(*image,"label","Preview");
8961 (void) AcquireUniqueFilename(filename);
8962 (void) FormatMagickString((*image)->filename,MaxTextExtent,"preview:%s",
8963 filename);
8964 status=WriteImage(image_info,*image);
8965 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8966 preview_image=ReadImage(image_info,&(*image)->exception);
8967 (void) RelinquishUniqueFileResource(filename);
8968 if (preview_image == (Image *) NULL)
8969 break;
8970 (void) FormatMagickString(preview_image->filename,MaxTextExtent,"show:%s",
8971 filename);
8972 status=WriteImage(image_info,preview_image);
8973 preview_image=DestroyImage(preview_image);
8974 if (status == MagickFalse)
8975 XNoticeWidget(display,windows,"Unable to show image preview",
8976 (*image)->filename);
8977 XDelay(display,1500);
8978 XSetCursorState(display,windows,MagickFalse);
8979 break;
8980 }
8981 case ShowHistogramCommand:
8982 {
8983 Image
8984 *histogram_image;
8985
8986 /*
8987 Show image histogram.
8988 */
8989 XSetCursorState(display,windows,MagickTrue);
8990 XCheckRefreshWindows(display,windows);
8991 image_info->group=(long) windows->image.id;
8992 (void) DeleteImageProperty(*image,"label");
8993 (void) SetImageProperty(*image,"label","Histogram");
8994 (void) AcquireUniqueFilename(filename);
8995 (void) FormatMagickString((*image)->filename,MaxTextExtent,"histogram:%s",
8996 filename);
8997 status=WriteImage(image_info,*image);
8998 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8999 histogram_image=ReadImage(image_info,&(*image)->exception);
9000 (void) RelinquishUniqueFileResource(filename);
9001 if (histogram_image == (Image *) NULL)
9002 break;
9003 (void) FormatMagickString(histogram_image->filename,MaxTextExtent,
9004 "show:%s",filename);
9005 status=WriteImage(image_info,histogram_image);
9006 histogram_image=DestroyImage(histogram_image);
9007 if (status == MagickFalse)
9008 XNoticeWidget(display,windows,"Unable to show histogram",
9009 (*image)->filename);
9010 XDelay(display,1500);
9011 XSetCursorState(display,windows,MagickFalse);
9012 break;
9013 }
9014 case ShowMatteCommand:
9015 {
9016 Image
9017 *matte_image;
9018
9019 if ((*image)->matte == MagickFalse)
9020 {
9021 XNoticeWidget(display,windows,
9022 "Image does not have any matte information",(*image)->filename);
9023 break;
9024 }
9025 /*
9026 Show image matte.
9027 */
9028 XSetCursorState(display,windows,MagickTrue);
9029 XCheckRefreshWindows(display,windows);
9030 image_info->group=(long) windows->image.id;
9031 (void) DeleteImageProperty(*image,"label");
9032 (void) SetImageProperty(*image,"label","Matte");
9033 (void) AcquireUniqueFilename(filename);
9034 (void) FormatMagickString((*image)->filename,MaxTextExtent,"matte:%s",
9035 filename);
9036 status=WriteImage(image_info,*image);
9037 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9038 matte_image=ReadImage(image_info,&(*image)->exception);
9039 (void) RelinquishUniqueFileResource(filename);
9040 if (matte_image == (Image *) NULL)
9041 break;
9042 (void) FormatMagickString(matte_image->filename,MaxTextExtent,"show:%s",
9043 filename);
9044 status=WriteImage(image_info,matte_image);
9045 matte_image=DestroyImage(matte_image);
9046 if (status == MagickFalse)
9047 XNoticeWidget(display,windows,"Unable to show matte",
9048 (*image)->filename);
9049 XDelay(display,1500);
9050 XSetCursorState(display,windows,MagickFalse);
9051 break;
9052 }
9053 case BackgroundCommand:
9054 {
9055 /*
9056 Background image.
9057 */
9058 status=XBackgroundImage(display,resource_info,windows,image);
9059 if (status == MagickFalse)
9060 break;
9061 nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9062 if (nexus != (Image *) NULL)
9063 XClientMessage(display,windows->image.id,windows->im_protocols,
9064 windows->im_next_image,CurrentTime);
9065 break;
9066 }
9067 case SlideShowCommand:
9068 {
9069 static char
9070 delay[MaxTextExtent] = "5";
9071
9072 /*
9073 Display next image after pausing.
9074 */
9075 (void) XDialogWidget(display,windows,"Slide Show",
9076 "Pause how many 1/100ths of a second between images:",delay);
9077 if (*delay == '\0')
9078 break;
9079 resource_info->delay=(unsigned long) atol(delay);
9080 XClientMessage(display,windows->image.id,windows->im_protocols,
9081 windows->im_next_image,CurrentTime);
9082 break;
9083 }
9084 case PreferencesCommand:
9085 {
9086 /*
9087 Set user preferences.
9088 */
9089 status=XPreferencesWidget(display,resource_info,windows);
9090 if (status == MagickFalse)
9091 break;
9092 nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9093 if (nexus != (Image *) NULL)
9094 XClientMessage(display,windows->image.id,windows->im_protocols,
9095 windows->im_next_image,CurrentTime);
9096 break;
9097 }
9098 case HelpCommand:
9099 {
9100 /*
9101 User requested help.
9102 */
9103 XTextViewWidget(display,resource_info,windows,MagickFalse,
9104 "Help Viewer - Display",DisplayHelp);
9105 break;
9106 }
9107 case BrowseDocumentationCommand:
9108 {
9109 Atom
9110 mozilla_atom;
9111
9112 Window
9113 mozilla_window,
9114 root_window;
9115
9116 /*
9117 Browse the ImageMagick documentation.
9118 */
9119 root_window=XRootWindow(display,XDefaultScreen(display));
9120 mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9121 mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9122 if (mozilla_window != (Window) NULL)
9123 {
9124 char
9125 command[MaxTextExtent],
9126 *url;
9127
9128 /*
9129 Display documentation using Netscape remote control.
9130 */
9131 url=GetMagickHomeURL();
9132 (void) FormatMagickString(command,MaxTextExtent,
9133 "openurl(%s,new-tab)",url);
9134 url=DestroyString(url);
9135 mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9136 (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9137 8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9138 XSetCursorState(display,windows,MagickFalse);
9139 break;
9140 }
9141 XSetCursorState(display,windows,MagickTrue);
9142 XCheckRefreshWindows(display,windows);
9143 status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9144 &(*image)->exception);
9145 if (status == MagickFalse)
9146 XNoticeWidget(display,windows,"Unable to browse documentation",
9147 (char *) NULL);
9148 XDelay(display,1500);
9149 XSetCursorState(display,windows,MagickFalse);
9150 break;
9151 }
9152 case VersionCommand:
9153 {
9154 XNoticeWidget(display,windows,GetMagickVersion((unsigned long *) NULL),
9155 GetMagickCopyright());
9156 break;
9157 }
9158 case SaveToUndoBufferCommand:
9159 break;
9160 default:
9161 {
9162 (void) XBell(display,0);
9163 break;
9164 }
9165 }
9166 image_info=DestroyImageInfo(image_info);
9167 return(nexus);
9168}
9169
9170/*
9171%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9172% %
9173% %
9174% %
9175+ X M a g n i f y I m a g e %
9176% %
9177% %
9178% %
9179%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9180%
9181% XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9182% The magnified portion is displayed in a separate window.
9183%
9184% The format of the XMagnifyImage method is:
9185%
9186% void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9187%
9188% A description of each parameter follows:
9189%
9190% o display: Specifies a connection to an X server; returned from
9191% XOpenDisplay.
9192%
9193% o windows: Specifies a pointer to a XWindows structure.
9194%
9195% o event: Specifies a pointer to a XEvent structure. If it is NULL,
9196% the entire image is refreshed.
9197%
9198*/
9199static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9200{
9201 char
9202 text[MaxTextExtent];
9203
9204 register int
9205 x,
9206 y;
9207
9208 unsigned long
9209 state;
9210
9211 /*
9212 Update magnified image until the mouse button is released.
9213 */
9214 (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9215 state=DefaultState;
9216 x=event->xbutton.x;
9217 y=event->xbutton.y;
9218 windows->magnify.x=windows->image.x+x;
9219 windows->magnify.y=windows->image.y+y;
9220 do
9221 {
9222 /*
9223 Map and unmap Info widget as text cursor crosses its boundaries.
9224 */
9225 if (windows->info.mapped != MagickFalse)
9226 {
9227 if ((x < (int) (windows->info.x+windows->info.width)) &&
9228 (y < (int) (windows->info.y+windows->info.height)))
9229 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9230 }
9231 else
9232 if ((x > (int) (windows->info.x+windows->info.width)) ||
9233 (y > (int) (windows->info.y+windows->info.height)))
9234 (void) XMapWindow(display,windows->info.id);
9235 if (windows->info.mapped != MagickFalse)
9236 {
9237 /*
9238 Display pointer position.
9239 */
9240 (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
9241 windows->magnify.x,windows->magnify.y);
9242 XInfoWidget(display,windows,text);
9243 }
9244 /*
9245 Wait for next event.
9246 */
9247 XScreenEvent(display,windows,event);
9248 switch (event->type)
9249 {
9250 case ButtonPress:
9251 break;
9252 case ButtonRelease:
9253 {
9254 /*
9255 User has finished magnifying image.
9256 */
9257 x=event->xbutton.x;
9258 y=event->xbutton.y;
9259 state|=ExitState;
9260 break;
9261 }
9262 case Expose:
9263 break;
9264 case MotionNotify:
9265 {
9266 x=event->xmotion.x;
9267 y=event->xmotion.y;
9268 break;
9269 }
9270 default:
9271 break;
9272 }
9273 /*
9274 Check boundary conditions.
9275 */
9276 if (x < 0)
9277 x=0;
9278 else
9279 if (x >= (int) windows->image.width)
9280 x=(int) windows->image.width-1;
9281 if (y < 0)
9282 y=0;
9283 else
9284 if (y >= (int) windows->image.height)
9285 y=(int) windows->image.height-1;
9286 } while ((state & ExitState) == 0);
9287 /*
9288 Display magnified image.
9289 */
9290 XSetCursorState(display,windows,MagickFalse);
9291}
9292
9293/*
9294%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9295% %
9296% %
9297% %
9298+ X M a g n i f y W i n d o w C o m m a n d %
9299% %
9300% %
9301% %
9302%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9303%
9304% XMagnifyWindowCommand() moves the image within an Magnify window by one
9305% pixel as specified by the key symbol.
9306%
9307% The format of the XMagnifyWindowCommand method is:
9308%
9309% void XMagnifyWindowCommand(Display *display,XWindows *windows,
9310% const MagickStatusType state,const KeySym key_symbol)
9311%
9312% A description of each parameter follows:
9313%
9314% o display: Specifies a connection to an X server; returned from
9315% XOpenDisplay.
9316%
9317% o windows: Specifies a pointer to a XWindows structure.
9318%
9319% o state: key mask.
9320%
9321% o key_symbol: Specifies a KeySym which indicates which side of the image
9322% to trim.
9323%
9324*/
9325static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9326 const MagickStatusType state,const KeySym key_symbol)
9327{
9328 unsigned int
9329 quantum;
9330
9331 /*
9332 User specified a magnify factor or position.
9333 */
9334 quantum=1;
9335 if ((state & Mod1Mask) != 0)
9336 quantum=10;
9337 switch ((int) key_symbol)
9338 {
9339 case QuitCommand:
9340 {
9341 (void) XWithdrawWindow(display,windows->magnify.id,
9342 windows->magnify.screen);
9343 break;
9344 }
9345 case XK_Home:
9346 case XK_KP_Home:
9347 {
9348 windows->magnify.x=(int) windows->image.width/2;
9349 windows->magnify.y=(int) windows->image.height/2;
9350 break;
9351 }
9352 case XK_Left:
9353 case XK_KP_Left:
9354 {
9355 if (windows->magnify.x > 0)
9356 windows->magnify.x-=quantum;
9357 break;
9358 }
9359 case XK_Up:
9360 case XK_KP_Up:
9361 {
9362 if (windows->magnify.y > 0)
9363 windows->magnify.y-=quantum;
9364 break;
9365 }
9366 case XK_Right:
9367 case XK_KP_Right:
9368 {
9369 if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9370 windows->magnify.x+=quantum;
9371 break;
9372 }
9373 case XK_Down:
9374 case XK_KP_Down:
9375 {
9376 if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9377 windows->magnify.y+=quantum;
9378 break;
9379 }
9380 case XK_0:
9381 case XK_1:
9382 case XK_2:
9383 case XK_3:
9384 case XK_4:
9385 case XK_5:
9386 case XK_6:
9387 case XK_7:
9388 case XK_8:
9389 case XK_9:
9390 {
9391 windows->magnify.data=(key_symbol-XK_0);
9392 break;
9393 }
9394 case XK_KP_0:
9395 case XK_KP_1:
9396 case XK_KP_2:
9397 case XK_KP_3:
9398 case XK_KP_4:
9399 case XK_KP_5:
9400 case XK_KP_6:
9401 case XK_KP_7:
9402 case XK_KP_8:
9403 case XK_KP_9:
9404 {
9405 windows->magnify.data=(key_symbol-XK_KP_0);
9406 break;
9407 }
9408 default:
9409 break;
9410 }
9411 XMakeMagnifyImage(display,windows);
9412}
9413
9414/*
9415%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9416% %
9417% %
9418% %
9419+ X M a k e P a n I m a g e %
9420% %
9421% %
9422% %
9423%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9424%
9425% XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9426% icon window.
9427%
9428% The format of the XMakePanImage method is:
9429%
9430% void XMakePanImage(Display *display,XResourceInfo *resource_info,
9431% XWindows *windows,Image *image)
9432%
9433% A description of each parameter follows:
9434%
9435% o display: Specifies a connection to an X server; returned from
9436% XOpenDisplay.
9437%
9438% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9439%
9440% o windows: Specifies a pointer to a XWindows structure.
9441%
9442% o image: the image.
9443%
9444*/
9445static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9446 XWindows *windows,Image *image)
9447{
9448 MagickStatusType
9449 status;
9450
9451 /*
9452 Create and display image for panning icon.
9453 */
9454 XSetCursorState(display,windows,MagickTrue);
9455 XCheckRefreshWindows(display,windows);
9456 windows->pan.x=windows->image.x;
9457 windows->pan.y=windows->image.y;
9458 status=XMakeImage(display,resource_info,&windows->pan,image,
9459 windows->pan.width,windows->pan.height);
9460 if (status == MagickFalse)
9461 ThrowXWindowFatalException(XServerError,image->exception.reason,
9462 image->exception.description);
9463 (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9464 windows->pan.pixmap);
9465 (void) XClearWindow(display,windows->pan.id);
9466 XDrawPanRectangle(display,windows);
9467 XSetCursorState(display,windows,MagickFalse);
9468}
9469
9470/*
9471%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9472% %
9473% %
9474% %
9475+ X M a t t a E d i t I m a g e %
9476% %
9477% %
9478% %
9479%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9480%
9481% XMatteEditImage() allows the user to interactively change the Matte channel
9482% of an image. If the image is PseudoClass it is promoted to DirectClass
9483% before the matte information is stored.
9484%
9485% The format of the XMatteEditImage method is:
9486%
9487% MagickBooleanType XMatteEditImage(Display *display,
9488% XResourceInfo *resource_info,XWindows *windows,Image **image)
9489%
9490% A description of each parameter follows:
9491%
9492% o display: Specifies a connection to an X server; returned from
9493% XOpenDisplay.
9494%
9495% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9496%
9497% o windows: Specifies a pointer to a XWindows structure.
9498%
9499% o image: the image; returned from ReadImage.
9500%
9501*/
9502static MagickBooleanType XMatteEditImage(Display *display,
9503 XResourceInfo *resource_info,XWindows *windows,Image **image)
9504{
9505 static char
9506 matte[MaxTextExtent] = "0";
9507
9508 static const char
9509 *MatteEditMenu[] =
9510 {
9511 "Method",
9512 "Border Color",
9513 "Fuzz",
9514 "Matte Value",
9515 "Undo",
9516 "Help",
9517 "Dismiss",
9518 (char *) NULL
9519 };
9520
9521 static const ModeType
9522 MatteEditCommands[] =
9523 {
9524 MatteEditMethod,
9525 MatteEditBorderCommand,
9526 MatteEditFuzzCommand,
9527 MatteEditValueCommand,
9528 MatteEditUndoCommand,
9529 MatteEditHelpCommand,
9530 MatteEditDismissCommand
9531 };
9532
9533 static PaintMethod
9534 method = PointMethod;
9535
9536 static XColor
9537 border_color = { 0, 0, 0, 0, 0, 0 };
9538
9539 char
9540 command[MaxTextExtent],
9541 text[MaxTextExtent];
9542
9543 Cursor
9544 cursor;
9545
9546 int
9547 entry,
9548 id,
9549 x,
9550 x_offset,
9551 y,
9552 y_offset;
9553
9554 register int
9555 i;
9556
9557 register PixelPacket
9558 *q;
9559
9560 unsigned int
9561 height,
9562 width;
9563
9564 unsigned long
9565 state;
9566
9567 XEvent
9568 event;
9569
9570 /*
9571 Map Command widget.
9572 */
9573 (void) CloneString(&windows->command.name,"Matte Edit");
9574 windows->command.data=4;
9575 (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9576 (void) XMapRaised(display,windows->command.id);
9577 XClientMessage(display,windows->image.id,windows->im_protocols,
9578 windows->im_update_widget,CurrentTime);
9579 /*
9580 Make cursor.
9581 */
9582 cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9583 resource_info->background_color,resource_info->foreground_color);
9584 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9585 /*
9586 Track pointer until button 1 is pressed.
9587 */
9588 XQueryPosition(display,windows->image.id,&x,&y);
9589 (void) XSelectInput(display,windows->image.id,
9590 windows->image.attributes.event_mask | PointerMotionMask);
9591 state=DefaultState;
9592 do
9593 {
9594 if (windows->info.mapped != MagickFalse)
9595 {
9596 /*
9597 Display pointer position.
9598 */
9599 (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
9600 x+windows->image.x,y+windows->image.y);
9601 XInfoWidget(display,windows,text);
9602 }
9603 /*
9604 Wait for next event.
9605 */
9606 XScreenEvent(display,windows,&event);
9607 if (event.xany.window == windows->command.id)
9608 {
9609 /*
9610 Select a command from the Command widget.
9611 */
9612 id=XCommandWidget(display,windows,MatteEditMenu,&event);
9613 if (id < 0)
9614 {
9615 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9616 continue;
9617 }
9618 switch (MatteEditCommands[id])
9619 {
9620 case MatteEditMethod:
9621 {
9622 char
9623 **methods;
9624
9625 /*
9626 Select a method from the pop-up menu.
9627 */
9628 methods=GetMagickOptions(MagickMethodOptions);
9629 if (methods == (char **) NULL)
9630 break;
9631 entry=XMenuWidget(display,windows,MatteEditMenu[id],
9632 (const char **) methods,command);
9633 if (entry >= 0)
9634 method=(PaintMethod) ParseMagickOption(MagickMethodOptions,
9635 MagickFalse,methods[entry]);
9636 methods=DestroyStringList(methods);
9637 break;
9638 }
9639 case MatteEditBorderCommand:
9640 {
9641 const char
9642 *ColorMenu[MaxNumberPens];
9643
9644 int
9645 pen_number;
9646
9647 /*
9648 Initialize menu selections.
9649 */
9650 for (i=0; i < (int) (MaxNumberPens-2); i++)
9651 ColorMenu[i]=resource_info->pen_colors[i];
9652 ColorMenu[MaxNumberPens-2]="Browser...";
9653 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9654 /*
9655 Select a pen color from the pop-up menu.
9656 */
9657 pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9658 (const char **) ColorMenu,command);
9659 if (pen_number < 0)
9660 break;
9661 if (pen_number == (MaxNumberPens-2))
9662 {
9663 static char
9664 color_name[MaxTextExtent] = "gray";
9665
9666 /*
9667 Select a pen color from a dialog.
9668 */
9669 resource_info->pen_colors[pen_number]=color_name;
9670 XColorBrowserWidget(display,windows,"Select",color_name);
9671 if (*color_name == '\0')
9672 break;
9673 }
9674 /*
9675 Set border color.
9676 */
9677 (void) XParseColor(display,windows->map_info->colormap,
9678 resource_info->pen_colors[pen_number],&border_color);
9679 break;
9680 }
9681 case MatteEditFuzzCommand:
9682 {
9683 static char
9684 fuzz[MaxTextExtent];
9685
9686 static const char
9687 *FuzzMenu[] =
9688 {
9689 "0%",
9690 "2%",
9691 "5%",
9692 "10%",
9693 "15%",
9694 "Dialog...",
9695 (char *) NULL,
9696 };
9697
9698 /*
9699 Select a command from the pop-up menu.
9700 */
9701 entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9702 command);
9703 if (entry < 0)
9704 break;
9705 if (entry != 5)
9706 {
9707 (*image)->fuzz=StringToDouble(FuzzMenu[entry],1.0*QuantumRange+
9708 1.0);
9709 break;
9710 }
9711 (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9712 (void) XDialogWidget(display,windows,"Ok",
9713 "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9714 if (*fuzz == '\0')
9715 break;
9716 (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9717 (*image)->fuzz=StringToDouble(fuzz,1.0*QuantumRange+1.0);
9718 break;
9719 }
9720 case MatteEditValueCommand:
9721 {
9722 static char
9723 message[MaxTextExtent];
9724
9725 static const char
9726 *MatteMenu[] =
9727 {
9728 "Opaque",
9729 "Transparent",
9730 "Dialog...",
9731 (char *) NULL,
9732 };
9733
9734 /*
9735 Select a command from the pop-up menu.
9736 */
9737 entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9738 command);
9739 if (entry < 0)
9740 break;
9741 if (entry != 2)
9742 {
9743 (void) FormatMagickString(matte,MaxTextExtent,QuantumFormat,
9744 OpaqueOpacity);
9745 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9746 (void) FormatMagickString(matte,MaxTextExtent,QuantumFormat,
9747 (Quantum) TransparentOpacity);
9748 break;
9749 }
9750 (void) FormatMagickString(message,MaxTextExtent,
9751 "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9752 QuantumRange);
9753 (void) XDialogWidget(display,windows,"Matte",message,matte);
9754 if (*matte == '\0')
9755 break;
9756 break;
9757 }
9758 case MatteEditUndoCommand:
9759 {
9760 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9761 image);
9762 break;
9763 }
9764 case MatteEditHelpCommand:
9765 {
9766 XTextViewWidget(display,resource_info,windows,MagickFalse,
9767 "Help Viewer - Matte Edit",ImageMatteEditHelp);
9768 break;
9769 }
9770 case MatteEditDismissCommand:
9771 {
9772 /*
9773 Prematurely exit.
9774 */
9775 state|=EscapeState;
9776 state|=ExitState;
9777 break;
9778 }
9779 default:
9780 break;
9781 }
9782 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9783 continue;
9784 }
9785 switch (event.type)
9786 {
9787 case ButtonPress:
9788 {
9789 if (event.xbutton.button != Button1)
9790 break;
9791 if ((event.xbutton.window != windows->image.id) &&
9792 (event.xbutton.window != windows->magnify.id))
9793 break;
9794 /*
9795 Update matte data.
9796 */
9797 x=event.xbutton.x;
9798 y=event.xbutton.y;
9799 (void) XMagickCommand(display,resource_info,windows,
9800 SaveToUndoBufferCommand,image);
9801 state|=UpdateConfigurationState;
9802 break;
9803 }
9804 case ButtonRelease:
9805 {
9806 if (event.xbutton.button != Button1)
9807 break;
9808 if ((event.xbutton.window != windows->image.id) &&
9809 (event.xbutton.window != windows->magnify.id))
9810 break;
9811 /*
9812 Update colormap information.
9813 */
9814 x=event.xbutton.x;
9815 y=event.xbutton.y;
9816 XConfigureImageColormap(display,resource_info,windows,*image);
9817 (void) XConfigureImage(display,resource_info,windows,*image);
9818 XInfoWidget(display,windows,text);
9819 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9820 state&=(~UpdateConfigurationState);
9821 break;
9822 }
9823 case Expose:
9824 break;
9825 case KeyPress:
9826 {
9827 char
9828 command[MaxTextExtent];
9829
9830 KeySym
9831 key_symbol;
9832
9833 if (event.xkey.window == windows->magnify.id)
9834 {
9835 Window
9836 window;
9837
9838 window=windows->magnify.id;
9839 while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9840 }
9841 if (event.xkey.window != windows->image.id)
9842 break;
9843 /*
9844 Respond to a user key press.
9845 */
9846 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9847 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9848 switch ((int) key_symbol)
9849 {
9850 case XK_Escape:
9851 case XK_F20:
9852 {
9853 /*
9854 Prematurely exit.
9855 */
9856 state|=ExitState;
9857 break;
9858 }
9859 case XK_F1:
9860 case XK_Help:
9861 {
9862 XTextViewWidget(display,resource_info,windows,MagickFalse,
9863 "Help Viewer - Matte Edit",ImageMatteEditHelp);
9864 break;
9865 }
9866 default:
9867 {
9868 (void) XBell(display,0);
9869 break;
9870 }
9871 }
9872 break;
9873 }
9874 case MotionNotify:
9875 {
9876 /*
9877 Map and unmap Info widget as cursor crosses its boundaries.
9878 */
9879 x=event.xmotion.x;
9880 y=event.xmotion.y;
9881 if (windows->info.mapped != MagickFalse)
9882 {
9883 if ((x < (int) (windows->info.x+windows->info.width)) &&
9884 (y < (int) (windows->info.y+windows->info.height)))
9885 (void) XWithdrawWindow(display,windows->info.id,
9886 windows->info.screen);
9887 }
9888 else
9889 if ((x > (int) (windows->info.x+windows->info.width)) ||
9890 (y > (int) (windows->info.y+windows->info.height)))
9891 (void) XMapWindow(display,windows->info.id);
9892 break;
9893 }
9894 default:
9895 break;
9896 }
9897 if (event.xany.window == windows->magnify.id)
9898 {
9899 x=windows->magnify.x-windows->image.x;
9900 y=windows->magnify.y-windows->image.y;
9901 }
9902 x_offset=x;
9903 y_offset=y;
9904 if ((state & UpdateConfigurationState) != 0)
9905 {
9906 ExceptionInfo
9907 *exception;
9908
9909 int
9910 x,
9911 y;
9912
9913 /*
9914 Matte edit is relative to image configuration.
9915 */
9916 (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
9917 MagickTrue);
9918 XPutPixel(windows->image.ximage,x_offset,y_offset,
9919 windows->pixel_info->background_color.pixel);
9920 width=(unsigned int) (*image)->columns;
9921 height=(unsigned int) (*image)->rows;
9922 x=0;
9923 y=0;
9924 if (windows->image.crop_geometry != (char *) NULL)
9925 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
9926 &width,&height);
9927 x_offset=(int)
9928 (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
9929 y_offset=(int)
9930 (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
9931 if ((x_offset < 0) || (y_offset < 0))
9932 continue;
9933 if ((x_offset >= (int) (*image)->columns) ||
9934 (y_offset >= (int) (*image)->rows))
9935 continue;
9936 if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
9937 return(MagickFalse);
9938 (*image)->matte=MagickTrue;
9939 exception=(&(*image)->exception);
9940 switch (method)
9941 {
9942 case PointMethod:
9943 default:
9944 {
9945 /*
9946 Update matte information using point algorithm.
9947 */
9948 q=GetAuthenticPixels(*image,x_offset,y_offset,1,1,exception);
9949 if (q == (PixelPacket *) NULL)
9950 break;
9951 q->opacity=(Quantum) atol(matte);
9952 (void) SyncAuthenticPixels(*image,exception);
9953 break;
9954 }
9955 case ReplaceMethod:
9956 {
9957 PixelPacket
9958 target;
9959
9960 /*
9961 Update matte information using replace algorithm.
9962 */
9963 (void) GetOneVirtualPixel(*image,x_offset,y_offset,&target,
9964 exception);
9965 for (y=0; y < (long) (*image)->rows; y++)
9966 {
9967 q=GetAuthenticPixels(*image,0,y,(*image)->columns,1,
9968 &(*image)->exception);
9969 if (q == (PixelPacket *) NULL)
9970 break;
9971 for (x=0; x < (int) (*image)->columns; x++)
9972 {
9973 if (IsColorSimilar(*image,q,&target))
9974 q->opacity=(Quantum) atol(matte);
9975 q++;
9976 }
9977 if (SyncAuthenticPixels(*image,exception) == MagickFalse)
9978 break;
9979 }
9980 break;
9981 }
9982 case FloodfillMethod:
9983 case FillToBorderMethod:
9984 {
9985 DrawInfo
9986 *draw_info;
9987
9988 MagickPixelPacket
9989 target;
9990
9991 /*
9992 Update matte information using floodfill algorithm.
9993 */
9994 (void) GetOneVirtualMagickPixel(*image,x_offset,y_offset,&target,
9995 exception);
9996 if (method == FillToBorderMethod)
9997 {
9998 target.red=(MagickRealType)
9999 ScaleShortToQuantum(border_color.red);
10000 target.green=(MagickRealType)
10001 ScaleShortToQuantum(border_color.green);
10002 target.blue=(MagickRealType)
10003 ScaleShortToQuantum(border_color.blue);
10004 }
10005 draw_info=CloneDrawInfo(resource_info->image_info,
10006 (DrawInfo *) NULL);
10007 draw_info->fill.opacity=RoundToQuantum(atof(matte));
10008 (void) FloodfillPaintImage(*image,OpacityChannel,draw_info,&target,
10009 x_offset,y_offset,method == FloodfillMethod ? MagickFalse :
10010 MagickTrue);
10011 draw_info=DestroyDrawInfo(draw_info);
10012 break;
10013 }
10014 case ResetMethod:
10015 {
10016 /*
10017 Update matte information using reset algorithm.
10018 */
10019 if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
10020 return(MagickFalse);
10021 for (y=0; y < (long) (*image)->rows; y++)
10022 {
10023 q=QueueAuthenticPixels(*image,0,y,(*image)->columns,1,exception);
10024 if (q == (PixelPacket *) NULL)
10025 break;
10026 for (x=0; x < (int) (*image)->columns; x++)
10027 {
10028 q->opacity=(Quantum) atol(matte);
10029 q++;
10030 }
10031 if (SyncAuthenticPixels(*image,exception) == MagickFalse)
10032 break;
10033 }
10034 if (atol(matte) == OpaqueOpacity)
10035 (*image)->matte=MagickFalse;
10036 break;
10037 }
10038 }
10039 state&=(~UpdateConfigurationState);
10040 }
10041 } while ((state & ExitState) == 0);
10042 (void) XSelectInput(display,windows->image.id,
10043 windows->image.attributes.event_mask);
10044 XSetCursorState(display,windows,MagickFalse);
10045 (void) XFreeCursor(display,cursor);
10046 return(MagickTrue);
10047}
10048
10049/*
10050%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10051% %
10052% %
10053% %
10054+ X O p e n I m a g e %
10055% %
10056% %
10057% %
10058%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10059%
10060% XOpenImage() loads an image from a file.
10061%
10062% The format of the XOpenImage method is:
10063%
10064% Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10065% XWindows *windows,const unsigned int command)
10066%
10067% A description of each parameter follows:
10068%
10069% o display: Specifies a connection to an X server; returned from
10070% XOpenDisplay.
10071%
10072% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10073%
10074% o windows: Specifies a pointer to a XWindows structure.
10075%
10076% o command: A value other than zero indicates that the file is selected
10077% from the command line argument list.
10078%
10079*/
10080static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10081 XWindows *windows,const MagickBooleanType command)
10082{
10083 const MagickInfo
10084 *magick_info;
10085
10086 ExceptionInfo
10087 *exception;
10088
10089 Image
10090 *nexus;
10091
10092 ImageInfo
10093 *image_info;
10094
10095 static char
10096 filename[MaxTextExtent] = "\0";
10097
10098 /*
10099 Request file name from user.
10100 */
10101 if (command == MagickFalse)
10102 XFileBrowserWidget(display,windows,"Open",filename);
10103 else
10104 {
10105 char
10106 **filelist,
10107 **files;
10108
10109 int
10110 count,
10111 status;
10112
10113 register int
10114 i,
10115 j;
10116
10117 /*
10118 Select next image from the command line.
10119 */
10120 status=XGetCommand(display,windows->image.id,&files,&count);
10121 if (status == 0)
10122 {
10123 ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10124 return((Image *) NULL);
10125 }
10126 filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10127 if (filelist == (char **) NULL)
10128 {
10129 ThrowXWindowFatalException(ResourceLimitError,
10130 "MemoryAllocationFailed","...");
10131 (void) XFreeStringList(files);
10132 return((Image *) NULL);
10133 }
10134 j=0;
10135 for (i=1; i < count; i++)
10136 if (*files[i] != '-')
10137 filelist[j++]=files[i];
10138 filelist[j]=(char *) NULL;
10139 XListBrowserWidget(display,windows,&windows->widget,
10140 (const char **) filelist,"Load","Select Image to Load:",filename);
10141 filelist=(char **) RelinquishMagickMemory(filelist);
10142 (void) XFreeStringList(files);
10143 }
10144 if (*filename == '\0')
10145 return((Image *) NULL);
10146 image_info=CloneImageInfo(resource_info->image_info);
10147 (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10148 (void *) NULL);
10149 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10150 exception=AcquireExceptionInfo();
10151 (void) SetImageInfo(image_info,MagickFalse,exception);
10152 if (LocaleCompare(image_info->magick,"X") == 0)
10153 {
10154 char
10155 seconds[MaxTextExtent];
10156
10157 /*
10158 User may want to delay the X server screen grab.
10159 */
10160 (void) CopyMagickString(seconds,"0",MaxTextExtent);
10161 (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10162 seconds);
10163 if (*seconds == '\0')
10164 return((Image *) NULL);
10165 XDelay(display,(unsigned long) (1000*atol(seconds)));
10166 }
10167 magick_info=GetMagickInfo(image_info->magick,exception);
10168 if ((magick_info != (const MagickInfo *) NULL) &&
10169 (magick_info->raw != MagickFalse))
10170 {
10171 char
10172 geometry[MaxTextExtent];
10173
10174 /*
10175 Request image size from the user.
10176 */
10177 (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10178 if (image_info->size != (char *) NULL)
10179 (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10180 (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10181 geometry);
10182 (void) CloneString(&image_info->size,geometry);
10183 }
10184 /*
10185 Load the image.
10186 */
10187 XSetCursorState(display,windows,MagickTrue);
10188 XCheckRefreshWindows(display,windows);
10189 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10190 nexus=ReadImage(image_info,exception);
10191 CatchException(exception);
10192 XSetCursorState(display,windows,MagickFalse);
10193 if (nexus != (Image *) NULL)
10194 XClientMessage(display,windows->image.id,windows->im_protocols,
10195 windows->im_next_image,CurrentTime);
10196 else
10197 {
10198 char
10199 *text,
10200 **textlist;
10201
10202 /*
10203 Unknown image format.
10204 */
10205 text=FileToString(filename,~0,exception);
10206 if (text == (char *) NULL)
10207 return((Image *) NULL);
10208 textlist=StringToList(text);
10209 if (textlist != (char **) NULL)
10210 {
10211 char
10212 title[MaxTextExtent];
10213
10214 register int
10215 i;
10216
10217 (void) FormatMagickString(title,MaxTextExtent,
10218 "Unknown format: %s",filename);
10219 XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10220 (const char **) textlist);
10221 for (i=0; textlist[i] != (char *) NULL; i++)
10222 textlist[i]=DestroyString(textlist[i]);
10223 textlist=(char **) RelinquishMagickMemory(textlist);
10224 }
10225 text=DestroyString(text);
10226 }
10227 exception=DestroyExceptionInfo(exception);
10228 image_info=DestroyImageInfo(image_info);
10229 return(nexus);
10230}
10231
10232/*
10233%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10234% %
10235% %
10236% %
10237+ X P a n I m a g e %
10238% %
10239% %
10240% %
10241%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10242%
10243% XPanImage() pans the image until the mouse button is released.
10244%
10245% The format of the XPanImage method is:
10246%
10247% void XPanImage(Display *display,XWindows *windows,XEvent *event)
10248%
10249% A description of each parameter follows:
10250%
10251% o display: Specifies a connection to an X server; returned from
10252% XOpenDisplay.
10253%
10254% o windows: Specifies a pointer to a XWindows structure.
10255%
10256% o event: Specifies a pointer to a XEvent structure. If it is NULL,
10257% the entire image is refreshed.
10258%
10259*/
10260static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10261{
10262 char
10263 text[MaxTextExtent];
10264
10265 Cursor
10266 cursor;
10267
10268 MagickRealType
10269 x_factor,
10270 y_factor;
10271
10272 RectangleInfo
10273 pan_info;
10274
10275 unsigned long
10276 state;
10277
10278 /*
10279 Define cursor.
10280 */
10281 if ((windows->image.ximage->width > (int) windows->image.width) &&
10282 (windows->image.ximage->height > (int) windows->image.height))
10283 cursor=XCreateFontCursor(display,XC_fleur);
10284 else
10285 if (windows->image.ximage->width > (int) windows->image.width)
10286 cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10287 else
10288 if (windows->image.ximage->height > (int) windows->image.height)
10289 cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10290 else
10291 cursor=XCreateFontCursor(display,XC_arrow);
10292 (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10293 /*
10294 Pan image as pointer moves until the mouse button is released.
10295 */
10296 x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10297 y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10298 pan_info.width=windows->pan.width*windows->image.width/
10299 windows->image.ximage->width;
10300 pan_info.height=windows->pan.height*windows->image.height/
10301 windows->image.ximage->height;
10302 pan_info.x=0;
10303 pan_info.y=0;
10304 state=UpdateConfigurationState;
10305 do
10306 {
10307 switch (event->type)
10308 {
10309 case ButtonPress:
10310 {
10311 /*
10312 User choose an initial pan location.
10313 */
10314 pan_info.x=event->xbutton.x;
10315 pan_info.y=event->xbutton.y;
10316 state|=UpdateConfigurationState;
10317 break;
10318 }
10319 case ButtonRelease:
10320 {
10321 /*
10322 User has finished panning the image.
10323 */
10324 pan_info.x=event->xbutton.x;
10325 pan_info.y=event->xbutton.y;
10326 state|=UpdateConfigurationState | ExitState;
10327 break;
10328 }
10329 case MotionNotify:
10330 {
10331 pan_info.x=event->xmotion.x;
10332 pan_info.y=event->xmotion.y;
10333 state|=UpdateConfigurationState;
10334 }
10335 default:
10336 break;
10337 }
10338 if ((state & UpdateConfigurationState) != 0)
10339 {
10340 /*
10341 Check boundary conditions.
10342 */
10343 if (pan_info.x < (int) (pan_info.width/2))
10344 pan_info.x=0;
10345 else
10346 pan_info.x=(int) (x_factor*(pan_info.x-(pan_info.width/2)));
10347 if (pan_info.x < 0)
10348 pan_info.x=0;
10349 else
10350 if ((int) (pan_info.x+windows->image.width) >
10351 windows->image.ximage->width)
10352 pan_info.x=(long)
10353 (windows->image.ximage->width-windows->image.width);
10354 if (pan_info.y < (long) (pan_info.height/2))
10355 pan_info.y=0;
10356 else
10357 pan_info.y=(long) (y_factor*(pan_info.y-(pan_info.height/2)));
10358 if (pan_info.y < 0)
10359 pan_info.y=0;
10360 else
10361 if ((int) (pan_info.y+windows->image.height) >
10362 windows->image.ximage->height)
10363 pan_info.y=(long)
10364 (windows->image.ximage->height-windows->image.height);
10365 if ((windows->image.x != (int) pan_info.x) ||
10366 (windows->image.y != (int) pan_info.y))
10367 {
10368 /*
10369 Display image pan offset.
10370 */
10371 windows->image.x=(int) pan_info.x;
10372 windows->image.y=(int) pan_info.y;
10373 (void) FormatMagickString(text,MaxTextExtent," %ux%u%+d%+d ",
10374 windows->image.width,windows->image.height,windows->image.x,
10375 windows->image.y);
10376 XInfoWidget(display,windows,text);
10377 /*
10378 Refresh Image window.
10379 */
10380 XDrawPanRectangle(display,windows);
10381 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10382 }
10383 state&=(~UpdateConfigurationState);
10384 }
10385 /*
10386 Wait for next event.
10387 */
10388 if ((state & ExitState) == 0)
10389 XScreenEvent(display,windows,event);
10390 } while ((state & ExitState) == 0);
10391 /*
10392 Restore cursor.
10393 */
10394 (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10395 (void) XFreeCursor(display,cursor);
10396 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10397}
10398
10399/*
10400%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10401% %
10402% %
10403% %
10404+ X P a s t e I m a g e %
10405% %
10406% %
10407% %
10408%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10409%
10410% XPasteImage() pastes an image previously saved with XCropImage in the X
10411% window image at a location the user chooses with the pointer.
10412%
10413% The format of the XPasteImage method is:
10414%
10415% MagickBooleanType XPasteImage(Display *display,
10416% XResourceInfo *resource_info,XWindows *windows,Image *image)
10417%
10418% A description of each parameter follows:
10419%
10420% o display: Specifies a connection to an X server; returned from
10421% XOpenDisplay.
10422%
10423% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10424%
10425% o windows: Specifies a pointer to a XWindows structure.
10426%
10427% o image: the image; returned from ReadImage.
10428%
10429*/
10430static MagickBooleanType XPasteImage(Display *display,
10431 XResourceInfo *resource_info,XWindows *windows,Image *image)
10432{
10433 static const char
10434 *PasteMenu[] =
10435 {
10436 "Operator",
10437 "Help",
10438 "Dismiss",
10439 (char *) NULL
10440 };
10441
10442 static const ModeType
10443 PasteCommands[] =
10444 {
10445 PasteOperatorsCommand,
10446 PasteHelpCommand,
10447 PasteDismissCommand
10448 };
10449
10450 static CompositeOperator
10451 compose = CopyCompositeOp;
10452
10453 char
10454 text[MaxTextExtent];
10455
10456 Cursor
10457 cursor;
10458
10459 Image
10460 *paste_image;
10461
10462 int
10463 entry,
10464 id,
10465 x,
10466 y;
10467
10468 MagickRealType
10469 scale_factor;
10470
10471 RectangleInfo
10472 highlight_info,
10473 paste_info;
10474
10475 unsigned int
10476 height,
10477 width;
10478
10479 unsigned long
10480 state;
10481
10482 XEvent
10483 event;
10484
10485 /*
10486 Copy image.
10487 */
10488 if (resource_info->copy_image == (Image *) NULL)
10489 return(MagickFalse);
10490 paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,
10491 &image->exception);
10492 /*
10493 Map Command widget.
10494 */
10495 (void) CloneString(&windows->command.name,"Paste");
10496 windows->command.data=1;
10497 (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10498 (void) XMapRaised(display,windows->command.id);
10499 XClientMessage(display,windows->image.id,windows->im_protocols,
10500 windows->im_update_widget,CurrentTime);
10501 /*
10502 Track pointer until button 1 is pressed.
10503 */
10504 XSetCursorState(display,windows,MagickFalse);
10505 XQueryPosition(display,windows->image.id,&x,&y);
10506 (void) XSelectInput(display,windows->image.id,
10507 windows->image.attributes.event_mask | PointerMotionMask);
10508 paste_info.x=windows->image.x+x;
10509 paste_info.y=windows->image.y+y;
10510 paste_info.width=0;
10511 paste_info.height=0;
10512 cursor=XCreateFontCursor(display,XC_ul_angle);
10513 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10514 state=DefaultState;
10515 do
10516 {
10517 if (windows->info.mapped != MagickFalse)
10518 {
10519 /*
10520 Display pointer position.
10521 */
10522 (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
10523 paste_info.x,paste_info.y);
10524 XInfoWidget(display,windows,text);
10525 }
10526 highlight_info=paste_info;
10527 highlight_info.x=paste_info.x-windows->image.x;
10528 highlight_info.y=paste_info.y-windows->image.y;
10529 XHighlightRectangle(display,windows->image.id,
10530 windows->image.highlight_context,&highlight_info);
10531 /*
10532 Wait for next event.
10533 */
10534 XScreenEvent(display,windows,&event);
10535 XHighlightRectangle(display,windows->image.id,
10536 windows->image.highlight_context,&highlight_info);
10537 if (event.xany.window == windows->command.id)
10538 {
10539 /*
10540 Select a command from the Command widget.
10541 */
10542 id=XCommandWidget(display,windows,PasteMenu,&event);
10543 if (id < 0)
10544 continue;
10545 switch (PasteCommands[id])
10546 {
10547 case PasteOperatorsCommand:
10548 {
10549 char
10550 command[MaxTextExtent],
10551 **operators;
10552
10553 /*
10554 Select a command from the pop-up menu.
10555 */
10556 operators=GetMagickOptions(MagickComposeOptions);
10557 if (operators == (char **) NULL)
10558 break;
10559 entry=XMenuWidget(display,windows,PasteMenu[id],
10560 (const char **) operators,command);
10561 if (entry >= 0)
10562 compose=(CompositeOperator) ParseMagickOption(
10563 MagickComposeOptions,MagickFalse,operators[entry]);
10564 operators=DestroyStringList(operators);
10565 break;
10566 }
10567 case PasteHelpCommand:
10568 {
10569 XTextViewWidget(display,resource_info,windows,MagickFalse,
10570 "Help Viewer - Image Composite",ImagePasteHelp);
10571 break;
10572 }
10573 case PasteDismissCommand:
10574 {
10575 /*
10576 Prematurely exit.
10577 */
10578 state|=EscapeState;
10579 state|=ExitState;
10580 break;
10581 }
10582 default:
10583 break;
10584 }
10585 continue;
10586 }
10587 switch (event.type)
10588 {
10589 case ButtonPress:
10590 {
10591 if (image->debug != MagickFalse)
10592 (void) LogMagickEvent(X11Event,GetMagickModule(),
10593 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10594 event.xbutton.button,event.xbutton.x,event.xbutton.y);
10595 if (event.xbutton.button != Button1)
10596 break;
10597 if (event.xbutton.window != windows->image.id)
10598 break;
10599 /*
10600 Paste rectangle is relative to image configuration.
10601 */
10602 width=(unsigned int) image->columns;
10603 height=(unsigned int) image->rows;
10604 x=0;
10605 y=0;
10606 if (windows->image.crop_geometry != (char *) NULL)
10607 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10608 &width,&height);
10609 scale_factor=(MagickRealType) windows->image.ximage->width/width;
10610 paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10611 scale_factor=(MagickRealType) windows->image.ximage->height/height;
10612 paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10613 (void) XCheckDefineCursor(display,windows->image.id,cursor);
10614 paste_info.x=windows->image.x+event.xbutton.x;
10615 paste_info.y=windows->image.y+event.xbutton.y;
10616 break;
10617 }
10618 case ButtonRelease:
10619 {
10620 if (image->debug != MagickFalse)
10621 (void) LogMagickEvent(X11Event,GetMagickModule(),
10622 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10623 event.xbutton.button,event.xbutton.x,event.xbutton.y);
10624 if (event.xbutton.button != Button1)
10625 break;
10626 if (event.xbutton.window != windows->image.id)
10627 break;
10628 if ((paste_info.width != 0) && (paste_info.height != 0))
10629 {
10630 /*
10631 User has selected the location of the paste image.
10632 */
10633 paste_info.x=windows->image.x+event.xbutton.x;
10634 paste_info.y=windows->image.y+event.xbutton.y;
10635 state|=ExitState;
10636 }
10637 break;
10638 }
10639 case Expose:
10640 break;
10641 case KeyPress:
10642 {
10643 char
10644 command[MaxTextExtent];
10645
10646 KeySym
10647 key_symbol;
10648
10649 int
10650 length;
10651
10652 if (event.xkey.window != windows->image.id)
10653 break;
10654 /*
10655 Respond to a user key press.
10656 */
10657 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10658 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10659 *(command+length)='\0';
10660 if (image->debug != MagickFalse)
10661 (void) LogMagickEvent(X11Event,GetMagickModule(),
10662 "Key press: 0x%lx (%s)",(long) key_symbol,command);
10663 switch ((int) key_symbol)
10664 {
10665 case XK_Escape:
10666 case XK_F20:
10667 {
10668 /*
10669 Prematurely exit.
10670 */
10671 paste_image=DestroyImage(paste_image);
10672 state|=EscapeState;
10673 state|=ExitState;
10674 break;
10675 }
10676 case XK_F1:
10677 case XK_Help:
10678 {
10679 (void) XSetFunction(display,windows->image.highlight_context,
10680 GXcopy);
10681 XTextViewWidget(display,resource_info,windows,MagickFalse,
10682 "Help Viewer - Image Composite",ImagePasteHelp);
10683 (void) XSetFunction(display,windows->image.highlight_context,
10684 GXinvert);
10685 break;
10686 }
10687 default:
10688 {
10689 (void) XBell(display,0);
10690 break;
10691 }
10692 }
10693 break;
10694 }
10695 case MotionNotify:
10696 {
10697 /*
10698 Map and unmap Info widget as text cursor crosses its boundaries.
10699 */
10700 x=event.xmotion.x;
10701 y=event.xmotion.y;
10702 if (windows->info.mapped != MagickFalse)
10703 {
10704 if ((x < (int) (windows->info.x+windows->info.width)) &&
10705 (y < (int) (windows->info.y+windows->info.height)))
10706 (void) XWithdrawWindow(display,windows->info.id,
10707 windows->info.screen);
10708 }
10709 else
10710 if ((x > (int) (windows->info.x+windows->info.width)) ||
10711 (y > (int) (windows->info.y+windows->info.height)))
10712 (void) XMapWindow(display,windows->info.id);
10713 paste_info.x=windows->image.x+x;
10714 paste_info.y=windows->image.y+y;
10715 break;
10716 }
10717 default:
10718 {
10719 if (image->debug != MagickFalse)
10720 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10721 event.type);
10722 break;
10723 }
10724 }
10725 } while ((state & ExitState) == 0);
10726 (void) XSelectInput(display,windows->image.id,
10727 windows->image.attributes.event_mask);
10728 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10729 XSetCursorState(display,windows,MagickFalse);
10730 (void) XFreeCursor(display,cursor);
10731 if ((state & EscapeState) != 0)
10732 return(MagickTrue);
10733 /*
10734 Image pasting is relative to image configuration.
10735 */
10736 XSetCursorState(display,windows,MagickTrue);
10737 XCheckRefreshWindows(display,windows);
10738 width=(unsigned int) image->columns;
10739 height=(unsigned int) image->rows;
10740 x=0;
10741 y=0;
10742 if (windows->image.crop_geometry != (char *) NULL)
10743 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10744 scale_factor=(MagickRealType) width/windows->image.ximage->width;
10745 paste_info.x+=x;
10746 paste_info.x=(int) (scale_factor*paste_info.x+0.5);
10747 paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10748 scale_factor=(MagickRealType) height/windows->image.ximage->height;
10749 paste_info.y+=y;
10750 paste_info.y=(int) (scale_factor*paste_info.y*scale_factor+0.5);
10751 paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10752 /*
10753 Paste image with X Image window.
10754 */
10755 (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10756 paste_image=DestroyImage(paste_image);
10757 XSetCursorState(display,windows,MagickFalse);
10758 /*
10759 Update image colormap.
10760 */
10761 XConfigureImageColormap(display,resource_info,windows,image);
10762 (void) XConfigureImage(display,resource_info,windows,image);
10763 return(MagickTrue);
10764}
10765
10766/*
10767%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10768% %
10769% %
10770% %
10771+ X P r i n t I m a g e %
10772% %
10773% %
10774% %
10775%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10776%
10777% XPrintImage() prints an image to a Postscript printer.
10778%
10779% The format of the XPrintImage method is:
10780%
10781% MagickBooleanType XPrintImage(Display *display,
10782% XResourceInfo *resource_info,XWindows *windows,Image *image)
10783%
10784% A description of each parameter follows:
10785%
10786% o display: Specifies a connection to an X server; returned from
10787% XOpenDisplay.
10788%
10789% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10790%
10791% o windows: Specifies a pointer to a XWindows structure.
10792%
10793% o image: the image.
10794%
10795*/
10796static MagickBooleanType XPrintImage(Display *display,
10797 XResourceInfo *resource_info,XWindows *windows,Image *image)
10798{
10799 char
10800 filename[MaxTextExtent],
10801 geometry[MaxTextExtent];
10802
10803 Image
10804 *print_image;
10805
10806 ImageInfo
10807 *image_info;
10808
10809 MagickStatusType
10810 status;
10811
10812 /*
10813 Request Postscript page geometry from user.
10814 */
10815 image_info=CloneImageInfo(resource_info->image_info);
10816 (void) FormatMagickString(geometry,MaxTextExtent,"Letter");
10817 if (image_info->page != (char *) NULL)
10818 (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10819 XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10820 "Select Postscript Page Geometry:",geometry);
10821 if (*geometry == '\0')
10822 return(MagickTrue);
10823 image_info->page=GetPageGeometry(geometry);
10824 /*
10825 Apply image transforms.
10826 */
10827 XSetCursorState(display,windows,MagickTrue);
10828 XCheckRefreshWindows(display,windows);
10829 print_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10830 if (print_image == (Image *) NULL)
10831 return(MagickFalse);
10832 (void) FormatMagickString(geometry,MaxTextExtent,"%dx%d!",
10833 windows->image.ximage->width,windows->image.ximage->height);
10834 (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10835 /*
10836 Print image.
10837 */
10838 (void) AcquireUniqueFilename(filename);
10839 (void) FormatMagickString(print_image->filename,MaxTextExtent,"print:%s",
10840 filename);
10841 status=WriteImage(image_info,print_image);
10842 (void) RelinquishUniqueFileResource(filename);
10843 print_image=DestroyImage(print_image);
10844 image_info=DestroyImageInfo(image_info);
10845 XSetCursorState(display,windows,MagickFalse);
10846 return(status != 0 ? MagickTrue : MagickFalse);
10847}
10848
10849/*
10850%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10851% %
10852% %
10853% %
10854+ X R O I I m a g e %
10855% %
10856% %
10857% %
10858%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10859%
10860% XROIImage() applies an image processing technique to a region of interest.
10861%
10862% The format of the XROIImage method is:
10863%
10864% MagickBooleanType XROIImage(Display *display,
10865% XResourceInfo *resource_info,XWindows *windows,Image **image)
10866%
10867% A description of each parameter follows:
10868%
10869% o display: Specifies a connection to an X server; returned from
10870% XOpenDisplay.
10871%
10872% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10873%
10874% o windows: Specifies a pointer to a XWindows structure.
10875%
10876% o image: the image; returned from ReadImage.
10877%
10878*/
10879static MagickBooleanType XROIImage(Display *display,
10880 XResourceInfo *resource_info,XWindows *windows,Image **image)
10881{
10882#define ApplyMenus 7
10883
10884 static const char
10885 *ROIMenu[] =
10886 {
10887 "Help",
10888 "Dismiss",
10889 (char *) NULL
10890 },
10891 *ApplyMenu[] =
10892 {
10893 "File",
10894 "Edit",
10895 "Transform",
10896 "Enhance",
10897 "Effects",
10898 "F/X",
10899 "Miscellany",
10900 "Help",
10901 "Dismiss",
10902 (char *) NULL
10903 },
10904 *FileMenu[] =
10905 {
10906 "Save...",
10907 "Print...",
10908 (char *) NULL
10909 },
10910 *EditMenu[] =
10911 {
10912 "Undo",
10913 "Redo",
10914 (char *) NULL
10915 },
10916 *TransformMenu[] =
10917 {
10918 "Flop",
10919 "Flip",
10920 "Rotate Right",
10921 "Rotate Left",
10922 (char *) NULL
10923 },
10924 *EnhanceMenu[] =
10925 {
10926 "Hue...",
10927 "Saturation...",
10928 "Brightness...",
10929 "Gamma...",
10930 "Spiff",
10931 "Dull",
10932 "Contrast Stretch...",
10933 "Sigmoidal Contrast...",
10934 "Normalize",
10935 "Equalize",
10936 "Negate",
10937 "Grayscale",
10938 "Map...",
10939 "Quantize...",
10940 (char *) NULL
10941 },
10942 *EffectsMenu[] =
10943 {
10944 "Despeckle",
10945 "Emboss",
10946 "Reduce Noise",
10947 "Add Noise",
10948 "Sharpen...",
10949 "Blur...",
10950 "Threshold...",
10951 "Edge Detect...",
10952 "Spread...",
10953 "Shade...",
10954 "Raise...",
10955 "Segment...",
10956 (char *) NULL
10957 },
10958 *FXMenu[] =
10959 {
10960 "Solarize...",
10961 "Sepia Tone...",
10962 "Swirl...",
10963 "Implode...",
10964 "Vignette...",
10965 "Wave...",
10966 "Oil Paint...",
10967 "Charcoal Draw...",
10968 (char *) NULL
10969 },
10970 *MiscellanyMenu[] =
10971 {
10972 "Image Info",
10973 "Zoom Image",
10974 "Show Preview...",
10975 "Show Histogram",
10976 "Show Matte",
10977 (char *) NULL
10978 };
10979
10980 static const char
10981 **Menus[ApplyMenus] =
10982 {
10983 FileMenu,
10984 EditMenu,
10985 TransformMenu,
10986 EnhanceMenu,
10987 EffectsMenu,
10988 FXMenu,
10989 MiscellanyMenu
10990 };
10991
10992 static const CommandType
10993 ApplyCommands[] =
10994 {
10995 NullCommand,
10996 NullCommand,
10997 NullCommand,
10998 NullCommand,
10999 NullCommand,
11000 NullCommand,
11001 NullCommand,
11002 HelpCommand,
11003 QuitCommand
11004 },
11005 FileCommands[] =
11006 {
11007 SaveCommand,
11008 PrintCommand
11009 },
11010 EditCommands[] =
11011 {
11012 UndoCommand,
11013 RedoCommand
11014 },
11015 TransformCommands[] =
11016 {
11017 FlopCommand,
11018 FlipCommand,
11019 RotateRightCommand,
11020 RotateLeftCommand
11021 },
11022 EnhanceCommands[] =
11023 {
11024 HueCommand,
11025 SaturationCommand,
11026 BrightnessCommand,
11027 GammaCommand,
11028 SpiffCommand,
11029 DullCommand,
11030 ContrastStretchCommand,
11031 SigmoidalContrastCommand,
11032 NormalizeCommand,
11033 EqualizeCommand,
11034 NegateCommand,
11035 GrayscaleCommand,
11036 MapCommand,
11037 QuantizeCommand
11038 },
11039 EffectsCommands[] =
11040 {
11041 DespeckleCommand,
11042 EmbossCommand,
11043 ReduceNoiseCommand,
11044 AddNoiseCommand,
11045 SharpenCommand,
11046 BlurCommand,
11047 EdgeDetectCommand,
11048 SpreadCommand,
11049 ShadeCommand,
11050 RaiseCommand,
11051 SegmentCommand
11052 },
11053 FXCommands[] =
11054 {
11055 SolarizeCommand,
11056 SepiaToneCommand,
11057 SwirlCommand,
11058 ImplodeCommand,
11059 VignetteCommand,
11060 WaveCommand,
11061 OilPaintCommand,
11062 CharcoalDrawCommand
11063 },
11064 MiscellanyCommands[] =
11065 {
11066 InfoCommand,
11067 ZoomCommand,
11068 ShowPreviewCommand,
11069 ShowHistogramCommand,
11070 ShowMatteCommand
11071 },
11072 ROICommands[] =
11073 {
11074 ROIHelpCommand,
11075 ROIDismissCommand
11076 };
11077
11078 static const CommandType
11079 *Commands[ApplyMenus] =
11080 {
11081 FileCommands,
11082 EditCommands,
11083 TransformCommands,
11084 EnhanceCommands,
11085 EffectsCommands,
11086 FXCommands,
11087 MiscellanyCommands
11088 };
11089
11090 char
11091 command[MaxTextExtent],
11092 text[MaxTextExtent];
11093
11094 CommandType
11095 command_type;
11096
11097 Cursor
11098 cursor;
11099
11100 Image
11101 *roi_image;
11102
11103 int
11104 entry,
11105 id,
11106 x,
11107 y;
11108
11109 MagickRealType
11110 scale_factor;
11111
11112 MagickProgressMonitor
11113 progress_monitor;
11114
11115 RectangleInfo
11116 crop_info,
11117 highlight_info,
11118 roi_info;
11119
11120 unsigned int
11121 height,
11122 width;
11123
11124 unsigned long
11125 state;
11126
11127 XEvent
11128 event;
11129
11130 /*
11131 Map Command widget.
11132 */
11133 (void) CloneString(&windows->command.name,"ROI");
11134 windows->command.data=0;
11135 (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11136 (void) XMapRaised(display,windows->command.id);
11137 XClientMessage(display,windows->image.id,windows->im_protocols,
11138 windows->im_update_widget,CurrentTime);
11139 /*
11140 Track pointer until button 1 is pressed.
11141 */
11142 XQueryPosition(display,windows->image.id,&x,&y);
11143 (void) XSelectInput(display,windows->image.id,
11144 windows->image.attributes.event_mask | PointerMotionMask);
11145 roi_info.x=windows->image.x+x;
11146 roi_info.y=windows->image.y+y;
11147 roi_info.width=0;
11148 roi_info.height=0;
11149 cursor=XCreateFontCursor(display,XC_fleur);
11150 state=DefaultState;
11151 do
11152 {
11153 if (windows->info.mapped != MagickFalse)
11154 {
11155 /*
11156 Display pointer position.
11157 */
11158 (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
11159 roi_info.x,roi_info.y);
11160 XInfoWidget(display,windows,text);
11161 }
11162 /*
11163 Wait for next event.
11164 */
11165 XScreenEvent(display,windows,&event);
11166 if (event.xany.window == windows->command.id)
11167 {
11168 /*
11169 Select a command from the Command widget.
11170 */
11171 id=XCommandWidget(display,windows,ROIMenu,&event);
11172 if (id < 0)
11173 continue;
11174 switch (ROICommands[id])
11175 {
11176 case ROIHelpCommand:
11177 {
11178 XTextViewWidget(display,resource_info,windows,MagickFalse,
11179 "Help Viewer - Region of Interest",ImageROIHelp);
11180 break;
11181 }
11182 case ROIDismissCommand:
11183 {
11184 /*
11185 Prematurely exit.
11186 */
11187 state|=EscapeState;
11188 state|=ExitState;
11189 break;
11190 }
11191 default:
11192 break;
11193 }
11194 continue;
11195 }
11196 switch (event.type)
11197 {
11198 case ButtonPress:
11199 {
11200 if (event.xbutton.button != Button1)
11201 break;
11202 if (event.xbutton.window != windows->image.id)
11203 break;
11204 /*
11205 Note first corner of region of interest rectangle-- exit loop.
11206 */
11207 (void) XCheckDefineCursor(display,windows->image.id,cursor);
11208 roi_info.x=windows->image.x+event.xbutton.x;
11209 roi_info.y=windows->image.y+event.xbutton.y;
11210 state|=ExitState;
11211 break;
11212 }
11213 case ButtonRelease:
11214 break;
11215 case Expose:
11216 break;
11217 case KeyPress:
11218 {
11219 KeySym
11220 key_symbol;
11221
11222 if (event.xkey.window != windows->image.id)
11223 break;
11224 /*
11225 Respond to a user key press.
11226 */
11227 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11228 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11229 switch ((int) key_symbol)
11230 {
11231 case XK_Escape:
11232 case XK_F20:
11233 {
11234 /*
11235 Prematurely exit.
11236 */
11237 state|=EscapeState;
11238 state|=ExitState;
11239 break;
11240 }
11241 case XK_F1:
11242 case XK_Help:
11243 {
11244 XTextViewWidget(display,resource_info,windows,MagickFalse,
11245 "Help Viewer - Region of Interest",ImageROIHelp);
11246 break;
11247 }
11248 default:
11249 {
11250 (void) XBell(display,0);
11251 break;
11252 }
11253 }
11254 break;
11255 }
11256 case MotionNotify:
11257 {
11258 /*
11259 Map and unmap Info widget as text cursor crosses its boundaries.
11260 */
11261 x=event.xmotion.x;
11262 y=event.xmotion.y;
11263 if (windows->info.mapped != MagickFalse)
11264 {
11265 if ((x < (int) (windows->info.x+windows->info.width)) &&
11266 (y < (int) (windows->info.y+windows->info.height)))
11267 (void) XWithdrawWindow(display,windows->info.id,
11268 windows->info.screen);
11269 }
11270 else
11271 if ((x > (int) (windows->info.x+windows->info.width)) ||
11272 (y > (int) (windows->info.y+windows->info.height)))
11273 (void) XMapWindow(display,windows->info.id);
11274 roi_info.x=windows->image.x+x;
11275 roi_info.y=windows->image.y+y;
11276 break;
11277 }
11278 default:
11279 break;
11280 }
11281 } while ((state & ExitState) == 0);
11282 (void) XSelectInput(display,windows->image.id,
11283 windows->image.attributes.event_mask);
11284 if ((state & EscapeState) != 0)
11285 {
11286 /*
11287 User want to exit without region of interest.
11288 */
11289 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11290 (void) XFreeCursor(display,cursor);
11291 return(MagickTrue);
11292 }
11293 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11294 do
11295 {
11296 /*
11297 Size rectangle as pointer moves until the mouse button is released.
11298 */
11299 x=(int) roi_info.x;
11300 y=(int) roi_info.y;
11301 roi_info.width=0;
11302 roi_info.height=0;
11303 state=DefaultState;
11304 do
11305 {
11306 highlight_info=roi_info;
11307 highlight_info.x=roi_info.x-windows->image.x;
11308 highlight_info.y=roi_info.y-windows->image.y;
11309 if ((highlight_info.width > 3) && (highlight_info.height > 3))
11310 {
11311 /*
11312 Display info and draw region of interest rectangle.
11313 */
11314 if (windows->info.mapped == MagickFalse)
11315 (void) XMapWindow(display,windows->info.id);
11316 (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
11317 roi_info.width,roi_info.height,roi_info.x,roi_info.y);
11318 XInfoWidget(display,windows,text);
11319 XHighlightRectangle(display,windows->image.id,
11320 windows->image.highlight_context,&highlight_info);
11321 }
11322 else
11323 if (windows->info.mapped != MagickFalse)
11324 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11325 /*
11326 Wait for next event.
11327 */
11328 XScreenEvent(display,windows,&event);
11329 if ((highlight_info.width > 3) && (highlight_info.height > 3))
11330 XHighlightRectangle(display,windows->image.id,
11331 windows->image.highlight_context,&highlight_info);
11332 switch (event.type)
11333 {
11334 case ButtonPress:
11335 {
11336 roi_info.x=windows->image.x+event.xbutton.x;
11337 roi_info.y=windows->image.y+event.xbutton.y;
11338 break;
11339 }
11340 case ButtonRelease:
11341 {
11342 /*
11343 User has committed to region of interest rectangle.
11344 */
11345 roi_info.x=windows->image.x+event.xbutton.x;
11346 roi_info.y=windows->image.y+event.xbutton.y;
11347 XSetCursorState(display,windows,MagickFalse);
11348 state|=ExitState;
11349 if (LocaleCompare(windows->command.name,"Apply") == 0)
11350 break;
11351 (void) CloneString(&windows->command.name,"Apply");
11352 windows->command.data=ApplyMenus;
11353 (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11354 break;
11355 }
11356 case Expose:
11357 break;
11358 case MotionNotify:
11359 {
11360 roi_info.x=windows->image.x+event.xmotion.x;
11361 roi_info.y=windows->image.y+event.xmotion.y;
11362 }
11363 default:
11364 break;
11365 }
11366 if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11367 ((state & ExitState) != 0))
11368 {
11369 /*
11370 Check boundary conditions.
11371 */
11372 if (roi_info.x < 0)
11373 roi_info.x=0;
11374 else
11375 if (roi_info.x > (int) windows->image.ximage->width)
11376 roi_info.x=windows->image.ximage->width;
11377 if ((int) roi_info.x < x)
11378 roi_info.width=(unsigned int) (x-roi_info.x);
11379 else
11380 {
11381 roi_info.width=(unsigned int) (roi_info.x-x);
11382 roi_info.x=x;
11383 }
11384 if (roi_info.y < 0)
11385 roi_info.y=0;
11386 else
11387 if (roi_info.y > (int) windows->image.ximage->height)
11388 roi_info.y=windows->image.ximage->height;
11389 if ((int) roi_info.y < y)
11390 roi_info.height=(unsigned int) (y-roi_info.y);
11391 else
11392 {
11393 roi_info.height=(unsigned int) (roi_info.y-y);
11394 roi_info.y=y;
11395 }
11396 }
11397 } while ((state & ExitState) == 0);
11398 /*
11399 Wait for user to grab a corner of the rectangle or press return.
11400 */
11401 state=DefaultState;
11402 command_type=NullCommand;
11403 (void) XMapWindow(display,windows->info.id);
11404 do
11405 {
11406 if (windows->info.mapped != MagickFalse)
11407 {
11408 /*
11409 Display pointer position.
11410 */
11411 (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
11412 roi_info.width,roi_info.height,roi_info.x,roi_info.y);
11413 XInfoWidget(display,windows,text);
11414 }
11415 highlight_info=roi_info;
11416 highlight_info.x=roi_info.x-windows->image.x;
11417 highlight_info.y=roi_info.y-windows->image.y;
11418 if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11419 {
11420 state|=EscapeState;
11421 state|=ExitState;
11422 break;
11423 }
11424 if ((state & UpdateRegionState) != 0)
11425 {
11426 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11427 switch (command_type)
11428 {
11429 case UndoCommand:
11430 case RedoCommand:
11431 {
11432 (void) XMagickCommand(display,resource_info,windows,command_type,
11433 image);
11434 break;
11435 }
11436 default:
11437 {
11438 /*
11439 Region of interest is relative to image configuration.
11440 */
11441 progress_monitor=SetImageProgressMonitor(*image,
11442 (MagickProgressMonitor) NULL,(*image)->client_data);
11443 crop_info=roi_info;
11444 width=(unsigned int) (*image)->columns;
11445 height=(unsigned int) (*image)->rows;
11446 x=0;
11447 y=0;
11448 if (windows->image.crop_geometry != (char *) NULL)
11449 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11450 &width,&height);
11451 scale_factor=(MagickRealType) width/windows->image.ximage->width;
11452 crop_info.x+=x;
11453 crop_info.x=(int) (scale_factor*crop_info.x+0.5);
11454 crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11455 scale_factor=(MagickRealType)
11456 height/windows->image.ximage->height;
11457 crop_info.y+=y;
11458 crop_info.y=(int) (scale_factor*crop_info.y+0.5);
11459 crop_info.height=(unsigned int)
11460 (scale_factor*crop_info.height+0.5);
11461 roi_image=CropImage(*image,&crop_info,&(*image)->exception);
11462 (void) SetImageProgressMonitor(*image,progress_monitor,
11463 (*image)->client_data);
11464 if (roi_image == (Image *) NULL)
11465 continue;
11466 /*
11467 Apply image processing technique to the region of interest.
11468 */
11469 windows->image.orphan=MagickTrue;
11470 (void) XMagickCommand(display,resource_info,windows,command_type,
11471 &roi_image);
11472 progress_monitor=SetImageProgressMonitor(*image,
11473 (MagickProgressMonitor) NULL,(*image)->client_data);
11474 (void) XMagickCommand(display,resource_info,windows,
11475 SaveToUndoBufferCommand,image);
11476 windows->image.orphan=MagickFalse;
11477 (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11478 crop_info.x,crop_info.y);
11479 roi_image=DestroyImage(roi_image);
11480 (void) SetImageProgressMonitor(*image,progress_monitor,
11481 (*image)->client_data);
11482 break;
11483 }
11484 }
11485 if (command_type != InfoCommand)
11486 {
11487 XConfigureImageColormap(display,resource_info,windows,*image);
11488 (void) XConfigureImage(display,resource_info,windows,*image);
11489 }
11490 XCheckRefreshWindows(display,windows);
11491 XInfoWidget(display,windows,text);
11492 (void) XSetFunction(display,windows->image.highlight_context,
11493 GXinvert);
11494 state&=(~UpdateRegionState);
11495 }
11496 XHighlightRectangle(display,windows->image.id,
11497 windows->image.highlight_context,&highlight_info);
11498 XScreenEvent(display,windows,&event);
11499 if (event.xany.window == windows->command.id)
11500 {
11501 /*
11502 Select a command from the Command widget.
11503 */
11504 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11505 command_type=NullCommand;
11506 id=XCommandWidget(display,windows,ApplyMenu,&event);
11507 if (id >= 0)
11508 {
11509 (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11510 command_type=ApplyCommands[id];
11511 if (id < ApplyMenus)
11512 {
11513 /*
11514 Select a command from a pop-up menu.
11515 */
11516 entry=XMenuWidget(display,windows,ApplyMenu[id],
11517 (const char **) Menus[id],command);
11518 if (entry >= 0)
11519 {
11520 (void) CopyMagickString(command,Menus[id][entry],
11521 MaxTextExtent);
11522 command_type=Commands[id][entry];
11523 }
11524 }
11525 }
11526 (void) XSetFunction(display,windows->image.highlight_context,
11527 GXinvert);
11528 XHighlightRectangle(display,windows->image.id,
11529 windows->image.highlight_context,&highlight_info);
11530 if (command_type == HelpCommand)
11531 {
11532 (void) XSetFunction(display,windows->image.highlight_context,
11533 GXcopy);
11534 XTextViewWidget(display,resource_info,windows,MagickFalse,
11535 "Help Viewer - Region of Interest",ImageROIHelp);
11536 (void) XSetFunction(display,windows->image.highlight_context,
11537 GXinvert);
11538 continue;
11539 }
11540 if (command_type == QuitCommand)
11541 {
11542 /*
11543 exit.
11544 */
11545 state|=EscapeState;
11546 state|=ExitState;
11547 continue;
11548 }
11549 if (command_type != NullCommand)
11550 state|=UpdateRegionState;
11551 continue;
11552 }
11553 XHighlightRectangle(display,windows->image.id,
11554 windows->image.highlight_context,&highlight_info);
11555 switch (event.type)
11556 {
11557 case ButtonPress:
11558 {
11559 x=windows->image.x;
11560 y=windows->image.y;
11561 if (event.xbutton.button != Button1)
11562 break;
11563 if (event.xbutton.window != windows->image.id)
11564 break;
11565 x=windows->image.x+event.xbutton.x;
11566 y=windows->image.y+event.xbutton.y;
11567 if ((x < (int) (roi_info.x+RoiDelta)) &&
11568 (x > (int) (roi_info.x-RoiDelta)) &&
11569 (y < (int) (roi_info.y+RoiDelta)) &&
11570 (y > (int) (roi_info.y-RoiDelta)))
11571 {
11572 roi_info.x=(long) (roi_info.x+roi_info.width);
11573 roi_info.y=(long) (roi_info.y+roi_info.height);
11574 state|=UpdateConfigurationState;
11575 break;
11576 }
11577 if ((x < (int) (roi_info.x+RoiDelta)) &&
11578 (x > (int) (roi_info.x-RoiDelta)) &&
11579 (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11580 (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11581 {
11582 roi_info.x=(long) (roi_info.x+roi_info.width);
11583 state|=UpdateConfigurationState;
11584 break;
11585 }
11586 if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11587 (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11588 (y < (int) (roi_info.y+RoiDelta)) &&
11589 (y > (int) (roi_info.y-RoiDelta)))
11590 {
11591 roi_info.y=(long) (roi_info.y+roi_info.height);
11592 state|=UpdateConfigurationState;
11593 break;
11594 }
11595 if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11596 (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11597 (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11598 (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11599 {
11600 state|=UpdateConfigurationState;
11601 break;
11602 }
11603 }
11604 case ButtonRelease:
11605 {
11606 if (event.xbutton.window == windows->pan.id)
11607 if ((highlight_info.x != crop_info.x-windows->image.x) ||
11608 (highlight_info.y != crop_info.y-windows->image.y))
11609 XHighlightRectangle(display,windows->image.id,
11610 windows->image.highlight_context,&highlight_info);
11611 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11612 event.xbutton.time);
11613 break;
11614 }
11615 case Expose:
11616 {
11617 if (event.xexpose.window == windows->image.id)
11618 if (event.xexpose.count == 0)
11619 {
11620 event.xexpose.x=(int) highlight_info.x;
11621 event.xexpose.y=(int) highlight_info.y;
11622 event.xexpose.width=(int) highlight_info.width;
11623 event.xexpose.height=(int) highlight_info.height;
11624 XRefreshWindow(display,&windows->image,&event);
11625 }
11626 if (event.xexpose.window == windows->info.id)
11627 if (event.xexpose.count == 0)
11628 XInfoWidget(display,windows,text);
11629 break;
11630 }
11631 case KeyPress:
11632 {
11633 KeySym
11634 key_symbol;
11635
11636 if (event.xkey.window != windows->image.id)
11637 break;
11638 /*
11639 Respond to a user key press.
11640 */
11641 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11642 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11643 switch ((int) key_symbol)
11644 {
11645 case XK_Shift_L:
11646 case XK_Shift_R:
11647 break;
11648 case XK_Escape:
11649 case XK_F20:
11650 state|=EscapeState;
11651 case XK_Return:
11652 {
11653 state|=ExitState;
11654 break;
11655 }
11656 case XK_Home:
11657 case XK_KP_Home:
11658 {
11659 roi_info.x=(long) (windows->image.width/2L-roi_info.width/2L);
11660 roi_info.y=(long) (windows->image.height/2L-roi_info.height/2L);
11661 break;
11662 }
11663 case XK_Left:
11664 case XK_KP_Left:
11665 {
11666 roi_info.x--;
11667 break;
11668 }
11669 case XK_Up:
11670 case XK_KP_Up:
11671 case XK_Next:
11672 {
11673 roi_info.y--;
11674 break;
11675 }
11676 case XK_Right:
11677 case XK_KP_Right:
11678 {
11679 roi_info.x++;
11680 break;
11681 }
11682 case XK_Prior:
11683 case XK_Down:
11684 case XK_KP_Down:
11685 {
11686 roi_info.y++;
11687 break;
11688 }
11689 case XK_F1:
11690 case XK_Help:
11691 {
11692 (void) XSetFunction(display,windows->image.highlight_context,
11693 GXcopy);
11694 XTextViewWidget(display,resource_info,windows,MagickFalse,
11695 "Help Viewer - Region of Interest",ImageROIHelp);
11696 (void) XSetFunction(display,windows->image.highlight_context,
11697 GXinvert);
11698 break;
11699 }
11700 default:
11701 {
11702 command_type=XImageWindowCommand(display,resource_info,windows,
11703 event.xkey.state,key_symbol,image);
11704 if (command_type != NullCommand)
11705 state|=UpdateRegionState;
11706 break;
11707 }
11708 }
11709 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11710 event.xkey.time);
11711 break;
11712 }
11713 case KeyRelease:
11714 break;
11715 case MotionNotify:
11716 {
11717 if (event.xbutton.window != windows->image.id)
11718 break;
11719 /*
11720 Map and unmap Info widget as text cursor crosses its boundaries.
11721 */
11722 x=event.xmotion.x;
11723 y=event.xmotion.y;
11724 if (windows->info.mapped != MagickFalse)
11725 {
11726 if ((x < (int) (windows->info.x+windows->info.width)) &&
11727 (y < (int) (windows->info.y+windows->info.height)))
11728 (void) XWithdrawWindow(display,windows->info.id,
11729 windows->info.screen);
11730 }
11731 else
11732 if ((x > (int) (windows->info.x+windows->info.width)) ||
11733 (y > (int) (windows->info.y+windows->info.height)))
11734 (void) XMapWindow(display,windows->info.id);
11735 roi_info.x=windows->image.x+event.xmotion.x;
11736 roi_info.y=windows->image.y+event.xmotion.y;
11737 break;
11738 }
11739 case SelectionRequest:
11740 {
11741 XSelectionEvent
11742 notify;
11743
11744 XSelectionRequestEvent
11745 *request;
11746
11747 /*
11748 Set primary selection.
11749 */
11750 (void) FormatMagickString(text,MaxTextExtent,"%lux%lu%+ld%+ld",
11751 roi_info.width,roi_info.height,roi_info.x,roi_info.y);
11752 request=(&(event.xselectionrequest));
11753 (void) XChangeProperty(request->display,request->requestor,
11754 request->property,request->target,8,PropModeReplace,
11755 (unsigned char *) text,(int) strlen(text));
11756 notify.type=SelectionNotify;
11757 notify.display=request->display;
11758 notify.requestor=request->requestor;
11759 notify.selection=request->selection;
11760 notify.target=request->target;
11761 notify.time=request->time;
11762 if (request->property == None)
11763 notify.property=request->target;
11764 else
11765 notify.property=request->property;
11766 (void) XSendEvent(request->display,request->requestor,False,0,
11767 (XEvent *) &notify);
11768 }
11769 default:
11770 break;
11771 }
11772 if ((state & UpdateConfigurationState) != 0)
11773 {
11774 (void) XPutBackEvent(display,&event);
11775 (void) XCheckDefineCursor(display,windows->image.id,cursor);
11776 break;
11777 }
11778 } while ((state & ExitState) == 0);
11779 } while ((state & ExitState) == 0);
11780 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11781 XSetCursorState(display,windows,MagickFalse);
11782 if ((state & EscapeState) != 0)
11783 return(MagickTrue);
11784 return(MagickTrue);
11785}
11786
11787/*
11788%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11789% %
11790% %
11791% %
11792+ X R o t a t e I m a g e %
11793% %
11794% %
11795% %
11796%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11797%
11798% XRotateImage() rotates the X image. If the degrees parameter if zero, the
11799% rotation angle is computed from the slope of a line drawn by the user.
11800%
11801% The format of the XRotateImage method is:
11802%
11803% MagickBooleanType XRotateImage(Display *display,
11804% XResourceInfo *resource_info,XWindows *windows,double degrees,
11805% Image **image)
11806%
11807% A description of each parameter follows:
11808%
11809% o display: Specifies a connection to an X server; returned from
11810% XOpenDisplay.
11811%
11812% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11813%
11814% o windows: Specifies a pointer to a XWindows structure.
11815%
11816% o degrees: Specifies the number of degrees to rotate the image.
11817%
11818% o image: the image.
11819%
11820*/
11821static MagickBooleanType XRotateImage(Display *display,
11822 XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image)
11823{
11824 static const char
11825 *RotateMenu[] =
11826 {
11827 "Pixel Color",
11828 "Direction",
11829 "Help",
11830 "Dismiss",
11831 (char *) NULL
11832 };
11833
11834 static ModeType
11835 direction = HorizontalRotateCommand;
11836
11837 static const ModeType
11838 DirectionCommands[] =
11839 {
11840 HorizontalRotateCommand,
11841 VerticalRotateCommand
11842 },
11843 RotateCommands[] =
11844 {
11845 RotateColorCommand,
11846 RotateDirectionCommand,
11847 RotateHelpCommand,
11848 RotateDismissCommand
11849 };
11850
11851 static unsigned int
11852 pen_id = 0;
11853
11854 char
11855 command[MaxTextExtent],
11856 text[MaxTextExtent];
11857
11858 Image
11859 *rotate_image;
11860
11861 int
11862 id,
11863 x,
11864 y;
11865
11866 MagickRealType
11867 normalized_degrees;
11868
11869 register int
11870 i;
11871
11872 unsigned int
11873 height,
11874 rotations,
11875 width;
11876
11877 if (degrees == 0.0)
11878 {
11879 unsigned int
11880 distance;
11881
11882 unsigned long
11883 state;
11884
11885 XEvent
11886 event;
11887
11888 XSegment
11889 rotate_info;
11890
11891 /*
11892 Map Command widget.
11893 */
11894 (void) CloneString(&windows->command.name,"Rotate");
11895 windows->command.data=2;
11896 (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
11897 (void) XMapRaised(display,windows->command.id);
11898 XClientMessage(display,windows->image.id,windows->im_protocols,
11899 windows->im_update_widget,CurrentTime);
11900 /*
11901 Wait for first button press.
11902 */
11903 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11904 XQueryPosition(display,windows->image.id,&x,&y);
11905 rotate_info.x1=x;
11906 rotate_info.y1=y;
11907 rotate_info.x2=x;
11908 rotate_info.y2=y;
11909 state=DefaultState;
11910 do
11911 {
11912 XHighlightLine(display,windows->image.id,
11913 windows->image.highlight_context,&rotate_info);
11914 /*
11915 Wait for next event.
11916 */
11917 XScreenEvent(display,windows,&event);
11918 XHighlightLine(display,windows->image.id,
11919 windows->image.highlight_context,&rotate_info);
11920 if (event.xany.window == windows->command.id)
11921 {
11922 /*
11923 Select a command from the Command widget.
11924 */
11925 id=XCommandWidget(display,windows,RotateMenu,&event);
11926 if (id < 0)
11927 continue;
11928 (void) XSetFunction(display,windows->image.highlight_context,
11929 GXcopy);
11930 switch (RotateCommands[id])
11931 {
11932 case RotateColorCommand:
11933 {
11934 const char
11935 *ColorMenu[MaxNumberPens];
11936
11937 int
11938 pen_number;
11939
11940 XColor
11941 color;
11942
11943 /*
11944 Initialize menu selections.
11945 */
11946 for (i=0; i < (int) (MaxNumberPens-2); i++)
11947 ColorMenu[i]=resource_info->pen_colors[i];
11948 ColorMenu[MaxNumberPens-2]="Browser...";
11949 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
11950 /*
11951 Select a pen color from the pop-up menu.
11952 */
11953 pen_number=XMenuWidget(display,windows,RotateMenu[id],
11954 (const char **) ColorMenu,command);
11955 if (pen_number < 0)
11956 break;
11957 if (pen_number == (MaxNumberPens-2))
11958 {
11959 static char
11960 color_name[MaxTextExtent] = "gray";
11961
11962 /*
11963 Select a pen color from a dialog.
11964 */
11965 resource_info->pen_colors[pen_number]=color_name;
11966 XColorBrowserWidget(display,windows,"Select",color_name);
11967 if (*color_name == '\0')
11968 break;
11969 }
11970 /*
11971 Set pen color.
11972 */
11973 (void) XParseColor(display,windows->map_info->colormap,
11974 resource_info->pen_colors[pen_number],&color);
11975 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
11976 (unsigned int) MaxColors,&color);
11977 windows->pixel_info->pen_colors[pen_number]=color;
11978 pen_id=(unsigned int) pen_number;
11979 break;
11980 }
11981 case RotateDirectionCommand:
11982 {
11983 static const char
11984 *Directions[] =
11985 {
11986 "horizontal",
11987 "vertical",
11988 (char *) NULL,
11989 };
11990
11991 /*
11992 Select a command from the pop-up menu.
11993 */
11994 id=XMenuWidget(display,windows,RotateMenu[id],
11995 Directions,command);
11996 if (id >= 0)
11997 direction=DirectionCommands[id];
11998 break;
11999 }
12000 case RotateHelpCommand:
12001 {
12002 XTextViewWidget(display,resource_info,windows,MagickFalse,
12003 "Help Viewer - Image Rotation",ImageRotateHelp);
12004 break;
12005 }
12006 case RotateDismissCommand:
12007 {
12008 /*
12009 Prematurely exit.
12010 */
12011 state|=EscapeState;
12012 state|=ExitState;
12013 break;
12014 }
12015 default:
12016 break;
12017 }
12018 (void) XSetFunction(display,windows->image.highlight_context,
12019 GXinvert);
12020 continue;
12021 }
12022 switch (event.type)
12023 {
12024 case ButtonPress:
12025 {
12026 if (event.xbutton.button != Button1)
12027 break;
12028 if (event.xbutton.window != windows->image.id)
12029 break;
12030 /*
12031 exit loop.
12032 */
12033 (void) XSetFunction(display,windows->image.highlight_context,
12034 GXcopy);
12035 rotate_info.x1=event.xbutton.x;
12036 rotate_info.y1=event.xbutton.y;
12037 state|=ExitState;
12038 break;
12039 }
12040 case ButtonRelease:
12041 break;
12042 case Expose:
12043 break;
12044 case KeyPress:
12045 {
12046 char
12047 command[MaxTextExtent];
12048
12049 KeySym
12050 key_symbol;
12051
12052 if (event.xkey.window != windows->image.id)
12053 break;
12054 /*
12055 Respond to a user key press.
12056 */
12057 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12058 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12059 switch ((int) key_symbol)
12060 {
12061 case XK_Escape:
12062 case XK_F20:
12063 {
12064 /*
12065 Prematurely exit.
12066 */
12067 state|=EscapeState;
12068 state|=ExitState;
12069 break;
12070 }
12071 case XK_F1:
12072 case XK_Help:
12073 {
12074 (void) XSetFunction(display,windows->image.highlight_context,
12075 GXcopy);
12076 XTextViewWidget(display,resource_info,windows,MagickFalse,
12077 "Help Viewer - Image Rotation",ImageRotateHelp);
12078 (void) XSetFunction(display,windows->image.highlight_context,
12079 GXinvert);
12080 break;
12081 }
12082 default:
12083 {
12084 (void) XBell(display,0);
12085 break;
12086 }
12087 }
12088 break;
12089 }
12090 case MotionNotify:
12091 {
12092 rotate_info.x1=event.xmotion.x;
12093 rotate_info.y1=event.xmotion.y;
12094 }
12095 }
12096 rotate_info.x2=rotate_info.x1;
12097 rotate_info.y2=rotate_info.y1;
12098 if (direction == HorizontalRotateCommand)
12099 rotate_info.x2+=32;
12100 else
12101 rotate_info.y2-=32;
12102 } while ((state & ExitState) == 0);
12103 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12104 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12105 if ((state & EscapeState) != 0)
12106 return(MagickTrue);
12107 /*
12108 Draw line as pointer moves until the mouse button is released.
12109 */
12110 distance=0;
12111 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12112 state=DefaultState;
12113 do
12114 {
12115 if (distance > 9)
12116 {
12117 /*
12118 Display info and draw rotation line.
12119 */
12120 if (windows->info.mapped == MagickFalse)
12121 (void) XMapWindow(display,windows->info.id);
12122 (void) FormatMagickString(text,MaxTextExtent," %g",
12123 direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12124 XInfoWidget(display,windows,text);
12125 XHighlightLine(display,windows->image.id,
12126 windows->image.highlight_context,&rotate_info);
12127 }
12128 else
12129 if (windows->info.mapped != MagickFalse)
12130 (void) XWithdrawWindow(display,windows->info.id,
12131 windows->info.screen);
12132 /*
12133 Wait for next event.
12134 */
12135 XScreenEvent(display,windows,&event);
12136 if (distance > 9)
12137 XHighlightLine(display,windows->image.id,
12138 windows->image.highlight_context,&rotate_info);
12139 switch (event.type)
12140 {
12141 case ButtonPress:
12142 break;
12143 case ButtonRelease:
12144 {
12145 /*
12146 User has committed to rotation line.
12147 */
12148 rotate_info.x2=event.xbutton.x;
12149 rotate_info.y2=event.xbutton.y;
12150 state|=ExitState;
12151 break;
12152 }
12153 case Expose:
12154 break;
12155 case MotionNotify:
12156 {
12157 rotate_info.x2=event.xmotion.x;
12158 rotate_info.y2=event.xmotion.y;
12159 }
12160 default:
12161 break;
12162 }
12163 /*
12164 Check boundary conditions.
12165 */
12166 if (rotate_info.x2 < 0)
12167 rotate_info.x2=0;
12168 else
12169 if (rotate_info.x2 > (int) windows->image.width)
12170 rotate_info.x2=(short) windows->image.width;
12171 if (rotate_info.y2 < 0)
12172 rotate_info.y2=0;
12173 else
12174 if (rotate_info.y2 > (int) windows->image.height)
12175 rotate_info.y2=(short) windows->image.height;
12176 /*
12177 Compute rotation angle from the slope of the line.
12178 */
12179 degrees=0.0;
12180 distance=(unsigned int)
12181 ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12182 ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12183 if (distance > 9)
12184 degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12185 rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12186 } while ((state & ExitState) == 0);
12187 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12188 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12189 if (distance <= 9)
12190 return(MagickTrue);
12191 }
12192 if (direction == VerticalRotateCommand)
12193 degrees-=90.0;
12194 if (degrees == 0.0)
12195 return(MagickTrue);
12196 /*
12197 Rotate image.
12198 */
12199 normalized_degrees=degrees;
12200 while (normalized_degrees < -45.0)
12201 normalized_degrees+=360.0;
12202 for (rotations=0; normalized_degrees > 45.0; rotations++)
12203 normalized_degrees-=90.0;
12204 if (normalized_degrees != 0.0)
12205 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
12206 XSetCursorState(display,windows,MagickTrue);
12207 XCheckRefreshWindows(display,windows);
12208 (*image)->background_color.red=ScaleShortToQuantum(
12209 windows->pixel_info->pen_colors[pen_id].red);
12210 (*image)->background_color.green=ScaleShortToQuantum(
12211 windows->pixel_info->pen_colors[pen_id].green);
12212 (*image)->background_color.blue=ScaleShortToQuantum(
12213 windows->pixel_info->pen_colors[pen_id].blue);
12214 rotate_image=RotateImage(*image,degrees,&(*image)->exception);
12215 XSetCursorState(display,windows,MagickFalse);
12216 if (rotate_image == (Image *) NULL)
12217 return(MagickFalse);
12218 *image=DestroyImage(*image);
12219 *image=rotate_image;
12220 if (windows->image.crop_geometry != (char *) NULL)
12221 {
12222 /*
12223 Rotate crop geometry.
12224 */
12225 width=(unsigned int) (*image)->columns;
12226 height=(unsigned int) (*image)->rows;
12227 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12228 switch (rotations % 4)
12229 {
12230 default:
12231 case 0:
12232 break;
12233 case 1:
12234 {
12235 /*
12236 Rotate 90 degrees.
12237 */
12238 (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
12239 "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12240 (int) height-y,x);
12241 break;
12242 }
12243 case 2:
12244 {
12245 /*
12246 Rotate 180 degrees.
12247 */
12248 (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
12249 "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12250 break;
12251 }
12252 case 3:
12253 {
12254 /*
12255 Rotate 270 degrees.
12256 */
12257 (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
12258 "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12259 break;
12260 }
12261 }
12262 }
12263 if (windows->image.orphan != MagickFalse)
12264 return(MagickTrue);
12265 if (normalized_degrees != 0.0)
12266 {
12267 /*
12268 Update image colormap.
12269 */
12270 windows->image.window_changes.width=(int) (*image)->columns;
12271 windows->image.window_changes.height=(int) (*image)->rows;
12272 if (windows->image.crop_geometry != (char *) NULL)
12273 {
12274 /*
12275 Obtain dimensions of image from crop geometry.
12276 */
12277 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12278 &width,&height);
12279 windows->image.window_changes.width=(int) width;
12280 windows->image.window_changes.height=(int) height;
12281 }
12282 XConfigureImageColormap(display,resource_info,windows,*image);
12283 }
12284 else
12285 if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12286 {
12287 windows->image.window_changes.width=windows->image.ximage->height;
12288 windows->image.window_changes.height=windows->image.ximage->width;
12289 }
12290 /*
12291 Update image configuration.
12292 */
12293 (void) XConfigureImage(display,resource_info,windows,*image);
12294 return(MagickTrue);
12295}
12296
12297/*
12298%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12299% %
12300% %
12301% %
12302+ X S a v e I m a g e %
12303% %
12304% %
12305% %
12306%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12307%
12308% XSaveImage() saves an image to a file.
12309%
12310% The format of the XSaveImage method is:
12311%
12312% MagickBooleanType XSaveImage(Display *display,
12313% XResourceInfo *resource_info,XWindows *windows,Image *image)
12314%
12315% A description of each parameter follows:
12316%
12317% o display: Specifies a connection to an X server; returned from
12318% XOpenDisplay.
12319%
12320% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12321%
12322% o windows: Specifies a pointer to a XWindows structure.
12323%
12324% o image: the image.
12325%
12326*/
12327static MagickBooleanType XSaveImage(Display *display,
12328 XResourceInfo *resource_info,XWindows *windows,Image *image)
12329{
12330 char
12331 filename[MaxTextExtent],
12332 geometry[MaxTextExtent];
12333
12334 Image
12335 *save_image;
12336
12337 ImageInfo
12338 *image_info;
12339
12340 MagickStatusType
12341 status;
12342
12343 /*
12344 Request file name from user.
12345 */
12346 if (resource_info->write_filename != (char *) NULL)
12347 (void) CopyMagickString(filename,resource_info->write_filename,
12348 MaxTextExtent);
12349 else
12350 {
12351 char
12352 path[MaxTextExtent];
12353
12354 int
12355 status;
12356
12357 GetPathComponent(image->filename,HeadPath,path);
12358 GetPathComponent(image->filename,TailPath,filename);
12359 status=chdir(path);
12360 if (status == -1)
12361 (void) ThrowMagickException(&image->exception,GetMagickModule(),
12362 FileOpenError,"UnableToOpenFile","%s",path);
12363 }
12364 XFileBrowserWidget(display,windows,"Save",filename);
12365 if (*filename == '\0')
12366 return(MagickTrue);
12367 if (IsPathAccessible(filename) != MagickFalse)
12368 {
12369 int
12370 status;
12371
12372 /*
12373 File exists-- seek user's permission before overwriting.
12374 */
12375 status=XConfirmWidget(display,windows,"Overwrite",filename);
12376 if (status <= 0)
12377 return(MagickTrue);
12378 }
12379 image_info=CloneImageInfo(resource_info->image_info);
12380 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12381 (void) SetImageInfo(image_info,MagickFalse,&image->exception);
12382 if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12383 (LocaleCompare(image_info->magick,"JPG") == 0))
12384 {
12385 char
12386 quality[MaxTextExtent];
12387
12388 int
12389 status;
12390
12391 /*
12392 Request JPEG quality from user.
12393 */
12394 (void) FormatMagickString(quality,MaxTextExtent,"%lu",image->quality);
12395 status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12396 quality);
12397 if (*quality == '\0')
12398 return(MagickTrue);
12399 image->quality=(unsigned long) atol(quality);
12400 image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12401 }
12402 if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12403 (LocaleCompare(image_info->magick,"PDF") == 0) ||
12404 (LocaleCompare(image_info->magick,"PS") == 0) ||
12405 (LocaleCompare(image_info->magick,"PS2") == 0))
12406 {
12407 char
12408 geometry[MaxTextExtent];
12409
12410 /*
12411 Request page geometry from user.
12412 */
cristyb93d9e72009-09-12 20:02:21 +000012413 (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +000012414 if (LocaleCompare(image_info->magick,"PDF") == 0)
cristyb93d9e72009-09-12 20:02:21 +000012415 (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +000012416 if (image_info->page != (char *) NULL)
12417 (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12418 XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12419 "Select page geometry:",geometry);
12420 if (*geometry != '\0')
12421 image_info->page=GetPageGeometry(geometry);
12422 }
12423 /*
12424 Apply image transforms.
12425 */
12426 XSetCursorState(display,windows,MagickTrue);
12427 XCheckRefreshWindows(display,windows);
12428 save_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12429 if (save_image == (Image *) NULL)
12430 return(MagickFalse);
12431 (void) FormatMagickString(geometry,MaxTextExtent,"%dx%d!",
12432 windows->image.ximage->width,windows->image.ximage->height);
12433 (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12434 /*
12435 Write image.
12436 */
12437 (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12438 status=WriteImage(image_info,save_image);
12439 if (status != MagickFalse)
12440 image->taint=MagickFalse;
12441 save_image=DestroyImage(save_image);
12442 image_info=DestroyImageInfo(image_info);
12443 XSetCursorState(display,windows,MagickFalse);
12444 return(status != 0 ? MagickTrue : MagickFalse);
12445}
12446
12447/*
12448%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12449% %
12450% %
12451% %
12452+ X S c r e e n E v e n t %
12453% %
12454% %
12455% %
12456%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12457%
12458% XScreenEvent() handles global events associated with the Pan and Magnify
12459% windows.
12460%
12461% The format of the XScreenEvent function is:
12462%
12463% void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12464%
12465% A description of each parameter follows:
12466%
12467% o display: Specifies a pointer to the Display structure; returned from
12468% XOpenDisplay.
12469%
12470% o windows: Specifies a pointer to a XWindows structure.
12471%
12472% o event: Specifies a pointer to a X11 XEvent structure.
12473%
12474%
12475*/
12476
12477#if defined(__cplusplus) || defined(c_plusplus)
12478extern "C" {
12479#endif
12480
12481static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12482{
12483 register XWindows
12484 *windows;
12485
12486 windows=(XWindows *) data;
12487 if ((event->type == ClientMessage) &&
12488 (event->xclient.window == windows->image.id))
12489 return(MagickFalse);
12490 return(MagickTrue);
12491}
12492
12493#if defined(__cplusplus) || defined(c_plusplus)
12494}
12495#endif
12496
12497static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12498{
12499 register int
12500 x,
12501 y;
12502
12503 (void) XIfEvent(display,event,XPredicate,(char *) windows);
12504 if (event->xany.window == windows->command.id)
12505 return;
12506 switch (event->type)
12507 {
12508 case ButtonPress:
12509 case ButtonRelease:
12510 {
12511 if ((event->xbutton.button == Button3) &&
12512 (event->xbutton.state & Mod1Mask))
12513 {
12514 /*
12515 Convert Alt-Button3 to Button2.
12516 */
12517 event->xbutton.button=Button2;
12518 event->xbutton.state&=(~Mod1Mask);
12519 }
12520 if (event->xbutton.window == windows->backdrop.id)
12521 {
12522 (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12523 event->xbutton.time);
12524 break;
12525 }
12526 if (event->xbutton.window == windows->pan.id)
12527 {
12528 XPanImage(display,windows,event);
12529 break;
12530 }
12531 if (event->xbutton.window == windows->image.id)
12532 if (event->xbutton.button == Button2)
12533 {
12534 /*
12535 Update magnified image.
12536 */
12537 x=event->xbutton.x;
12538 y=event->xbutton.y;
12539 if (x < 0)
12540 x=0;
12541 else
12542 if (x >= (int) windows->image.width)
12543 x=(int) (windows->image.width-1);
12544 windows->magnify.x=windows->image.x+x;
12545 if (y < 0)
12546 y=0;
12547 else
12548 if (y >= (int) windows->image.height)
12549 y=(int) (windows->image.height-1);
12550 windows->magnify.y=windows->image.y+y;
12551 if (windows->magnify.mapped == MagickFalse)
12552 (void) XMapRaised(display,windows->magnify.id);
12553 XMakeMagnifyImage(display,windows);
12554 if (event->type == ButtonRelease)
12555 (void) XWithdrawWindow(display,windows->info.id,
12556 windows->info.screen);
12557 break;
12558 }
12559 break;
12560 }
12561 case ClientMessage:
12562 {
12563 /*
12564 If client window delete message, exit.
12565 */
12566 if (event->xclient.message_type != windows->wm_protocols)
12567 break;
12568 if (*event->xclient.data.l != (long) windows->wm_delete_window)
12569 break;
12570 if (event->xclient.window == windows->magnify.id)
12571 {
12572 (void) XWithdrawWindow(display,windows->magnify.id,
12573 windows->magnify.screen);
12574 break;
12575 }
12576 break;
12577 }
12578 case ConfigureNotify:
12579 {
12580 if (event->xconfigure.window == windows->magnify.id)
12581 {
12582 unsigned int
12583 magnify;
12584
12585 /*
12586 Magnify window has a new configuration.
12587 */
12588 windows->magnify.width=(unsigned int) event->xconfigure.width;
12589 windows->magnify.height=(unsigned int) event->xconfigure.height;
12590 if (windows->magnify.mapped == MagickFalse)
12591 break;
12592 magnify=1;
12593 while ((int) magnify <= event->xconfigure.width)
12594 magnify<<=1;
12595 while ((int) magnify <= event->xconfigure.height)
12596 magnify<<=1;
12597 magnify>>=1;
12598 if (((int) magnify != event->xconfigure.width) ||
12599 ((int) magnify != event->xconfigure.height))
12600 {
12601 XWindowChanges
12602 window_changes;
12603
12604 window_changes.width=(int) magnify;
12605 window_changes.height=(int) magnify;
12606 (void) XReconfigureWMWindow(display,windows->magnify.id,
12607 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12608 &window_changes);
12609 break;
12610 }
12611 XMakeMagnifyImage(display,windows);
12612 break;
12613 }
12614 break;
12615 }
12616 case Expose:
12617 {
12618 if (event->xexpose.window == windows->image.id)
12619 {
12620 XRefreshWindow(display,&windows->image,event);
12621 break;
12622 }
12623 if (event->xexpose.window == windows->pan.id)
12624 if (event->xexpose.count == 0)
12625 {
12626 XDrawPanRectangle(display,windows);
12627 break;
12628 }
12629 if (event->xexpose.window == windows->magnify.id)
12630 if (event->xexpose.count == 0)
12631 {
12632 XMakeMagnifyImage(display,windows);
12633 break;
12634 }
12635 break;
12636 }
12637 case KeyPress:
12638 {
12639 char
12640 command[MaxTextExtent];
12641
12642 KeySym
12643 key_symbol;
12644
12645 if (event->xkey.window != windows->magnify.id)
12646 break;
12647 /*
12648 Respond to a user key press.
12649 */
12650 (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12651 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12652 XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12653 break;
12654 }
12655 case MapNotify:
12656 {
12657 if (event->xmap.window == windows->magnify.id)
12658 {
12659 windows->magnify.mapped=MagickTrue;
12660 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12661 break;
12662 }
12663 if (event->xmap.window == windows->info.id)
12664 {
12665 windows->info.mapped=MagickTrue;
12666 break;
12667 }
12668 break;
12669 }
12670 case MotionNotify:
12671 {
12672 while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12673 if (event->xmotion.window == windows->image.id)
12674 if (windows->magnify.mapped != MagickFalse)
12675 {
12676 /*
12677 Update magnified image.
12678 */
12679 x=event->xmotion.x;
12680 y=event->xmotion.y;
12681 if (x < 0)
12682 x=0;
12683 else
12684 if (x >= (int) windows->image.width)
12685 x=(int) (windows->image.width-1);
12686 windows->magnify.x=windows->image.x+x;
12687 if (y < 0)
12688 y=0;
12689 else
12690 if (y >= (int) windows->image.height)
12691 y=(int) (windows->image.height-1);
12692 windows->magnify.y=windows->image.y+y;
12693 XMakeMagnifyImage(display,windows);
12694 }
12695 break;
12696 }
12697 case UnmapNotify:
12698 {
12699 if (event->xunmap.window == windows->magnify.id)
12700 {
12701 windows->magnify.mapped=MagickFalse;
12702 break;
12703 }
12704 if (event->xunmap.window == windows->info.id)
12705 {
12706 windows->info.mapped=MagickFalse;
12707 break;
12708 }
12709 break;
12710 }
12711 default:
12712 break;
12713 }
12714}
12715
12716/*
12717%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12718% %
12719% %
12720% %
12721+ X S e t C r o p G e o m e t r y %
12722% %
12723% %
12724% %
12725%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12726%
12727% XSetCropGeometry() accepts a cropping geometry relative to the Image window
12728% and translates it to a cropping geometry relative to the image.
12729%
12730% The format of the XSetCropGeometry method is:
12731%
12732% void XSetCropGeometry(Display *display,XWindows *windows,
12733% RectangleInfo *crop_info,Image *image)
12734%
12735% A description of each parameter follows:
12736%
12737% o display: Specifies a connection to an X server; returned from
12738% XOpenDisplay.
12739%
12740% o windows: Specifies a pointer to a XWindows structure.
12741%
12742% o crop_info: A pointer to a RectangleInfo that defines a region of the
12743% Image window to crop.
12744%
12745% o image: the image.
12746%
12747*/
12748static void XSetCropGeometry(Display *display,XWindows *windows,
12749 RectangleInfo *crop_info,Image *image)
12750{
12751 char
12752 text[MaxTextExtent];
12753
12754 int
12755 x,
12756 y;
12757
12758 MagickRealType
12759 scale_factor;
12760
12761 unsigned int
12762 height,
12763 width;
12764
12765 if (windows->info.mapped != MagickFalse)
12766 {
12767 /*
12768 Display info on cropping rectangle.
12769 */
12770 (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
12771 crop_info->width,crop_info->height,crop_info->x,crop_info->y);
12772 XInfoWidget(display,windows,text);
12773 }
12774 /*
12775 Cropping geometry is relative to any previous crop geometry.
12776 */
12777 x=0;
12778 y=0;
12779 width=(unsigned int) image->columns;
12780 height=(unsigned int) image->rows;
12781 if (windows->image.crop_geometry != (char *) NULL)
12782 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12783 else
12784 windows->image.crop_geometry=AcquireString((char *) NULL);
12785 /*
12786 Define the crop geometry string from the cropping rectangle.
12787 */
12788 scale_factor=(MagickRealType) width/windows->image.ximage->width;
12789 if (crop_info->x > 0)
12790 x+=(int) (scale_factor*crop_info->x+0.5);
12791 width=(unsigned int) (scale_factor*crop_info->width+0.5);
12792 if (width == 0)
12793 width=1;
12794 scale_factor=(MagickRealType) height/windows->image.ximage->height;
12795 if (crop_info->y > 0)
12796 y+=(int) (scale_factor*crop_info->y+0.5);
12797 height=(unsigned int) (scale_factor*crop_info->height+0.5);
12798 if (height == 0)
12799 height=1;
12800 (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
12801 "%ux%u%+d%+d",width,height,x,y);
12802}
12803
12804/*
12805%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12806% %
12807% %
12808% %
12809+ X T i l e I m a g e %
12810% %
12811% %
12812% %
12813%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12814%
12815% XTileImage() loads or deletes a selected tile from a visual image directory.
12816% The load or delete command is chosen from a menu.
12817%
12818% The format of the XTileImage method is:
12819%
12820% Image *XTileImage(Display *display,XResourceInfo *resource_info,
12821% XWindows *windows,Image *image,XEvent *event)
12822%
12823% A description of each parameter follows:
12824%
12825% o tile_image: XTileImage reads or deletes the tile image
12826% and returns it. A null image is returned if an error occurs.
12827%
12828% o display: Specifies a connection to an X server; returned from
12829% XOpenDisplay.
12830%
12831% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12832%
12833% o windows: Specifies a pointer to a XWindows structure.
12834%
12835% o image: the image; returned from ReadImage.
12836%
12837% o event: Specifies a pointer to a XEvent structure. If it is NULL,
12838% the entire image is refreshed.
12839%
12840*/
12841static Image *XTileImage(Display *display,XResourceInfo *resource_info,
12842 XWindows *windows,Image *image,XEvent *event)
12843{
12844 static const char
12845 *VerbMenu[] =
12846 {
12847 "Load",
12848 "Next",
12849 "Former",
12850 "Delete",
12851 "Update",
12852 (char *) NULL,
12853 };
12854
12855 static const ModeType
12856 TileCommands[] =
12857 {
12858 TileLoadCommand,
12859 TileNextCommand,
12860 TileFormerCommand,
12861 TileDeleteCommand,
12862 TileUpdateCommand
12863 };
12864
12865 char
12866 command[MaxTextExtent],
12867 filename[MaxTextExtent];
12868
12869 Image
12870 *tile_image;
12871
12872 int
12873 id,
12874 status,
12875 tile,
12876 x,
12877 y;
12878
12879 MagickRealType
12880 scale_factor;
12881
12882 register char
12883 *p,
12884 *q;
12885
12886 register int
12887 i;
12888
12889 unsigned int
12890 height,
12891 width;
12892
12893 /*
12894 Tile image is relative to montage image configuration.
12895 */
12896 x=0;
12897 y=0;
12898 width=(unsigned int) image->columns;
12899 height=(unsigned int) image->rows;
12900 if (windows->image.crop_geometry != (char *) NULL)
12901 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12902 scale_factor=(MagickRealType) width/windows->image.ximage->width;
12903 event->xbutton.x+=windows->image.x;
12904 event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
12905 scale_factor=(MagickRealType) height/windows->image.ximage->height;
12906 event->xbutton.y+=windows->image.y;
12907 event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
12908 /*
12909 Determine size and location of each tile in the visual image directory.
12910 */
12911 width=(unsigned int) image->columns;
12912 height=(unsigned int) image->rows;
12913 x=0;
12914 y=0;
12915 (void) XParseGeometry(image->montage,&x,&y,&width,&height);
12916 tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
12917 (event->xbutton.x-x)/width;
12918 if (tile < 0)
12919 {
12920 /*
12921 Button press is outside any tile.
12922 */
12923 (void) XBell(display,0);
12924 return((Image *) NULL);
12925 }
12926 /*
12927 Determine file name from the tile directory.
12928 */
12929 p=image->directory;
12930 for (i=tile; (i != 0) && (*p != '\0'); )
12931 {
12932 if (*p == '\n')
12933 i--;
12934 p++;
12935 }
12936 if (*p == '\0')
12937 {
12938 /*
12939 Button press is outside any tile.
12940 */
12941 (void) XBell(display,0);
12942 return((Image *) NULL);
12943 }
12944 /*
12945 Select a command from the pop-up menu.
12946 */
12947 id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
12948 if (id < 0)
12949 return((Image *) NULL);
12950 q=p;
12951 while ((*q != '\n') && (*q != '\0'))
12952 q++;
12953 (void) CopyMagickString(filename,p,(size_t) (q-p+1));
12954 /*
12955 Perform command for the selected tile.
12956 */
12957 XSetCursorState(display,windows,MagickTrue);
12958 XCheckRefreshWindows(display,windows);
12959 tile_image=NewImageList();
12960 switch (TileCommands[id])
12961 {
12962 case TileLoadCommand:
12963 {
12964 /*
12965 Load tile image.
12966 */
12967 XCheckRefreshWindows(display,windows);
12968 (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
12969 MaxTextExtent);
12970 (void) CopyMagickString(resource_info->image_info->filename,filename,
12971 MaxTextExtent);
12972 tile_image=ReadImage(resource_info->image_info,&image->exception);
12973 CatchException(&image->exception);
12974 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12975 break;
12976 }
12977 case TileNextCommand:
12978 {
12979 /*
12980 Display next image.
12981 */
12982 XClientMessage(display,windows->image.id,windows->im_protocols,
12983 windows->im_next_image,CurrentTime);
12984 break;
12985 }
12986 case TileFormerCommand:
12987 {
12988 /*
12989 Display former image.
12990 */
12991 XClientMessage(display,windows->image.id,windows->im_protocols,
12992 windows->im_former_image,CurrentTime);
12993 break;
12994 }
12995 case TileDeleteCommand:
12996 {
12997 /*
12998 Delete tile image.
12999 */
13000 if (IsPathAccessible(filename) == MagickFalse)
13001 {
13002 XNoticeWidget(display,windows,"Image file does not exist:",filename);
13003 break;
13004 }
13005 status=XConfirmWidget(display,windows,"Really delete tile",filename);
13006 if (status <= 0)
13007 break;
13008 status=remove(filename) != 0 ? MagickTrue : MagickFalse;
13009 if (status != MagickFalse)
13010 {
13011 XNoticeWidget(display,windows,"Unable to delete image file:",
13012 filename);
13013 break;
13014 }
13015 }
13016 case TileUpdateCommand:
13017 {
13018 ExceptionInfo
13019 *exception;
13020
13021 int
13022 x_offset,
13023 y_offset;
13024
13025 PixelPacket
13026 pixel;
13027
13028 register int
13029 j;
13030
13031 register PixelPacket
13032 *s;
13033
13034 /*
13035 Ensure all the images exist.
13036 */
13037 tile=0;
13038 for (p=image->directory; *p != '\0'; p++)
13039 {
13040 q=p;
13041 while ((*q != '\n') && (*q != '\0'))
13042 q++;
13043 (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13044 p=q;
13045 if (IsPathAccessible(filename) != MagickFalse)
13046 {
13047 tile++;
13048 continue;
13049 }
13050 /*
13051 Overwrite tile with background color.
13052 */
13053 x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13054 y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13055 exception=(&image->exception);
13056 (void) GetOneVirtualPixel(image,0,0,&pixel,exception);
13057 for (i=0; i < (int) height; i++)
13058 {
13059 s=GetAuthenticPixels(image,x_offset,y_offset+i,width,1,exception);
13060 if (s == (PixelPacket *) NULL)
13061 break;
13062 for (j=0; j < (int) width; j++)
13063 *s++=pixel;
13064 if (SyncAuthenticPixels(image,exception) == MagickFalse)
13065 break;
13066 }
13067 tile++;
13068 }
13069 windows->image.window_changes.width=(int) image->columns;
13070 windows->image.window_changes.height=(int) image->rows;
13071 XConfigureImageColormap(display,resource_info,windows,image);
13072 (void) XConfigureImage(display,resource_info,windows,image);
13073 break;
13074 }
13075 default:
13076 break;
13077 }
13078 XSetCursorState(display,windows,MagickFalse);
13079 return(tile_image);
13080}
13081
13082/*
13083%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13084% %
13085% %
13086% %
13087+ X T r a n s l a t e I m a g e %
13088% %
13089% %
13090% %
13091%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13092%
13093% XTranslateImage() translates the image within an Image window by one pixel
13094% as specified by the key symbol. If the image has a `montage string the
13095% translation is respect to the width and height contained within the string.
13096%
13097% The format of the XTranslateImage method is:
13098%
13099% void XTranslateImage(Display *display,XWindows *windows,
13100% Image *image,const KeySym key_symbol)
13101%
13102% A description of each parameter follows:
13103%
13104% o display: Specifies a connection to an X server; returned from
13105% XOpenDisplay.
13106%
13107% o windows: Specifies a pointer to a XWindows structure.
13108%
13109% o image: the image.
13110%
13111% o key_symbol: Specifies a KeySym which indicates which side of the image
13112% to trim.
13113%
13114*/
13115static void XTranslateImage(Display *display,XWindows *windows,
13116 Image *image,const KeySym key_symbol)
13117{
13118 char
13119 text[MaxTextExtent];
13120
13121 int
13122 x,
13123 y;
13124
13125 unsigned int
13126 x_offset,
13127 y_offset;
13128
13129 /*
13130 User specified a pan position offset.
13131 */
13132 x_offset=windows->image.width;
13133 y_offset=windows->image.height;
13134 if (image->montage != (char *) NULL)
13135 (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13136 switch ((int) key_symbol)
13137 {
13138 case XK_Home:
13139 case XK_KP_Home:
13140 {
13141 windows->image.x=(int) windows->image.width/2;
13142 windows->image.y=(int) windows->image.height/2;
13143 break;
13144 }
13145 case XK_Left:
13146 case XK_KP_Left:
13147 {
13148 windows->image.x-=x_offset;
13149 break;
13150 }
13151 case XK_Next:
13152 case XK_Up:
13153 case XK_KP_Up:
13154 {
13155 windows->image.y-=y_offset;
13156 break;
13157 }
13158 case XK_Right:
13159 case XK_KP_Right:
13160 {
13161 windows->image.x+=x_offset;
13162 break;
13163 }
13164 case XK_Prior:
13165 case XK_Down:
13166 case XK_KP_Down:
13167 {
13168 windows->image.y+=y_offset;
13169 break;
13170 }
13171 default:
13172 return;
13173 }
13174 /*
13175 Check boundary conditions.
13176 */
13177 if (windows->image.x < 0)
13178 windows->image.x=0;
13179 else
13180 if ((int) (windows->image.x+windows->image.width) >
13181 windows->image.ximage->width)
13182 windows->image.x=windows->image.ximage->width-windows->image.width;
13183 if (windows->image.y < 0)
13184 windows->image.y=0;
13185 else
13186 if ((int) (windows->image.y+windows->image.height) >
13187 windows->image.ximage->height)
13188 windows->image.y=windows->image.ximage->height-windows->image.height;
13189 /*
13190 Refresh Image window.
13191 */
13192 (void) FormatMagickString(text,MaxTextExtent," %ux%u%+d%+d ",
13193 windows->image.width,windows->image.height,windows->image.x,
13194 windows->image.y);
13195 XInfoWidget(display,windows,text);
13196 XCheckRefreshWindows(display,windows);
13197 XDrawPanRectangle(display,windows);
13198 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13199 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13200}
13201
13202/*
13203%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13204% %
13205% %
13206% %
13207+ X T r i m I m a g e %
13208% %
13209% %
13210% %
13211%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13212%
13213% XTrimImage() trims the edges from the Image window.
13214%
13215% The format of the XTrimImage method is:
13216%
13217% MagickBooleanType XTrimImage(Display *display,
13218% XResourceInfo *resource_info,XWindows *windows,Image *image)
13219%
13220% A description of each parameter follows:
13221%
13222% o display: Specifies a connection to an X server; returned from
13223% XOpenDisplay.
13224%
13225% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13226%
13227% o windows: Specifies a pointer to a XWindows structure.
13228%
13229% o image: the image.
13230%
13231*/
13232static MagickBooleanType XTrimImage(Display *display,
13233 XResourceInfo *resource_info,XWindows *windows,Image *image)
13234{
13235 RectangleInfo
13236 trim_info;
13237
13238 register int
13239 x,
13240 y;
13241
13242 unsigned long
13243 background,
13244 pixel;
13245
13246 /*
13247 Trim edges from image.
13248 */
13249 XSetCursorState(display,windows,MagickTrue);
13250 XCheckRefreshWindows(display,windows);
13251 /*
13252 Crop the left edge.
13253 */
13254 background=XGetPixel(windows->image.ximage,0,0);
13255 trim_info.width=(unsigned long) windows->image.ximage->width;
13256 for (x=0; x < windows->image.ximage->width; x++)
13257 {
13258 for (y=0; y < windows->image.ximage->height; y++)
13259 {
13260 pixel=XGetPixel(windows->image.ximage,x,y);
13261 if (pixel != background)
13262 break;
13263 }
13264 if (y < windows->image.ximage->height)
13265 break;
13266 }
13267 trim_info.x=x;
13268 if (trim_info.x == (int) windows->image.ximage->width)
13269 {
13270 XSetCursorState(display,windows,MagickFalse);
13271 return(MagickFalse);
13272 }
13273 /*
13274 Crop the right edge.
13275 */
13276 background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13277 for (x=windows->image.ximage->width-1; x != 0; x--)
13278 {
13279 for (y=0; y < windows->image.ximage->height; y++)
13280 {
13281 pixel=XGetPixel(windows->image.ximage,x,y);
13282 if (pixel != background)
13283 break;
13284 }
13285 if (y < windows->image.ximage->height)
13286 break;
13287 }
13288 trim_info.width=(unsigned long) (x-trim_info.x+1);
13289 /*
13290 Crop the top edge.
13291 */
13292 background=XGetPixel(windows->image.ximage,0,0);
13293 trim_info.height=(unsigned long) windows->image.ximage->height;
13294 for (y=0; y < windows->image.ximage->height; y++)
13295 {
13296 for (x=0; x < windows->image.ximage->width; x++)
13297 {
13298 pixel=XGetPixel(windows->image.ximage,x,y);
13299 if (pixel != background)
13300 break;
13301 }
13302 if (x < windows->image.ximage->width)
13303 break;
13304 }
13305 trim_info.y=y;
13306 /*
13307 Crop the bottom edge.
13308 */
13309 background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13310 for (y=windows->image.ximage->height-1; y != 0; y--)
13311 {
13312 for (x=0; x < windows->image.ximage->width; x++)
13313 {
13314 pixel=XGetPixel(windows->image.ximage,x,y);
13315 if (pixel != background)
13316 break;
13317 }
13318 if (x < windows->image.ximage->width)
13319 break;
13320 }
13321 trim_info.height=(unsigned long) y-trim_info.y+1;
13322 if (((unsigned int) trim_info.width != windows->image.width) ||
13323 ((unsigned int) trim_info.height != windows->image.height))
13324 {
13325 /*
13326 Reconfigure Image window as defined by the trimming rectangle.
13327 */
13328 XSetCropGeometry(display,windows,&trim_info,image);
13329 windows->image.window_changes.width=(int) trim_info.width;
13330 windows->image.window_changes.height=(int) trim_info.height;
13331 (void) XConfigureImage(display,resource_info,windows,image);
13332 }
13333 XSetCursorState(display,windows,MagickFalse);
13334 return(MagickTrue);
13335}
13336
13337/*
13338%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13339% %
13340% %
13341% %
13342+ X V i s u a l D i r e c t o r y I m a g e %
13343% %
13344% %
13345% %
13346%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13347%
13348% XVisualDirectoryImage() creates a Visual Image Directory.
13349%
13350% The format of the XVisualDirectoryImage method is:
13351%
13352% Image *XVisualDirectoryImage(Display *display,
13353% XResourceInfo *resource_info,XWindows *windows)
13354%
13355% A description of each parameter follows:
13356%
13357% o nexus: Method XVisualDirectoryImage returns a visual image
13358% directory if it can be created successfully. Otherwise a null image
13359% is returned.
13360%
13361% o display: Specifies a connection to an X server; returned from
13362% XOpenDisplay.
13363%
13364% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13365%
13366% o windows: Specifies a pointer to a XWindows structure.
13367%
13368*/
13369static Image *XVisualDirectoryImage(Display *display,
13370 XResourceInfo *resource_info,XWindows *windows)
13371{
13372#define TileImageTag "Scale/Image"
13373#define XClientName "montage"
13374
13375 char
13376 **filelist;
13377
13378 ExceptionInfo
13379 *exception;
13380
13381 Image
13382 *images,
13383 *montage_image,
13384 *next_image,
13385 *thumbnail_image;
13386
13387 ImageInfo
13388 *read_info;
13389
13390 int
13391 number_files;
13392
13393 MagickBooleanType
13394 backdrop;
13395
13396 MagickStatusType
13397 status;
13398
13399 MontageInfo
13400 *montage_info;
13401
13402 RectangleInfo
13403 geometry;
13404
13405 register int
13406 i;
13407
13408 static char
13409 filename[MaxTextExtent] = "\0",
13410 filenames[MaxTextExtent] = "*";
13411
13412 XResourceInfo
13413 background_resources;
13414
13415 /*
13416 Request file name from user.
13417 */
13418 XFileBrowserWidget(display,windows,"Directory",filenames);
13419 if (*filenames == '\0')
13420 return((Image *) NULL);
13421 /*
13422 Expand the filenames.
13423 */
13424 filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13425 if (filelist == (char **) NULL)
13426 {
13427 ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13428 filenames);
13429 return((Image *) NULL);
13430 }
13431 number_files=1;
13432 filelist[0]=filenames;
13433 status=ExpandFilenames(&number_files,&filelist);
13434 if ((status == MagickFalse) || (number_files == 0))
13435 {
13436 if (number_files == 0)
13437 ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13438 else
13439 ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13440 filenames);
13441 return((Image *) NULL);
13442 }
13443 /*
13444 Set image background resources.
13445 */
13446 background_resources=(*resource_info);
13447 background_resources.window_id=AcquireString("");
13448 (void) FormatMagickString(background_resources.window_id,MaxTextExtent,
13449 "0x%lx",windows->image.id);
13450 background_resources.backdrop=MagickTrue;
13451 /*
13452 Read each image and convert them to a tile.
13453 */
13454 backdrop=(windows->visual_info->klass == TrueColor) ||
13455 (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13456 read_info=CloneImageInfo(resource_info->image_info);
13457 (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13458 (void *) NULL);
13459 images=NewImageList();
13460 exception=AcquireExceptionInfo();
13461 XSetCursorState(display,windows,MagickTrue);
13462 XCheckRefreshWindows(display,windows);
13463 for (i=0; i < (long) number_files; i++)
13464 {
13465 (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13466 filelist[i]=DestroyString(filelist[i]);
13467 *read_info->magick='\0';
13468 (void) CloneString(&read_info->size,DefaultTileGeometry);
13469 next_image=ReadImage(read_info,exception);
13470 CatchException(exception);
13471 if (next_image != (Image *) NULL)
13472 {
13473 (void) DeleteImageProperty(next_image,"label");
13474 (void) SetImageProperty(next_image,"label",DefaultTileLabel);
13475 (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13476 exception);
13477 thumbnail_image=ThumbnailImage(next_image,geometry.width,
13478 geometry.height,exception);
13479 if (thumbnail_image != (Image *) NULL)
13480 {
13481 next_image=DestroyImage(next_image);
13482 next_image=thumbnail_image;
13483 }
13484 if (backdrop)
13485 {
13486 (void) XDisplayBackgroundImage(display,&background_resources,
13487 next_image);
13488 XSetCursorState(display,windows,MagickTrue);
13489 }
13490 AppendImageToList(&images,next_image);
13491 if (images->progress_monitor != (MagickProgressMonitor) NULL)
13492 {
13493 MagickBooleanType
13494 proceed;
13495
13496 proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13497 (MagickSizeType) number_files);
13498 if (proceed == MagickFalse)
13499 break;
13500 }
13501 }
13502 }
13503 exception=DestroyExceptionInfo(exception);
13504 filelist=(char **) RelinquishMagickMemory(filelist);
13505 read_info=DestroyImageInfo(read_info);
13506 if (images == (Image *) NULL)
13507 {
13508 XSetCursorState(display,windows,MagickFalse);
13509 ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13510 return((Image *) NULL);
13511 }
13512 /*
13513 Create the Visual Image Directory.
13514 */
13515 montage_info=CloneMontageInfo(resource_info->image_info,(MontageInfo *) NULL);
13516 if (resource_info->font != (char *) NULL)
13517 (void) CloneString(&montage_info->font,resource_info->font);
13518 (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13519 montage_image=MontageImageList(resource_info->image_info,montage_info,
13520 GetFirstImageInList(images),&images->exception);
13521 montage_info=DestroyMontageInfo(montage_info);
13522 images=DestroyImageList(images);
13523 XSetCursorState(display,windows,MagickFalse);
13524 if (montage_image == (Image *) NULL)
13525 return(montage_image);
13526 XClientMessage(display,windows->image.id,windows->im_protocols,
13527 windows->im_next_image,CurrentTime);
13528 return(montage_image);
13529}
13530
13531/*
13532%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13533% %
13534% %
13535% %
13536% X D i s p l a y B a c k g r o u n d I m a g e %
13537% %
13538% %
13539% %
13540%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13541%
13542% XDisplayBackgroundImage() displays an image in the background of a window.
13543%
13544% The format of the XDisplayBackgroundImage method is:
13545%
13546% MagickBooleanType XDisplayBackgroundImage(Display *display,
13547% XResourceInfo *resource_info,Image *image)
13548%
13549% A description of each parameter follows:
13550%
13551% o display: Specifies a connection to an X server; returned from
13552% XOpenDisplay.
13553%
13554% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13555%
13556% o image: the image.
13557%
13558*/
13559MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13560 XResourceInfo *resource_info,Image *image)
13561{
13562 char
13563 geometry[MaxTextExtent],
13564 visual_type[MaxTextExtent];
13565
13566 int
13567 height,
13568 status,
13569 width;
13570
13571 RectangleInfo
13572 geometry_info;
13573
13574 static XPixelInfo
13575 pixel;
13576
13577 static XStandardColormap
13578 *map_info;
13579
13580 static XVisualInfo
13581 *visual_info = (XVisualInfo *) NULL;
13582
13583 static XWindowInfo
13584 window_info;
13585
13586 unsigned long
13587 delay;
13588
13589 Window
13590 root_window;
13591
13592 XGCValues
13593 context_values;
13594
13595 XResourceInfo
13596 resources;
13597
13598 XWindowAttributes
13599 window_attributes;
13600
13601 /*
13602 Determine target window.
13603 */
13604 assert(image != (Image *) NULL);
13605 assert(image->signature == MagickSignature);
13606 if (image->debug != MagickFalse)
13607 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13608 resources=(*resource_info);
13609 window_info.id=(Window) NULL;
13610 root_window=XRootWindow(display,XDefaultScreen(display));
13611 if (LocaleCompare(resources.window_id,"root") == 0)
13612 window_info.id=root_window;
13613 else
13614 {
13615 if (isdigit((unsigned char) *resources.window_id) != 0)
13616 window_info.id=XWindowByID(display,root_window,
13617 (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13618 if (window_info.id == (Window) NULL)
13619 window_info.id=XWindowByName(display,root_window,resources.window_id);
13620 }
13621 if (window_info.id == (Window) NULL)
13622 {
13623 ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13624 resources.window_id);
13625 return(MagickFalse);
13626 }
13627 /*
13628 Determine window visual id.
13629 */
13630 window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13631 window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13632 (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13633 status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13634 if (status != 0)
13635 (void) FormatMagickString(visual_type,MaxTextExtent,"0x%lx",
13636 XVisualIDFromVisual(window_attributes.visual));
13637 if (visual_info == (XVisualInfo *) NULL)
13638 {
13639 /*
13640 Allocate standard colormap.
13641 */
13642 map_info=XAllocStandardColormap();
13643 if (map_info == (XStandardColormap *) NULL)
13644 ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13645 image->filename);
13646 map_info->colormap=(Colormap) NULL;
13647 pixel.pixels=(unsigned long *) NULL;
13648 /*
13649 Initialize visual info.
13650 */
13651 resources.map_type=(char *) NULL;
13652 resources.visual_type=visual_type;
13653 visual_info=XBestVisualInfo(display,map_info,&resources);
13654 if (visual_info == (XVisualInfo *) NULL)
13655 ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13656 resources.visual_type);
13657 /*
13658 Initialize window info.
13659 */
13660 window_info.ximage=(XImage *) NULL;
13661 window_info.matte_image=(XImage *) NULL;
13662 window_info.pixmap=(Pixmap) NULL;
13663 window_info.matte_pixmap=(Pixmap) NULL;
13664 }
13665 /*
13666 Free previous root colors.
13667 */
13668 if (window_info.id == root_window)
13669 (void) XDestroyWindowColors(display,root_window);
13670 /*
13671 Initialize Standard Colormap.
13672 */
13673 resources.colormap=SharedColormap;
13674 XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13675 /*
13676 Graphic context superclass.
13677 */
13678 context_values.background=pixel.background_color.pixel;
13679 context_values.foreground=pixel.foreground_color.pixel;
13680 pixel.annotate_context=XCreateGC(display,window_info.id,
13681 (unsigned long) (GCBackground | GCForeground),&context_values);
13682 if (pixel.annotate_context == (GC) NULL)
13683 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13684 image->filename);
13685 /*
13686 Initialize Image window attributes.
13687 */
13688 window_info.name=AcquireString("\0");
13689 window_info.icon_name=AcquireString("\0");
13690 XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13691 &resources,&window_info);
13692 /*
13693 Create the X image.
13694 */
13695 window_info.width=(unsigned int) image->columns;
13696 window_info.height=(unsigned int) image->rows;
13697 if ((image->columns != window_info.width) ||
13698 (image->rows != window_info.height))
13699 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13700 image->filename);
13701 (void) FormatMagickString(geometry,MaxTextExtent,"%ux%u+0+0>",
13702 window_attributes.width,window_attributes.height);
13703 geometry_info.width=window_info.width;
13704 geometry_info.height=window_info.height;
13705 geometry_info.x=window_info.x;
13706 geometry_info.y=window_info.y;
13707 (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13708 &geometry_info.width,&geometry_info.height);
13709 window_info.width=(unsigned int) geometry_info.width;
13710 window_info.height=(unsigned int) geometry_info.height;
13711 window_info.x=(int) geometry_info.x;
13712 window_info.y=(int) geometry_info.y;
13713 status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13714 window_info.height);
13715 if (status == MagickFalse)
13716 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13717 image->filename);
13718 window_info.x=0;
13719 window_info.y=0;
13720 if (image->debug != MagickFalse)
13721 {
13722 (void) LogMagickEvent(X11Event,GetMagickModule(),
13723 "Image: %s[%lu] %lux%lu ",image->filename,image->scene,
13724 image->columns,image->rows);
13725 if (image->colors != 0)
13726 (void) LogMagickEvent(X11Event,GetMagickModule(),"%luc ",image->colors);
13727 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13728 }
13729 /*
13730 Adjust image dimensions as specified by backdrop or geometry options.
13731 */
13732 width=(int) window_info.width;
13733 height=(int) window_info.height;
13734 if (resources.backdrop != MagickFalse)
13735 {
13736 /*
13737 Center image on window.
13738 */
13739 window_info.x=(window_attributes.width/2)-
13740 (window_info.ximage->width/2);
13741 window_info.y=(window_attributes.height/2)-
13742 (window_info.ximage->height/2);
13743 width=window_attributes.width;
13744 height=window_attributes.height;
13745 }
13746 if ((resources.image_geometry != (char *) NULL) &&
13747 (*resources.image_geometry != '\0'))
13748 {
13749 char
13750 default_geometry[MaxTextExtent];
13751
13752 int
13753 flags,
13754 gravity;
13755
13756 XSizeHints
13757 *size_hints;
13758
13759 /*
13760 User specified geometry.
13761 */
13762 size_hints=XAllocSizeHints();
13763 if (size_hints == (XSizeHints *) NULL)
13764 ThrowXWindowFatalException(ResourceLimitFatalError,
13765 "MemoryAllocationFailed",image->filename);
13766 size_hints->flags=0L;
13767 (void) FormatMagickString(default_geometry,MaxTextExtent,"%dx%d",
13768 width,height);
13769 flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13770 default_geometry,window_info.border_width,size_hints,&window_info.x,
13771 &window_info.y,&width,&height,&gravity);
13772 if (flags & (XValue | YValue))
13773 {
13774 width=window_attributes.width;
13775 height=window_attributes.height;
13776 }
13777 (void) XFree((void *) size_hints);
13778 }
13779 /*
13780 Create the X pixmap.
13781 */
13782 window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13783 (unsigned int) height,window_info.depth);
13784 if (window_info.pixmap == (Pixmap) NULL)
13785 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13786 image->filename);
13787 /*
13788 Display pixmap on the window.
13789 */
13790 if (((unsigned int) width > window_info.width) ||
13791 ((unsigned int) height > window_info.height))
13792 (void) XFillRectangle(display,window_info.pixmap,
13793 window_info.annotate_context,0,0,(unsigned int) width,
13794 (unsigned int) height);
13795 (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13796 window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13797 window_info.width,(unsigned int) window_info.height);
13798 (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13799 (void) XClearWindow(display,window_info.id);
13800 delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13801 XDelay(display,delay == 0UL ? 10UL : delay);
13802 (void) XSync(display,MagickFalse);
13803 return(window_info.id == root_window ? MagickTrue : MagickFalse);
13804}
13805
13806/*
13807%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13808% %
13809% %
13810% %
13811+ X D i s p l a y I m a g e %
13812% %
13813% %
13814% %
13815%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13816%
13817% XDisplayImage() displays an image via X11. A new image is created and
13818% returned if the user interactively transforms the displayed image.
13819%
13820% The format of the XDisplayImage method is:
13821%
13822% Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13823% char **argv,int argc,Image **image,unsigned long *state)
13824%
13825% A description of each parameter follows:
13826%
13827% o nexus: Method XDisplayImage returns an image when the
13828% user chooses 'Open Image' from the command menu or picks a tile
13829% from the image directory. Otherwise a null image is returned.
13830%
13831% o display: Specifies a connection to an X server; returned from
13832% XOpenDisplay.
13833%
13834% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13835%
13836% o argv: Specifies the application's argument list.
13837%
13838% o argc: Specifies the number of arguments.
13839%
13840% o image: Specifies an address to an address of an Image structure;
13841%
13842*/
13843MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13844 char **argv,int argc,Image **image,unsigned long *state)
13845{
13846#define MagnifySize 256 /* must be a power of 2 */
13847#define MagickMenus 10
13848#define MagickTitle "Commands"
13849
13850 static const char
13851 *CommandMenu[] =
13852 {
13853 "File",
13854 "Edit",
13855 "View",
13856 "Transform",
13857 "Enhance",
13858 "Effects",
13859 "F/X",
13860 "Image Edit",
13861 "Miscellany",
13862 "Help",
13863 (char *) NULL
13864 },
13865 *FileMenu[] =
13866 {
13867 "Open...",
13868 "Next",
13869 "Former",
13870 "Select...",
13871 "Save...",
13872 "Print...",
13873 "Delete...",
13874 "New...",
13875 "Visual Directory...",
13876 "Quit",
13877 (char *) NULL
13878 },
13879 *EditMenu[] =
13880 {
13881 "Undo",
13882 "Redo",
13883 "Cut",
13884 "Copy",
13885 "Paste",
13886 (char *) NULL
13887 },
13888 *ViewMenu[] =
13889 {
13890 "Half Size",
13891 "Original Size",
13892 "Double Size",
13893 "Resize...",
13894 "Apply",
13895 "Refresh",
13896 "Restore",
13897 (char *) NULL
13898 },
13899 *TransformMenu[] =
13900 {
13901 "Crop",
13902 "Chop",
13903 "Flop",
13904 "Flip",
13905 "Rotate Right",
13906 "Rotate Left",
13907 "Rotate...",
13908 "Shear...",
13909 "Roll...",
13910 "Trim Edges",
13911 (char *) NULL
13912 },
13913 *EnhanceMenu[] =
13914 {
13915 "Hue...",
13916 "Saturation...",
13917 "Brightness...",
13918 "Gamma...",
13919 "Spiff",
13920 "Dull",
13921 "Contrast Stretch...",
13922 "Sigmoidal Contrast...",
13923 "Normalize",
13924 "Equalize",
13925 "Negate",
13926 "Grayscale",
13927 "Map...",
13928 "Quantize...",
13929 (char *) NULL
13930 },
13931 *EffectsMenu[] =
13932 {
13933 "Despeckle",
13934 "Emboss",
13935 "Reduce Noise",
13936 "Add Noise...",
13937 "Sharpen...",
13938 "Blur...",
13939 "Threshold...",
13940 "Edge Detect...",
13941 "Spread...",
13942 "Shade...",
13943 "Raise...",
13944 "Segment...",
13945 (char *) NULL
13946 },
13947 *FXMenu[] =
13948 {
13949 "Solarize...",
13950 "Sepia Tone...",
13951 "Swirl...",
13952 "Implode...",
13953 "Vignette...",
13954 "Wave...",
13955 "Oil Paint...",
13956 "Charcoal Draw...",
13957 (char *) NULL
13958 },
13959 *ImageEditMenu[] =
13960 {
13961 "Annotate...",
13962 "Draw...",
13963 "Color...",
13964 "Matte...",
13965 "Composite...",
13966 "Add Border...",
13967 "Add Frame...",
13968 "Comment...",
13969 "Launch...",
13970 "Region of Interest...",
13971 (char *) NULL
13972 },
13973 *MiscellanyMenu[] =
13974 {
13975 "Image Info",
13976 "Zoom Image",
13977 "Show Preview...",
13978 "Show Histogram",
13979 "Show Matte",
13980 "Background...",
13981 "Slide Show...",
13982 "Preferences...",
13983 (char *) NULL
13984 },
13985 *HelpMenu[] =
13986 {
13987 "Overview",
13988 "Browse Documentation",
13989 "About Display",
13990 (char *) NULL
13991 },
13992 *ShortCutsMenu[] =
13993 {
13994 "Next",
13995 "Former",
13996 "Open...",
13997 "Save...",
13998 "Print...",
13999 "Undo",
14000 "Restore",
14001 "Image Info",
14002 "Quit",
14003 (char *) NULL
14004 },
14005 *VirtualMenu[] =
14006 {
14007 "Image Info",
14008 "Print",
14009 "Next",
14010 "Quit",
14011 (char *) NULL
14012 };
14013
14014 static const char
14015 **Menus[MagickMenus] =
14016 {
14017 FileMenu,
14018 EditMenu,
14019 ViewMenu,
14020 TransformMenu,
14021 EnhanceMenu,
14022 EffectsMenu,
14023 FXMenu,
14024 ImageEditMenu,
14025 MiscellanyMenu,
14026 HelpMenu
14027 };
14028
14029 static CommandType
14030 CommandMenus[] =
14031 {
14032 NullCommand,
14033 NullCommand,
14034 NullCommand,
14035 NullCommand,
14036 NullCommand,
14037 NullCommand,
14038 NullCommand,
14039 NullCommand,
14040 NullCommand,
14041 NullCommand,
14042 },
14043 FileCommands[] =
14044 {
14045 OpenCommand,
14046 NextCommand,
14047 FormerCommand,
14048 SelectCommand,
14049 SaveCommand,
14050 PrintCommand,
14051 DeleteCommand,
14052 NewCommand,
14053 VisualDirectoryCommand,
14054 QuitCommand
14055 },
14056 EditCommands[] =
14057 {
14058 UndoCommand,
14059 RedoCommand,
14060 CutCommand,
14061 CopyCommand,
14062 PasteCommand
14063 },
14064 ViewCommands[] =
14065 {
14066 HalfSizeCommand,
14067 OriginalSizeCommand,
14068 DoubleSizeCommand,
14069 ResizeCommand,
14070 ApplyCommand,
14071 RefreshCommand,
14072 RestoreCommand
14073 },
14074 TransformCommands[] =
14075 {
14076 CropCommand,
14077 ChopCommand,
14078 FlopCommand,
14079 FlipCommand,
14080 RotateRightCommand,
14081 RotateLeftCommand,
14082 RotateCommand,
14083 ShearCommand,
14084 RollCommand,
14085 TrimCommand
14086 },
14087 EnhanceCommands[] =
14088 {
14089 HueCommand,
14090 SaturationCommand,
14091 BrightnessCommand,
14092 GammaCommand,
14093 SpiffCommand,
14094 DullCommand,
14095 ContrastStretchCommand,
14096 SigmoidalContrastCommand,
14097 NormalizeCommand,
14098 EqualizeCommand,
14099 NegateCommand,
14100 GrayscaleCommand,
14101 MapCommand,
14102 QuantizeCommand
14103 },
14104 EffectsCommands[] =
14105 {
14106 DespeckleCommand,
14107 EmbossCommand,
14108 ReduceNoiseCommand,
14109 AddNoiseCommand,
14110 SharpenCommand,
14111 BlurCommand,
14112 ThresholdCommand,
14113 EdgeDetectCommand,
14114 SpreadCommand,
14115 ShadeCommand,
14116 RaiseCommand,
14117 SegmentCommand
14118 },
14119 FXCommands[] =
14120 {
14121 SolarizeCommand,
14122 SepiaToneCommand,
14123 SwirlCommand,
14124 ImplodeCommand,
14125 VignetteCommand,
14126 WaveCommand,
14127 OilPaintCommand,
14128 CharcoalDrawCommand
14129 },
14130 ImageEditCommands[] =
14131 {
14132 AnnotateCommand,
14133 DrawCommand,
14134 ColorCommand,
14135 MatteCommand,
14136 CompositeCommand,
14137 AddBorderCommand,
14138 AddFrameCommand,
14139 CommentCommand,
14140 LaunchCommand,
14141 RegionofInterestCommand
14142 },
14143 MiscellanyCommands[] =
14144 {
14145 InfoCommand,
14146 ZoomCommand,
14147 ShowPreviewCommand,
14148 ShowHistogramCommand,
14149 ShowMatteCommand,
14150 BackgroundCommand,
14151 SlideShowCommand,
14152 PreferencesCommand
14153 },
14154 HelpCommands[] =
14155 {
14156 HelpCommand,
14157 BrowseDocumentationCommand,
14158 VersionCommand
14159 },
14160 ShortCutsCommands[] =
14161 {
14162 NextCommand,
14163 FormerCommand,
14164 OpenCommand,
14165 SaveCommand,
14166 PrintCommand,
14167 UndoCommand,
14168 RestoreCommand,
14169 InfoCommand,
14170 QuitCommand
14171 },
14172 VirtualCommands[] =
14173 {
14174 InfoCommand,
14175 PrintCommand,
14176 NextCommand,
14177 QuitCommand
14178 };
14179
14180 static CommandType
14181 *Commands[MagickMenus] =
14182 {
14183 FileCommands,
14184 EditCommands,
14185 ViewCommands,
14186 TransformCommands,
14187 EnhanceCommands,
14188 EffectsCommands,
14189 FXCommands,
14190 ImageEditCommands,
14191 MiscellanyCommands,
14192 HelpCommands
14193 };
14194
14195 char
14196 command[MaxTextExtent],
14197 *cwd,
14198 geometry[MaxTextExtent],
14199 resource_name[MaxTextExtent];
14200
14201 CommandType
14202 command_type;
14203
14204 Image
14205 *display_image,
14206 *nexus;
14207
14208 int
14209 entry,
14210 id;
14211
14212 KeySym
14213 key_symbol;
14214
14215 MagickStatusType
14216 context_mask,
14217 status;
14218
14219 RectangleInfo
14220 geometry_info;
14221
14222 register int
14223 i;
14224
14225 static char
14226 working_directory[MaxTextExtent];
14227
14228 static XPoint
14229 vid_info;
14230
14231 static XWindowInfo
14232 *magick_windows[MaxXWindows];
14233
14234 static unsigned int
14235 number_windows;
14236
14237 struct stat
14238 attributes;
14239
14240 time_t
14241 timer,
14242 timestamp,
14243 update_time;
14244
14245 unsigned int
14246 height,
14247 width;
14248
14249 unsigned long
14250 delay;
14251
14252 WarningHandler
14253 warning_handler;
14254
14255 Window
14256 root_window;
14257
14258 XClassHint
14259 *class_hints;
14260
14261 XEvent
14262 event;
14263
14264 XFontStruct
14265 *font_info;
14266
14267 XGCValues
14268 context_values;
14269
14270 XPixelInfo
14271 *icon_pixel,
14272 *pixel;
14273
14274 XResourceInfo
14275 *icon_resources;
14276
14277 XStandardColormap
14278 *icon_map,
14279 *map_info;
14280
14281 XVisualInfo
14282 *icon_visual,
14283 *visual_info;
14284
14285 XWindowChanges
14286 window_changes;
14287
14288 XWindows
14289 *windows;
14290
14291 XWMHints
14292 *manager_hints;
14293
14294 assert(image != (Image **) NULL);
14295 assert((*image)->signature == MagickSignature);
14296 if ((*image)->debug != MagickFalse)
14297 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14298 display_image=(*image);
14299 warning_handler=(WarningHandler) NULL;
14300 windows=XSetWindows((XWindows *) ~0);
14301 if (windows != (XWindows *) NULL)
14302 {
14303 int
14304 status;
14305
14306 status=chdir(working_directory);
14307 if (status == -1)
14308 (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
14309 FileOpenError,"UnableToOpenFile","%s",working_directory);
14310 warning_handler=resource_info->display_warnings ?
14311 SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14312 warning_handler=resource_info->display_warnings ?
14313 SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14314 }
14315 else
14316 {
14317 /*
14318 Allocate windows structure.
14319 */
14320 resource_info->colors=display_image->colors;
14321 windows=XSetWindows(XInitializeWindows(display,resource_info));
14322 if (windows == (XWindows *) NULL)
14323 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14324 (*image)->filename);
14325 /*
14326 Initialize window id's.
14327 */
14328 number_windows=0;
14329 magick_windows[number_windows++]=(&windows->icon);
14330 magick_windows[number_windows++]=(&windows->backdrop);
14331 magick_windows[number_windows++]=(&windows->image);
14332 magick_windows[number_windows++]=(&windows->info);
14333 magick_windows[number_windows++]=(&windows->command);
14334 magick_windows[number_windows++]=(&windows->widget);
14335 magick_windows[number_windows++]=(&windows->popup);
14336 magick_windows[number_windows++]=(&windows->magnify);
14337 magick_windows[number_windows++]=(&windows->pan);
14338 for (i=0; i < (int) number_windows; i++)
14339 magick_windows[i]->id=(Window) NULL;
14340 vid_info.x=0;
14341 vid_info.y=0;
14342 }
14343 /*
14344 Initialize font info.
14345 */
14346 if (windows->font_info != (XFontStruct *) NULL)
14347 (void) XFreeFont(display,windows->font_info);
14348 windows->font_info=XBestFont(display,resource_info,MagickFalse);
14349 if (windows->font_info == (XFontStruct *) NULL)
14350 ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14351 resource_info->font);
14352 /*
14353 Initialize Standard Colormap.
14354 */
14355 map_info=windows->map_info;
14356 icon_map=windows->icon_map;
14357 visual_info=windows->visual_info;
14358 icon_visual=windows->icon_visual;
14359 pixel=windows->pixel_info;
14360 icon_pixel=windows->icon_pixel;
14361 font_info=windows->font_info;
14362 icon_resources=windows->icon_resources;
14363 class_hints=windows->class_hints;
14364 manager_hints=windows->manager_hints;
14365 root_window=XRootWindow(display,visual_info->screen);
14366 nexus=NewImageList();
14367 if (display_image->debug != MagickFalse)
14368 {
14369 (void) LogMagickEvent(X11Event,GetMagickModule(),
14370 "Image: %s[%lu] %lux%lu ",display_image->filename,
14371 display_image->scene,display_image->columns,display_image->rows);
14372 if (display_image->colors != 0)
14373 (void) LogMagickEvent(X11Event,GetMagickModule(),"%luc ",
14374 display_image->colors);
14375 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14376 display_image->magick);
14377 }
14378 XMakeStandardColormap(display,visual_info,resource_info,display_image,
14379 map_info,pixel);
14380 display_image->taint=MagickFalse;
14381 /*
14382 Initialize graphic context.
14383 */
14384 windows->context.id=(Window) NULL;
14385 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14386 resource_info,&windows->context);
14387 (void) CloneString(&class_hints->res_name,resource_info->client_name);
14388 (void) CloneString(&class_hints->res_class,resource_info->client_name);
14389 class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14390 manager_hints->flags=InputHint | StateHint;
14391 manager_hints->input=MagickFalse;
14392 manager_hints->initial_state=WithdrawnState;
14393 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14394 &windows->context);
14395 if (display_image->debug != MagickFalse)
14396 (void) LogMagickEvent(X11Event,GetMagickModule(),
14397 "Window id: 0x%lx (context)",windows->context.id);
14398 context_values.background=pixel->background_color.pixel;
14399 context_values.font=font_info->fid;
14400 context_values.foreground=pixel->foreground_color.pixel;
14401 context_values.graphics_exposures=MagickFalse;
14402 context_mask=(MagickStatusType)
14403 (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14404 if (pixel->annotate_context != (GC) NULL)
14405 (void) XFreeGC(display,pixel->annotate_context);
14406 pixel->annotate_context=XCreateGC(display,windows->context.id,
14407 context_mask,&context_values);
14408 if (pixel->annotate_context == (GC) NULL)
14409 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14410 display_image->filename);
14411 context_values.background=pixel->depth_color.pixel;
14412 if (pixel->widget_context != (GC) NULL)
14413 (void) XFreeGC(display,pixel->widget_context);
14414 pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14415 &context_values);
14416 if (pixel->widget_context == (GC) NULL)
14417 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14418 display_image->filename);
14419 context_values.background=pixel->foreground_color.pixel;
14420 context_values.foreground=pixel->background_color.pixel;
14421 context_values.plane_mask=context_values.background ^
14422 context_values.foreground;
14423 if (pixel->highlight_context != (GC) NULL)
14424 (void) XFreeGC(display,pixel->highlight_context);
14425 pixel->highlight_context=XCreateGC(display,windows->context.id,
14426 (unsigned long) (context_mask | GCPlaneMask),&context_values);
14427 if (pixel->highlight_context == (GC) NULL)
14428 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14429 display_image->filename);
14430 (void) XDestroyWindow(display,windows->context.id);
14431 /*
14432 Initialize icon window.
14433 */
14434 XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14435 icon_resources,&windows->icon);
14436 windows->icon.geometry=resource_info->icon_geometry;
14437 XBestIconSize(display,&windows->icon,display_image);
14438 windows->icon.attributes.colormap=XDefaultColormap(display,
14439 icon_visual->screen);
14440 windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14441 manager_hints->flags=InputHint | StateHint;
14442 manager_hints->input=MagickFalse;
14443 manager_hints->initial_state=IconicState;
14444 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14445 &windows->icon);
14446 if (display_image->debug != MagickFalse)
14447 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14448 windows->icon.id);
14449 /*
14450 Initialize graphic context for icon window.
14451 */
14452 if (icon_pixel->annotate_context != (GC) NULL)
14453 (void) XFreeGC(display,icon_pixel->annotate_context);
14454 context_values.background=icon_pixel->background_color.pixel;
14455 context_values.foreground=icon_pixel->foreground_color.pixel;
14456 icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14457 (unsigned long) (GCBackground | GCForeground),&context_values);
14458 if (icon_pixel->annotate_context == (GC) NULL)
14459 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14460 display_image->filename);
14461 windows->icon.annotate_context=icon_pixel->annotate_context;
14462 /*
14463 Initialize Image window.
14464 */
14465 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14466 &windows->image);
14467 windows->image.shape=MagickTrue; /* non-rectangular shape hint */
14468 if (resource_info->use_shared_memory == MagickFalse)
14469 windows->image.shared_memory=MagickFalse;
14470 if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14471 {
14472 char
14473 *title;
14474
14475 title=InterpretImageProperties(resource_info->image_info,display_image,
14476 resource_info->title);
14477 (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14478 (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14479 title=DestroyString(title);
14480 }
14481 else
14482 {
14483 char
14484 filename[MaxTextExtent];
14485
14486 /*
14487 Window name is the base of the filename.
14488 */
14489 GetPathComponent(display_image->magick_filename,TailPath,filename);
14490 if (GetImageListLength(display_image) == 1)
14491 (void) FormatMagickString(windows->image.name,MaxTextExtent,
14492 "ImageMagick: %s",filename);
14493 else
14494 (void) FormatMagickString(windows->image.name,MaxTextExtent,
14495 "ImageMagick: %s[%lu of %lu]",filename,display_image->scene,
14496 GetImageListLength(display_image));
14497 (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14498 }
14499 if (resource_info->immutable)
14500 windows->image.immutable=MagickTrue;
14501 windows->image.use_pixmap=resource_info->use_pixmap;
14502 windows->image.geometry=resource_info->image_geometry;
14503 (void) FormatMagickString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14504 XDisplayWidth(display,visual_info->screen),
14505 XDisplayHeight(display,visual_info->screen));
14506 geometry_info.width=display_image->columns;
14507 geometry_info.height=display_image->rows;
14508 geometry_info.x=0;
14509 geometry_info.y=0;
14510 (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14511 &geometry_info.width,&geometry_info.height);
14512 windows->image.width=(unsigned int) geometry_info.width;
14513 windows->image.height=(unsigned int) geometry_info.height;
14514 windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14515 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14516 KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14517 PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14518 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14519 resource_info,&windows->backdrop);
14520 if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14521 {
14522 /*
14523 Initialize backdrop window.
14524 */
14525 windows->backdrop.x=0;
14526 windows->backdrop.y=0;
14527 (void) CloneString(&windows->backdrop.name,"ImageMagick Backdrop");
14528 windows->backdrop.flags=(unsigned long) (USSize | USPosition);
14529 windows->backdrop.width=(unsigned int)
14530 XDisplayWidth(display,visual_info->screen);
14531 windows->backdrop.height=(unsigned int)
14532 XDisplayHeight(display,visual_info->screen);
14533 windows->backdrop.border_width=0;
14534 windows->backdrop.immutable=MagickTrue;
14535 windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14536 ButtonReleaseMask;
14537 windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14538 StructureNotifyMask;
14539 manager_hints->flags=IconWindowHint | InputHint | StateHint;
14540 manager_hints->icon_window=windows->icon.id;
14541 manager_hints->input=MagickTrue;
14542 manager_hints->initial_state=resource_info->iconic ? IconicState :
14543 NormalState;
14544 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14545 &windows->backdrop);
14546 if (display_image->debug != MagickFalse)
14547 (void) LogMagickEvent(X11Event,GetMagickModule(),
14548 "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14549 (void) XMapWindow(display,windows->backdrop.id);
14550 (void) XClearWindow(display,windows->backdrop.id);
14551 if (windows->image.id != (Window) NULL)
14552 {
14553 (void) XDestroyWindow(display,windows->image.id);
14554 windows->image.id=(Window) NULL;
14555 }
14556 /*
14557 Position image in the center the backdrop.
14558 */
14559 windows->image.flags|=USPosition;
14560 windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14561 (windows->image.width/2);
14562 windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14563 (windows->image.height/2);
14564 }
14565 manager_hints->flags=IconWindowHint | InputHint | StateHint;
14566 manager_hints->icon_window=windows->icon.id;
14567 manager_hints->input=MagickTrue;
14568 manager_hints->initial_state=resource_info->iconic ? IconicState :
14569 NormalState;
14570 if (windows->group_leader.id != (Window) NULL)
14571 {
14572 /*
14573 Follow the leader.
14574 */
14575 manager_hints->flags|=WindowGroupHint;
14576 manager_hints->window_group=windows->group_leader.id;
14577 (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14578 if (display_image->debug != MagickFalse)
14579 (void) LogMagickEvent(X11Event,GetMagickModule(),
14580 "Window id: 0x%lx (group leader)",windows->group_leader.id);
14581 }
14582 XMakeWindow(display,
14583 (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14584 argv,argc,class_hints,manager_hints,&windows->image);
14585 (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14586 XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14587 if (windows->group_leader.id != (Window) NULL)
14588 (void) XSetTransientForHint(display,windows->image.id,
14589 windows->group_leader.id);
14590 if (display_image->debug != MagickFalse)
14591 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14592 windows->image.id);
14593 /*
14594 Initialize Info widget.
14595 */
14596 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14597 &windows->info);
14598 (void) CloneString(&windows->info.name,"Info");
14599 (void) CloneString(&windows->info.icon_name,"Info");
14600 windows->info.border_width=1;
14601 windows->info.x=2;
14602 windows->info.y=2;
14603 windows->info.flags|=PPosition;
14604 windows->info.attributes.win_gravity=UnmapGravity;
14605 windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14606 StructureNotifyMask;
14607 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14608 manager_hints->input=MagickFalse;
14609 manager_hints->initial_state=NormalState;
14610 manager_hints->window_group=windows->image.id;
14611 XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14612 &windows->info);
14613 windows->info.highlight_stipple=XCreateBitmapFromData(display,
14614 windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14615 windows->info.shadow_stipple=XCreateBitmapFromData(display,
14616 windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14617 (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14618 if (windows->image.mapped != MagickFalse)
14619 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14620 if (display_image->debug != MagickFalse)
14621 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14622 windows->info.id);
14623 /*
14624 Initialize Command widget.
14625 */
14626 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14627 resource_info,&windows->command);
14628 windows->command.data=MagickMenus;
14629 (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14630 (void) FormatMagickString(resource_name,MaxTextExtent,"%s.command",
14631 resource_info->client_name);
14632 windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14633 resource_name,"geometry",(char *) NULL);
14634 (void) CloneString(&windows->command.name,MagickTitle);
14635 windows->command.border_width=0;
14636 windows->command.flags|=PPosition;
14637 windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14638 ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14639 OwnerGrabButtonMask | StructureNotifyMask;
14640 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14641 manager_hints->input=MagickTrue;
14642 manager_hints->initial_state=NormalState;
14643 manager_hints->window_group=windows->image.id;
14644 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14645 &windows->command);
14646 windows->command.highlight_stipple=XCreateBitmapFromData(display,
14647 windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14648 HighlightHeight);
14649 windows->command.shadow_stipple=XCreateBitmapFromData(display,
14650 windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14651 (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14652 if (windows->command.mapped != MagickFalse)
14653 (void) XMapRaised(display,windows->command.id);
14654 if (display_image->debug != MagickFalse)
14655 (void) LogMagickEvent(X11Event,GetMagickModule(),
14656 "Window id: 0x%lx (command)",windows->command.id);
14657 /*
14658 Initialize Widget window.
14659 */
14660 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14661 resource_info,&windows->widget);
14662 (void) FormatMagickString(resource_name,MaxTextExtent,"%s.widget",
14663 resource_info->client_name);
14664 windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14665 resource_name,"geometry",(char *) NULL);
14666 windows->widget.border_width=0;
14667 windows->widget.flags|=PPosition;
14668 windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14669 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14670 KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14671 StructureNotifyMask;
14672 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14673 manager_hints->input=MagickTrue;
14674 manager_hints->initial_state=NormalState;
14675 manager_hints->window_group=windows->image.id;
14676 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14677 &windows->widget);
14678 windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14679 windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14680 windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14681 windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14682 (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14683 if (display_image->debug != MagickFalse)
14684 (void) LogMagickEvent(X11Event,GetMagickModule(),
14685 "Window id: 0x%lx (widget)",windows->widget.id);
14686 /*
14687 Initialize popup window.
14688 */
14689 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14690 resource_info,&windows->popup);
14691 windows->popup.border_width=0;
14692 windows->popup.flags|=PPosition;
14693 windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14694 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14695 KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14696 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14697 manager_hints->input=MagickTrue;
14698 manager_hints->initial_state=NormalState;
14699 manager_hints->window_group=windows->image.id;
14700 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14701 &windows->popup);
14702 windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14703 windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14704 windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14705 windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14706 (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14707 if (display_image->debug != MagickFalse)
14708 (void) LogMagickEvent(X11Event,GetMagickModule(),
14709 "Window id: 0x%lx (pop up)",windows->popup.id);
14710 /*
14711 Initialize Magnify window and cursor.
14712 */
14713 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14714 resource_info,&windows->magnify);
14715 if (resource_info->use_shared_memory == MagickFalse)
14716 windows->magnify.shared_memory=MagickFalse;
14717 (void) FormatMagickString(resource_name,MaxTextExtent,"%s.magnify",
14718 resource_info->client_name);
14719 windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14720 resource_name,"geometry",(char *) NULL);
14721 (void) FormatMagickString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14722 resource_info->magnify);
14723 if (windows->magnify.cursor != (Cursor) NULL)
14724 (void) XFreeCursor(display,windows->magnify.cursor);
14725 windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14726 map_info->colormap,resource_info->background_color,
14727 resource_info->foreground_color);
14728 if (windows->magnify.cursor == (Cursor) NULL)
14729 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14730 display_image->filename);
14731 windows->magnify.width=MagnifySize;
14732 windows->magnify.height=MagnifySize;
14733 windows->magnify.flags|=PPosition;
14734 windows->magnify.min_width=MagnifySize;
14735 windows->magnify.min_height=MagnifySize;
14736 windows->magnify.width_inc=MagnifySize;
14737 windows->magnify.height_inc=MagnifySize;
14738 windows->magnify.data=resource_info->magnify;
14739 windows->magnify.attributes.cursor=windows->magnify.cursor;
14740 windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14741 ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14742 StructureNotifyMask;
14743 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14744 manager_hints->input=MagickTrue;
14745 manager_hints->initial_state=NormalState;
14746 manager_hints->window_group=windows->image.id;
14747 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14748 &windows->magnify);
14749 if (display_image->debug != MagickFalse)
14750 (void) LogMagickEvent(X11Event,GetMagickModule(),
14751 "Window id: 0x%lx (magnify)",windows->magnify.id);
14752 (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14753 /*
14754 Initialize panning window.
14755 */
14756 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14757 resource_info,&windows->pan);
14758 (void) CloneString(&windows->pan.name,"Pan Icon");
14759 windows->pan.width=windows->icon.width;
14760 windows->pan.height=windows->icon.height;
14761 (void) FormatMagickString(resource_name,MaxTextExtent,"%s.pan",
14762 resource_info->client_name);
14763 windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14764 resource_name,"geometry",(char *) NULL);
14765 (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14766 &windows->pan.width,&windows->pan.height);
14767 windows->pan.flags|=PPosition;
14768 windows->pan.immutable=MagickTrue;
14769 windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14770 ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14771 StructureNotifyMask;
14772 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14773 manager_hints->input=MagickFalse;
14774 manager_hints->initial_state=NormalState;
14775 manager_hints->window_group=windows->image.id;
14776 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14777 &windows->pan);
14778 if (display_image->debug != MagickFalse)
14779 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14780 windows->pan.id);
14781 (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14782 if (windows->info.mapped != MagickFalse)
14783 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14784 if ((windows->image.mapped == MagickFalse) ||
14785 (windows->backdrop.id != (Window) NULL))
14786 (void) XMapWindow(display,windows->image.id);
14787 /*
14788 Set our progress monitor and warning handlers.
14789 */
14790 if (warning_handler == (WarningHandler) NULL)
14791 {
14792 warning_handler=resource_info->display_warnings ?
14793 SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14794 warning_handler=resource_info->display_warnings ?
14795 SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14796 }
14797 /*
14798 Initialize Image and Magnify X images.
14799 */
14800 windows->image.x=0;
14801 windows->image.y=0;
14802 windows->magnify.shape=MagickFalse;
14803 width=(unsigned int) display_image->columns;
14804 height=(unsigned int) display_image->rows;
14805 if ((display_image->columns != width) || (display_image->rows != height))
14806 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14807 display_image->filename);
14808 status=XMakeImage(display,resource_info,&windows->image,display_image,
14809 width,height);
14810 if (status == MagickFalse)
14811 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14812 display_image->filename);
14813 status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14814 windows->magnify.width,windows->magnify.height);
14815 if (status == MagickFalse)
14816 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14817 display_image->filename);
14818 if (windows->magnify.mapped != MagickFalse)
14819 (void) XMapRaised(display,windows->magnify.id);
14820 if (windows->pan.mapped != MagickFalse)
14821 (void) XMapRaised(display,windows->pan.id);
14822 windows->image.window_changes.width=(int) display_image->columns;
14823 windows->image.window_changes.height=(int) display_image->rows;
14824 (void) XConfigureImage(display,resource_info,windows,display_image);
14825 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14826 (void) XSync(display,MagickFalse);
14827 /*
14828 Respond to events.
14829 */
14830 delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
14831 timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
14832 update_time=0;
14833 if (resource_info->update != MagickFalse)
14834 {
14835 MagickBooleanType
14836 status;
14837
14838 /*
14839 Determine when file data was last modified.
14840 */
14841 status=GetPathAttributes(display_image->filename,&attributes);
14842 if (status != MagickFalse)
14843 update_time=attributes.st_mtime;
14844 }
14845 *state&=(~FormerImageState);
14846 *state&=(~MontageImageState);
14847 *state&=(~NextImageState);
14848 do
14849 {
14850 /*
14851 Handle a window event.
14852 */
14853 if (windows->image.mapped != MagickFalse)
14854 if ((display_image->delay != 0) || (resource_info->update != 0))
14855 {
14856 if (timer < time((time_t *) NULL))
14857 {
14858 if (resource_info->update == MagickFalse)
14859 *state|=NextImageState | ExitState;
14860 else
14861 {
14862 MagickBooleanType
14863 status;
14864
14865 /*
14866 Determine if image file was modified.
14867 */
14868 status=GetPathAttributes(display_image->filename,&attributes);
14869 if (status != MagickFalse)
14870 if (update_time != attributes.st_mtime)
14871 {
14872 /*
14873 Redisplay image.
14874 */
14875 (void) FormatMagickString(
14876 resource_info->image_info->filename,MaxTextExtent,
14877 "%s:%s",display_image->magick,
14878 display_image->filename);
14879 nexus=ReadImage(resource_info->image_info,
14880 &display_image->exception);
14881 if (nexus != (Image *) NULL)
14882 {
14883 nexus=DestroyImage(nexus);
14884 *state|=NextImageState | ExitState;
14885 }
14886 }
14887 delay=display_image->delay/MagickMax(
14888 display_image->ticks_per_second,1L);
14889 timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
14890 }
14891 }
14892 if (XEventsQueued(display,QueuedAfterFlush) == 0)
14893 {
14894 /*
14895 Do not block if delay > 0.
14896 */
14897 XDelay(display,SuspendTime << 2);
14898 continue;
14899 }
14900 }
14901 timestamp=time((time_t *) NULL);
14902 (void) XNextEvent(display,&event);
14903 if (windows->image.stasis == MagickFalse)
14904 windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
14905 MagickTrue : MagickFalse;
14906 if (windows->magnify.stasis == MagickFalse)
14907 windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
14908 MagickTrue : MagickFalse;
14909 if (event.xany.window == windows->command.id)
14910 {
14911 /*
14912 Select a command from the Command widget.
14913 */
14914 id=XCommandWidget(display,windows,CommandMenu,&event);
14915 if (id < 0)
14916 continue;
14917 (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
14918 command_type=CommandMenus[id];
14919 if (id < MagickMenus)
14920 {
14921 /*
14922 Select a command from a pop-up menu.
14923 */
14924 entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
14925 command);
14926 if (entry < 0)
14927 continue;
14928 (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
14929 command_type=Commands[id][entry];
14930 }
14931 if (command_type != NullCommand)
14932 nexus=XMagickCommand(display,resource_info,windows,command_type,
14933 &display_image);
14934 continue;
14935 }
14936 switch (event.type)
14937 {
14938 case ButtonPress:
14939 {
14940 if (display_image->debug != MagickFalse)
14941 (void) LogMagickEvent(X11Event,GetMagickModule(),
14942 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
14943 event.xbutton.button,event.xbutton.x,event.xbutton.y);
14944 if ((event.xbutton.button == Button3) &&
14945 (event.xbutton.state & Mod1Mask))
14946 {
14947 /*
14948 Convert Alt-Button3 to Button2.
14949 */
14950 event.xbutton.button=Button2;
14951 event.xbutton.state&=(~Mod1Mask);
14952 }
14953 if (event.xbutton.window == windows->backdrop.id)
14954 {
14955 (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
14956 event.xbutton.time);
14957 break;
14958 }
14959 if (event.xbutton.window == windows->image.id)
14960 {
14961 switch (event.xbutton.button)
14962 {
14963 case Button1:
14964 {
14965 if (resource_info->immutable)
14966 {
14967 /*
14968 Select a command from the Virtual menu.
14969 */
14970 entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
14971 command);
14972 if (entry >= 0)
14973 nexus=XMagickCommand(display,resource_info,windows,
14974 VirtualCommands[entry],&display_image);
14975 break;
14976 }
14977 /*
14978 Map/unmap Command widget.
14979 */
14980 if (windows->command.mapped != MagickFalse)
14981 (void) XWithdrawWindow(display,windows->command.id,
14982 windows->command.screen);
14983 else
14984 {
14985 (void) XCommandWidget(display,windows,CommandMenu,
14986 (XEvent *) NULL);
14987 (void) XMapRaised(display,windows->command.id);
14988 }
14989 break;
14990 }
14991 case Button2:
14992 {
14993 /*
14994 User pressed the image magnify button.
14995 */
14996 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
14997 &display_image);
14998 XMagnifyImage(display,windows,&event);
14999 break;
15000 }
15001 case Button3:
15002 {
15003 if (resource_info->immutable)
15004 {
15005 /*
15006 Select a command from the Virtual menu.
15007 */
15008 entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15009 command);
15010 if (entry >= 0)
15011 nexus=XMagickCommand(display,resource_info,windows,
15012 VirtualCommands[entry],&display_image);
15013 break;
15014 }
15015 if (display_image->montage != (char *) NULL)
15016 {
15017 /*
15018 Open or delete a tile from a visual image directory.
15019 */
15020 nexus=XTileImage(display,resource_info,windows,
15021 display_image,&event);
15022 if (nexus != (Image *) NULL)
15023 *state|=MontageImageState | NextImageState | ExitState;
15024 vid_info.x=windows->image.x;
15025 vid_info.y=windows->image.y;
15026 break;
15027 }
15028 /*
15029 Select a command from the Short Cuts menu.
15030 */
15031 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15032 command);
15033 if (entry >= 0)
15034 nexus=XMagickCommand(display,resource_info,windows,
15035 ShortCutsCommands[entry],&display_image);
15036 break;
15037 }
15038 case Button4:
15039 {
15040 /*
15041 Wheel up.
15042 */
15043 XTranslateImage(display,windows,*image,XK_Up);
15044 break;
15045 }
15046 case Button5:
15047 {
15048 /*
15049 Wheel down.
15050 */
15051 XTranslateImage(display,windows,*image,XK_Down);
15052 break;
15053 }
15054 default:
15055 break;
15056 }
15057 break;
15058 }
15059 if (event.xbutton.window == windows->magnify.id)
15060 {
15061 int
15062 factor;
15063
15064 static const char
15065 *MagnifyMenu[] =
15066 {
15067 "2",
15068 "4",
15069 "5",
15070 "6",
15071 "7",
15072 "8",
15073 "9",
15074 "3",
15075 (char *) NULL,
15076 };
15077
15078 static KeySym
15079 MagnifyCommands[] =
15080 {
15081 XK_2,
15082 XK_4,
15083 XK_5,
15084 XK_6,
15085 XK_7,
15086 XK_8,
15087 XK_9,
15088 XK_3
15089 };
15090
15091 /*
15092 Select a magnify factor from the pop-up menu.
15093 */
15094 factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15095 if (factor >= 0)
15096 XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15097 break;
15098 }
15099 if (event.xbutton.window == windows->pan.id)
15100 {
15101 switch (event.xbutton.button)
15102 {
15103 case Button4:
15104 {
15105 /*
15106 Wheel up.
15107 */
15108 XTranslateImage(display,windows,*image,XK_Up);
15109 break;
15110 }
15111 case Button5:
15112 {
15113 /*
15114 Wheel down.
15115 */
15116 XTranslateImage(display,windows,*image,XK_Down);
15117 break;
15118 }
15119 default:
15120 {
15121 XPanImage(display,windows,&event);
15122 break;
15123 }
15124 }
15125 break;
15126 }
15127 delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15128 1L);
15129 timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15130 break;
15131 }
15132 case ButtonRelease:
15133 {
15134 if (display_image->debug != MagickFalse)
15135 (void) LogMagickEvent(X11Event,GetMagickModule(),
15136 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15137 event.xbutton.button,event.xbutton.x,event.xbutton.y);
15138 break;
15139 }
15140 case ClientMessage:
15141 {
15142 if (display_image->debug != MagickFalse)
15143 (void) LogMagickEvent(X11Event,GetMagickModule(),
15144 "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15145 event.xclient.message_type,event.xclient.format,(unsigned long)
15146 event.xclient.data.l[0]);
15147 if (event.xclient.message_type == windows->im_protocols)
15148 {
15149 if (*event.xclient.data.l == (long) windows->im_update_widget)
15150 {
15151 (void) CloneString(&windows->command.name,MagickTitle);
15152 windows->command.data=MagickMenus;
15153 (void) XCommandWidget(display,windows,CommandMenu,
15154 (XEvent *) NULL);
15155 break;
15156 }
15157 if (*event.xclient.data.l == (long) windows->im_update_colormap)
15158 {
15159 /*
15160 Update graphic context and window colormap.
15161 */
15162 for (i=0; i < (int) number_windows; i++)
15163 {
15164 if (magick_windows[i]->id == windows->icon.id)
15165 continue;
15166 context_values.background=pixel->background_color.pixel;
15167 context_values.foreground=pixel->foreground_color.pixel;
15168 (void) XChangeGC(display,magick_windows[i]->annotate_context,
15169 context_mask,&context_values);
15170 (void) XChangeGC(display,magick_windows[i]->widget_context,
15171 context_mask,&context_values);
15172 context_values.background=pixel->foreground_color.pixel;
15173 context_values.foreground=pixel->background_color.pixel;
15174 context_values.plane_mask=context_values.background ^
15175 context_values.foreground;
15176 (void) XChangeGC(display,magick_windows[i]->highlight_context,
15177 (unsigned long) (context_mask | GCPlaneMask),
15178 &context_values);
15179 magick_windows[i]->attributes.background_pixel=
15180 pixel->background_color.pixel;
15181 magick_windows[i]->attributes.border_pixel=
15182 pixel->border_color.pixel;
15183 magick_windows[i]->attributes.colormap=map_info->colormap;
15184 (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15185 magick_windows[i]->mask,&magick_windows[i]->attributes);
15186 }
15187 if (windows->pan.mapped != MagickFalse)
15188 {
15189 (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15190 windows->pan.pixmap);
15191 (void) XClearWindow(display,windows->pan.id);
15192 XDrawPanRectangle(display,windows);
15193 }
15194 if (windows->backdrop.id != (Window) NULL)
15195 (void) XInstallColormap(display,map_info->colormap);
15196 break;
15197 }
15198 if (*event.xclient.data.l == (long) windows->im_former_image)
15199 {
15200 *state|=FormerImageState | ExitState;
15201 break;
15202 }
15203 if (*event.xclient.data.l == (long) windows->im_next_image)
15204 {
15205 *state|=NextImageState | ExitState;
15206 break;
15207 }
15208 if (*event.xclient.data.l == (long) windows->im_retain_colors)
15209 {
15210 *state|=RetainColorsState;
15211 break;
15212 }
15213 if (*event.xclient.data.l == (long) windows->im_exit)
15214 {
15215 *state|=ExitState;
15216 break;
15217 }
15218 break;
15219 }
15220 if (event.xclient.message_type == windows->dnd_protocols)
15221 {
15222 Atom
15223 selection,
15224 type;
15225
15226 int
15227 format,
15228 status;
15229
15230 unsigned char
15231 *data;
15232
15233 unsigned long
15234 after,
15235 length;
15236
15237 /*
15238 Display image named by the Drag-and-Drop selection.
15239 */
15240 if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15241 break;
15242 selection=XInternAtom(display,"DndSelection",MagickFalse);
15243 status=XGetWindowProperty(display,root_window,selection,0L,(long)
15244 MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15245 &length,&after,&data);
15246 if ((status != Success) || (length == 0))
15247 break;
15248 if (*event.xclient.data.l == 2)
15249 {
15250 /*
15251 Offix DND.
15252 */
15253 (void) CopyMagickString(resource_info->image_info->filename,
15254 (char *) data,MaxTextExtent);
15255 }
15256 else
15257 {
15258 /*
15259 XDND.
15260 */
15261 if (strncmp((char *) data, "file:", 5) != 0)
15262 {
15263 (void) XFree((void *) data);
15264 break;
15265 }
15266 (void) CopyMagickString(resource_info->image_info->filename,
15267 ((char *) data)+5,MaxTextExtent);
15268 }
15269 nexus=ReadImage(resource_info->image_info,
15270 &display_image->exception);
15271 CatchException(&display_image->exception);
15272 if (nexus != (Image *) NULL)
15273 *state|=NextImageState | ExitState;
15274 (void) XFree((void *) data);
15275 break;
15276 }
15277 /*
15278 If client window delete message, exit.
15279 */
15280 if (event.xclient.message_type != windows->wm_protocols)
15281 break;
15282 if (*event.xclient.data.l != (long) windows->wm_delete_window)
15283 break;
15284 (void) XWithdrawWindow(display,event.xclient.window,
15285 visual_info->screen);
15286 if (event.xclient.window == windows->image.id)
15287 {
15288 *state|=ExitState;
15289 break;
15290 }
15291 if (event.xclient.window == windows->pan.id)
15292 {
15293 /*
15294 Restore original image size when pan window is deleted.
15295 */
15296 windows->image.window_changes.width=windows->image.ximage->width;
15297 windows->image.window_changes.height=windows->image.ximage->height;
15298 (void) XConfigureImage(display,resource_info,windows,
15299 display_image);
15300 }
15301 break;
15302 }
15303 case ConfigureNotify:
15304 {
15305 if (display_image->debug != MagickFalse)
15306 (void) LogMagickEvent(X11Event,GetMagickModule(),
15307 "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15308 event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15309 event.xconfigure.y,event.xconfigure.send_event);
15310 if (event.xconfigure.window == windows->image.id)
15311 {
15312 /*
15313 Image window has a new configuration.
15314 */
15315 if (event.xconfigure.send_event != 0)
15316 {
15317 XWindowChanges
15318 window_changes;
15319
15320 /*
15321 Position the transient windows relative of the Image window.
15322 */
15323 if (windows->command.geometry == (char *) NULL)
15324 if (windows->command.mapped == MagickFalse)
15325 {
15326 windows->command.x=event.xconfigure.x-
15327 windows->command.width-25;
15328 windows->command.y=event.xconfigure.y;
15329 XConstrainWindowPosition(display,&windows->command);
15330 window_changes.x=windows->command.x;
15331 window_changes.y=windows->command.y;
15332 (void) XReconfigureWMWindow(display,windows->command.id,
15333 windows->command.screen,(unsigned int) (CWX | CWY),
15334 &window_changes);
15335 }
15336 if (windows->widget.geometry == (char *) NULL)
15337 if (windows->widget.mapped == MagickFalse)
15338 {
15339 windows->widget.x=event.xconfigure.x+
15340 event.xconfigure.width/10;
15341 windows->widget.y=event.xconfigure.y+
15342 event.xconfigure.height/10;
15343 XConstrainWindowPosition(display,&windows->widget);
15344 window_changes.x=windows->widget.x;
15345 window_changes.y=windows->widget.y;
15346 (void) XReconfigureWMWindow(display,windows->widget.id,
15347 windows->widget.screen,(unsigned int) (CWX | CWY),
15348 &window_changes);
15349 }
15350 if (windows->magnify.geometry == (char *) NULL)
15351 if (windows->magnify.mapped == MagickFalse)
15352 {
15353 windows->magnify.x=event.xconfigure.x+
15354 event.xconfigure.width+25;
15355 windows->magnify.y=event.xconfigure.y;
15356 XConstrainWindowPosition(display,&windows->magnify);
15357 window_changes.x=windows->magnify.x;
15358 window_changes.y=windows->magnify.y;
15359 (void) XReconfigureWMWindow(display,windows->magnify.id,
15360 windows->magnify.screen,(unsigned int) (CWX | CWY),
15361 &window_changes);
15362 }
15363 if (windows->pan.geometry == (char *) NULL)
15364 if (windows->pan.mapped == MagickFalse)
15365 {
15366 windows->pan.x=event.xconfigure.x+
15367 event.xconfigure.width+25;
15368 windows->pan.y=event.xconfigure.y+
15369 windows->magnify.height+50;
15370 XConstrainWindowPosition(display,&windows->pan);
15371 window_changes.x=windows->pan.x;
15372 window_changes.y=windows->pan.y;
15373 (void) XReconfigureWMWindow(display,windows->pan.id,
15374 windows->pan.screen,(unsigned int) (CWX | CWY),
15375 &window_changes);
15376 }
15377 }
15378 if ((event.xconfigure.width == (long) windows->image.width) &&
15379 (event.xconfigure.height == (long) windows->image.height))
15380 break;
15381 windows->image.width=(unsigned int) event.xconfigure.width;
15382 windows->image.height=(unsigned int) event.xconfigure.height;
15383 windows->image.x=0;
15384 windows->image.y=0;
15385 if (display_image->montage != (char *) NULL)
15386 {
15387 windows->image.x=vid_info.x;
15388 windows->image.y=vid_info.y;
15389 }
15390 /*
15391 Update pan window configuration.
15392 */
15393 if ((event.xconfigure.width < windows->image.ximage->width) ||
15394 (event.xconfigure.height < windows->image.ximage->height))
15395 {
15396 (void) XMapRaised(display,windows->pan.id);
15397 XDrawPanRectangle(display,windows);
15398 }
15399 else
15400 if (windows->pan.mapped != MagickFalse)
15401 (void) XWithdrawWindow(display,windows->pan.id,
15402 windows->pan.screen);
15403 break;
15404 }
15405 if (event.xconfigure.window == windows->magnify.id)
15406 {
15407 unsigned int
15408 magnify;
15409
15410 /*
15411 Magnify window has a new configuration.
15412 */
15413 windows->magnify.width=(unsigned int) event.xconfigure.width;
15414 windows->magnify.height=(unsigned int) event.xconfigure.height;
15415 if (windows->magnify.mapped == MagickFalse)
15416 break;
15417 magnify=1;
15418 while ((int) magnify <= event.xconfigure.width)
15419 magnify<<=1;
15420 while ((int) magnify <= event.xconfigure.height)
15421 magnify<<=1;
15422 magnify>>=1;
15423 if (((int) magnify != event.xconfigure.width) ||
15424 ((int) magnify != event.xconfigure.height))
15425 {
15426 window_changes.width=(int) magnify;
15427 window_changes.height=(int) magnify;
15428 (void) XReconfigureWMWindow(display,windows->magnify.id,
15429 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15430 &window_changes);
15431 break;
15432 }
15433 if ((windows->magnify.mapped != MagickFalse) &&
15434 (windows->magnify.stasis != MagickFalse))
15435 {
15436 status=XMakeImage(display,resource_info,&windows->magnify,
15437 display_image,windows->magnify.width,windows->magnify.height);
15438 XMakeMagnifyImage(display,windows);
15439 }
15440 break;
15441 }
15442 if ((windows->magnify.mapped != MagickFalse) &&
15443 (event.xconfigure.window == windows->pan.id))
15444 {
15445 /*
15446 Pan icon window has a new configuration.
15447 */
15448 if (event.xconfigure.send_event != 0)
15449 {
15450 windows->pan.x=event.xconfigure.x;
15451 windows->pan.y=event.xconfigure.y;
15452 }
15453 windows->pan.width=(unsigned int) event.xconfigure.width;
15454 windows->pan.height=(unsigned int) event.xconfigure.height;
15455 break;
15456 }
15457 if (event.xconfigure.window == windows->icon.id)
15458 {
15459 /*
15460 Icon window has a new configuration.
15461 */
15462 windows->icon.width=(unsigned int) event.xconfigure.width;
15463 windows->icon.height=(unsigned int) event.xconfigure.height;
15464 break;
15465 }
15466 break;
15467 }
15468 case DestroyNotify:
15469 {
15470 /*
15471 Group leader has exited.
15472 */
15473 if (display_image->debug != MagickFalse)
15474 (void) LogMagickEvent(X11Event,GetMagickModule(),
15475 "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15476 if (event.xdestroywindow.window == windows->group_leader.id)
15477 {
15478 *state|=ExitState;
15479 break;
15480 }
15481 break;
15482 }
15483 case EnterNotify:
15484 {
15485 /*
15486 Selectively install colormap.
15487 */
15488 if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15489 if (event.xcrossing.mode != NotifyUngrab)
15490 XInstallColormap(display,map_info->colormap);
15491 break;
15492 }
15493 case Expose:
15494 {
15495 if (display_image->debug != MagickFalse)
15496 (void) LogMagickEvent(X11Event,GetMagickModule(),
15497 "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15498 event.xexpose.width,event.xexpose.height,event.xexpose.x,
15499 event.xexpose.y);
15500 /*
15501 Refresh windows that are now exposed.
15502 */
15503 if (event.xexpose.window == windows->image.id)
15504 if (windows->image.mapped != MagickFalse)
15505 {
15506 XRefreshWindow(display,&windows->image,&event);
15507 delay=display_image->delay/MagickMax(
15508 display_image->ticks_per_second,1L);
15509 timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15510 break;
15511 }
15512 if (event.xexpose.window == windows->magnify.id)
15513 if (event.xexpose.count == 0)
15514 if (windows->magnify.mapped != MagickFalse)
15515 {
15516 XMakeMagnifyImage(display,windows);
15517 break;
15518 }
15519 if (event.xexpose.window == windows->pan.id)
15520 if (event.xexpose.count == 0)
15521 {
15522 XDrawPanRectangle(display,windows);
15523 break;
15524 }
15525 if (event.xexpose.window == windows->icon.id)
15526 if (event.xexpose.count == 0)
15527 {
15528 XRefreshWindow(display,&windows->icon,&event);
15529 break;
15530 }
15531 break;
15532 }
15533 case KeyPress:
15534 {
15535 int
15536 length;
15537
15538 /*
15539 Respond to a user key press.
15540 */
15541 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15542 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15543 *(command+length)='\0';
15544 if (display_image->debug != MagickFalse)
15545 (void) LogMagickEvent(X11Event,GetMagickModule(),
15546 "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15547 key_symbol,command);
15548 if (event.xkey.window == windows->image.id)
15549 {
15550 command_type=XImageWindowCommand(display,resource_info,windows,
15551 event.xkey.state,key_symbol,&display_image);
15552 if (command_type != NullCommand)
15553 nexus=XMagickCommand(display,resource_info,windows,command_type,
15554 &display_image);
15555 }
15556 if (event.xkey.window == windows->magnify.id)
15557 XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15558 if (event.xkey.window == windows->pan.id)
15559 {
15560 if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15561 (void) XWithdrawWindow(display,windows->pan.id,
15562 windows->pan.screen);
15563 else
15564 if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15565 XTextViewWidget(display,resource_info,windows,MagickFalse,
15566 "Help Viewer - Image Pan",ImagePanHelp);
15567 else
15568 XTranslateImage(display,windows,*image,key_symbol);
15569 }
15570 delay=display_image->delay/MagickMax(
15571 display_image->ticks_per_second,1L);
15572 timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15573 break;
15574 }
15575 case KeyRelease:
15576 {
15577 /*
15578 Respond to a user key release.
15579 */
15580 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15581 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15582 if (display_image->debug != MagickFalse)
15583 (void) LogMagickEvent(X11Event,GetMagickModule(),
15584 "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15585 break;
15586 }
15587 case LeaveNotify:
15588 {
15589 /*
15590 Selectively uninstall colormap.
15591 */
15592 if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15593 if (event.xcrossing.mode != NotifyUngrab)
15594 XUninstallColormap(display,map_info->colormap);
15595 break;
15596 }
15597 case MapNotify:
15598 {
15599 if (display_image->debug != MagickFalse)
15600 (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15601 event.xmap.window);
15602 if (event.xmap.window == windows->backdrop.id)
15603 {
15604 (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15605 CurrentTime);
15606 windows->backdrop.mapped=MagickTrue;
15607 break;
15608 }
15609 if (event.xmap.window == windows->image.id)
15610 {
15611 if (windows->backdrop.id != (Window) NULL)
15612 (void) XInstallColormap(display,map_info->colormap);
15613 if (LocaleCompare(display_image->magick,"LOGO") == 0)
15614 {
15615 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15616 nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15617 }
15618 if (((int) windows->image.width < windows->image.ximage->width) ||
15619 ((int) windows->image.height < windows->image.ximage->height))
15620 (void) XMapRaised(display,windows->pan.id);
15621 windows->image.mapped=MagickTrue;
15622 break;
15623 }
15624 if (event.xmap.window == windows->magnify.id)
15625 {
15626 XMakeMagnifyImage(display,windows);
15627 windows->magnify.mapped=MagickTrue;
15628 (void) XWithdrawWindow(display,windows->info.id,
15629 windows->info.screen);
15630 break;
15631 }
15632 if (event.xmap.window == windows->pan.id)
15633 {
15634 XMakePanImage(display,resource_info,windows,display_image);
15635 windows->pan.mapped=MagickTrue;
15636 break;
15637 }
15638 if (event.xmap.window == windows->info.id)
15639 {
15640 windows->info.mapped=MagickTrue;
15641 break;
15642 }
15643 if (event.xmap.window == windows->icon.id)
15644 {
15645 MagickBooleanType
15646 taint;
15647
15648 /*
15649 Create an icon image.
15650 */
15651 taint=display_image->taint;
15652 XMakeStandardColormap(display,icon_visual,icon_resources,
15653 display_image,icon_map,icon_pixel);
15654 (void) XMakeImage(display,icon_resources,&windows->icon,
15655 display_image,windows->icon.width,windows->icon.height);
15656 display_image->taint=taint;
15657 (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15658 windows->icon.pixmap);
15659 (void) XClearWindow(display,windows->icon.id);
15660 (void) XWithdrawWindow(display,windows->info.id,
15661 windows->info.screen);
15662 windows->icon.mapped=MagickTrue;
15663 break;
15664 }
15665 if (event.xmap.window == windows->command.id)
15666 {
15667 windows->command.mapped=MagickTrue;
15668 break;
15669 }
15670 if (event.xmap.window == windows->popup.id)
15671 {
15672 windows->popup.mapped=MagickTrue;
15673 break;
15674 }
15675 if (event.xmap.window == windows->widget.id)
15676 {
15677 windows->widget.mapped=MagickTrue;
15678 break;
15679 }
15680 break;
15681 }
15682 case MappingNotify:
15683 {
15684 (void) XRefreshKeyboardMapping(&event.xmapping);
15685 break;
15686 }
15687 case NoExpose:
15688 break;
15689 case PropertyNotify:
15690 {
15691 Atom
15692 type;
15693
15694 int
15695 format,
15696 status;
15697
15698 unsigned char
15699 *data;
15700
15701 unsigned long
15702 after,
15703 length;
15704
15705 if (display_image->debug != MagickFalse)
15706 (void) LogMagickEvent(X11Event,GetMagickModule(),
15707 "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15708 event.xproperty.atom,event.xproperty.state);
15709 if (event.xproperty.atom != windows->im_remote_command)
15710 break;
15711 /*
15712 Display image named by the remote command protocol.
15713 */
15714 status=XGetWindowProperty(display,event.xproperty.window,
15715 event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15716 AnyPropertyType,&type,&format,&length,&after,&data);
15717 if ((status != Success) || (length == 0))
15718 break;
15719 if (LocaleCompare((char *) data,"-quit") == 0)
15720 {
15721 XClientMessage(display,windows->image.id,windows->im_protocols,
15722 windows->im_exit,CurrentTime);
15723 (void) XFree((void *) data);
15724 break;
15725 }
15726 (void) CopyMagickString(resource_info->image_info->filename,
15727 (char *) data,MaxTextExtent);
15728 (void) XFree((void *) data);
15729 nexus=ReadImage(resource_info->image_info,&display_image->exception);
15730 CatchException(&display_image->exception);
15731 if (nexus != (Image *) NULL)
15732 *state|=NextImageState | ExitState;
15733 break;
15734 }
15735 case ReparentNotify:
15736 {
15737 if (display_image->debug != MagickFalse)
15738 (void) LogMagickEvent(X11Event,GetMagickModule(),
15739 "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15740 event.xreparent.window);
15741 break;
15742 }
15743 case UnmapNotify:
15744 {
15745 if (display_image->debug != MagickFalse)
15746 (void) LogMagickEvent(X11Event,GetMagickModule(),
15747 "Unmap Notify: 0x%lx",event.xunmap.window);
15748 if (event.xunmap.window == windows->backdrop.id)
15749 {
15750 windows->backdrop.mapped=MagickFalse;
15751 break;
15752 }
15753 if (event.xunmap.window == windows->image.id)
15754 {
15755 windows->image.mapped=MagickFalse;
15756 break;
15757 }
15758 if (event.xunmap.window == windows->magnify.id)
15759 {
15760 windows->magnify.mapped=MagickFalse;
15761 break;
15762 }
15763 if (event.xunmap.window == windows->pan.id)
15764 {
15765 windows->pan.mapped=MagickFalse;
15766 break;
15767 }
15768 if (event.xunmap.window == windows->info.id)
15769 {
15770 windows->info.mapped=MagickFalse;
15771 break;
15772 }
15773 if (event.xunmap.window == windows->icon.id)
15774 {
15775 if (map_info->colormap == icon_map->colormap)
15776 XConfigureImageColormap(display,resource_info,windows,
15777 display_image);
15778 (void) XFreeStandardColormap(display,icon_visual,icon_map,
15779 icon_pixel);
15780 windows->icon.mapped=MagickFalse;
15781 break;
15782 }
15783 if (event.xunmap.window == windows->command.id)
15784 {
15785 windows->command.mapped=MagickFalse;
15786 break;
15787 }
15788 if (event.xunmap.window == windows->popup.id)
15789 {
15790 if (windows->backdrop.id != (Window) NULL)
15791 (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15792 CurrentTime);
15793 windows->popup.mapped=MagickFalse;
15794 break;
15795 }
15796 if (event.xunmap.window == windows->widget.id)
15797 {
15798 if (windows->backdrop.id != (Window) NULL)
15799 (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15800 CurrentTime);
15801 windows->widget.mapped=MagickFalse;
15802 break;
15803 }
15804 break;
15805 }
15806 default:
15807 {
15808 if (display_image->debug != MagickFalse)
15809 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
15810 event.type);
15811 break;
15812 }
15813 }
15814 } while (!(*state & ExitState));
15815 if ((*state & ExitState) == 0)
15816 (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
15817 &display_image);
15818 else
15819 if (resource_info->confirm_edit != MagickFalse)
15820 {
15821 /*
15822 Query user if image has changed.
15823 */
15824 if ((resource_info->immutable == MagickFalse) &&
15825 (display_image->taint != MagickFalse))
15826 {
15827 int
15828 status;
15829
15830 status=XConfirmWidget(display,windows,"Your image changed.",
15831 "Do you want to save it");
15832 if (status == 0)
15833 *state&=(~ExitState);
15834 else
15835 if (status > 0)
15836 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
15837 &display_image);
15838 }
15839 }
15840 if ((windows->visual_info->klass == GrayScale) ||
15841 (windows->visual_info->klass == PseudoColor) ||
15842 (windows->visual_info->klass == DirectColor))
15843 {
15844 /*
15845 Withdraw pan and Magnify window.
15846 */
15847 if (windows->info.mapped != MagickFalse)
15848 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15849 if (windows->magnify.mapped != MagickFalse)
15850 (void) XWithdrawWindow(display,windows->magnify.id,
15851 windows->magnify.screen);
15852 if (windows->command.mapped != MagickFalse)
15853 (void) XWithdrawWindow(display,windows->command.id,
15854 windows->command.screen);
15855 }
15856 if (windows->pan.mapped != MagickFalse)
15857 (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
15858 if (resource_info->backdrop == MagickFalse)
15859 if (windows->backdrop.mapped)
15860 {
15861 (void) XWithdrawWindow(display,windows->backdrop.id,
15862 windows->backdrop.screen);
15863 (void) XDestroyWindow(display,windows->backdrop.id);
15864 windows->backdrop.id=(Window) NULL;
15865 (void) XWithdrawWindow(display,windows->image.id,
15866 windows->image.screen);
15867 (void) XDestroyWindow(display,windows->image.id);
15868 windows->image.id=(Window) NULL;
15869 }
15870 XSetCursorState(display,windows,MagickTrue);
15871 XCheckRefreshWindows(display,windows);
15872 if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
15873 *state&=(~ExitState);
15874 if (*state & ExitState)
15875 {
15876 /*
15877 Free Standard Colormap.
15878 */
15879 (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
15880 if (resource_info->map_type == (char *) NULL)
15881 (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
15882 /*
15883 Free X resources.
15884 */
15885 if (resource_info->copy_image != (Image *) NULL)
15886 {
15887 resource_info->copy_image=DestroyImage(resource_info->copy_image);
15888 resource_info->copy_image=NewImageList();
15889 }
15890 DestroyXResources();
15891 }
15892 (void) XSync(display,MagickFalse);
15893 /*
15894 Restore our progress monitor and warning handlers.
15895 */
15896 (void) SetErrorHandler(warning_handler);
15897 (void) SetWarningHandler(warning_handler);
15898 /*
15899 Change to home directory.
15900 */
15901 cwd=getcwd(working_directory,MaxTextExtent);
15902 {
15903 int
15904 status;
15905
15906 status=chdir(resource_info->home_directory);
15907 if (status == -1)
15908 (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
15909 FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
15910 }
15911 *image=display_image;
15912 return(nexus);
15913}
15914#else
15915
15916/*
15917%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15918% %
15919% %
15920% %
15921+ D i s p l a y I m a g e s %
15922% %
15923% %
15924% %
15925%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15926%
15927% DisplayImages() displays an image sequence to any X window screen. It
15928% returns a value other than 0 if successful. Check the exception member
15929% of image to determine the reason for any failure.
15930%
15931% The format of the DisplayImages method is:
15932%
15933% MagickBooleanType DisplayImages(const ImageInfo *image_info,
15934% Image *images)
15935%
15936% A description of each parameter follows:
15937%
15938% o image_info: the image info.
15939%
15940% o image: the image.
15941%
15942*/
15943MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
15944 Image *image)
15945{
15946 assert(image_info != (const ImageInfo *) NULL);
15947 assert(image_info->signature == MagickSignature);
15948 assert(image != (Image *) NULL);
15949 assert(image->signature == MagickSignature);
15950 if (image->debug != MagickFalse)
15951 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
15952 (void) ThrowMagickException(&image->exception,GetMagickModule(),
15953 MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
15954 image->filename);
15955 return(MagickFalse);
15956}
15957
15958/*
15959%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15960% %
15961% %
15962% %
15963+ R e m o t e D i s p l a y C o m m a n d %
15964% %
15965% %
15966% %
15967%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15968%
15969% RemoteDisplayCommand() encourages a remote display program to display the
15970% specified image filename.
15971%
15972% The format of the RemoteDisplayCommand method is:
15973%
15974% MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
15975% const char *window,const char *filename,ExceptionInfo *exception)
15976%
15977% A description of each parameter follows:
15978%
15979% o image_info: the image info.
15980%
15981% o window: Specifies the name or id of an X window.
15982%
15983% o filename: the name of the image filename to display.
15984%
15985% o exception: return any errors or warnings in this structure.
15986%
15987*/
15988MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
15989 const char *window,const char *filename,ExceptionInfo *exception)
15990{
15991 assert(image_info != (const ImageInfo *) NULL);
15992 assert(image_info->signature == MagickSignature);
15993 assert(filename != (char *) NULL);
15994 (void) window;
15995 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
15996 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
15997 "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
15998 return(MagickFalse);
15999}
16000#endif