blob: 527d3e5e3ea028c6efda081b005721931c9fd9ff [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% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 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"
cristyf2f27272009-12-17 14:48:46 +000083#include "magick/string-private.h"
cristy3ed852e2009-09-05 21:47:34 +000084#include "magick/transform.h"
85#include "magick/threshold.h"
86#include "magick/utility.h"
87#include "magick/version.h"
88#include "magick/widget.h"
89#include "magick/xwindow-private.h"
90
91#if defined(MAGICKCORE_X11_DELEGATE)
92/*
93 Define declarations.
94*/
cristy49e2d862010-11-12 02:50:30 +000095#define MaxColors MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
cristy3ed852e2009-09-05 21:47:34 +000096
97/*
98 Constant declarations.
99*/
100static const unsigned char
101 HighlightBitmap[8] =
102 {
103 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
104 },
cristydd05beb2010-11-21 21:23:39 +0000105 OpaqueBitmap[8] =
106 {
107 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
108 },
cristy3ed852e2009-09-05 21:47:34 +0000109 ShadowBitmap[8] =
110 {
111 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
112 };
113
114static const char
115 *PageSizes[] =
116 {
117 "Letter",
118 "Tabloid",
119 "Ledger",
120 "Legal",
121 "Statement",
122 "Executive",
123 "A3",
124 "A4",
125 "A5",
126 "B4",
127 "B5",
128 "Folio",
129 "Quarto",
130 "10x14",
131 (char *) NULL
132 };
133
134/*
135 Help widget declarations.
136*/
137static const char
138 *ImageAnnotateHelp[] =
139 {
140 "In annotate mode, the Command widget has these options:",
141 "",
142 " Font Name",
143 " fixed",
144 " variable",
145 " 5x8",
146 " 6x10",
147 " 7x13bold",
148 " 8x13bold",
149 " 9x15bold",
150 " 10x20",
151 " 12x24",
152 " Browser...",
153 " Font Color",
154 " black",
155 " blue",
156 " cyan",
157 " green",
158 " gray",
159 " red",
160 " magenta",
161 " yellow",
162 " white",
163 " transparent",
164 " Browser...",
165 " Font Color",
166 " black",
167 " blue",
168 " cyan",
169 " green",
170 " gray",
171 " red",
172 " magenta",
173 " yellow",
174 " white",
175 " transparent",
176 " Browser...",
177 " Rotate Text",
178 " -90",
179 " -45",
180 " -30",
181 " 0",
182 " 30",
183 " 45",
184 " 90",
185 " 180",
186 " Dialog...",
187 " Help",
188 " Dismiss",
189 "",
190 "Choose a font name from the Font Name sub-menu. Additional",
191 "font names can be specified with the font browser. You can",
192 "change the menu names by setting the X resources font1",
193 "through font9.",
194 "",
195 "Choose a font color from the Font Color sub-menu.",
196 "Additional font colors can be specified with the color",
197 "browser. You can change the menu colors by setting the X",
198 "resources pen1 through pen9.",
199 "",
200 "If you select the color browser and press Grab, you can",
201 "choose the font color by moving the pointer to the desired",
202 "color on the screen and press any button.",
203 "",
204 "If you choose to rotate the text, choose Rotate Text from the",
205 "menu and select an angle. Typically you will only want to",
206 "rotate one line of text at a time. Depending on the angle you",
207 "choose, subsequent lines may end up overwriting each other.",
208 "",
209 "Choosing a font and its color is optional. The default font",
210 "is fixed and the default color is black. However, you must",
211 "choose a location to begin entering text and press button 1.",
212 "An underscore character will appear at the location of the",
213 "pointer. The cursor changes to a pencil to indicate you are",
214 "in text mode. To exit immediately, press Dismiss.",
215 "",
216 "In text mode, any key presses will display the character at",
217 "the location of the underscore and advance the underscore",
218 "cursor. Enter your text and once completed press Apply to",
219 "finish your image annotation. To correct errors press BACK",
220 "SPACE. To delete an entire line of text, press DELETE. Any",
221 "text that exceeds the boundaries of the image window is",
222 "automagically continued onto the next line.",
223 "",
224 "The actual color you request for the font is saved in the",
225 "image. However, the color that appears in your image window",
226 "may be different. For example, on a monochrome screen the",
227 "text will appear black or white even if you choose the color",
228 "red as the font color. However, the image saved to a file",
229 "with -write is written with red lettering. To assure the",
230 "correct color text in the final image, any PseudoClass image",
231 "is promoted to DirectClass (see miff(5)). To force a",
232 "PseudoClass image to remain PseudoClass, use -colors.",
233 (char *) NULL,
234 },
235 *ImageChopHelp[] =
236 {
237 "In chop mode, the Command widget has these options:",
238 "",
239 " Direction",
240 " horizontal",
241 " vertical",
242 " Help",
243 " Dismiss",
244 "",
245 "If the you choose the horizontal direction (this the",
246 "default), the area of the image between the two horizontal",
247 "endpoints of the chop line is removed. Otherwise, the area",
248 "of the image between the two vertical endpoints of the chop",
249 "line is removed.",
250 "",
251 "Select a location within the image window to begin your chop,",
252 "press and hold any button. Next, move the pointer to",
253 "another location in the image. As you move a line will",
254 "connect the initial location and the pointer. When you",
255 "release the button, the area within the image to chop is",
256 "determined by which direction you choose from the Command",
257 "widget.",
258 "",
259 "To cancel the image chopping, move the pointer back to the",
260 "starting point of the line and release the button.",
261 (char *) NULL,
262 },
263 *ImageColorEditHelp[] =
264 {
265 "In color edit mode, the Command widget has these options:",
266 "",
267 " Method",
268 " point",
269 " replace",
270 " floodfill",
271 " filltoborder",
272 " reset",
273 " Pixel Color",
274 " black",
275 " blue",
276 " cyan",
277 " green",
278 " gray",
279 " red",
280 " magenta",
281 " yellow",
282 " white",
283 " Browser...",
284 " Border Color",
285 " black",
286 " blue",
287 " cyan",
288 " green",
289 " gray",
290 " red",
291 " magenta",
292 " yellow",
293 " white",
294 " Browser...",
295 " Fuzz",
296 " 0%",
297 " 2%",
298 " 5%",
299 " 10%",
300 " 15%",
301 " Dialog...",
302 " Undo",
303 " Help",
304 " Dismiss",
305 "",
306 "Choose a color editing method from the Method sub-menu",
307 "of the Command widget. The point method recolors any pixel",
308 "selected with the pointer until the button is released. The",
309 "replace method recolors any pixel that matches the color of",
310 "the pixel you select with a button press. Floodfill recolors",
311 "any pixel that matches the color of the pixel you select with",
312 "a button press and is a neighbor. Whereas filltoborder recolors",
313 "any neighbor pixel that is not the border color. Finally reset",
314 "changes the entire image to the designated color.",
315 "",
316 "Next, choose a pixel color from the Pixel Color sub-menu.",
317 "Additional pixel colors can be specified with the color",
318 "browser. You can change the menu colors by setting the X",
319 "resources pen1 through pen9.",
320 "",
321 "Now press button 1 to select a pixel within the image window",
322 "to change its color. Additional pixels may be recolored as",
323 "prescribed by the method you choose.",
324 "",
325 "If the Magnify widget is mapped, it can be helpful in positioning",
326 "your pointer within the image (refer to button 2).",
327 "",
328 "The actual color you request for the pixels is saved in the",
329 "image. However, the color that appears in your image window",
330 "may be different. For example, on a monochrome screen the",
331 "pixel will appear black or white even if you choose the",
332 "color red as the pixel color. However, the image saved to a",
333 "file with -write is written with red pixels. To assure the",
334 "correct color text in the final image, any PseudoClass image",
335 "is promoted to DirectClass (see miff(5)). To force a",
336 "PseudoClass image to remain PseudoClass, use -colors.",
337 (char *) NULL,
338 },
339 *ImageCompositeHelp[] =
340 {
341 "First a widget window is displayed requesting you to enter an",
342 "image name. Press Composite, Grab or type a file name.",
343 "Press Cancel if you choose not to create a composite image.",
344 "When you choose Grab, move the pointer to the desired window",
345 "and press any button.",
346 "",
347 "If the Composite image does not have any matte information,",
348 "you are informed and the file browser is displayed again.",
349 "Enter the name of a mask image. The image is typically",
350 "grayscale and the same size as the composite image. If the",
351 "image is not grayscale, it is converted to grayscale and the",
352 "resulting intensities are used as matte information.",
353 "",
354 "A small window appears showing the location of the cursor in",
355 "the image window. You are now in composite mode. To exit",
356 "immediately, press Dismiss. In composite mode, the Command",
357 "widget has these options:",
358 "",
359 " Operators",
360 " Over",
361 " In",
362 " Out",
363 " Atop",
364 " Xor",
365 " Plus",
366 " Minus",
367 " Add",
368 " Subtract",
369 " Difference",
370 " Multiply",
371 " Bumpmap",
372 " Copy",
373 " CopyRed",
374 " CopyGreen",
375 " CopyBlue",
376 " CopyOpacity",
377 " Clear",
378 " Dissolve",
379 " Displace",
380 " Help",
381 " Dismiss",
382 "",
383 "Choose a composite operation from the Operators sub-menu of",
384 "the Command widget. How each operator behaves is described",
385 "below. Image window is the image currently displayed on",
386 "your X server and image is the image obtained with the File",
387 "Browser widget.",
388 "",
389 "Over The result is the union of the two image shapes,",
390 " with image obscuring image window in the region of",
391 " overlap.",
392 "",
393 "In The result is simply image cut by the shape of",
394 " image window. None of the image data of image",
395 " window is in the result.",
396 "",
397 "Out The resulting image is image with the shape of",
398 " image window cut out.",
399 "",
400 "Atop The result is the same shape as image image window,",
401 " with image obscuring image window where the image",
402 " shapes overlap. Note this differs from over",
403 " because the portion of image outside image window's",
404 " shape does not appear in the result.",
405 "",
406 "Xor The result is the image data from both image and",
407 " image window that is outside the overlap region.",
408 " The overlap region is blank.",
409 "",
410 "Plus The result is just the sum of the image data.",
411 " Output values are cropped to QuantumRange (no overflow).",
412 "",
413 "Minus The result of image - image window, with underflow",
414 " cropped to zero.",
415 "",
416 "Add The result of image + image window, with overflow",
417 " wrapping around (mod 256).",
418 "",
419 "Subtract The result of image - image window, with underflow",
420 " wrapping around (mod 256). The add and subtract",
421 " operators can be used to perform reversible",
422 " transformations.",
423 "",
424 "Difference",
425 " The result of abs(image - image window). This",
426 " useful for comparing two very similar images.",
427 "",
428 "Multiply",
429 " The result of image * image window. This",
430 " useful for the creation of drop-shadows.",
431 "",
432 "Bumpmap The result of surface normals from image * image",
433 " window.",
434 "",
435 "Copy The resulting image is image window replaced with",
436 " image. Here the matte information is ignored.",
437 "",
438 "CopyRed The red layer of the image window is replace with",
439 " the red layer of the image. The other layers are",
440 " untouched.",
441 "",
442 "CopyGreen",
443 " The green layer of the image window is replace with",
444 " the green layer of the image. The other layers are",
445 " untouched.",
446 "",
447 "CopyBlue The blue layer of the image window is replace with",
448 " the blue layer of the image. The other layers are",
449 " untouched.",
450 "",
451 "CopyOpacity",
452 " The matte layer of the image window is replace with",
453 " the matte layer of the image. The other layers are",
454 " untouched.",
455 "",
456 "The image compositor requires a matte, or alpha channel in",
457 "the image for some operations. This extra channel usually",
458 "defines a mask which represents a sort of a cookie-cutter",
459 "for the image. This the case when matte is opaque (full",
460 "coverage) for pixels inside the shape, zero outside, and",
461 "between 0 and QuantumRange on the boundary. If image does not",
462 "have a matte channel, it is initialized with 0 for any pixel",
463 "matching in color to pixel location (0,0), otherwise QuantumRange.",
464 "",
465 "If you choose Dissolve, the composite operator becomes Over. The",
466 "image matte channel percent transparency is initialized to factor.",
467 "The image window is initialized to (100-factor). Where factor is the",
468 "value you specify in the Dialog widget.",
469 "",
470 "Displace shifts the image pixels as defined by a displacement",
471 "map. With this option, image is used as a displacement map.",
472 "Black, within the displacement map, is a maximum positive",
473 "displacement. White is a maximum negative displacement and",
474 "middle gray is neutral. The displacement is scaled to determine",
475 "the pixel shift. By default, the displacement applies in both the",
476 "horizontal and vertical directions. However, if you specify a mask,",
477 "image is the horizontal X displacement and mask the vertical Y",
478 "displacement.",
479 "",
480 "Note that matte information for image window is not retained",
481 "for colormapped X server visuals (e.g. StaticColor,",
482 "StaticColor, GrayScale, PseudoColor). Correct compositing",
483 "behavior may require a TrueColor or DirectColor visual or a",
484 "Standard Colormap.",
485 "",
486 "Choosing a composite operator is optional. The default",
487 "operator is replace. However, you must choose a location to",
488 "composite your image and press button 1. Press and hold the",
489 "button before releasing and an outline of the image will",
490 "appear to help you identify your location.",
491 "",
492 "The actual colors of the composite image is saved. However,",
493 "the color that appears in image window may be different.",
494 "For example, on a monochrome screen image window will appear",
495 "black or white even though your composited image may have",
496 "many colors. If the image is saved to a file it is written",
497 "with the correct colors. To assure the correct colors are",
498 "saved in the final image, any PseudoClass image is promoted",
499 "to DirectClass (see miff(5)). To force a PseudoClass image",
500 "to remain PseudoClass, use -colors.",
501 (char *) NULL,
502 },
503 *ImageCutHelp[] =
504 {
505 "In cut mode, the Command widget has these options:",
506 "",
507 " Help",
508 " Dismiss",
509 "",
510 "To define a cut region, press button 1 and drag. The",
511 "cut region is defined by a highlighted rectangle that",
512 "expands or contracts as it follows the pointer. Once you",
513 "are satisfied with the cut region, release the button.",
514 "You are now in rectify mode. In rectify mode, the Command",
515 "widget has these options:",
516 "",
517 " Cut",
518 " Help",
519 " Dismiss",
520 "",
521 "You can make adjustments by moving the pointer to one of the",
522 "cut rectangle corners, pressing a button, and dragging.",
523 "Finally, press Cut to commit your copy region. To",
524 "exit without cutting the image, press Dismiss.",
525 (char *) NULL,
526 },
527 *ImageCopyHelp[] =
528 {
529 "In copy mode, the Command widget has these options:",
530 "",
531 " Help",
532 " Dismiss",
533 "",
534 "To define a copy region, press button 1 and drag. The",
535 "copy region is defined by a highlighted rectangle that",
536 "expands or contracts as it follows the pointer. Once you",
537 "are satisfied with the copy region, release the button.",
538 "You are now in rectify mode. In rectify mode, the Command",
539 "widget has these options:",
540 "",
541 " Copy",
542 " Help",
543 " Dismiss",
544 "",
545 "You can make adjustments by moving the pointer to one of the",
546 "copy rectangle corners, pressing a button, and dragging.",
547 "Finally, press Copy to commit your copy region. To",
548 "exit without copying the image, press Dismiss.",
549 (char *) NULL,
550 },
551 *ImageCropHelp[] =
552 {
553 "In crop mode, the Command widget has these options:",
554 "",
555 " Help",
556 " Dismiss",
557 "",
558 "To define a cropping region, press button 1 and drag. The",
559 "cropping region is defined by a highlighted rectangle that",
560 "expands or contracts as it follows the pointer. Once you",
561 "are satisfied with the cropping region, release the button.",
562 "You are now in rectify mode. In rectify mode, the Command",
563 "widget has these options:",
564 "",
565 " Crop",
566 " Help",
567 " Dismiss",
568 "",
569 "You can make adjustments by moving the pointer to one of the",
570 "cropping rectangle corners, pressing a button, and dragging.",
571 "Finally, press Crop to commit your cropping region. To",
572 "exit without cropping the image, press Dismiss.",
573 (char *) NULL,
574 },
575 *ImageDrawHelp[] =
576 {
577 "The cursor changes to a crosshair to indicate you are in",
578 "draw mode. To exit immediately, press Dismiss. In draw mode,",
579 "the Command widget has these options:",
580 "",
581 " Element",
582 " point",
583 " line",
584 " rectangle",
585 " fill rectangle",
586 " circle",
587 " fill circle",
588 " ellipse",
589 " fill ellipse",
590 " polygon",
591 " fill polygon",
592 " Color",
593 " black",
594 " blue",
595 " cyan",
596 " green",
597 " gray",
598 " red",
599 " magenta",
600 " yellow",
601 " white",
602 " transparent",
603 " Browser...",
604 " Stipple",
605 " Brick",
606 " Diagonal",
607 " Scales",
608 " Vertical",
609 " Wavy",
610 " Translucent",
611 " Opaque",
612 " Open...",
613 " Width",
614 " 1",
615 " 2",
616 " 4",
617 " 8",
618 " 16",
619 " Dialog...",
620 " Undo",
621 " Help",
622 " Dismiss",
623 "",
624 "Choose a drawing primitive from the Element sub-menu.",
625 "",
626 "Choose a color from the Color sub-menu. Additional",
627 "colors can be specified with the color browser.",
628 "",
629 "If you choose the color browser and press Grab, you can",
630 "select the color by moving the pointer to the desired",
631 "color on the screen and press any button. The transparent",
632 "color updates the image matte channel and is useful for",
633 "image compositing.",
634 "",
635 "Choose a stipple, if appropriate, from the Stipple sub-menu.",
636 "Additional stipples can be specified with the file browser.",
637 "Stipples obtained from the file browser must be on disk in the",
638 "X11 bitmap format.",
639 "",
640 "Choose a width, if appropriate, from the Width sub-menu. To",
641 "choose a specific width select the Dialog widget.",
642 "",
643 "Choose a point in the Image window and press button 1 and",
644 "hold. Next, move the pointer to another location in the",
645 "image. As you move, a line connects the initial location and",
646 "the pointer. When you release the button, the image is",
647 "updated with the primitive you just drew. For polygons, the",
648 "image is updated when you press and release the button without",
649 "moving the pointer.",
650 "",
651 "To cancel image drawing, move the pointer back to the",
652 "starting point of the line and release the button.",
653 (char *) NULL,
654 },
655 *DisplayHelp[] =
656 {
657 "BUTTONS",
658 " The effects of each button press is described below. Three",
659 " buttons are required. If you have a two button mouse,",
660 " button 1 and 3 are returned. Press ALT and button 3 to",
661 " simulate button 2.",
662 "",
663 " 1 Press this button to map or unmap the Command widget.",
664 "",
665 " 2 Press and drag to define a region of the image to",
666 " magnify.",
667 "",
668 " 3 Press and drag to choose from a select set of commands.",
669 " This button behaves differently if the image being",
670 " displayed is a visual image directory. Here, choose a",
671 " particular tile of the directory and press this button and",
672 " drag to select a command from a pop-up menu. Choose from",
673 " these menu items:",
674 "",
675 " Open",
676 " Next",
677 " Former",
678 " Delete",
679 " Update",
680 "",
681 " If you choose Open, the image represented by the tile is",
682 " displayed. To return to the visual image directory, choose",
683 " Next from the Command widget. Next and Former moves to the",
684 " next or former image respectively. Choose Delete to delete",
685 " a particular image tile. Finally, choose Update to",
686 " synchronize all the image tiles with their respective",
687 " images.",
688 "",
689 "COMMAND WIDGET",
690 " The Command widget lists a number of sub-menus and commands.",
691 " They are",
692 "",
693 " File",
694 " Open...",
695 " Next",
696 " Former",
697 " Select...",
698 " Save...",
699 " Print...",
700 " Delete...",
701 " New...",
702 " Visual Directory...",
703 " Quit",
704 " Edit",
705 " Undo",
706 " Redo",
707 " Cut",
708 " Copy",
709 " Paste",
710 " View",
711 " Half Size",
712 " Original Size",
713 " Double Size",
714 " Resize...",
715 " Apply",
716 " Refresh",
717 " Restore",
718 " Transform",
719 " Crop",
720 " Chop",
721 " Flop",
722 " Flip",
723 " Rotate Right",
724 " Rotate Left",
725 " Rotate...",
726 " Shear...",
727 " Roll...",
728 " Trim Edges",
729 " Enhance",
730 " Brightness...",
731 " Saturation...",
732 " Hue...",
733 " Gamma...",
734 " Sharpen...",
735 " Dull",
736 " Contrast Stretch...",
737 " Sigmoidal Contrast...",
738 " Normalize",
739 " Equalize",
740 " Negate",
741 " Grayscale",
742 " Map...",
743 " Quantize...",
744 " Effects",
745 " Despeckle",
746 " Emboss",
747 " Reduce Noise",
748 " Add Noise",
749 " Sharpen...",
750 " Blur...",
751 " Threshold...",
752 " Edge Detect...",
753 " Spread...",
754 " Shade...",
755 " Painting...",
756 " Segment...",
757 " F/X",
758 " Solarize...",
759 " Sepia Tone...",
760 " Swirl...",
761 " Implode...",
762 " Vignette...",
763 " Wave...",
764 " Oil Painting...",
765 " Charcoal Drawing...",
766 " Image Edit",
767 " Annotate...",
768 " Draw...",
769 " Color...",
770 " Matte...",
771 " Composite...",
772 " Add Border...",
773 " Add Frame...",
774 " Comment...",
775 " Launch...",
776 " Region of Interest...",
777 " Miscellany",
778 " Image Info",
779 " Zoom Image",
780 " Show Preview...",
781 " Show Histogram",
782 " Show Matte",
783 " Background...",
784 " Slide Show",
785 " Preferences...",
786 " Help",
787 " Overview",
788 " Browse Documentation",
789 " About Display",
790 "",
791 " Menu items with a indented triangle have a sub-menu. They",
792 " are represented above as the indented items. To access a",
793 " sub-menu item, move the pointer to the appropriate menu and",
794 " press a button and drag. When you find the desired sub-menu",
795 " item, release the button and the command is executed. Move",
796 " the pointer away from the sub-menu if you decide not to",
797 " execute a particular command.",
798 "",
799 "KEYBOARD ACCELERATORS",
800 " Accelerators are one or two key presses that effect a",
801 " particular command. The keyboard accelerators that",
802 " display(1) understands is:",
803 "",
804 " Ctl+O Press to open an image from a file.",
805 "",
806 " space Press to display the next image.",
807 "",
808 " If the image is a multi-paged document such as a Postscript",
809 " document, you can skip ahead several pages by preceding",
810 " this command with a number. For example to display the",
811 " third page beyond the current page, press 3<space>.",
812 "",
813 " backspace Press to display the former image.",
814 "",
815 " If the image is a multi-paged document such as a Postscript",
816 " document, you can skip behind several pages by preceding",
817 " this command with a number. For example to display the",
818 " third page preceding the current page, press 3<backspace>.",
819 "",
820 " Ctl+S Press to write the image to a file.",
821 "",
822 " Ctl+P Press to print the image to a Postscript printer.",
823 "",
824 " Ctl+D Press to delete an image file.",
825 "",
826 " Ctl+N Press to create a blank canvas.",
827 "",
828 " Ctl+Q Press to discard all images and exit program.",
829 "",
830 " Ctl+Z Press to undo last image transformation.",
831 "",
832 " Ctl+R Press to redo last image transformation.",
833 "",
834 " Ctl+X Press to cut a region of the image.",
835 "",
836 " Ctl+C Press to copy a region of the image.",
837 "",
838 " Ctl+V Press to paste a region to the image.",
839 "",
840 " < Press to half the image size.",
841 "",
842 " - Press to return to the original image size.",
843 "",
844 " > Press to double the image size.",
845 "",
846 " % Press to resize the image to a width and height you",
847 " specify.",
848 "",
849 "Cmd-A Press to make any image transformations permanent."
850 "",
851 " By default, any image size transformations are applied",
852 " to the original image to create the image displayed on",
853 " the X server. However, the transformations are not",
854 " permanent (i.e. the original image does not change",
855 " size only the X image does). For example, if you",
856 " press > the X image will appear to double in size,",
857 " but the original image will in fact remain the same size.",
858 " To force the original image to double in size, press >",
859 " followed by Cmd-A.",
860 "",
861 " @ Press to refresh the image window.",
862 "",
863 " C Press to cut out a rectangular region of the image.",
864 "",
865 " [ Press to chop the image.",
866 "",
867 " H Press to flop image in the horizontal direction.",
868 "",
869 " V Press to flip image in the vertical direction.",
870 "",
871 " / Press to rotate the image 90 degrees clockwise.",
872 "",
873 " \\ Press to rotate the image 90 degrees counter-clockwise.",
874 "",
875 " * Press to rotate the image the number of degrees you",
876 " specify.",
877 "",
878 " S Press to shear the image the number of degrees you",
879 " specify.",
880 "",
881 " R Press to roll the image.",
882 "",
883 " T Press to trim the image edges.",
884 "",
885 " Shft-H Press to vary the image hue.",
886 "",
887 " Shft-S Press to vary the color saturation.",
888 "",
889 " Shft-L Press to vary the color brightness.",
890 "",
891 " Shft-G Press to gamma correct the image.",
892 "",
893 " Shft-C Press to sharpen the image contrast.",
894 "",
895 " Shft-Z Press to dull the image contrast.",
896 "",
897 " = Press to perform histogram equalization on the image.",
898 "",
899 " Shft-N Press to perform histogram normalization on the image.",
900 "",
901 " Shft-~ Press to negate the colors of the image.",
902 "",
903 " . Press to convert the image colors to gray.",
904 "",
905 " Shft-# Press to set the maximum number of unique colors in the",
906 " image.",
907 "",
908 " F2 Press to reduce the speckles in an image.",
909 "",
910 " F3 Press to eliminate peak noise from an image.",
911 "",
912 " F4 Press to add noise to an image.",
913 "",
914 " F5 Press to sharpen an image.",
915 "",
916 " F6 Press to delete an image file.",
917 "",
918 " F7 Press to threshold the image.",
919 "",
920 " F8 Press to detect edges within an image.",
921 "",
922 " F9 Press to emboss an image.",
923 "",
924 " F10 Press to displace pixels by a random amount.",
925 "",
926 " F11 Press to negate all pixels above the threshold level.",
927 "",
928 " F12 Press to shade the image using a distant light source.",
929 "",
930 " F13 Press to lighten or darken image edges to create a 3-D effect.",
931 "",
932 " F14 Press to segment the image by color.",
933 "",
934 " Meta-S Press to swirl image pixels about the center.",
935 "",
936 " Meta-I Press to implode image pixels about the center.",
937 "",
cristycee97112010-05-28 00:44:52 +0000938 " Meta-W Press to alter an image along a sine wave.",
cristy3ed852e2009-09-05 21:47:34 +0000939 "",
940 " Meta-P Press to simulate an oil painting.",
941 "",
942 " Meta-C Press to simulate a charcoal drawing.",
943 "",
944 " Alt-A Press to annotate the image with text.",
945 "",
946 " Alt-D Press to draw on an image.",
947 "",
948 " Alt-P Press to edit an image pixel color.",
949 "",
950 " Alt-M Press to edit the image matte information.",
951 "",
952 " Alt-V Press to composite the image with another.",
953 "",
954 " Alt-B Press to add a border to the image.",
955 "",
956 " Alt-F Press to add an ornamental border to the image.",
957 "",
958 " Alt-Shft-!",
959 " Press to add an image comment.",
960 "",
961 " Ctl-A Press to apply image processing techniques to a region",
962 " of interest.",
963 "",
964 " Shft-? Press to display information about the image.",
965 "",
966 " Shft-+ Press to map the zoom image window.",
967 "",
968 " Shft-P Press to preview an image enhancement, effect, or f/x.",
969 "",
970 " F1 Press to display helpful information about display(1).",
971 "",
972 " Find Press to browse documentation about ImageMagick.",
973 "",
974 " 1-9 Press to change the level of magnification.",
975 "",
976 " Use the arrow keys to move the image one pixel up, down,",
977 " left, or right within the magnify window. Be sure to first",
978 " map the magnify window by pressing button 2.",
979 "",
980 " Press ALT and one of the arrow keys to trim off one pixel",
981 " from any side of the image.",
982 (char *) NULL,
983 },
984 *ImageMatteEditHelp[] =
985 {
986 "Matte information within an image is useful for some",
987 "operations such as image compositing (See IMAGE",
988 "COMPOSITING). This extra channel usually defines a mask",
989 "which represents a sort of a cookie-cutter for the image.",
990 "This the case when matte is opaque (full coverage) for",
991 "pixels inside the shape, zero outside, and between 0 and",
992 "QuantumRange on the boundary.",
993 "",
994 "A small window appears showing the location of the cursor in",
995 "the image window. You are now in matte edit mode. To exit",
996 "immediately, press Dismiss. In matte edit mode, the Command",
997 "widget has these options:",
998 "",
999 " Method",
1000 " point",
1001 " replace",
1002 " floodfill",
1003 " filltoborder",
1004 " reset",
1005 " Border Color",
1006 " black",
1007 " blue",
1008 " cyan",
1009 " green",
1010 " gray",
1011 " red",
1012 " magenta",
1013 " yellow",
1014 " white",
1015 " Browser...",
1016 " Fuzz",
1017 " 0%",
1018 " 2%",
1019 " 5%",
1020 " 10%",
1021 " 15%",
1022 " Dialog...",
1023 " Matte",
1024 " Opaque",
1025 " Transparent",
1026 " Dialog...",
1027 " Undo",
1028 " Help",
1029 " Dismiss",
1030 "",
1031 "Choose a matte editing method from the Method sub-menu of",
1032 "the Command widget. The point method changes the matte value",
1033 "of any pixel selected with the pointer until the button is",
1034 "is released. The replace method changes the matte value of",
1035 "any pixel that matches the color of the pixel you select with",
1036 "a button press. Floodfill changes the matte value of any pixel",
1037 "that matches the color of the pixel you select with a button",
1038 "press and is a neighbor. Whereas filltoborder changes the matte",
1039 "value any neighbor pixel that is not the border color. Finally",
1040 "reset changes the entire image to the designated matte value.",
1041 "",
1042 "Choose Matte Value and pick Opaque or Transarent. For other values",
1043 "select the Dialog entry. Here a dialog appears requesting a matte",
1044 "value. The value you select is assigned as the opacity value of the",
1045 "selected pixel or pixels.",
1046 "",
1047 "Now, press any button to select a pixel within the image",
1048 "window to change its matte value.",
1049 "",
1050 "If the Magnify widget is mapped, it can be helpful in positioning",
1051 "your pointer within the image (refer to button 2).",
1052 "",
1053 "Matte information is only valid in a DirectClass image.",
1054 "Therefore, any PseudoClass image is promoted to DirectClass",
1055 "(see miff(5)). Note that matte information for PseudoClass",
1056 "is not retained for colormapped X server visuals (e.g.",
1057 "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1058 "immediately save your image to a file (refer to Write).",
1059 "Correct matte editing behavior may require a TrueColor or",
1060 "DirectColor visual or a Standard Colormap.",
1061 (char *) NULL,
1062 },
1063 *ImagePanHelp[] =
1064 {
1065 "When an image exceeds the width or height of the X server",
1066 "screen, display maps a small panning icon. The rectangle",
1067 "within the panning icon shows the area that is currently",
1068 "displayed in the image window. To pan about the image,",
1069 "press any button and drag the pointer within the panning",
1070 "icon. The pan rectangle moves with the pointer and the",
1071 "image window is updated to reflect the location of the",
1072 "rectangle within the panning icon. When you have selected",
1073 "the area of the image you wish to view, release the button.",
1074 "",
1075 "Use the arrow keys to pan the image one pixel up, down,",
1076 "left, or right within the image window.",
1077 "",
1078 "The panning icon is withdrawn if the image becomes smaller",
1079 "than the dimensions of the X server screen.",
1080 (char *) NULL,
1081 },
1082 *ImagePasteHelp[] =
1083 {
1084 "A small window appears showing the location of the cursor in",
1085 "the image window. You are now in paste mode. To exit",
1086 "immediately, press Dismiss. In paste mode, the Command",
1087 "widget has these options:",
1088 "",
1089 " Operators",
1090 " over",
1091 " in",
1092 " out",
1093 " atop",
1094 " xor",
1095 " plus",
1096 " minus",
1097 " add",
1098 " subtract",
1099 " difference",
1100 " replace",
1101 " Help",
1102 " Dismiss",
1103 "",
1104 "Choose a composite operation from the Operators sub-menu of",
1105 "the Command widget. How each operator behaves is described",
1106 "below. Image window is the image currently displayed on",
1107 "your X server and image is the image obtained with the File",
1108 "Browser widget.",
1109 "",
1110 "Over The result is the union of the two image shapes,",
1111 " with image obscuring image window in the region of",
1112 " overlap.",
1113 "",
1114 "In The result is simply image cut by the shape of",
1115 " image window. None of the image data of image",
1116 " window is in the result.",
1117 "",
1118 "Out The resulting image is image with the shape of",
1119 " image window cut out.",
1120 "",
1121 "Atop The result is the same shape as image image window,",
1122 " with image obscuring image window where the image",
1123 " shapes overlap. Note this differs from over",
1124 " because the portion of image outside image window's",
1125 " shape does not appear in the result.",
1126 "",
1127 "Xor The result is the image data from both image and",
1128 " image window that is outside the overlap region.",
1129 " The overlap region is blank.",
1130 "",
1131 "Plus The result is just the sum of the image data.",
1132 " Output values are cropped to QuantumRange (no overflow).",
1133 " This operation is independent of the matte",
1134 " channels.",
1135 "",
1136 "Minus The result of image - image window, with underflow",
1137 " cropped to zero.",
1138 "",
1139 "Add The result of image + image window, with overflow",
1140 " wrapping around (mod 256).",
1141 "",
1142 "Subtract The result of image - image window, with underflow",
1143 " wrapping around (mod 256). The add and subtract",
1144 " operators can be used to perform reversible",
1145 " transformations.",
1146 "",
1147 "Difference",
1148 " The result of abs(image - image window). This",
1149 " useful for comparing two very similar images.",
1150 "",
1151 "Copy The resulting image is image window replaced with",
1152 " image. Here the matte information is ignored.",
1153 "",
1154 "CopyRed The red layer of the image window is replace with",
1155 " the red layer of the image. The other layers are",
1156 " untouched.",
1157 "",
1158 "CopyGreen",
1159 " The green layer of the image window is replace with",
1160 " the green layer of the image. The other layers are",
1161 " untouched.",
1162 "",
1163 "CopyBlue The blue layer of the image window is replace with",
1164 " the blue layer of the image. The other layers are",
1165 " untouched.",
1166 "",
1167 "CopyOpacity",
1168 " The matte layer of the image window is replace with",
1169 " the matte layer of the image. The other layers are",
1170 " untouched.",
1171 "",
1172 "The image compositor requires a matte, or alpha channel in",
1173 "the image for some operations. This extra channel usually",
1174 "defines a mask which represents a sort of a cookie-cutter",
1175 "for the image. This the case when matte is opaque (full",
1176 "coverage) for pixels inside the shape, zero outside, and",
1177 "between 0 and QuantumRange on the boundary. If image does not",
1178 "have a matte channel, it is initialized with 0 for any pixel",
1179 "matching in color to pixel location (0,0), otherwise QuantumRange.",
1180 "",
1181 "Note that matte information for image window is not retained",
1182 "for colormapped X server visuals (e.g. StaticColor,",
1183 "StaticColor, GrayScale, PseudoColor). Correct compositing",
1184 "behavior may require a TrueColor or DirectColor visual or a",
1185 "Standard Colormap.",
1186 "",
1187 "Choosing a composite operator is optional. The default",
1188 "operator is replace. However, you must choose a location to",
1189 "paste your image and press button 1. Press and hold the",
1190 "button before releasing and an outline of the image will",
1191 "appear to help you identify your location.",
1192 "",
1193 "The actual colors of the pasted image is saved. However,",
1194 "the color that appears in image window may be different.",
1195 "For example, on a monochrome screen image window will appear",
1196 "black or white even though your pasted image may have",
1197 "many colors. If the image is saved to a file it is written",
1198 "with the correct colors. To assure the correct colors are",
1199 "saved in the final image, any PseudoClass image is promoted",
1200 "to DirectClass (see miff(5)). To force a PseudoClass image",
1201 "to remain PseudoClass, use -colors.",
1202 (char *) NULL,
1203 },
1204 *ImageROIHelp[] =
1205 {
1206 "In region of interest mode, the Command widget has these",
1207 "options:",
1208 "",
1209 " Help",
1210 " Dismiss",
1211 "",
1212 "To define a region of interest, press button 1 and drag.",
1213 "The region of interest is defined by a highlighted rectangle",
1214 "that expands or contracts as it follows the pointer. Once",
1215 "you are satisfied with the region of interest, release the",
1216 "button. You are now in apply mode. In apply mode the",
1217 "Command widget has these options:",
1218 "",
1219 " File",
1220 " Save...",
1221 " Print...",
1222 " Edit",
1223 " Undo",
1224 " Redo",
1225 " Transform",
1226 " Flop",
1227 " Flip",
1228 " Rotate Right",
1229 " Rotate Left",
1230 " Enhance",
1231 " Hue...",
1232 " Saturation...",
1233 " Brightness...",
1234 " Gamma...",
1235 " Spiff",
1236 " Dull",
1237 " Contrast Stretch",
1238 " Sigmoidal Contrast...",
1239 " Normalize",
1240 " Equalize",
1241 " Negate",
1242 " Grayscale",
1243 " Map...",
1244 " Quantize...",
1245 " Effects",
1246 " Despeckle",
1247 " Emboss",
1248 " Reduce Noise",
1249 " Sharpen...",
1250 " Blur...",
1251 " Threshold...",
1252 " Edge Detect...",
1253 " Spread...",
1254 " Shade...",
1255 " Raise...",
1256 " Segment...",
1257 " F/X",
1258 " Solarize...",
1259 " Sepia Tone...",
1260 " Swirl...",
1261 " Implode...",
1262 " Vignette...",
1263 " Wave...",
1264 " Oil Painting...",
1265 " Charcoal Drawing...",
1266 " Miscellany",
1267 " Image Info",
1268 " Zoom Image",
1269 " Show Preview...",
1270 " Show Histogram",
1271 " Show Matte",
1272 " Help",
1273 " Dismiss",
1274 "",
1275 "You can make adjustments to the region of interest by moving",
1276 "the pointer to one of the rectangle corners, pressing a",
1277 "button, and dragging. Finally, choose an image processing",
1278 "technique from the Command widget. You can choose more than",
1279 "one image processing technique to apply to an area.",
1280 "Alternatively, you can move the region of interest before",
1281 "applying another image processing technique. To exit, press",
1282 "Dismiss.",
1283 (char *) NULL,
1284 },
1285 *ImageRotateHelp[] =
1286 {
1287 "In rotate mode, the Command widget has these options:",
1288 "",
1289 " Pixel Color",
1290 " black",
1291 " blue",
1292 " cyan",
1293 " green",
1294 " gray",
1295 " red",
1296 " magenta",
1297 " yellow",
1298 " white",
1299 " Browser...",
1300 " Direction",
1301 " horizontal",
1302 " vertical",
1303 " Help",
1304 " Dismiss",
1305 "",
1306 "Choose a background color from the Pixel Color sub-menu.",
1307 "Additional background colors can be specified with the color",
1308 "browser. You can change the menu colors by setting the X",
1309 "resources pen1 through pen9.",
1310 "",
1311 "If you choose the color browser and press Grab, you can",
1312 "select the background color by moving the pointer to the",
1313 "desired color on the screen and press any button.",
1314 "",
1315 "Choose a point in the image window and press this button and",
1316 "hold. Next, move the pointer to another location in the",
1317 "image. As you move a line connects the initial location and",
1318 "the pointer. When you release the button, the degree of",
1319 "image rotation is determined by the slope of the line you",
1320 "just drew. The slope is relative to the direction you",
1321 "choose from the Direction sub-menu of the Command widget.",
1322 "",
1323 "To cancel the image rotation, move the pointer back to the",
1324 "starting point of the line and release the button.",
1325 (char *) NULL,
1326 };
1327
1328/*
1329 Enumeration declarations.
1330*/
1331typedef enum
1332{
1333 CopyMode,
1334 CropMode,
1335 CutMode
1336} ClipboardMode;
1337
1338typedef enum
1339{
1340 OpenCommand,
1341 NextCommand,
1342 FormerCommand,
1343 SelectCommand,
1344 SaveCommand,
1345 PrintCommand,
1346 DeleteCommand,
1347 NewCommand,
1348 VisualDirectoryCommand,
1349 QuitCommand,
1350 UndoCommand,
1351 RedoCommand,
1352 CutCommand,
1353 CopyCommand,
1354 PasteCommand,
1355 HalfSizeCommand,
1356 OriginalSizeCommand,
1357 DoubleSizeCommand,
1358 ResizeCommand,
1359 ApplyCommand,
1360 RefreshCommand,
1361 RestoreCommand,
1362 CropCommand,
1363 ChopCommand,
1364 FlopCommand,
1365 FlipCommand,
1366 RotateRightCommand,
1367 RotateLeftCommand,
1368 RotateCommand,
1369 ShearCommand,
1370 RollCommand,
1371 TrimCommand,
1372 HueCommand,
1373 SaturationCommand,
1374 BrightnessCommand,
1375 GammaCommand,
1376 SpiffCommand,
1377 DullCommand,
1378 ContrastStretchCommand,
1379 SigmoidalContrastCommand,
1380 NormalizeCommand,
1381 EqualizeCommand,
1382 NegateCommand,
1383 GrayscaleCommand,
1384 MapCommand,
1385 QuantizeCommand,
1386 DespeckleCommand,
1387 EmbossCommand,
1388 ReduceNoiseCommand,
1389 AddNoiseCommand,
1390 SharpenCommand,
1391 BlurCommand,
1392 ThresholdCommand,
1393 EdgeDetectCommand,
1394 SpreadCommand,
1395 ShadeCommand,
1396 RaiseCommand,
1397 SegmentCommand,
1398 SolarizeCommand,
1399 SepiaToneCommand,
1400 SwirlCommand,
1401 ImplodeCommand,
1402 VignetteCommand,
1403 WaveCommand,
1404 OilPaintCommand,
1405 CharcoalDrawCommand,
1406 AnnotateCommand,
1407 DrawCommand,
1408 ColorCommand,
1409 MatteCommand,
1410 CompositeCommand,
1411 AddBorderCommand,
1412 AddFrameCommand,
1413 CommentCommand,
1414 LaunchCommand,
1415 RegionofInterestCommand,
1416 ROIHelpCommand,
1417 ROIDismissCommand,
1418 InfoCommand,
1419 ZoomCommand,
1420 ShowPreviewCommand,
1421 ShowHistogramCommand,
1422 ShowMatteCommand,
1423 BackgroundCommand,
1424 SlideShowCommand,
1425 PreferencesCommand,
1426 HelpCommand,
1427 BrowseDocumentationCommand,
1428 VersionCommand,
1429 SaveToUndoBufferCommand,
1430 FreeBuffersCommand,
1431 NullCommand
1432} CommandType;
1433
1434typedef enum
1435{
1436 AnnotateNameCommand,
1437 AnnotateFontColorCommand,
1438 AnnotateBackgroundColorCommand,
1439 AnnotateRotateCommand,
1440 AnnotateHelpCommand,
1441 AnnotateDismissCommand,
1442 TextHelpCommand,
1443 TextApplyCommand,
1444 ChopDirectionCommand,
1445 ChopHelpCommand,
1446 ChopDismissCommand,
1447 HorizontalChopCommand,
1448 VerticalChopCommand,
1449 ColorEditMethodCommand,
1450 ColorEditColorCommand,
1451 ColorEditBorderCommand,
1452 ColorEditFuzzCommand,
1453 ColorEditUndoCommand,
1454 ColorEditHelpCommand,
1455 ColorEditDismissCommand,
1456 CompositeOperatorsCommand,
1457 CompositeDissolveCommand,
1458 CompositeDisplaceCommand,
1459 CompositeHelpCommand,
1460 CompositeDismissCommand,
1461 CropHelpCommand,
1462 CropDismissCommand,
1463 RectifyCopyCommand,
1464 RectifyHelpCommand,
1465 RectifyDismissCommand,
1466 DrawElementCommand,
1467 DrawColorCommand,
1468 DrawStippleCommand,
1469 DrawWidthCommand,
1470 DrawUndoCommand,
1471 DrawHelpCommand,
1472 DrawDismissCommand,
1473 MatteEditMethod,
1474 MatteEditBorderCommand,
1475 MatteEditFuzzCommand,
1476 MatteEditValueCommand,
1477 MatteEditUndoCommand,
1478 MatteEditHelpCommand,
1479 MatteEditDismissCommand,
1480 PasteOperatorsCommand,
1481 PasteHelpCommand,
1482 PasteDismissCommand,
1483 RotateColorCommand,
1484 RotateDirectionCommand,
1485 RotateCropCommand,
1486 RotateSharpenCommand,
1487 RotateHelpCommand,
1488 RotateDismissCommand,
1489 HorizontalRotateCommand,
1490 VerticalRotateCommand,
1491 TileLoadCommand,
1492 TileNextCommand,
1493 TileFormerCommand,
1494 TileDeleteCommand,
1495 TileUpdateCommand
1496} ModeType;
1497
1498/*
1499 Stipples.
1500*/
1501#define BricksWidth 20
1502#define BricksHeight 20
1503#define DiagonalWidth 16
1504#define DiagonalHeight 16
1505#define HighlightWidth 8
1506#define HighlightHeight 8
cristydd05beb2010-11-21 21:23:39 +00001507#define OpaqueWidth 8
1508#define OpaqueHeight 8
cristy3ed852e2009-09-05 21:47:34 +00001509#define ScalesWidth 16
1510#define ScalesHeight 16
1511#define ShadowWidth 8
1512#define ShadowHeight 8
1513#define VerticalWidth 16
1514#define VerticalHeight 16
1515#define WavyWidth 16
1516#define WavyHeight 16
1517
1518/*
1519 Constant declaration.
1520*/
1521static const int
1522 RoiDelta = 8;
1523
1524static const unsigned char
1525 BricksBitmap[] =
1526 {
1527 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1528 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1529 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1530 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1531 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1532 },
1533 DiagonalBitmap[] =
1534 {
1535 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1536 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1537 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1538 },
1539 ScalesBitmap[] =
1540 {
1541 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1542 0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1543 0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1544 },
1545 VerticalBitmap[] =
1546 {
1547 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1548 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1549 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1550 },
1551 WavyBitmap[] =
1552 {
1553 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1554 0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1555 0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1556 };
1557
1558/*
1559 Function prototypes.
1560*/
1561static CommandType
1562 XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1563 const MagickStatusType,KeySym,Image **);
1564
1565static Image
1566 *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1567 Image **),
1568 *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1569 *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *),
1570 *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *);
1571
1572static MagickBooleanType
1573 XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *),
1574 XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1575 XChopImage(Display *,XResourceInfo *,XWindows *,Image **),
1576 XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode),
1577 XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **),
1578 XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1579 XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *),
1580 XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *),
1581 XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1582 XPasteImage(Display *,XResourceInfo *,XWindows *,Image *),
1583 XPrintImage(Display *,XResourceInfo *,XWindows *,Image *),
1584 XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **),
1585 XROIImage(Display *,XResourceInfo *,XWindows *,Image **),
1586 XSaveImage(Display *,XResourceInfo *,XWindows *,Image *),
1587 XTrimImage(Display *,XResourceInfo *,XWindows *,Image *);
1588
1589static void
1590 XDrawPanRectangle(Display *,XWindows *),
1591 XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **),
1592 XMagnifyImage(Display *,XWindows *,XEvent *),
1593 XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *),
1594 XPanImage(Display *,XWindows *,XEvent *),
1595 XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1596 const KeySym),
1597 XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1598 XScreenEvent(Display *,XWindows *,XEvent *),
1599 XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1600
1601/*
1602%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1603% %
1604% %
1605% %
1606% D i s p l a y I m a g e s %
1607% %
1608% %
1609% %
1610%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1611%
1612% DisplayImages() displays an image sequence to any X window screen. It
1613% returns a value other than 0 if successful. Check the exception member
1614% of image to determine the reason for any failure.
1615%
1616% The format of the DisplayImages method is:
1617%
1618% MagickBooleanType DisplayImages(const ImageInfo *image_info,
1619% Image *images)
1620%
1621% A description of each parameter follows:
1622%
1623% o image_info: the image info.
1624%
1625% o image: the image.
1626%
1627*/
1628MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1629 Image *images)
1630{
1631 char
1632 *argv[1];
1633
1634 Display
1635 *display;
1636
1637 Image
1638 *image;
1639
cristybb503372010-05-27 20:51:26 +00001640 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001641 i;
1642
cristybb503372010-05-27 20:51:26 +00001643 size_t
cristy3ed852e2009-09-05 21:47:34 +00001644 state;
1645
1646 XrmDatabase
1647 resource_database;
1648
1649 XResourceInfo
1650 resource_info;
1651
1652 assert(image_info != (const ImageInfo *) NULL);
1653 assert(image_info->signature == MagickSignature);
1654 assert(images != (Image *) NULL);
1655 assert(images->signature == MagickSignature);
1656 if (images->debug != MagickFalse)
1657 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1658 display=XOpenDisplay(image_info->server_name);
1659 if (display == (Display *) NULL)
1660 {
1661 (void) ThrowMagickException(&images->exception,GetMagickModule(),
1662 XServerError,"UnableToOpenXServer","`%s'",XDisplayName(
1663 image_info->server_name));
1664 return(MagickFalse);
1665 }
1666 if (images->exception.severity != UndefinedException)
1667 CatchException(&images->exception);
1668 (void) XSetErrorHandler(XError);
1669 resource_database=XGetResourceDatabase(display,GetClientName());
1670 (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1671 XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1672 if (image_info->page != (char *) NULL)
1673 resource_info.image_geometry=AcquireString(image_info->page);
1674 resource_info.immutable=MagickTrue;
1675 argv[0]=AcquireString(GetClientName());
1676 state=DefaultState;
1677 for (i=0; (state & ExitState) == 0; i++)
1678 {
cristybb503372010-05-27 20:51:26 +00001679 if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
cristy3ed852e2009-09-05 21:47:34 +00001680 break;
1681 image=GetImageFromList(images,i % GetImageListLength(images));
1682 (void) XDisplayImage(display,&resource_info,argv,1,&image,&state);
1683 }
1684 argv[0]=DestroyString(argv[0]);
1685 (void) XCloseDisplay(display);
1686 XDestroyResourceInfo(&resource_info);
1687 if (images->exception.severity != UndefinedException)
1688 return(MagickFalse);
1689 return(MagickTrue);
1690}
1691
1692/*
1693%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1694% %
1695% %
1696% %
1697% R e m o t e D i s p l a y C o m m a n d %
1698% %
1699% %
1700% %
1701%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1702%
1703% RemoteDisplayCommand() encourages a remote display program to display the
1704% specified image filename.
1705%
1706% The format of the RemoteDisplayCommand method is:
1707%
1708% MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1709% const char *window,const char *filename,ExceptionInfo *exception)
1710%
1711% A description of each parameter follows:
1712%
1713% o image_info: the image info.
1714%
1715% o window: Specifies the name or id of an X window.
1716%
1717% o filename: the name of the image filename to display.
1718%
1719% o exception: return any errors or warnings in this structure.
1720%
1721*/
1722MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1723 const char *window,const char *filename,ExceptionInfo *exception)
1724{
1725 Display
1726 *display;
1727
1728 MagickStatusType
1729 status;
1730
1731 assert(image_info != (const ImageInfo *) NULL);
1732 assert(image_info->signature == MagickSignature);
1733 assert(filename != (char *) NULL);
1734 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1735 display=XOpenDisplay(image_info->server_name);
1736 if (display == (Display *) NULL)
1737 {
1738 (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1739 "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1740 return(MagickFalse);
1741 }
1742 (void) XSetErrorHandler(XError);
1743 status=XRemoteCommand(display,window,filename);
1744 (void) XCloseDisplay(display);
1745 return(status != 0 ? MagickTrue : MagickFalse);
1746}
1747
1748/*
1749%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1750% %
1751% %
1752% %
1753+ X A n n o t a t e E d i t I m a g e %
1754% %
1755% %
1756% %
1757%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1758%
1759% XAnnotateEditImage() annotates the image with text.
1760%
1761% The format of the XAnnotateEditImage method is:
1762%
1763% MagickBooleanType XAnnotateEditImage(Display *display,
1764% XResourceInfo *resource_info,XWindows *windows,Image *image)
1765%
1766% A description of each parameter follows:
1767%
1768% o display: Specifies a connection to an X server; returned from
1769% XOpenDisplay.
1770%
1771% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1772%
1773% o windows: Specifies a pointer to a XWindows structure.
1774%
1775% o image: the image; returned from ReadImage.
1776%
1777*/
1778
cristybb503372010-05-27 20:51:26 +00001779static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001780{
1781 if (x > y)
1782 return(x);
1783 return(y);
1784}
1785
cristybb503372010-05-27 20:51:26 +00001786static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001787{
1788 if (x < y)
1789 return(x);
1790 return(y);
1791}
1792
1793static MagickBooleanType XAnnotateEditImage(Display *display,
1794 XResourceInfo *resource_info,XWindows *windows,Image *image)
1795{
1796 static const char
1797 *AnnotateMenu[] =
1798 {
1799 "Font Name",
1800 "Font Color",
1801 "Box Color",
1802 "Rotate Text",
1803 "Help",
1804 "Dismiss",
1805 (char *) NULL
1806 },
1807 *TextMenu[] =
1808 {
1809 "Help",
1810 "Apply",
1811 (char *) NULL
1812 };
1813
1814 static const ModeType
1815 AnnotateCommands[] =
1816 {
1817 AnnotateNameCommand,
1818 AnnotateFontColorCommand,
1819 AnnotateBackgroundColorCommand,
1820 AnnotateRotateCommand,
1821 AnnotateHelpCommand,
1822 AnnotateDismissCommand
1823 },
1824 TextCommands[] =
1825 {
1826 TextHelpCommand,
1827 TextApplyCommand
1828 };
1829
1830 static MagickBooleanType
1831 transparent_box = MagickTrue,
1832 transparent_pen = MagickFalse;
1833
1834 static MagickRealType
1835 degrees = 0.0;
1836
1837 static unsigned int
1838 box_id = MaxNumberPens-2,
1839 font_id = 0,
1840 pen_id = 0;
1841
1842 char
1843 command[MaxTextExtent],
1844 text[MaxTextExtent];
1845
1846 const char
1847 *ColorMenu[MaxNumberPens+1];
1848
1849 Cursor
1850 cursor;
1851
1852 GC
1853 annotate_context;
1854
1855 int
1856 id,
1857 pen_number,
1858 status,
1859 x,
1860 y;
1861
1862 KeySym
1863 key_symbol;
1864
1865 register char
1866 *p;
1867
cristybb503372010-05-27 20:51:26 +00001868 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001869 i;
1870
1871 unsigned int
1872 height,
1873 width;
1874
cristybb503372010-05-27 20:51:26 +00001875 size_t
cristy3ed852e2009-09-05 21:47:34 +00001876 state;
1877
1878 XAnnotateInfo
1879 *annotate_info,
1880 *previous_info;
1881
1882 XColor
1883 color;
1884
1885 XFontStruct
1886 *font_info;
1887
1888 XEvent
1889 event,
1890 text_event;
1891
1892 /*
1893 Map Command widget.
1894 */
1895 (void) CloneString(&windows->command.name,"Annotate");
1896 windows->command.data=4;
1897 (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1898 (void) XMapRaised(display,windows->command.id);
1899 XClientMessage(display,windows->image.id,windows->im_protocols,
1900 windows->im_update_widget,CurrentTime);
1901 /*
1902 Track pointer until button 1 is pressed.
1903 */
1904 XQueryPosition(display,windows->image.id,&x,&y);
1905 (void) XSelectInput(display,windows->image.id,
1906 windows->image.attributes.event_mask | PointerMotionMask);
1907 cursor=XCreateFontCursor(display,XC_left_side);
1908 (void) XCheckDefineCursor(display,windows->image.id,cursor);
1909 state=DefaultState;
1910 do
1911 {
1912 if (windows->info.mapped != MagickFalse)
1913 {
1914 /*
1915 Display pointer position.
1916 */
1917 (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
1918 x+windows->image.x,y+windows->image.y);
1919 XInfoWidget(display,windows,text);
1920 }
1921 /*
1922 Wait for next event.
1923 */
1924 XScreenEvent(display,windows,&event);
1925 if (event.xany.window == windows->command.id)
1926 {
1927 /*
1928 Select a command from the Command widget.
1929 */
1930 id=XCommandWidget(display,windows,AnnotateMenu,&event);
1931 (void) XCheckDefineCursor(display,windows->image.id,cursor);
1932 if (id < 0)
1933 continue;
1934 switch (AnnotateCommands[id])
1935 {
1936 case AnnotateNameCommand:
1937 {
1938 const char
1939 *FontMenu[MaxNumberFonts];
1940
1941 int
1942 font_number;
1943
1944 /*
1945 Initialize menu selections.
1946 */
1947 for (i=0; i < MaxNumberFonts; i++)
1948 FontMenu[i]=resource_info->font_name[i];
1949 FontMenu[MaxNumberFonts-2]="Browser...";
1950 FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1951 /*
1952 Select a font name from the pop-up menu.
1953 */
1954 font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1955 (const char **) FontMenu,command);
1956 if (font_number < 0)
1957 break;
1958 if (font_number == (MaxNumberFonts-2))
1959 {
1960 static char
1961 font_name[MaxTextExtent] = "fixed";
1962
1963 /*
1964 Select a font name from a browser.
1965 */
1966 resource_info->font_name[font_number]=font_name;
1967 XFontBrowserWidget(display,windows,"Select",font_name);
1968 if (*font_name == '\0')
1969 break;
1970 }
1971 /*
1972 Initialize font info.
1973 */
1974 font_info=XLoadQueryFont(display,resource_info->font_name[
1975 font_number]);
1976 if (font_info == (XFontStruct *) NULL)
1977 {
1978 XNoticeWidget(display,windows,"Unable to load font:",
1979 resource_info->font_name[font_number]);
1980 break;
1981 }
1982 font_id=(unsigned int) font_number;
1983 (void) XFreeFont(display,font_info);
1984 break;
1985 }
1986 case AnnotateFontColorCommand:
1987 {
1988 /*
1989 Initialize menu selections.
1990 */
1991 for (i=0; i < (int) (MaxNumberPens-2); i++)
1992 ColorMenu[i]=resource_info->pen_colors[i];
1993 ColorMenu[MaxNumberPens-2]="transparent";
1994 ColorMenu[MaxNumberPens-1]="Browser...";
1995 ColorMenu[MaxNumberPens]=(const char *) NULL;
1996 /*
1997 Select a pen color from the pop-up menu.
1998 */
1999 pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2000 (const char **) ColorMenu,command);
2001 if (pen_number < 0)
2002 break;
2003 transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2004 MagickFalse;
2005 if (transparent_pen != MagickFalse)
2006 break;
2007 if (pen_number == (MaxNumberPens-1))
2008 {
2009 static char
2010 color_name[MaxTextExtent] = "gray";
2011
2012 /*
2013 Select a pen color from a dialog.
2014 */
2015 resource_info->pen_colors[pen_number]=color_name;
2016 XColorBrowserWidget(display,windows,"Select",color_name);
2017 if (*color_name == '\0')
2018 break;
2019 }
2020 /*
2021 Set pen color.
2022 */
2023 (void) XParseColor(display,windows->map_info->colormap,
2024 resource_info->pen_colors[pen_number],&color);
2025 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2026 (unsigned int) MaxColors,&color);
2027 windows->pixel_info->pen_colors[pen_number]=color;
2028 pen_id=(unsigned int) pen_number;
2029 break;
2030 }
2031 case AnnotateBackgroundColorCommand:
2032 {
2033 /*
2034 Initialize menu selections.
2035 */
2036 for (i=0; i < (int) (MaxNumberPens-2); i++)
2037 ColorMenu[i]=resource_info->pen_colors[i];
2038 ColorMenu[MaxNumberPens-2]="transparent";
2039 ColorMenu[MaxNumberPens-1]="Browser...";
2040 ColorMenu[MaxNumberPens]=(const char *) NULL;
2041 /*
2042 Select a pen color from the pop-up menu.
2043 */
2044 pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2045 (const char **) ColorMenu,command);
2046 if (pen_number < 0)
2047 break;
2048 transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2049 MagickFalse;
2050 if (transparent_box != MagickFalse)
2051 break;
2052 if (pen_number == (MaxNumberPens-1))
2053 {
2054 static char
2055 color_name[MaxTextExtent] = "gray";
2056
2057 /*
2058 Select a pen color from a dialog.
2059 */
2060 resource_info->pen_colors[pen_number]=color_name;
2061 XColorBrowserWidget(display,windows,"Select",color_name);
2062 if (*color_name == '\0')
2063 break;
2064 }
2065 /*
2066 Set pen color.
2067 */
2068 (void) XParseColor(display,windows->map_info->colormap,
2069 resource_info->pen_colors[pen_number],&color);
2070 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2071 (unsigned int) MaxColors,&color);
2072 windows->pixel_info->pen_colors[pen_number]=color;
2073 box_id=(unsigned int) pen_number;
2074 break;
2075 }
2076 case AnnotateRotateCommand:
2077 {
2078 int
2079 entry;
2080
2081 static char
2082 angle[MaxTextExtent] = "30.0";
2083
2084 static const char
2085 *RotateMenu[] =
2086 {
2087 "-90",
2088 "-45",
2089 "-30",
2090 "0",
2091 "30",
2092 "45",
2093 "90",
2094 "180",
2095 "Dialog...",
2096 (char *) NULL,
2097 };
2098
2099 /*
2100 Select a command from the pop-up menu.
2101 */
2102 entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2103 command);
2104 if (entry < 0)
2105 break;
2106 if (entry != 8)
2107 {
cristyf2f27272009-12-17 14:48:46 +00002108 degrees=StringToDouble(RotateMenu[entry]);
cristy3ed852e2009-09-05 21:47:34 +00002109 break;
2110 }
2111 (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2112 angle);
2113 if (*angle == '\0')
2114 break;
cristyf2f27272009-12-17 14:48:46 +00002115 degrees=StringToDouble(angle);
cristy3ed852e2009-09-05 21:47:34 +00002116 break;
2117 }
2118 case AnnotateHelpCommand:
2119 {
2120 XTextViewWidget(display,resource_info,windows,MagickFalse,
2121 "Help Viewer - Image Annotation",ImageAnnotateHelp);
2122 break;
2123 }
2124 case AnnotateDismissCommand:
2125 {
2126 /*
2127 Prematurely exit.
2128 */
2129 state|=EscapeState;
2130 state|=ExitState;
2131 break;
2132 }
2133 default:
2134 break;
2135 }
2136 continue;
2137 }
2138 switch (event.type)
2139 {
2140 case ButtonPress:
2141 {
2142 if (event.xbutton.button != Button1)
2143 break;
2144 if (event.xbutton.window != windows->image.id)
2145 break;
2146 /*
2147 Change to text entering mode.
2148 */
2149 x=event.xbutton.x;
2150 y=event.xbutton.y;
2151 state|=ExitState;
2152 break;
2153 }
2154 case ButtonRelease:
2155 break;
2156 case Expose:
2157 break;
2158 case KeyPress:
2159 {
2160 if (event.xkey.window != windows->image.id)
2161 break;
2162 /*
2163 Respond to a user key press.
2164 */
2165 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2166 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2167 switch ((int) key_symbol)
2168 {
2169 case XK_Escape:
2170 case XK_F20:
2171 {
2172 /*
2173 Prematurely exit.
2174 */
2175 state|=EscapeState;
2176 state|=ExitState;
2177 break;
2178 }
2179 case XK_F1:
2180 case XK_Help:
2181 {
2182 XTextViewWidget(display,resource_info,windows,MagickFalse,
2183 "Help Viewer - Image Annotation",ImageAnnotateHelp);
2184 break;
2185 }
2186 default:
2187 {
2188 (void) XBell(display,0);
2189 break;
2190 }
2191 }
2192 break;
2193 }
2194 case MotionNotify:
2195 {
2196 /*
2197 Map and unmap Info widget as cursor crosses its boundaries.
2198 */
2199 x=event.xmotion.x;
2200 y=event.xmotion.y;
2201 if (windows->info.mapped != MagickFalse)
2202 {
2203 if ((x < (int) (windows->info.x+windows->info.width)) &&
2204 (y < (int) (windows->info.y+windows->info.height)))
2205 (void) XWithdrawWindow(display,windows->info.id,
2206 windows->info.screen);
2207 }
2208 else
2209 if ((x > (int) (windows->info.x+windows->info.width)) ||
2210 (y > (int) (windows->info.y+windows->info.height)))
2211 (void) XMapWindow(display,windows->info.id);
2212 break;
2213 }
2214 default:
2215 break;
2216 }
2217 } while ((state & ExitState) == 0);
2218 (void) XSelectInput(display,windows->image.id,
2219 windows->image.attributes.event_mask);
2220 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2221 if ((state & EscapeState) != 0)
2222 return(MagickTrue);
2223 /*
2224 Set font info and check boundary conditions.
2225 */
2226 font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2227 if (font_info == (XFontStruct *) NULL)
2228 {
2229 XNoticeWidget(display,windows,"Unable to load font:",
2230 resource_info->font_name[font_id]);
2231 font_info=windows->font_info;
2232 }
2233 if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2234 x=(int) windows->image.width-font_info->max_bounds.width;
2235 if (y < (int) (font_info->ascent+font_info->descent))
2236 y=(int) font_info->ascent+font_info->descent;
2237 if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2238 ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2239 return(MagickFalse);
2240 /*
2241 Initialize annotate structure.
2242 */
cristy73bd4a52010-10-05 11:24:23 +00002243 annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
cristy3ed852e2009-09-05 21:47:34 +00002244 if (annotate_info == (XAnnotateInfo *) NULL)
2245 return(MagickFalse);
2246 XGetAnnotateInfo(annotate_info);
2247 annotate_info->x=x;
2248 annotate_info->y=y;
2249 if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2250 annotate_info->stencil=OpaqueStencil;
2251 else
2252 if (transparent_box == MagickFalse)
2253 annotate_info->stencil=BackgroundStencil;
2254 else
2255 annotate_info->stencil=ForegroundStencil;
2256 annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2257 annotate_info->degrees=degrees;
2258 annotate_info->font_info=font_info;
2259 annotate_info->text=(char *) AcquireQuantumMemory((size_t)
cristy49e2d862010-11-12 02:50:30 +00002260 windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
cristy3ed852e2009-09-05 21:47:34 +00002261 sizeof(*annotate_info->text));
2262 if (annotate_info->text == (char *) NULL)
2263 return(MagickFalse);
2264 /*
2265 Create cursor and set graphic context.
2266 */
2267 cursor=XCreateFontCursor(display,XC_pencil);
2268 (void) XCheckDefineCursor(display,windows->image.id,cursor);
2269 annotate_context=windows->image.annotate_context;
2270 (void) XSetFont(display,annotate_context,font_info->fid);
2271 (void) XSetBackground(display,annotate_context,
2272 windows->pixel_info->pen_colors[box_id].pixel);
2273 (void) XSetForeground(display,annotate_context,
2274 windows->pixel_info->pen_colors[pen_id].pixel);
2275 /*
2276 Begin annotating the image with text.
2277 */
2278 (void) CloneString(&windows->command.name,"Text");
2279 windows->command.data=0;
2280 (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2281 state=DefaultState;
2282 (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2283 text_event.xexpose.width=(int) font_info->max_bounds.width;
2284 text_event.xexpose.height=font_info->max_bounds.ascent+
2285 font_info->max_bounds.descent;
2286 p=annotate_info->text;
2287 do
2288 {
2289 /*
2290 Display text cursor.
2291 */
2292 *p='\0';
2293 (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2294 /*
2295 Wait for next event.
2296 */
2297 XScreenEvent(display,windows,&event);
2298 if (event.xany.window == windows->command.id)
2299 {
2300 /*
2301 Select a command from the Command widget.
2302 */
2303 (void) XSetBackground(display,annotate_context,
2304 windows->pixel_info->background_color.pixel);
2305 (void) XSetForeground(display,annotate_context,
2306 windows->pixel_info->foreground_color.pixel);
2307 id=XCommandWidget(display,windows,AnnotateMenu,&event);
2308 (void) XSetBackground(display,annotate_context,
2309 windows->pixel_info->pen_colors[box_id].pixel);
2310 (void) XSetForeground(display,annotate_context,
2311 windows->pixel_info->pen_colors[pen_id].pixel);
2312 if (id < 0)
2313 continue;
2314 switch (TextCommands[id])
2315 {
2316 case TextHelpCommand:
2317 {
2318 XTextViewWidget(display,resource_info,windows,MagickFalse,
2319 "Help Viewer - Image Annotation",ImageAnnotateHelp);
2320 (void) XCheckDefineCursor(display,windows->image.id,cursor);
2321 break;
2322 }
2323 case TextApplyCommand:
2324 {
2325 /*
2326 Finished annotating.
2327 */
2328 annotate_info->width=(unsigned int) XTextWidth(font_info,
2329 annotate_info->text,(int) strlen(annotate_info->text));
2330 XRefreshWindow(display,&windows->image,&text_event);
2331 state|=ExitState;
2332 break;
2333 }
2334 default:
2335 break;
2336 }
2337 continue;
2338 }
2339 /*
2340 Erase text cursor.
2341 */
2342 text_event.xexpose.x=x;
2343 text_event.xexpose.y=y-font_info->max_bounds.ascent;
2344 (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2345 (unsigned int) text_event.xexpose.width,(unsigned int)
2346 text_event.xexpose.height,MagickFalse);
2347 XRefreshWindow(display,&windows->image,&text_event);
2348 switch (event.type)
2349 {
2350 case ButtonPress:
2351 {
2352 if (event.xbutton.window != windows->image.id)
2353 break;
2354 if (event.xbutton.button == Button2)
2355 {
2356 /*
2357 Request primary selection.
2358 */
2359 (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2360 windows->image.id,CurrentTime);
2361 break;
2362 }
2363 break;
2364 }
2365 case Expose:
2366 {
2367 if (event.xexpose.count == 0)
2368 {
2369 XAnnotateInfo
2370 *text_info;
2371
2372 /*
2373 Refresh Image window.
2374 */
2375 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2376 text_info=annotate_info;
2377 while (text_info != (XAnnotateInfo *) NULL)
2378 {
2379 if (annotate_info->stencil == ForegroundStencil)
2380 (void) XDrawString(display,windows->image.id,annotate_context,
2381 text_info->x,text_info->y,text_info->text,
2382 (int) strlen(text_info->text));
2383 else
2384 (void) XDrawImageString(display,windows->image.id,
2385 annotate_context,text_info->x,text_info->y,text_info->text,
2386 (int) strlen(text_info->text));
2387 text_info=text_info->previous;
2388 }
2389 (void) XDrawString(display,windows->image.id,annotate_context,
2390 x,y,"_",1);
2391 }
2392 break;
2393 }
2394 case KeyPress:
2395 {
2396 int
2397 length;
2398
2399 if (event.xkey.window != windows->image.id)
2400 break;
2401 /*
2402 Respond to a user key press.
2403 */
2404 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2405 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2406 *(command+length)='\0';
2407 if (((event.xkey.state & ControlMask) != 0) ||
2408 ((event.xkey.state & Mod1Mask) != 0))
2409 state|=ModifierState;
2410 if ((state & ModifierState) != 0)
2411 switch ((int) key_symbol)
2412 {
2413 case XK_u:
2414 case XK_U:
2415 {
2416 key_symbol=DeleteCommand;
2417 break;
2418 }
2419 default:
2420 break;
2421 }
2422 switch ((int) key_symbol)
2423 {
2424 case XK_BackSpace:
2425 {
2426 /*
2427 Erase one character.
2428 */
2429 if (p == annotate_info->text)
2430 {
2431 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2432 break;
2433 else
2434 {
2435 /*
2436 Go to end of the previous line of text.
2437 */
2438 annotate_info=annotate_info->previous;
2439 p=annotate_info->text;
2440 x=annotate_info->x+annotate_info->width;
2441 y=annotate_info->y;
2442 if (annotate_info->width != 0)
2443 p+=strlen(annotate_info->text);
2444 break;
2445 }
2446 }
2447 p--;
2448 x-=XTextWidth(font_info,p,1);
2449 text_event.xexpose.x=x;
2450 text_event.xexpose.y=y-font_info->max_bounds.ascent;
2451 XRefreshWindow(display,&windows->image,&text_event);
2452 break;
2453 }
2454 case XK_bracketleft:
2455 {
2456 key_symbol=XK_Escape;
2457 break;
2458 }
2459 case DeleteCommand:
2460 {
2461 /*
2462 Erase the entire line of text.
2463 */
2464 while (p != annotate_info->text)
2465 {
2466 p--;
2467 x-=XTextWidth(font_info,p,1);
2468 text_event.xexpose.x=x;
2469 XRefreshWindow(display,&windows->image,&text_event);
2470 }
2471 break;
2472 }
2473 case XK_Escape:
2474 case XK_F20:
2475 {
2476 /*
2477 Finished annotating.
2478 */
2479 annotate_info->width=(unsigned int) XTextWidth(font_info,
2480 annotate_info->text,(int) strlen(annotate_info->text));
2481 XRefreshWindow(display,&windows->image,&text_event);
2482 state|=ExitState;
2483 break;
2484 }
2485 default:
2486 {
2487 /*
2488 Draw a single character on the Image window.
2489 */
2490 if ((state & ModifierState) != 0)
2491 break;
2492 if (*command == '\0')
2493 break;
2494 *p=(*command);
2495 if (annotate_info->stencil == ForegroundStencil)
2496 (void) XDrawString(display,windows->image.id,annotate_context,
2497 x,y,p,1);
2498 else
2499 (void) XDrawImageString(display,windows->image.id,
2500 annotate_context,x,y,p,1);
2501 x+=XTextWidth(font_info,p,1);
2502 p++;
2503 if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2504 break;
2505 }
2506 case XK_Return:
2507 case XK_KP_Enter:
2508 {
2509 /*
2510 Advance to the next line of text.
2511 */
2512 *p='\0';
2513 annotate_info->width=(unsigned int) XTextWidth(font_info,
2514 annotate_info->text,(int) strlen(annotate_info->text));
2515 if (annotate_info->next != (XAnnotateInfo *) NULL)
2516 {
2517 /*
2518 Line of text already exists.
2519 */
2520 annotate_info=annotate_info->next;
2521 x=annotate_info->x;
2522 y=annotate_info->y;
2523 p=annotate_info->text;
2524 break;
2525 }
2526 annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2527 sizeof(*annotate_info->next));
2528 if (annotate_info->next == (XAnnotateInfo *) NULL)
2529 return(MagickFalse);
2530 *annotate_info->next=(*annotate_info);
2531 annotate_info->next->previous=annotate_info;
2532 annotate_info=annotate_info->next;
2533 annotate_info->text=(char *) AcquireQuantumMemory((size_t)
cristy49e2d862010-11-12 02:50:30 +00002534 windows->image.width/MagickMax((ssize_t)
2535 font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
cristy3ed852e2009-09-05 21:47:34 +00002536 if (annotate_info->text == (char *) NULL)
2537 return(MagickFalse);
2538 annotate_info->y+=annotate_info->height;
2539 if (annotate_info->y > (int) windows->image.height)
2540 annotate_info->y=(int) annotate_info->height;
2541 annotate_info->next=(XAnnotateInfo *) NULL;
2542 x=annotate_info->x;
2543 y=annotate_info->y;
2544 p=annotate_info->text;
2545 break;
2546 }
2547 }
2548 break;
2549 }
2550 case KeyRelease:
2551 {
2552 /*
2553 Respond to a user key release.
2554 */
2555 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2556 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2557 state&=(~ModifierState);
2558 break;
2559 }
2560 case SelectionNotify:
2561 {
2562 Atom
2563 type;
2564
2565 int
2566 format;
2567
2568 unsigned char
2569 *data;
2570
cristyf2faecf2010-05-28 19:19:36 +00002571 unsigned long
cristy3ed852e2009-09-05 21:47:34 +00002572 after,
2573 length;
2574
2575 /*
2576 Obtain response from primary selection.
2577 */
2578 if (event.xselection.property == (Atom) None)
2579 break;
2580 status=XGetWindowProperty(display,event.xselection.requestor,
cristyecd0ab52010-05-30 14:59:20 +00002581 event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
cristy3ed852e2009-09-05 21:47:34 +00002582 &type,&format,&length,&after,&data);
2583 if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2584 (length == 0))
2585 break;
2586 /*
2587 Annotate Image window with primary selection.
2588 */
cristybb503372010-05-27 20:51:26 +00002589 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00002590 {
2591 if ((char) data[i] != '\n')
2592 {
2593 /*
2594 Draw a single character on the Image window.
2595 */
2596 *p=(char) data[i];
2597 (void) XDrawString(display,windows->image.id,annotate_context,
2598 x,y,p,1);
2599 x+=XTextWidth(font_info,p,1);
2600 p++;
2601 if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2602 continue;
2603 }
2604 /*
2605 Advance to the next line of text.
2606 */
2607 *p='\0';
2608 annotate_info->width=(unsigned int) XTextWidth(font_info,
2609 annotate_info->text,(int) strlen(annotate_info->text));
2610 if (annotate_info->next != (XAnnotateInfo *) NULL)
2611 {
2612 /*
2613 Line of text already exists.
2614 */
2615 annotate_info=annotate_info->next;
2616 x=annotate_info->x;
2617 y=annotate_info->y;
2618 p=annotate_info->text;
2619 continue;
2620 }
2621 annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2622 sizeof(*annotate_info->next));
2623 if (annotate_info->next == (XAnnotateInfo *) NULL)
2624 return(MagickFalse);
2625 *annotate_info->next=(*annotate_info);
2626 annotate_info->next->previous=annotate_info;
2627 annotate_info=annotate_info->next;
2628 annotate_info->text=(char *) AcquireQuantumMemory((size_t)
cristy49e2d862010-11-12 02:50:30 +00002629 windows->image.width/MagickMax((ssize_t)
2630 font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
cristy3ed852e2009-09-05 21:47:34 +00002631 if (annotate_info->text == (char *) NULL)
2632 return(MagickFalse);
2633 annotate_info->y+=annotate_info->height;
2634 if (annotate_info->y > (int) windows->image.height)
2635 annotate_info->y=(int) annotate_info->height;
2636 annotate_info->next=(XAnnotateInfo *) NULL;
2637 x=annotate_info->x;
2638 y=annotate_info->y;
2639 p=annotate_info->text;
2640 }
2641 (void) XFree((void *) data);
2642 break;
2643 }
2644 default:
2645 break;
2646 }
2647 } while ((state & ExitState) == 0);
2648 (void) XFreeCursor(display,cursor);
2649 /*
2650 Annotation is relative to image configuration.
2651 */
2652 width=(unsigned int) image->columns;
2653 height=(unsigned int) image->rows;
2654 x=0;
2655 y=0;
2656 if (windows->image.crop_geometry != (char *) NULL)
2657 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2658 /*
2659 Initialize annotated image.
2660 */
2661 XSetCursorState(display,windows,MagickTrue);
2662 XCheckRefreshWindows(display,windows);
2663 while (annotate_info != (XAnnotateInfo *) NULL)
2664 {
2665 if (annotate_info->width == 0)
2666 {
2667 /*
2668 No text on this line-- go to the next line of text.
2669 */
2670 previous_info=annotate_info->previous;
2671 annotate_info->text=(char *)
2672 RelinquishMagickMemory(annotate_info->text);
2673 annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2674 annotate_info=previous_info;
2675 continue;
2676 }
2677 /*
2678 Determine pixel index for box and pen color.
2679 */
2680 windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2681 if (windows->pixel_info->colors != 0)
cristybb503372010-05-27 20:51:26 +00002682 for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002683 if (windows->pixel_info->pixels[i] ==
2684 windows->pixel_info->pen_colors[box_id].pixel)
2685 {
2686 windows->pixel_info->box_index=(unsigned short) i;
2687 break;
2688 }
2689 windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2690 if (windows->pixel_info->colors != 0)
cristybb503372010-05-27 20:51:26 +00002691 for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002692 if (windows->pixel_info->pixels[i] ==
2693 windows->pixel_info->pen_colors[pen_id].pixel)
2694 {
2695 windows->pixel_info->pen_index=(unsigned short) i;
2696 break;
2697 }
2698 /*
2699 Define the annotate geometry string.
2700 */
2701 annotate_info->x=(int)
2702 width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2703 annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2704 windows->image.y)/windows->image.ximage->height;
2705 (void) FormatMagickString(annotate_info->geometry,MaxTextExtent,
2706 "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2707 height*annotate_info->height/windows->image.ximage->height,
2708 annotate_info->x+x,annotate_info->y+y);
2709 /*
2710 Annotate image with text.
2711 */
2712 status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
2713 if (status == 0)
2714 return(MagickFalse);
2715 /*
2716 Free up memory.
2717 */
2718 previous_info=annotate_info->previous;
2719 annotate_info->text=DestroyString(annotate_info->text);
2720 annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2721 annotate_info=previous_info;
2722 }
2723 (void) XSetForeground(display,annotate_context,
2724 windows->pixel_info->foreground_color.pixel);
2725 (void) XSetBackground(display,annotate_context,
2726 windows->pixel_info->background_color.pixel);
2727 (void) XSetFont(display,annotate_context,windows->font_info->fid);
2728 XSetCursorState(display,windows,MagickFalse);
2729 (void) XFreeFont(display,font_info);
2730 /*
2731 Update image configuration.
2732 */
2733 XConfigureImageColormap(display,resource_info,windows,image);
2734 (void) XConfigureImage(display,resource_info,windows,image);
2735 return(MagickTrue);
2736}
2737
2738/*
2739%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2740% %
2741% %
2742% %
2743+ X B a c k g r o u n d I m a g e %
2744% %
2745% %
2746% %
2747%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2748%
2749% XBackgroundImage() displays the image in the background of a window.
2750%
2751% The format of the XBackgroundImage method is:
2752%
2753% MagickBooleanType XBackgroundImage(Display *display,
2754% XResourceInfo *resource_info,XWindows *windows,Image **image)
2755%
2756% A description of each parameter follows:
2757%
2758% o display: Specifies a connection to an X server; returned from
2759% XOpenDisplay.
2760%
2761% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2762%
2763% o windows: Specifies a pointer to a XWindows structure.
2764%
2765% o image: the image.
2766%
2767*/
2768static MagickBooleanType XBackgroundImage(Display *display,
2769 XResourceInfo *resource_info,XWindows *windows,Image **image)
2770{
2771#define BackgroundImageTag "Background/Image"
2772
2773 int
2774 status;
2775
2776 static char
2777 window_id[MaxTextExtent] = "root";
2778
2779 XResourceInfo
2780 background_resources;
2781
2782 /*
2783 Put image in background.
2784 */
2785 status=XDialogWidget(display,windows,"Background",
2786 "Enter window id (id 0x00 selects window with pointer):",window_id);
2787 if (*window_id == '\0')
2788 return(MagickFalse);
2789 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
2790 XInfoWidget(display,windows,BackgroundImageTag);
2791 XSetCursorState(display,windows,MagickTrue);
2792 XCheckRefreshWindows(display,windows);
2793 background_resources=(*resource_info);
2794 background_resources.window_id=window_id;
2795 background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2796 status=XDisplayBackgroundImage(display,&background_resources,*image);
2797 if (status != MagickFalse)
2798 XClientMessage(display,windows->image.id,windows->im_protocols,
2799 windows->im_retain_colors,CurrentTime);
2800 XSetCursorState(display,windows,MagickFalse);
2801 (void) XMagickCommand(display,resource_info,windows,UndoCommand,image);
2802 return(MagickTrue);
2803}
2804
2805/*
2806%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2807% %
2808% %
2809% %
2810+ X C h o p I m a g e %
2811% %
2812% %
2813% %
2814%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2815%
2816% XChopImage() chops the X image.
2817%
2818% The format of the XChopImage method is:
2819%
2820% MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2821% XWindows *windows,Image **image)
2822%
2823% A description of each parameter follows:
2824%
2825% o display: Specifies a connection to an X server; returned from
2826% XOpenDisplay.
2827%
2828% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2829%
2830% o windows: Specifies a pointer to a XWindows structure.
2831%
2832% o image: the image.
2833%
2834*/
2835static MagickBooleanType XChopImage(Display *display,
2836 XResourceInfo *resource_info,XWindows *windows,Image **image)
2837{
2838 static const char
2839 *ChopMenu[] =
2840 {
2841 "Direction",
2842 "Help",
2843 "Dismiss",
2844 (char *) NULL
2845 };
2846
2847 static ModeType
2848 direction = HorizontalChopCommand;
2849
2850 static const ModeType
2851 ChopCommands[] =
2852 {
2853 ChopDirectionCommand,
2854 ChopHelpCommand,
2855 ChopDismissCommand
2856 },
2857 DirectionCommands[] =
2858 {
2859 HorizontalChopCommand,
2860 VerticalChopCommand
2861 };
2862
2863 char
2864 text[MaxTextExtent];
2865
2866 Image
2867 *chop_image;
2868
2869 int
2870 id,
2871 x,
2872 y;
2873
2874 MagickRealType
2875 scale_factor;
2876
2877 RectangleInfo
2878 chop_info;
2879
2880 unsigned int
2881 distance,
2882 height,
2883 width;
2884
cristybb503372010-05-27 20:51:26 +00002885 size_t
cristy3ed852e2009-09-05 21:47:34 +00002886 state;
2887
2888 XEvent
2889 event;
2890
2891 XSegment
2892 segment_info;
2893
2894 /*
2895 Map Command widget.
2896 */
2897 (void) CloneString(&windows->command.name,"Chop");
2898 windows->command.data=1;
2899 (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2900 (void) XMapRaised(display,windows->command.id);
2901 XClientMessage(display,windows->image.id,windows->im_protocols,
2902 windows->im_update_widget,CurrentTime);
2903 /*
2904 Track pointer until button 1 is pressed.
2905 */
2906 XQueryPosition(display,windows->image.id,&x,&y);
2907 (void) XSelectInput(display,windows->image.id,
2908 windows->image.attributes.event_mask | PointerMotionMask);
2909 state=DefaultState;
2910 do
2911 {
2912 if (windows->info.mapped != MagickFalse)
2913 {
2914 /*
2915 Display pointer position.
2916 */
2917 (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
2918 x+windows->image.x,y+windows->image.y);
2919 XInfoWidget(display,windows,text);
2920 }
2921 /*
2922 Wait for next event.
2923 */
2924 XScreenEvent(display,windows,&event);
2925 if (event.xany.window == windows->command.id)
2926 {
2927 /*
2928 Select a command from the Command widget.
2929 */
2930 id=XCommandWidget(display,windows,ChopMenu,&event);
2931 if (id < 0)
2932 continue;
2933 switch (ChopCommands[id])
2934 {
2935 case ChopDirectionCommand:
2936 {
2937 char
2938 command[MaxTextExtent];
2939
2940 static const char
2941 *Directions[] =
2942 {
2943 "horizontal",
2944 "vertical",
2945 (char *) NULL,
2946 };
2947
2948 /*
2949 Select a command from the pop-up menu.
2950 */
2951 id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2952 if (id >= 0)
2953 direction=DirectionCommands[id];
2954 break;
2955 }
2956 case ChopHelpCommand:
2957 {
2958 XTextViewWidget(display,resource_info,windows,MagickFalse,
2959 "Help Viewer - Image Chop",ImageChopHelp);
2960 break;
2961 }
2962 case ChopDismissCommand:
2963 {
2964 /*
2965 Prematurely exit.
2966 */
2967 state|=EscapeState;
2968 state|=ExitState;
2969 break;
2970 }
2971 default:
2972 break;
2973 }
2974 continue;
2975 }
2976 switch (event.type)
2977 {
2978 case ButtonPress:
2979 {
2980 if (event.xbutton.button != Button1)
2981 break;
2982 if (event.xbutton.window != windows->image.id)
2983 break;
2984 /*
2985 User has committed to start point of chopping line.
2986 */
2987 segment_info.x1=(short int) event.xbutton.x;
2988 segment_info.x2=(short int) event.xbutton.x;
2989 segment_info.y1=(short int) event.xbutton.y;
2990 segment_info.y2=(short int) event.xbutton.y;
2991 state|=ExitState;
2992 break;
2993 }
2994 case ButtonRelease:
2995 break;
2996 case Expose:
2997 break;
2998 case KeyPress:
2999 {
3000 char
3001 command[MaxTextExtent];
3002
3003 KeySym
3004 key_symbol;
3005
3006 if (event.xkey.window != windows->image.id)
3007 break;
3008 /*
3009 Respond to a user key press.
3010 */
3011 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3012 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3013 switch ((int) key_symbol)
3014 {
3015 case XK_Escape:
3016 case XK_F20:
3017 {
3018 /*
3019 Prematurely exit.
3020 */
3021 state|=EscapeState;
3022 state|=ExitState;
3023 break;
3024 }
3025 case XK_F1:
3026 case XK_Help:
3027 {
3028 (void) XSetFunction(display,windows->image.highlight_context,
3029 GXcopy);
3030 XTextViewWidget(display,resource_info,windows,MagickFalse,
3031 "Help Viewer - Image Chop",ImageChopHelp);
3032 (void) XSetFunction(display,windows->image.highlight_context,
3033 GXinvert);
3034 break;
3035 }
3036 default:
3037 {
3038 (void) XBell(display,0);
3039 break;
3040 }
3041 }
3042 break;
3043 }
3044 case MotionNotify:
3045 {
3046 /*
3047 Map and unmap Info widget as text cursor crosses its boundaries.
3048 */
3049 x=event.xmotion.x;
3050 y=event.xmotion.y;
3051 if (windows->info.mapped != MagickFalse)
3052 {
3053 if ((x < (int) (windows->info.x+windows->info.width)) &&
3054 (y < (int) (windows->info.y+windows->info.height)))
3055 (void) XWithdrawWindow(display,windows->info.id,
3056 windows->info.screen);
3057 }
3058 else
3059 if ((x > (int) (windows->info.x+windows->info.width)) ||
3060 (y > (int) (windows->info.y+windows->info.height)))
3061 (void) XMapWindow(display,windows->info.id);
3062 }
3063 }
3064 } while ((state & ExitState) == 0);
3065 (void) XSelectInput(display,windows->image.id,
3066 windows->image.attributes.event_mask);
3067 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3068 if ((state & EscapeState) != 0)
3069 return(MagickTrue);
3070 /*
3071 Draw line as pointer moves until the mouse button is released.
3072 */
3073 chop_info.width=0;
3074 chop_info.height=0;
3075 chop_info.x=0;
3076 chop_info.y=0;
3077 distance=0;
3078 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3079 state=DefaultState;
3080 do
3081 {
3082 if (distance > 9)
3083 {
3084 /*
3085 Display info and draw chopping line.
3086 */
3087 if (windows->info.mapped == MagickFalse)
3088 (void) XMapWindow(display,windows->info.id);
cristye8c25f92010-06-03 00:53:06 +00003089 (void) FormatMagickString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00003090 " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +00003091 chop_info.height,(double) chop_info.x,(double) chop_info.y);
cristy3ed852e2009-09-05 21:47:34 +00003092 XInfoWidget(display,windows,text);
3093 XHighlightLine(display,windows->image.id,
3094 windows->image.highlight_context,&segment_info);
3095 }
3096 else
3097 if (windows->info.mapped != MagickFalse)
3098 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3099 /*
3100 Wait for next event.
3101 */
3102 XScreenEvent(display,windows,&event);
3103 if (distance > 9)
3104 XHighlightLine(display,windows->image.id,
3105 windows->image.highlight_context,&segment_info);
3106 switch (event.type)
3107 {
3108 case ButtonPress:
3109 {
3110 segment_info.x2=(short int) event.xmotion.x;
3111 segment_info.y2=(short int) event.xmotion.y;
3112 break;
3113 }
3114 case ButtonRelease:
3115 {
3116 /*
3117 User has committed to chopping line.
3118 */
3119 segment_info.x2=(short int) event.xbutton.x;
3120 segment_info.y2=(short int) event.xbutton.y;
3121 state|=ExitState;
3122 break;
3123 }
3124 case Expose:
3125 break;
3126 case MotionNotify:
3127 {
3128 segment_info.x2=(short int) event.xmotion.x;
3129 segment_info.y2=(short int) event.xmotion.y;
3130 }
3131 default:
3132 break;
3133 }
3134 /*
3135 Check boundary conditions.
3136 */
3137 if (segment_info.x2 < 0)
3138 segment_info.x2=0;
3139 else
3140 if (segment_info.x2 > windows->image.ximage->width)
3141 segment_info.x2=windows->image.ximage->width;
3142 if (segment_info.y2 < 0)
3143 segment_info.y2=0;
3144 else
3145 if (segment_info.y2 > windows->image.ximage->height)
3146 segment_info.y2=windows->image.ximage->height;
3147 distance=(unsigned int)
3148 (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3149 ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3150 /*
3151 Compute chopping geometry.
3152 */
3153 if (direction == HorizontalChopCommand)
3154 {
cristybb503372010-05-27 20:51:26 +00003155 chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
cristy49e2d862010-11-12 02:50:30 +00003156 chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
cristy3ed852e2009-09-05 21:47:34 +00003157 chop_info.height=0;
3158 chop_info.y=0;
3159 if (segment_info.x1 > (int) segment_info.x2)
3160 {
cristybb503372010-05-27 20:51:26 +00003161 chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
cristy49e2d862010-11-12 02:50:30 +00003162 chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
cristy3ed852e2009-09-05 21:47:34 +00003163 }
3164 }
3165 else
3166 {
3167 chop_info.width=0;
cristybb503372010-05-27 20:51:26 +00003168 chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
cristy3ed852e2009-09-05 21:47:34 +00003169 chop_info.x=0;
cristy49e2d862010-11-12 02:50:30 +00003170 chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
cristy3ed852e2009-09-05 21:47:34 +00003171 if (segment_info.y1 > segment_info.y2)
3172 {
cristy49e2d862010-11-12 02:50:30 +00003173 chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3174 chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
cristy3ed852e2009-09-05 21:47:34 +00003175 }
3176 }
3177 } while ((state & ExitState) == 0);
3178 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3179 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3180 if (distance <= 9)
3181 return(MagickTrue);
3182 /*
3183 Image chopping is relative to image configuration.
3184 */
3185 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
3186 XSetCursorState(display,windows,MagickTrue);
3187 XCheckRefreshWindows(display,windows);
3188 windows->image.window_changes.width=windows->image.ximage->width-
3189 (unsigned int) chop_info.width;
3190 windows->image.window_changes.height=windows->image.ximage->height-
3191 (unsigned int) chop_info.height;
3192 width=(unsigned int) (*image)->columns;
3193 height=(unsigned int) (*image)->rows;
3194 x=0;
3195 y=0;
3196 if (windows->image.crop_geometry != (char *) NULL)
3197 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3198 scale_factor=(MagickRealType) width/windows->image.ximage->width;
3199 chop_info.x+=x;
cristy49e2d862010-11-12 02:50:30 +00003200 chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003201 chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3202 scale_factor=(MagickRealType) height/windows->image.ximage->height;
3203 chop_info.y+=y;
cristy49e2d862010-11-12 02:50:30 +00003204 chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003205 chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3206 /*
3207 Chop image.
3208 */
3209 chop_image=ChopImage(*image,&chop_info,&(*image)->exception);
3210 XSetCursorState(display,windows,MagickFalse);
3211 if (chop_image == (Image *) NULL)
3212 return(MagickFalse);
3213 *image=DestroyImage(*image);
3214 *image=chop_image;
3215 /*
3216 Update image configuration.
3217 */
3218 XConfigureImageColormap(display,resource_info,windows,*image);
3219 (void) XConfigureImage(display,resource_info,windows,*image);
3220 return(MagickTrue);
3221}
3222
3223/*
3224%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3225% %
3226% %
3227% %
3228+ X C o l o r E d i t I m a g e %
3229% %
3230% %
3231% %
3232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3233%
3234% XColorEditImage() allows the user to interactively change the color of one
3235% pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3236%
3237% The format of the XColorEditImage method is:
3238%
3239% MagickBooleanType XColorEditImage(Display *display,
3240% XResourceInfo *resource_info,XWindows *windows,Image **image)
3241%
3242% A description of each parameter follows:
3243%
3244% o display: Specifies a connection to an X server; returned from
3245% XOpenDisplay.
3246%
3247% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3248%
3249% o windows: Specifies a pointer to a XWindows structure.
3250%
3251% o image: the image; returned from ReadImage.
3252%
3253*/
3254
3255
3256static MagickBooleanType XColorEditImage(Display *display,
3257 XResourceInfo *resource_info,XWindows *windows,Image **image)
3258{
3259 static const char
3260 *ColorEditMenu[] =
3261 {
3262 "Method",
3263 "Pixel Color",
3264 "Border Color",
3265 "Fuzz",
3266 "Undo",
3267 "Help",
3268 "Dismiss",
3269 (char *) NULL
3270 };
3271
3272 static const ModeType
3273 ColorEditCommands[] =
3274 {
3275 ColorEditMethodCommand,
3276 ColorEditColorCommand,
3277 ColorEditBorderCommand,
3278 ColorEditFuzzCommand,
3279 ColorEditUndoCommand,
3280 ColorEditHelpCommand,
3281 ColorEditDismissCommand
3282 };
3283
3284 static PaintMethod
3285 method = PointMethod;
3286
3287 static unsigned int
3288 pen_id = 0;
3289
3290 static XColor
3291 border_color = { 0, 0, 0, 0, 0, 0 };
3292
3293 char
3294 command[MaxTextExtent],
3295 text[MaxTextExtent];
3296
3297 Cursor
3298 cursor;
3299
3300 ExceptionInfo
3301 *exception;
3302
3303 int
3304 entry,
3305 id,
3306 x,
3307 x_offset,
3308 y,
3309 y_offset;
3310
3311 register PixelPacket
3312 *q;
3313
cristybb503372010-05-27 20:51:26 +00003314 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003315 i;
3316
3317 unsigned int
3318 height,
3319 width;
3320
cristybb503372010-05-27 20:51:26 +00003321 size_t
cristy3ed852e2009-09-05 21:47:34 +00003322 state;
3323
3324 XColor
3325 color;
3326
3327 XEvent
3328 event;
3329
3330 /*
3331 Map Command widget.
3332 */
3333 (void) CloneString(&windows->command.name,"Color Edit");
3334 windows->command.data=4;
3335 (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3336 (void) XMapRaised(display,windows->command.id);
3337 XClientMessage(display,windows->image.id,windows->im_protocols,
3338 windows->im_update_widget,CurrentTime);
3339 /*
3340 Make cursor.
3341 */
3342 cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3343 resource_info->background_color,resource_info->foreground_color);
3344 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3345 /*
3346 Track pointer until button 1 is pressed.
3347 */
3348 XQueryPosition(display,windows->image.id,&x,&y);
3349 (void) XSelectInput(display,windows->image.id,
3350 windows->image.attributes.event_mask | PointerMotionMask);
3351 state=DefaultState;
3352 do
3353 {
3354 if (windows->info.mapped != MagickFalse)
3355 {
3356 /*
3357 Display pointer position.
3358 */
3359 (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
3360 x+windows->image.x,y+windows->image.y);
3361 XInfoWidget(display,windows,text);
3362 }
3363 /*
3364 Wait for next event.
3365 */
3366 XScreenEvent(display,windows,&event);
3367 if (event.xany.window == windows->command.id)
3368 {
3369 /*
3370 Select a command from the Command widget.
3371 */
3372 id=XCommandWidget(display,windows,ColorEditMenu,&event);
3373 if (id < 0)
3374 {
3375 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3376 continue;
3377 }
3378 switch (ColorEditCommands[id])
3379 {
3380 case ColorEditMethodCommand:
3381 {
3382 char
3383 **methods;
3384
3385 /*
3386 Select a method from the pop-up menu.
3387 */
3388 methods=(char **) GetMagickOptions(MagickMethodOptions);
3389 if (methods == (char **) NULL)
3390 break;
3391 entry=XMenuWidget(display,windows,ColorEditMenu[id],
3392 (const char **) methods,command);
3393 if (entry >= 0)
3394 method=(PaintMethod) ParseMagickOption(MagickMethodOptions,
3395 MagickFalse,methods[entry]);
3396 methods=DestroyStringList(methods);
3397 break;
3398 }
3399 case ColorEditColorCommand:
3400 {
3401 const char
3402 *ColorMenu[MaxNumberPens];
3403
3404 int
3405 pen_number;
3406
3407 /*
3408 Initialize menu selections.
3409 */
3410 for (i=0; i < (int) (MaxNumberPens-2); i++)
3411 ColorMenu[i]=resource_info->pen_colors[i];
3412 ColorMenu[MaxNumberPens-2]="Browser...";
3413 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3414 /*
3415 Select a pen color from the pop-up menu.
3416 */
3417 pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3418 (const char **) ColorMenu,command);
3419 if (pen_number < 0)
3420 break;
3421 if (pen_number == (MaxNumberPens-2))
3422 {
3423 static char
3424 color_name[MaxTextExtent] = "gray";
3425
3426 /*
3427 Select a pen color from a dialog.
3428 */
3429 resource_info->pen_colors[pen_number]=color_name;
3430 XColorBrowserWidget(display,windows,"Select",color_name);
3431 if (*color_name == '\0')
3432 break;
3433 }
3434 /*
3435 Set pen color.
3436 */
3437 (void) XParseColor(display,windows->map_info->colormap,
3438 resource_info->pen_colors[pen_number],&color);
3439 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3440 (unsigned int) MaxColors,&color);
3441 windows->pixel_info->pen_colors[pen_number]=color;
3442 pen_id=(unsigned int) pen_number;
3443 break;
3444 }
3445 case ColorEditBorderCommand:
3446 {
3447 const char
3448 *ColorMenu[MaxNumberPens];
3449
3450 int
3451 pen_number;
3452
3453 /*
3454 Initialize menu selections.
3455 */
3456 for (i=0; i < (int) (MaxNumberPens-2); i++)
3457 ColorMenu[i]=resource_info->pen_colors[i];
3458 ColorMenu[MaxNumberPens-2]="Browser...";
3459 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3460 /*
3461 Select a pen color from the pop-up menu.
3462 */
3463 pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3464 (const char **) ColorMenu,command);
3465 if (pen_number < 0)
3466 break;
3467 if (pen_number == (MaxNumberPens-2))
3468 {
3469 static char
3470 color_name[MaxTextExtent] = "gray";
3471
3472 /*
3473 Select a pen color from a dialog.
3474 */
3475 resource_info->pen_colors[pen_number]=color_name;
3476 XColorBrowserWidget(display,windows,"Select",color_name);
3477 if (*color_name == '\0')
3478 break;
3479 }
3480 /*
3481 Set border color.
3482 */
3483 (void) XParseColor(display,windows->map_info->colormap,
3484 resource_info->pen_colors[pen_number],&border_color);
3485 break;
3486 }
3487 case ColorEditFuzzCommand:
3488 {
3489 static char
3490 fuzz[MaxTextExtent];
3491
3492 static const char
3493 *FuzzMenu[] =
3494 {
3495 "0%",
3496 "2%",
3497 "5%",
3498 "10%",
3499 "15%",
3500 "Dialog...",
3501 (char *) NULL,
3502 };
3503
3504 /*
3505 Select a command from the pop-up menu.
3506 */
3507 entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3508 command);
3509 if (entry < 0)
3510 break;
3511 if (entry != 5)
3512 {
cristy40a08ad2010-02-09 02:27:44 +00003513 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
3514 QuantumRange+1.0);
cristy3ed852e2009-09-05 21:47:34 +00003515 break;
3516 }
3517 (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3518 (void) XDialogWidget(display,windows,"Ok",
3519 "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3520 if (*fuzz == '\0')
3521 break;
3522 (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
cristyf2f27272009-12-17 14:48:46 +00003523 (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
cristy3ed852e2009-09-05 21:47:34 +00003524 break;
3525 }
3526 case ColorEditUndoCommand:
3527 {
3528 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3529 image);
3530 break;
3531 }
3532 case ColorEditHelpCommand:
3533 default:
3534 {
3535 XTextViewWidget(display,resource_info,windows,MagickFalse,
3536 "Help Viewer - Image Annotation",ImageColorEditHelp);
3537 break;
3538 }
3539 case ColorEditDismissCommand:
3540 {
3541 /*
3542 Prematurely exit.
3543 */
3544 state|=EscapeState;
3545 state|=ExitState;
3546 break;
3547 }
3548 }
3549 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3550 continue;
3551 }
3552 switch (event.type)
3553 {
3554 case ButtonPress:
3555 {
3556 if (event.xbutton.button != Button1)
3557 break;
3558 if ((event.xbutton.window != windows->image.id) &&
3559 (event.xbutton.window != windows->magnify.id))
3560 break;
3561 /*
3562 exit loop.
3563 */
3564 x=event.xbutton.x;
3565 y=event.xbutton.y;
3566 (void) XMagickCommand(display,resource_info,windows,
3567 SaveToUndoBufferCommand,image);
3568 state|=UpdateConfigurationState;
3569 break;
3570 }
3571 case ButtonRelease:
3572 {
3573 if (event.xbutton.button != Button1)
3574 break;
3575 if ((event.xbutton.window != windows->image.id) &&
3576 (event.xbutton.window != windows->magnify.id))
3577 break;
3578 /*
3579 Update colormap information.
3580 */
3581 x=event.xbutton.x;
3582 y=event.xbutton.y;
3583 XConfigureImageColormap(display,resource_info,windows,*image);
3584 (void) XConfigureImage(display,resource_info,windows,*image);
3585 XInfoWidget(display,windows,text);
3586 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3587 state&=(~UpdateConfigurationState);
3588 break;
3589 }
3590 case Expose:
3591 break;
3592 case KeyPress:
3593 {
3594 KeySym
3595 key_symbol;
3596
3597 if (event.xkey.window == windows->magnify.id)
3598 {
3599 Window
3600 window;
3601
3602 window=windows->magnify.id;
3603 while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3604 }
3605 if (event.xkey.window != windows->image.id)
3606 break;
3607 /*
3608 Respond to a user key press.
3609 */
3610 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3611 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3612 switch ((int) key_symbol)
3613 {
3614 case XK_Escape:
3615 case XK_F20:
3616 {
3617 /*
3618 Prematurely exit.
3619 */
3620 state|=ExitState;
3621 break;
3622 }
3623 case XK_F1:
3624 case XK_Help:
3625 {
3626 XTextViewWidget(display,resource_info,windows,MagickFalse,
3627 "Help Viewer - Image Annotation",ImageColorEditHelp);
3628 break;
3629 }
3630 default:
3631 {
3632 (void) XBell(display,0);
3633 break;
3634 }
3635 }
3636 break;
3637 }
3638 case MotionNotify:
3639 {
3640 /*
3641 Map and unmap Info widget as cursor crosses its boundaries.
3642 */
3643 x=event.xmotion.x;
3644 y=event.xmotion.y;
3645 if (windows->info.mapped != MagickFalse)
3646 {
3647 if ((x < (int) (windows->info.x+windows->info.width)) &&
3648 (y < (int) (windows->info.y+windows->info.height)))
3649 (void) XWithdrawWindow(display,windows->info.id,
3650 windows->info.screen);
3651 }
3652 else
3653 if ((x > (int) (windows->info.x+windows->info.width)) ||
3654 (y > (int) (windows->info.y+windows->info.height)))
3655 (void) XMapWindow(display,windows->info.id);
3656 break;
3657 }
3658 default:
3659 break;
3660 }
3661 if (event.xany.window == windows->magnify.id)
3662 {
3663 x=windows->magnify.x-windows->image.x;
3664 y=windows->magnify.y-windows->image.y;
3665 }
3666 x_offset=x;
3667 y_offset=y;
3668 if ((state & UpdateConfigurationState) != 0)
3669 {
cristy49e2d862010-11-12 02:50:30 +00003670 CacheView
3671 *image_view;
3672
cristy3ed852e2009-09-05 21:47:34 +00003673 int
3674 x,
3675 y;
3676
3677 /*
3678 Pixel edit is relative to image configuration.
3679 */
3680 (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3681 MagickTrue);
3682 color=windows->pixel_info->pen_colors[pen_id];
3683 XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3684 width=(unsigned int) (*image)->columns;
3685 height=(unsigned int) (*image)->rows;
3686 x=0;
3687 y=0;
3688 if (windows->image.crop_geometry != (char *) NULL)
3689 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3690 &width,&height);
3691 x_offset=(int)
3692 (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3693 y_offset=(int)
3694 (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3695 if ((x_offset < 0) || (y_offset < 0))
3696 continue;
cristy49e2d862010-11-12 02:50:30 +00003697 if ((x_offset >= (int) (*image)->columns) ||
3698 (y_offset >= (int) (*image)->rows))
cristy3ed852e2009-09-05 21:47:34 +00003699 continue;
3700 exception=(&(*image)->exception);
cristy49e2d862010-11-12 02:50:30 +00003701 image_view=AcquireCacheView(*image);
cristy3ed852e2009-09-05 21:47:34 +00003702 switch (method)
3703 {
3704 case PointMethod:
3705 default:
3706 {
3707 /*
3708 Update color information using point algorithm.
3709 */
3710 if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3711 return(MagickFalse);
cristy49e2d862010-11-12 02:50:30 +00003712 q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3713 (ssize_t)y_offset,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00003714 if (q == (PixelPacket *) NULL)
3715 break;
3716 q->red=ScaleShortToQuantum(color.red);
3717 q->green=ScaleShortToQuantum(color.green);
3718 q->blue=ScaleShortToQuantum(color.blue);
cristy49e2d862010-11-12 02:50:30 +00003719 (void) SyncCacheViewAuthenticPixels(image_view,
3720 &(*image)->exception);
cristy3ed852e2009-09-05 21:47:34 +00003721 break;
3722 }
3723 case ReplaceMethod:
3724 {
3725 PixelPacket
3726 target;
3727
3728 /*
3729 Update color information using replace algorithm.
3730 */
cristy49e2d862010-11-12 02:50:30 +00003731 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3732 (ssize_t) y_offset,&target,&(*image)->exception);
cristy3ed852e2009-09-05 21:47:34 +00003733 if ((*image)->storage_class == DirectClass)
3734 {
cristy49e2d862010-11-12 02:50:30 +00003735 for (y=0; y < (int) (*image)->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003736 {
cristy49e2d862010-11-12 02:50:30 +00003737 q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3738 (*image)->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00003739 if (q == (PixelPacket *) NULL)
3740 break;
3741 for (x=0; x < (int) (*image)->columns; x++)
3742 {
3743 if (IsColorSimilar(*image,q,&target))
3744 {
3745 q->red=ScaleShortToQuantum(color.red);
3746 q->green=ScaleShortToQuantum(color.green);
3747 q->blue=ScaleShortToQuantum(color.blue);
3748 }
3749 q++;
3750 }
cristy49e2d862010-11-12 02:50:30 +00003751 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003752 break;
3753 }
3754 }
3755 else
3756 {
cristy49e2d862010-11-12 02:50:30 +00003757 for (i=0; i < (ssize_t) (*image)->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00003758 if (IsColorSimilar(*image,(*image)->colormap+i,&target))
3759 {
3760 (*image)->colormap[i].red=ScaleShortToQuantum(color.red);
3761 (*image)->colormap[i].green=ScaleShortToQuantum(
3762 color.green);
3763 (*image)->colormap[i].blue=ScaleShortToQuantum(
3764 color.blue);
3765 }
3766 (void) SyncImage(*image);
3767 }
3768 break;
3769 }
3770 case FloodfillMethod:
3771 case FillToBorderMethod:
3772 {
3773 DrawInfo
3774 *draw_info;
3775
3776 MagickPixelPacket
3777 target;
3778
3779 /*
3780 Update color information using floodfill algorithm.
3781 */
cristy49e2d862010-11-12 02:50:30 +00003782 (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
3783 (ssize_t) y_offset,&target,exception);
cristy3ed852e2009-09-05 21:47:34 +00003784 if (method == FillToBorderMethod)
3785 {
3786 target.red=(MagickRealType)
3787 ScaleShortToQuantum(border_color.red);
3788 target.green=(MagickRealType)
3789 ScaleShortToQuantum(border_color.green);
3790 target.blue=(MagickRealType)
3791 ScaleShortToQuantum(border_color.blue);
3792 }
3793 draw_info=CloneDrawInfo(resource_info->image_info,
3794 (DrawInfo *) NULL);
3795 (void) QueryColorDatabase(resource_info->pen_colors[pen_id],
3796 &draw_info->fill,exception);
3797 (void) FloodfillPaintImage(*image,DefaultChannels,draw_info,&target,
cristy49e2d862010-11-12 02:50:30 +00003798 (ssize_t) x_offset,(ssize_t) y_offset,
3799 method == FloodfillMethod ? MagickFalse : MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003800 draw_info=DestroyDrawInfo(draw_info);
3801 break;
3802 }
3803 case ResetMethod:
3804 {
3805 /*
3806 Update color information using reset algorithm.
3807 */
3808 if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3809 return(MagickFalse);
cristy49e2d862010-11-12 02:50:30 +00003810 for (y=0; y < (int) (*image)->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003811 {
cristy49e2d862010-11-12 02:50:30 +00003812 q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3813 (*image)->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00003814 if (q == (PixelPacket *) NULL)
3815 break;
3816 for (x=0; x < (int) (*image)->columns; x++)
3817 {
3818 q->red=ScaleShortToQuantum(color.red);
3819 q->green=ScaleShortToQuantum(color.green);
3820 q->blue=ScaleShortToQuantum(color.blue);
3821 q++;
3822 }
cristy49e2d862010-11-12 02:50:30 +00003823 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003824 break;
3825 }
3826 break;
3827 }
3828 }
cristy49e2d862010-11-12 02:50:30 +00003829 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003830 state&=(~UpdateConfigurationState);
3831 }
3832 } while ((state & ExitState) == 0);
3833 (void) XSelectInput(display,windows->image.id,
3834 windows->image.attributes.event_mask);
3835 XSetCursorState(display,windows,MagickFalse);
3836 (void) XFreeCursor(display,cursor);
3837 return(MagickTrue);
3838}
3839
3840/*
3841%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3842% %
3843% %
3844% %
3845+ X C o m p o s i t e I m a g e %
3846% %
3847% %
3848% %
3849%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3850%
3851% XCompositeImage() requests an image name from the user, reads the image and
3852% composites it with the X window image at a location the user chooses with
3853% the pointer.
3854%
3855% The format of the XCompositeImage method is:
3856%
3857% MagickBooleanType XCompositeImage(Display *display,
3858% XResourceInfo *resource_info,XWindows *windows,Image *image)
3859%
3860% A description of each parameter follows:
3861%
3862% o display: Specifies a connection to an X server; returned from
3863% XOpenDisplay.
3864%
3865% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3866%
3867% o windows: Specifies a pointer to a XWindows structure.
3868%
3869% o image: the image; returned from ReadImage.
3870%
3871*/
3872static MagickBooleanType XCompositeImage(Display *display,
3873 XResourceInfo *resource_info,XWindows *windows,Image *image)
3874{
3875 static char
3876 displacement_geometry[MaxTextExtent] = "30x30",
3877 filename[MaxTextExtent] = "\0";
3878
3879 static const char
3880 *CompositeMenu[] =
3881 {
3882 "Operators",
3883 "Dissolve",
3884 "Displace",
3885 "Help",
3886 "Dismiss",
3887 (char *) NULL
3888 };
3889
3890 static CompositeOperator
3891 compose = CopyCompositeOp;
3892
3893 static const ModeType
3894 CompositeCommands[] =
3895 {
3896 CompositeOperatorsCommand,
3897 CompositeDissolveCommand,
3898 CompositeDisplaceCommand,
3899 CompositeHelpCommand,
3900 CompositeDismissCommand
3901 };
3902
3903 char
3904 text[MaxTextExtent];
3905
3906 Cursor
3907 cursor;
3908
3909 Image
3910 *composite_image;
3911
3912 int
3913 entry,
3914 id,
3915 x,
3916 y;
3917
3918 MagickRealType
3919 blend,
3920 scale_factor;
3921
3922 RectangleInfo
3923 highlight_info,
3924 composite_info;
3925
3926 unsigned int
3927 height,
3928 width;
3929
cristybb503372010-05-27 20:51:26 +00003930 size_t
cristy3ed852e2009-09-05 21:47:34 +00003931 state;
3932
3933 XEvent
3934 event;
3935
3936 /*
3937 Request image file name from user.
3938 */
3939 XFileBrowserWidget(display,windows,"Composite",filename);
3940 if (*filename == '\0')
3941 return(MagickTrue);
3942 /*
3943 Read image.
3944 */
3945 XSetCursorState(display,windows,MagickTrue);
3946 XCheckRefreshWindows(display,windows);
3947 (void) CopyMagickString(resource_info->image_info->filename,filename,
3948 MaxTextExtent);
3949 composite_image=ReadImage(resource_info->image_info,&image->exception);
3950 CatchException(&image->exception);
3951 XSetCursorState(display,windows,MagickFalse);
3952 if (composite_image == (Image *) NULL)
3953 return(MagickFalse);
3954 /*
3955 Map Command widget.
3956 */
3957 (void) CloneString(&windows->command.name,"Composite");
3958 windows->command.data=1;
3959 (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3960 (void) XMapRaised(display,windows->command.id);
3961 XClientMessage(display,windows->image.id,windows->im_protocols,
3962 windows->im_update_widget,CurrentTime);
3963 /*
3964 Track pointer until button 1 is pressed.
3965 */
3966 XQueryPosition(display,windows->image.id,&x,&y);
3967 (void) XSelectInput(display,windows->image.id,
3968 windows->image.attributes.event_mask | PointerMotionMask);
cristy49e2d862010-11-12 02:50:30 +00003969 composite_info.x=(ssize_t) windows->image.x+x;
3970 composite_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +00003971 composite_info.width=0;
3972 composite_info.height=0;
3973 cursor=XCreateFontCursor(display,XC_ul_angle);
3974 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3975 blend=0.0;
3976 state=DefaultState;
3977 do
3978 {
3979 if (windows->info.mapped != MagickFalse)
3980 {
3981 /*
3982 Display pointer position.
3983 */
3984 (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
cristyf2faecf2010-05-28 19:19:36 +00003985 (long) composite_info.x,(long) composite_info.y);
cristy3ed852e2009-09-05 21:47:34 +00003986 XInfoWidget(display,windows,text);
3987 }
3988 highlight_info=composite_info;
3989 highlight_info.x=composite_info.x-windows->image.x;
3990 highlight_info.y=composite_info.y-windows->image.y;
3991 XHighlightRectangle(display,windows->image.id,
3992 windows->image.highlight_context,&highlight_info);
3993 /*
3994 Wait for next event.
3995 */
3996 XScreenEvent(display,windows,&event);
3997 XHighlightRectangle(display,windows->image.id,
3998 windows->image.highlight_context,&highlight_info);
3999 if (event.xany.window == windows->command.id)
4000 {
4001 /*
4002 Select a command from the Command widget.
4003 */
4004 id=XCommandWidget(display,windows,CompositeMenu,&event);
4005 if (id < 0)
4006 continue;
4007 switch (CompositeCommands[id])
4008 {
4009 case CompositeOperatorsCommand:
4010 {
4011 char
4012 command[MaxTextExtent],
4013 **operators;
4014
4015 /*
4016 Select a command from the pop-up menu.
4017 */
4018 operators=GetMagickOptions(MagickComposeOptions);
4019 if (operators == (char **) NULL)
4020 break;
4021 entry=XMenuWidget(display,windows,CompositeMenu[id],
4022 (const char **) operators,command);
4023 if (entry >= 0)
4024 compose=(CompositeOperator) ParseMagickOption(
4025 MagickComposeOptions,MagickFalse,operators[entry]);
4026 operators=DestroyStringList(operators);
4027 break;
4028 }
4029 case CompositeDissolveCommand:
4030 {
4031 static char
4032 factor[MaxTextExtent] = "20.0";
4033
4034 /*
4035 Dissolve the two images a given percent.
4036 */
4037 (void) XSetFunction(display,windows->image.highlight_context,
4038 GXcopy);
4039 (void) XDialogWidget(display,windows,"Dissolve",
4040 "Enter the blend factor (0.0 - 99.9%):",factor);
4041 (void) XSetFunction(display,windows->image.highlight_context,
4042 GXinvert);
4043 if (*factor == '\0')
4044 break;
cristyf2f27272009-12-17 14:48:46 +00004045 blend=StringToDouble(factor);
cristy3ed852e2009-09-05 21:47:34 +00004046 compose=DissolveCompositeOp;
4047 break;
4048 }
4049 case CompositeDisplaceCommand:
4050 {
4051 /*
4052 Get horizontal and vertical scale displacement geometry.
4053 */
4054 (void) XSetFunction(display,windows->image.highlight_context,
4055 GXcopy);
4056 (void) XDialogWidget(display,windows,"Displace",
4057 "Enter the horizontal and vertical scale:",displacement_geometry);
4058 (void) XSetFunction(display,windows->image.highlight_context,
4059 GXinvert);
4060 if (*displacement_geometry == '\0')
4061 break;
4062 compose=DisplaceCompositeOp;
4063 break;
4064 }
4065 case CompositeHelpCommand:
4066 {
4067 (void) XSetFunction(display,windows->image.highlight_context,
4068 GXcopy);
4069 XTextViewWidget(display,resource_info,windows,MagickFalse,
4070 "Help Viewer - Image Composite",ImageCompositeHelp);
4071 (void) XSetFunction(display,windows->image.highlight_context,
4072 GXinvert);
4073 break;
4074 }
4075 case CompositeDismissCommand:
4076 {
4077 /*
4078 Prematurely exit.
4079 */
4080 state|=EscapeState;
4081 state|=ExitState;
4082 break;
4083 }
4084 default:
4085 break;
4086 }
4087 continue;
4088 }
4089 switch (event.type)
4090 {
4091 case ButtonPress:
4092 {
4093 if (image->debug != MagickFalse)
4094 (void) LogMagickEvent(X11Event,GetMagickModule(),
4095 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4096 event.xbutton.button,event.xbutton.x,event.xbutton.y);
4097 if (event.xbutton.button != Button1)
4098 break;
4099 if (event.xbutton.window != windows->image.id)
4100 break;
4101 /*
4102 Change cursor.
4103 */
4104 composite_info.width=composite_image->columns;
4105 composite_info.height=composite_image->rows;
4106 (void) XCheckDefineCursor(display,windows->image.id,cursor);
cristy49e2d862010-11-12 02:50:30 +00004107 composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4108 composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +00004109 break;
4110 }
4111 case ButtonRelease:
4112 {
4113 if (image->debug != MagickFalse)
4114 (void) LogMagickEvent(X11Event,GetMagickModule(),
4115 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4116 event.xbutton.button,event.xbutton.x,event.xbutton.y);
4117 if (event.xbutton.button != Button1)
4118 break;
4119 if (event.xbutton.window != windows->image.id)
4120 break;
4121 if ((composite_info.width != 0) && (composite_info.height != 0))
4122 {
4123 /*
4124 User has selected the location of the composite image.
4125 */
cristy49e2d862010-11-12 02:50:30 +00004126 composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4127 composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +00004128 state|=ExitState;
4129 }
4130 break;
4131 }
4132 case Expose:
4133 break;
4134 case KeyPress:
4135 {
4136 char
4137 command[MaxTextExtent];
4138
4139 KeySym
4140 key_symbol;
4141
4142 int
4143 length;
4144
4145 if (event.xkey.window != windows->image.id)
4146 break;
4147 /*
4148 Respond to a user key press.
4149 */
4150 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4151 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4152 *(command+length)='\0';
4153 if (image->debug != MagickFalse)
4154 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00004155 "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
cristy3ed852e2009-09-05 21:47:34 +00004156 switch ((int) key_symbol)
4157 {
4158 case XK_Escape:
4159 case XK_F20:
4160 {
4161 /*
4162 Prematurely exit.
4163 */
4164 composite_image=DestroyImage(composite_image);
4165 state|=EscapeState;
4166 state|=ExitState;
4167 break;
4168 }
4169 case XK_F1:
4170 case XK_Help:
4171 {
4172 (void) XSetFunction(display,windows->image.highlight_context,
4173 GXcopy);
4174 XTextViewWidget(display,resource_info,windows,MagickFalse,
4175 "Help Viewer - Image Composite",ImageCompositeHelp);
4176 (void) XSetFunction(display,windows->image.highlight_context,
4177 GXinvert);
4178 break;
4179 }
4180 default:
4181 {
4182 (void) XBell(display,0);
4183 break;
4184 }
4185 }
4186 break;
4187 }
4188 case MotionNotify:
4189 {
4190 /*
4191 Map and unmap Info widget as text cursor crosses its boundaries.
4192 */
4193 x=event.xmotion.x;
4194 y=event.xmotion.y;
4195 if (windows->info.mapped != MagickFalse)
4196 {
4197 if ((x < (int) (windows->info.x+windows->info.width)) &&
4198 (y < (int) (windows->info.y+windows->info.height)))
4199 (void) XWithdrawWindow(display,windows->info.id,
4200 windows->info.screen);
4201 }
4202 else
4203 if ((x > (int) (windows->info.x+windows->info.width)) ||
4204 (y > (int) (windows->info.y+windows->info.height)))
4205 (void) XMapWindow(display,windows->info.id);
cristy49e2d862010-11-12 02:50:30 +00004206 composite_info.x=(ssize_t) windows->image.x+x;
4207 composite_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +00004208 break;
4209 }
4210 default:
4211 {
4212 if (image->debug != MagickFalse)
4213 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4214 event.type);
4215 break;
4216 }
4217 }
4218 } while ((state & ExitState) == 0);
4219 (void) XSelectInput(display,windows->image.id,
4220 windows->image.attributes.event_mask);
4221 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4222 XSetCursorState(display,windows,MagickFalse);
4223 (void) XFreeCursor(display,cursor);
4224 if ((state & EscapeState) != 0)
4225 return(MagickTrue);
4226 /*
4227 Image compositing is relative to image configuration.
4228 */
4229 XSetCursorState(display,windows,MagickTrue);
4230 XCheckRefreshWindows(display,windows);
4231 width=(unsigned int) image->columns;
4232 height=(unsigned int) image->rows;
4233 x=0;
4234 y=0;
4235 if (windows->image.crop_geometry != (char *) NULL)
4236 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4237 scale_factor=(MagickRealType) width/windows->image.ximage->width;
4238 composite_info.x+=x;
cristy49e2d862010-11-12 02:50:30 +00004239 composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +00004240 composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4241 scale_factor=(MagickRealType) height/windows->image.ximage->height;
4242 composite_info.y+=y;
cristy49e2d862010-11-12 02:50:30 +00004243 composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00004244 composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4245 if ((composite_info.width != composite_image->columns) ||
4246 (composite_info.height != composite_image->rows))
4247 {
4248 Image
4249 *resize_image;
4250
4251 /*
4252 Scale composite image.
4253 */
cristy15b98cd2010-09-12 19:42:50 +00004254 resize_image=ResizeImage(composite_image,composite_info.width,
4255 composite_info.height,composite_image->filter,composite_image->blur,
4256 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +00004257 composite_image=DestroyImage(composite_image);
4258 if (resize_image == (Image *) NULL)
4259 {
4260 XSetCursorState(display,windows,MagickFalse);
4261 return(MagickFalse);
4262 }
4263 composite_image=resize_image;
4264 }
4265 if (compose == DisplaceCompositeOp)
4266 (void) SetImageArtifact(composite_image,"compose:args",
4267 displacement_geometry);
4268 if (blend != 0.0)
4269 {
cristy49e2d862010-11-12 02:50:30 +00004270 CacheView
4271 *image_view;
4272
cristy3ed852e2009-09-05 21:47:34 +00004273 ExceptionInfo
4274 *exception;
4275
4276 int
4277 y;
4278
4279 Quantum
4280 opacity;
4281
4282 register int
4283 x;
4284
4285 register PixelPacket
4286 *q;
4287
4288 /*
4289 Create mattes for blending.
4290 */
4291 (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel);
4292 opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
cristybb503372010-05-27 20:51:26 +00004293 ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
cristy3ed852e2009-09-05 21:47:34 +00004294 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
4295 return(MagickFalse);
4296 image->matte=MagickTrue;
4297 exception=(&image->exception);
cristy49e2d862010-11-12 02:50:30 +00004298 image_view=AcquireCacheView(image);
4299 for (y=0; y < (int) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004300 {
cristy49e2d862010-11-12 02:50:30 +00004301 q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4302 exception);
cristy3ed852e2009-09-05 21:47:34 +00004303 if (q == (PixelPacket *) NULL)
4304 break;
4305 for (x=0; x < (int) image->columns; x++)
4306 {
4307 q->opacity=opacity;
4308 q++;
4309 }
cristy49e2d862010-11-12 02:50:30 +00004310 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004311 break;
4312 }
cristy49e2d862010-11-12 02:50:30 +00004313 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00004314 }
4315 /*
4316 Composite image with X Image window.
4317 */
4318 (void) CompositeImage(image,compose,composite_image,composite_info.x,
4319 composite_info.y);
4320 composite_image=DestroyImage(composite_image);
4321 XSetCursorState(display,windows,MagickFalse);
4322 /*
4323 Update image configuration.
4324 */
4325 XConfigureImageColormap(display,resource_info,windows,image);
4326 (void) XConfigureImage(display,resource_info,windows,image);
4327 return(MagickTrue);
4328}
4329
4330/*
4331%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4332% %
4333% %
4334% %
4335+ X C o n f i g u r e I m a g e %
4336% %
4337% %
4338% %
4339%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4340%
4341% XConfigureImage() creates a new X image. It also notifies the window
4342% manager of the new image size and configures the transient widows.
4343%
4344% The format of the XConfigureImage method is:
4345%
4346% MagickBooleanType XConfigureImage(Display *display,
4347% XResourceInfo *resource_info,XWindows *windows,Image *image)
4348%
4349% A description of each parameter follows:
4350%
4351% o display: Specifies a connection to an X server; returned from
4352% XOpenDisplay.
4353%
4354% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4355%
4356% o windows: Specifies a pointer to a XWindows structure.
4357%
4358% o image: the image.
4359%
4360%
4361*/
4362static MagickBooleanType XConfigureImage(Display *display,
4363 XResourceInfo *resource_info,XWindows *windows,Image *image)
4364{
4365 char
4366 geometry[MaxTextExtent];
4367
cristy3ed852e2009-09-05 21:47:34 +00004368 MagickStatusType
4369 status;
4370
cristybb503372010-05-27 20:51:26 +00004371 size_t
cristy3ed852e2009-09-05 21:47:34 +00004372 mask,
4373 height,
4374 width;
4375
cristy9d314ff2011-03-09 01:30:28 +00004376 ssize_t
4377 x,
4378 y;
4379
cristy3ed852e2009-09-05 21:47:34 +00004380 XSizeHints
4381 *size_hints;
4382
4383 XWindowChanges
4384 window_changes;
4385
4386 /*
4387 Dismiss if window dimensions are zero.
4388 */
4389 width=(unsigned int) windows->image.window_changes.width;
4390 height=(unsigned int) windows->image.window_changes.height;
4391 if (image->debug != MagickFalse)
4392 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004393 "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4394 windows->image.ximage->height,(double) width,(double) height);
cristy3ed852e2009-09-05 21:47:34 +00004395 if ((width*height) == 0)
4396 return(MagickTrue);
4397 x=0;
4398 y=0;
4399 /*
4400 Resize image to fit Image window dimensions.
4401 */
4402 XSetCursorState(display,windows,MagickTrue);
4403 (void) XFlush(display);
4404 if (((int) width != windows->image.ximage->width) ||
4405 ((int) height != windows->image.ximage->height))
4406 image->taint=MagickTrue;
4407 windows->magnify.x=(int)
4408 width*windows->magnify.x/windows->image.ximage->width;
4409 windows->magnify.y=(int)
4410 height*windows->magnify.y/windows->image.ximage->height;
4411 windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4412 windows->image.y=(int)
4413 (height*windows->image.y/windows->image.ximage->height);
4414 status=XMakeImage(display,resource_info,&windows->image,image,
4415 (unsigned int) width,(unsigned int) height);
4416 if (status == MagickFalse)
4417 XNoticeWidget(display,windows,"Unable to configure X image:",
4418 windows->image.name);
4419 /*
4420 Notify window manager of the new configuration.
4421 */
4422 if (resource_info->image_geometry != (char *) NULL)
4423 (void) FormatMagickString(geometry,MaxTextExtent,"%s>!",
4424 resource_info->image_geometry);
4425 else
4426 (void) FormatMagickString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4427 XDisplayWidth(display,windows->image.screen),
4428 XDisplayHeight(display,windows->image.screen));
4429 (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4430 window_changes.width=(int) width;
4431 if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4432 window_changes.width=XDisplayWidth(display,windows->image.screen);
4433 window_changes.height=(int) height;
4434 if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4435 window_changes.height=XDisplayHeight(display,windows->image.screen);
cristybb503372010-05-27 20:51:26 +00004436 mask=(size_t) (CWWidth | CWHeight);
cristy3ed852e2009-09-05 21:47:34 +00004437 if (resource_info->backdrop)
4438 {
4439 mask|=CWX | CWY;
4440 window_changes.x=(int)
4441 ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4442 window_changes.y=(int)
4443 ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4444 }
4445 (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4446 (unsigned int) mask,&window_changes);
4447 (void) XClearWindow(display,windows->image.id);
4448 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4449 /*
4450 Update Magnify window configuration.
4451 */
4452 if (windows->magnify.mapped != MagickFalse)
4453 XMakeMagnifyImage(display,windows);
4454 windows->pan.crop_geometry=windows->image.crop_geometry;
4455 XBestIconSize(display,&windows->pan,image);
4456 while (((windows->pan.width << 1) < MaxIconSize) &&
4457 ((windows->pan.height << 1) < MaxIconSize))
4458 {
4459 windows->pan.width<<=1;
4460 windows->pan.height<<=1;
4461 }
4462 if (windows->pan.geometry != (char *) NULL)
4463 (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4464 &windows->pan.width,&windows->pan.height);
4465 window_changes.width=(int) windows->pan.width;
4466 window_changes.height=(int) windows->pan.height;
4467 size_hints=XAllocSizeHints();
4468 if (size_hints != (XSizeHints *) NULL)
4469 {
4470 /*
4471 Set new size hints.
4472 */
4473 size_hints->flags=PSize | PMinSize | PMaxSize;
4474 size_hints->width=window_changes.width;
4475 size_hints->height=window_changes.height;
4476 size_hints->min_width=size_hints->width;
4477 size_hints->min_height=size_hints->height;
4478 size_hints->max_width=size_hints->width;
4479 size_hints->max_height=size_hints->height;
4480 (void) XSetNormalHints(display,windows->pan.id,size_hints);
4481 (void) XFree((void *) size_hints);
4482 }
4483 (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4484 (unsigned int) (CWWidth | CWHeight),&window_changes);
4485 /*
4486 Update icon window configuration.
4487 */
4488 windows->icon.crop_geometry=windows->image.crop_geometry;
4489 XBestIconSize(display,&windows->icon,image);
4490 window_changes.width=(int) windows->icon.width;
4491 window_changes.height=(int) windows->icon.height;
4492 (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4493 (unsigned int) (CWWidth | CWHeight),&window_changes);
4494 XSetCursorState(display,windows,MagickFalse);
4495 return(status != 0 ? MagickTrue : MagickFalse);
4496}
4497
4498/*
4499%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4500% %
4501% %
4502% %
4503+ X C r o p I m a g e %
4504% %
4505% %
4506% %
4507%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4508%
4509% XCropImage() allows the user to select a region of the image and crop, copy,
4510% or cut it. For copy or cut, the image can subsequently be composited onto
4511% the image with XPasteImage.
4512%
4513% The format of the XCropImage method is:
4514%
4515% MagickBooleanType XCropImage(Display *display,
4516% XResourceInfo *resource_info,XWindows *windows,Image *image,
4517% const ClipboardMode mode)
4518%
4519% A description of each parameter follows:
4520%
4521% o display: Specifies a connection to an X server; returned from
4522% XOpenDisplay.
4523%
4524% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4525%
4526% o windows: Specifies a pointer to a XWindows structure.
4527%
4528% o image: the image; returned from ReadImage.
4529%
4530% o mode: This unsigned value specified whether the image should be
4531% cropped, copied, or cut.
4532%
4533*/
4534static MagickBooleanType XCropImage(Display *display,
4535 XResourceInfo *resource_info,XWindows *windows,Image *image,
4536 const ClipboardMode mode)
4537{
4538 static const char
4539 *CropModeMenu[] =
4540 {
4541 "Help",
4542 "Dismiss",
4543 (char *) NULL
4544 },
4545 *RectifyModeMenu[] =
4546 {
4547 "Crop",
4548 "Help",
4549 "Dismiss",
4550 (char *) NULL
4551 };
4552
4553 static const ModeType
4554 CropCommands[] =
4555 {
4556 CropHelpCommand,
4557 CropDismissCommand
4558 },
4559 RectifyCommands[] =
4560 {
4561 RectifyCopyCommand,
4562 RectifyHelpCommand,
4563 RectifyDismissCommand
4564 };
4565
cristy49e2d862010-11-12 02:50:30 +00004566 CacheView
4567 *image_view;
4568
cristy3ed852e2009-09-05 21:47:34 +00004569 char
4570 command[MaxTextExtent],
4571 text[MaxTextExtent];
4572
4573 Cursor
4574 cursor;
4575
4576 ExceptionInfo
4577 *exception;
4578
4579 int
4580 id,
4581 x,
4582 y;
4583
4584 KeySym
4585 key_symbol;
4586
4587 Image
4588 *crop_image;
4589
4590 MagickRealType
4591 scale_factor;
4592
4593 RectangleInfo
4594 crop_info,
4595 highlight_info;
4596
4597 register PixelPacket
4598 *q;
4599
4600 unsigned int
4601 height,
4602 width;
4603
cristybb503372010-05-27 20:51:26 +00004604 size_t
cristy3ed852e2009-09-05 21:47:34 +00004605 state;
4606
4607 XEvent
4608 event;
4609
4610 /*
4611 Map Command widget.
4612 */
4613 switch (mode)
4614 {
4615 case CopyMode:
4616 {
4617 (void) CloneString(&windows->command.name,"Copy");
4618 break;
4619 }
4620 case CropMode:
4621 {
4622 (void) CloneString(&windows->command.name,"Crop");
4623 break;
4624 }
4625 case CutMode:
4626 {
4627 (void) CloneString(&windows->command.name,"Cut");
4628 break;
4629 }
4630 }
4631 RectifyModeMenu[0]=windows->command.name;
4632 windows->command.data=0;
4633 (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4634 (void) XMapRaised(display,windows->command.id);
4635 XClientMessage(display,windows->image.id,windows->im_protocols,
4636 windows->im_update_widget,CurrentTime);
4637 /*
4638 Track pointer until button 1 is pressed.
4639 */
4640 XQueryPosition(display,windows->image.id,&x,&y);
4641 (void) XSelectInput(display,windows->image.id,
4642 windows->image.attributes.event_mask | PointerMotionMask);
cristy49e2d862010-11-12 02:50:30 +00004643 crop_info.x=(ssize_t) windows->image.x+x;
4644 crop_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +00004645 crop_info.width=0;
4646 crop_info.height=0;
4647 cursor=XCreateFontCursor(display,XC_fleur);
4648 state=DefaultState;
4649 do
4650 {
4651 if (windows->info.mapped != MagickFalse)
4652 {
4653 /*
4654 Display pointer position.
4655 */
4656 (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
cristyf2faecf2010-05-28 19:19:36 +00004657 (long) crop_info.x,(long) crop_info.y);
cristy3ed852e2009-09-05 21:47:34 +00004658 XInfoWidget(display,windows,text);
4659 }
4660 /*
4661 Wait for next event.
4662 */
4663 XScreenEvent(display,windows,&event);
4664 if (event.xany.window == windows->command.id)
4665 {
4666 /*
4667 Select a command from the Command widget.
4668 */
4669 id=XCommandWidget(display,windows,CropModeMenu,&event);
4670 if (id < 0)
4671 continue;
4672 switch (CropCommands[id])
4673 {
4674 case CropHelpCommand:
4675 {
4676 switch (mode)
4677 {
4678 case CopyMode:
4679 {
4680 XTextViewWidget(display,resource_info,windows,MagickFalse,
4681 "Help Viewer - Image Copy",ImageCopyHelp);
4682 break;
4683 }
4684 case CropMode:
4685 {
4686 XTextViewWidget(display,resource_info,windows,MagickFalse,
4687 "Help Viewer - Image Crop",ImageCropHelp);
4688 break;
4689 }
4690 case CutMode:
4691 {
4692 XTextViewWidget(display,resource_info,windows,MagickFalse,
4693 "Help Viewer - Image Cut",ImageCutHelp);
4694 break;
4695 }
4696 }
4697 break;
4698 }
4699 case CropDismissCommand:
4700 {
4701 /*
4702 Prematurely exit.
4703 */
4704 state|=EscapeState;
4705 state|=ExitState;
4706 break;
4707 }
4708 default:
4709 break;
4710 }
4711 continue;
4712 }
4713 switch (event.type)
4714 {
4715 case ButtonPress:
4716 {
4717 if (event.xbutton.button != Button1)
4718 break;
4719 if (event.xbutton.window != windows->image.id)
4720 break;
4721 /*
4722 Note first corner of cropping rectangle-- exit loop.
4723 */
4724 (void) XCheckDefineCursor(display,windows->image.id,cursor);
cristy49e2d862010-11-12 02:50:30 +00004725 crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4726 crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +00004727 state|=ExitState;
4728 break;
4729 }
4730 case ButtonRelease:
4731 break;
4732 case Expose:
4733 break;
4734 case KeyPress:
4735 {
4736 if (event.xkey.window != windows->image.id)
4737 break;
4738 /*
4739 Respond to a user key press.
4740 */
4741 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4742 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4743 switch ((int) key_symbol)
4744 {
4745 case XK_Escape:
4746 case XK_F20:
4747 {
4748 /*
4749 Prematurely exit.
4750 */
4751 state|=EscapeState;
4752 state|=ExitState;
4753 break;
4754 }
4755 case XK_F1:
4756 case XK_Help:
4757 {
4758 switch (mode)
4759 {
4760 case CopyMode:
4761 {
4762 XTextViewWidget(display,resource_info,windows,MagickFalse,
4763 "Help Viewer - Image Copy",ImageCopyHelp);
4764 break;
4765 }
4766 case CropMode:
4767 {
4768 XTextViewWidget(display,resource_info,windows,MagickFalse,
4769 "Help Viewer - Image Crop",ImageCropHelp);
4770 break;
4771 }
4772 case CutMode:
4773 {
4774 XTextViewWidget(display,resource_info,windows,MagickFalse,
4775 "Help Viewer - Image Cut",ImageCutHelp);
4776 break;
4777 }
4778 }
4779 break;
4780 }
4781 default:
4782 {
4783 (void) XBell(display,0);
4784 break;
4785 }
4786 }
4787 break;
4788 }
4789 case MotionNotify:
4790 {
4791 if (event.xmotion.window != windows->image.id)
4792 break;
4793 /*
4794 Map and unmap Info widget as text cursor crosses its boundaries.
4795 */
4796 x=event.xmotion.x;
4797 y=event.xmotion.y;
4798 if (windows->info.mapped != MagickFalse)
4799 {
4800 if ((x < (int) (windows->info.x+windows->info.width)) &&
4801 (y < (int) (windows->info.y+windows->info.height)))
4802 (void) XWithdrawWindow(display,windows->info.id,
4803 windows->info.screen);
4804 }
4805 else
4806 if ((x > (int) (windows->info.x+windows->info.width)) ||
4807 (y > (int) (windows->info.y+windows->info.height)))
4808 (void) XMapWindow(display,windows->info.id);
cristy49e2d862010-11-12 02:50:30 +00004809 crop_info.x=(ssize_t) windows->image.x+x;
4810 crop_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +00004811 break;
4812 }
4813 default:
4814 break;
4815 }
4816 } while ((state & ExitState) == 0);
4817 (void) XSelectInput(display,windows->image.id,
4818 windows->image.attributes.event_mask);
4819 if ((state & EscapeState) != 0)
4820 {
4821 /*
4822 User want to exit without cropping.
4823 */
4824 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4825 (void) XFreeCursor(display,cursor);
4826 return(MagickTrue);
4827 }
4828 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4829 do
4830 {
4831 /*
4832 Size rectangle as pointer moves until the mouse button is released.
4833 */
4834 x=(int) crop_info.x;
4835 y=(int) crop_info.y;
4836 crop_info.width=0;
4837 crop_info.height=0;
4838 state=DefaultState;
4839 do
4840 {
4841 highlight_info=crop_info;
4842 highlight_info.x=crop_info.x-windows->image.x;
4843 highlight_info.y=crop_info.y-windows->image.y;
4844 if ((highlight_info.width > 3) && (highlight_info.height > 3))
4845 {
4846 /*
4847 Display info and draw cropping rectangle.
4848 */
4849 if (windows->info.mapped == MagickFalse)
4850 (void) XMapWindow(display,windows->info.id);
cristye8c25f92010-06-03 00:53:06 +00004851 (void) FormatMagickString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00004852 " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +00004853 crop_info.height,(double) crop_info.x,(double) crop_info.y);
cristy3ed852e2009-09-05 21:47:34 +00004854 XInfoWidget(display,windows,text);
4855 XHighlightRectangle(display,windows->image.id,
4856 windows->image.highlight_context,&highlight_info);
4857 }
4858 else
4859 if (windows->info.mapped != MagickFalse)
4860 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4861 /*
4862 Wait for next event.
4863 */
4864 XScreenEvent(display,windows,&event);
4865 if ((highlight_info.width > 3) && (highlight_info.height > 3))
4866 XHighlightRectangle(display,windows->image.id,
4867 windows->image.highlight_context,&highlight_info);
4868 switch (event.type)
4869 {
4870 case ButtonPress:
4871 {
cristy49e2d862010-11-12 02:50:30 +00004872 crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4873 crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +00004874 break;
4875 }
4876 case ButtonRelease:
4877 {
4878 /*
4879 User has committed to cropping rectangle.
4880 */
cristy49e2d862010-11-12 02:50:30 +00004881 crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4882 crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +00004883 XSetCursorState(display,windows,MagickFalse);
4884 state|=ExitState;
4885 windows->command.data=0;
4886 (void) XCommandWidget(display,windows,RectifyModeMenu,
4887 (XEvent *) NULL);
4888 break;
4889 }
4890 case Expose:
4891 break;
4892 case MotionNotify:
4893 {
cristy49e2d862010-11-12 02:50:30 +00004894 crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4895 crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
cristy3ed852e2009-09-05 21:47:34 +00004896 }
4897 default:
4898 break;
4899 }
4900 if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4901 ((state & ExitState) != 0))
4902 {
4903 /*
4904 Check boundary conditions.
4905 */
4906 if (crop_info.x < 0)
4907 crop_info.x=0;
4908 else
cristy49e2d862010-11-12 02:50:30 +00004909 if (crop_info.x > (ssize_t) windows->image.ximage->width)
4910 crop_info.x=(ssize_t) windows->image.ximage->width;
cristy3ed852e2009-09-05 21:47:34 +00004911 if ((int) crop_info.x < x)
4912 crop_info.width=(unsigned int) (x-crop_info.x);
4913 else
4914 {
4915 crop_info.width=(unsigned int) (crop_info.x-x);
cristy49e2d862010-11-12 02:50:30 +00004916 crop_info.x=(ssize_t) x;
cristy3ed852e2009-09-05 21:47:34 +00004917 }
4918 if (crop_info.y < 0)
4919 crop_info.y=0;
4920 else
cristy49e2d862010-11-12 02:50:30 +00004921 if (crop_info.y > (ssize_t) windows->image.ximage->height)
4922 crop_info.y=(ssize_t) windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +00004923 if ((int) crop_info.y < y)
4924 crop_info.height=(unsigned int) (y-crop_info.y);
4925 else
4926 {
4927 crop_info.height=(unsigned int) (crop_info.y-y);
cristy49e2d862010-11-12 02:50:30 +00004928 crop_info.y=(ssize_t) y;
cristy3ed852e2009-09-05 21:47:34 +00004929 }
4930 }
4931 } while ((state & ExitState) == 0);
4932 /*
4933 Wait for user to grab a corner of the rectangle or press return.
4934 */
4935 state=DefaultState;
4936 (void) XMapWindow(display,windows->info.id);
4937 do
4938 {
4939 if (windows->info.mapped != MagickFalse)
4940 {
4941 /*
4942 Display pointer position.
4943 */
cristye8c25f92010-06-03 00:53:06 +00004944 (void) FormatMagickString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00004945 " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +00004946 crop_info.height,(double) crop_info.x,(double) crop_info.y);
cristy3ed852e2009-09-05 21:47:34 +00004947 XInfoWidget(display,windows,text);
4948 }
4949 highlight_info=crop_info;
4950 highlight_info.x=crop_info.x-windows->image.x;
4951 highlight_info.y=crop_info.y-windows->image.y;
4952 if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4953 {
4954 state|=EscapeState;
4955 state|=ExitState;
4956 break;
4957 }
4958 XHighlightRectangle(display,windows->image.id,
4959 windows->image.highlight_context,&highlight_info);
4960 XScreenEvent(display,windows,&event);
4961 if (event.xany.window == windows->command.id)
4962 {
4963 /*
4964 Select a command from the Command widget.
4965 */
4966 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4967 id=XCommandWidget(display,windows,RectifyModeMenu,&event);
4968 (void) XSetFunction(display,windows->image.highlight_context,
4969 GXinvert);
4970 XHighlightRectangle(display,windows->image.id,
4971 windows->image.highlight_context,&highlight_info);
4972 if (id >= 0)
4973 switch (RectifyCommands[id])
4974 {
4975 case RectifyCopyCommand:
4976 {
4977 state|=ExitState;
4978 break;
4979 }
4980 case RectifyHelpCommand:
4981 {
4982 (void) XSetFunction(display,windows->image.highlight_context,
4983 GXcopy);
4984 switch (mode)
4985 {
4986 case CopyMode:
4987 {
4988 XTextViewWidget(display,resource_info,windows,MagickFalse,
4989 "Help Viewer - Image Copy",ImageCopyHelp);
4990 break;
4991 }
4992 case CropMode:
4993 {
4994 XTextViewWidget(display,resource_info,windows,MagickFalse,
4995 "Help Viewer - Image Crop",ImageCropHelp);
4996 break;
4997 }
4998 case CutMode:
4999 {
5000 XTextViewWidget(display,resource_info,windows,MagickFalse,
5001 "Help Viewer - Image Cut",ImageCutHelp);
5002 break;
5003 }
5004 }
5005 (void) XSetFunction(display,windows->image.highlight_context,
5006 GXinvert);
5007 break;
5008 }
5009 case RectifyDismissCommand:
5010 {
5011 /*
5012 Prematurely exit.
5013 */
5014 state|=EscapeState;
5015 state|=ExitState;
5016 break;
5017 }
5018 default:
5019 break;
5020 }
5021 continue;
5022 }
5023 XHighlightRectangle(display,windows->image.id,
5024 windows->image.highlight_context,&highlight_info);
5025 switch (event.type)
5026 {
5027 case ButtonPress:
5028 {
5029 if (event.xbutton.button != Button1)
5030 break;
5031 if (event.xbutton.window != windows->image.id)
5032 break;
5033 x=windows->image.x+event.xbutton.x;
5034 y=windows->image.y+event.xbutton.y;
5035 if ((x < (int) (crop_info.x+RoiDelta)) &&
5036 (x > (int) (crop_info.x-RoiDelta)) &&
5037 (y < (int) (crop_info.y+RoiDelta)) &&
5038 (y > (int) (crop_info.y-RoiDelta)))
5039 {
cristybb503372010-05-27 20:51:26 +00005040 crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5041 crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
cristy3ed852e2009-09-05 21:47:34 +00005042 state|=UpdateConfigurationState;
5043 break;
5044 }
5045 if ((x < (int) (crop_info.x+RoiDelta)) &&
5046 (x > (int) (crop_info.x-RoiDelta)) &&
5047 (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5048 (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5049 {
cristybb503372010-05-27 20:51:26 +00005050 crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
cristy3ed852e2009-09-05 21:47:34 +00005051 state|=UpdateConfigurationState;
5052 break;
5053 }
5054 if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5055 (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5056 (y < (int) (crop_info.y+RoiDelta)) &&
5057 (y > (int) (crop_info.y-RoiDelta)))
5058 {
cristybb503372010-05-27 20:51:26 +00005059 crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
cristy3ed852e2009-09-05 21:47:34 +00005060 state|=UpdateConfigurationState;
5061 break;
5062 }
5063 if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5064 (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5065 (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5066 (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5067 {
5068 state|=UpdateConfigurationState;
5069 break;
5070 }
5071 }
5072 case ButtonRelease:
5073 {
5074 if (event.xbutton.window == windows->pan.id)
5075 if ((highlight_info.x != crop_info.x-windows->image.x) ||
5076 (highlight_info.y != crop_info.y-windows->image.y))
5077 XHighlightRectangle(display,windows->image.id,
5078 windows->image.highlight_context,&highlight_info);
5079 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5080 event.xbutton.time);
5081 break;
5082 }
5083 case Expose:
5084 {
5085 if (event.xexpose.window == windows->image.id)
5086 if (event.xexpose.count == 0)
5087 {
5088 event.xexpose.x=(int) highlight_info.x;
5089 event.xexpose.y=(int) highlight_info.y;
5090 event.xexpose.width=(int) highlight_info.width;
5091 event.xexpose.height=(int) highlight_info.height;
5092 XRefreshWindow(display,&windows->image,&event);
5093 }
5094 if (event.xexpose.window == windows->info.id)
5095 if (event.xexpose.count == 0)
5096 XInfoWidget(display,windows,text);
5097 break;
5098 }
5099 case KeyPress:
5100 {
5101 if (event.xkey.window != windows->image.id)
5102 break;
5103 /*
5104 Respond to a user key press.
5105 */
5106 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5107 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5108 switch ((int) key_symbol)
5109 {
5110 case XK_Escape:
5111 case XK_F20:
5112 state|=EscapeState;
5113 case XK_Return:
5114 {
5115 state|=ExitState;
5116 break;
5117 }
5118 case XK_Home:
5119 case XK_KP_Home:
5120 {
cristy49e2d862010-11-12 02:50:30 +00005121 crop_info.x=(ssize_t) (windows->image.width/2L-
5122 crop_info.width/2L);
5123 crop_info.y=(ssize_t) (windows->image.height/2L-
5124 crop_info.height/2L);
cristy3ed852e2009-09-05 21:47:34 +00005125 break;
5126 }
5127 case XK_Left:
5128 case XK_KP_Left:
5129 {
5130 crop_info.x--;
5131 break;
5132 }
5133 case XK_Up:
5134 case XK_KP_Up:
5135 case XK_Next:
5136 {
5137 crop_info.y--;
5138 break;
5139 }
5140 case XK_Right:
5141 case XK_KP_Right:
5142 {
5143 crop_info.x++;
5144 break;
5145 }
5146 case XK_Prior:
5147 case XK_Down:
5148 case XK_KP_Down:
5149 {
5150 crop_info.y++;
5151 break;
5152 }
5153 case XK_F1:
5154 case XK_Help:
5155 {
5156 (void) XSetFunction(display,windows->image.highlight_context,
5157 GXcopy);
5158 switch (mode)
5159 {
5160 case CopyMode:
5161 {
5162 XTextViewWidget(display,resource_info,windows,MagickFalse,
5163 "Help Viewer - Image Copy",ImageCopyHelp);
5164 break;
5165 }
5166 case CropMode:
5167 {
5168 XTextViewWidget(display,resource_info,windows,MagickFalse,
5169 "Help Viewer - Image Cropg",ImageCropHelp);
5170 break;
5171 }
5172 case CutMode:
5173 {
5174 XTextViewWidget(display,resource_info,windows,MagickFalse,
5175 "Help Viewer - Image Cutg",ImageCutHelp);
5176 break;
5177 }
5178 }
5179 (void) XSetFunction(display,windows->image.highlight_context,
5180 GXinvert);
5181 break;
5182 }
5183 default:
5184 {
5185 (void) XBell(display,0);
5186 break;
5187 }
5188 }
5189 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5190 event.xkey.time);
5191 break;
5192 }
5193 case KeyRelease:
5194 break;
5195 case MotionNotify:
5196 {
5197 if (event.xmotion.window != windows->image.id)
5198 break;
5199 /*
5200 Map and unmap Info widget as text cursor crosses its boundaries.
5201 */
5202 x=event.xmotion.x;
5203 y=event.xmotion.y;
5204 if (windows->info.mapped != MagickFalse)
5205 {
5206 if ((x < (int) (windows->info.x+windows->info.width)) &&
5207 (y < (int) (windows->info.y+windows->info.height)))
5208 (void) XWithdrawWindow(display,windows->info.id,
5209 windows->info.screen);
5210 }
5211 else
5212 if ((x > (int) (windows->info.x+windows->info.width)) ||
5213 (y > (int) (windows->info.y+windows->info.height)))
5214 (void) XMapWindow(display,windows->info.id);
cristy49e2d862010-11-12 02:50:30 +00005215 crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5216 crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
cristy3ed852e2009-09-05 21:47:34 +00005217 break;
5218 }
5219 case SelectionRequest:
5220 {
5221 XSelectionEvent
5222 notify;
5223
5224 XSelectionRequestEvent
5225 *request;
5226
5227 /*
5228 Set primary selection.
5229 */
cristye8c25f92010-06-03 00:53:06 +00005230 (void) FormatMagickString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00005231 "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +00005232 crop_info.height,(double) crop_info.x,(double) crop_info.y);
cristy3ed852e2009-09-05 21:47:34 +00005233 request=(&(event.xselectionrequest));
5234 (void) XChangeProperty(request->display,request->requestor,
5235 request->property,request->target,8,PropModeReplace,
5236 (unsigned char *) text,(int) strlen(text));
5237 notify.type=SelectionNotify;
5238 notify.display=request->display;
5239 notify.requestor=request->requestor;
5240 notify.selection=request->selection;
5241 notify.target=request->target;
5242 notify.time=request->time;
5243 if (request->property == None)
5244 notify.property=request->target;
5245 else
5246 notify.property=request->property;
5247 (void) XSendEvent(request->display,request->requestor,False,0,
5248 (XEvent *) &notify);
5249 }
5250 default:
5251 break;
5252 }
5253 if ((state & UpdateConfigurationState) != 0)
5254 {
5255 (void) XPutBackEvent(display,&event);
5256 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5257 break;
5258 }
5259 } while ((state & ExitState) == 0);
5260 } while ((state & ExitState) == 0);
5261 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5262 XSetCursorState(display,windows,MagickFalse);
5263 if ((state & EscapeState) != 0)
5264 return(MagickTrue);
5265 if (mode == CropMode)
5266 if (((int) crop_info.width != windows->image.ximage->width) ||
5267 ((int) crop_info.height != windows->image.ximage->height))
5268 {
5269 /*
5270 Reconfigure Image window as defined by cropping rectangle.
5271 */
5272 XSetCropGeometry(display,windows,&crop_info,image);
5273 windows->image.window_changes.width=(int) crop_info.width;
5274 windows->image.window_changes.height=(int) crop_info.height;
5275 (void) XConfigureImage(display,resource_info,windows,image);
5276 return(MagickTrue);
5277 }
5278 /*
5279 Copy image before applying image transforms.
5280 */
5281 XSetCursorState(display,windows,MagickTrue);
5282 XCheckRefreshWindows(display,windows);
5283 width=(unsigned int) image->columns;
5284 height=(unsigned int) image->rows;
5285 x=0;
5286 y=0;
5287 if (windows->image.crop_geometry != (char *) NULL)
5288 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5289 scale_factor=(MagickRealType) width/windows->image.ximage->width;
5290 crop_info.x+=x;
cristy49e2d862010-11-12 02:50:30 +00005291 crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +00005292 crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5293 scale_factor=(MagickRealType) height/windows->image.ximage->height;
5294 crop_info.y+=y;
cristy49e2d862010-11-12 02:50:30 +00005295 crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00005296 crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5297 crop_image=CropImage(image,&crop_info,&image->exception);
5298 XSetCursorState(display,windows,MagickFalse);
5299 if (crop_image == (Image *) NULL)
5300 return(MagickFalse);
5301 if (resource_info->copy_image != (Image *) NULL)
5302 resource_info->copy_image=DestroyImage(resource_info->copy_image);
5303 resource_info->copy_image=crop_image;
5304 if (mode == CopyMode)
5305 {
5306 (void) XConfigureImage(display,resource_info,windows,image);
5307 return(MagickTrue);
5308 }
5309 /*
5310 Cut image.
5311 */
5312 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
5313 return(MagickFalse);
5314 image->matte=MagickTrue;
5315 exception=(&image->exception);
cristy49e2d862010-11-12 02:50:30 +00005316 image_view=AcquireCacheView(image);
5317 for (y=0; y < (int) crop_info.height; y++)
cristy3ed852e2009-09-05 21:47:34 +00005318 {
cristy49e2d862010-11-12 02:50:30 +00005319 q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5320 crop_info.width,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00005321 if (q == (PixelPacket *) NULL)
5322 break;
5323 for (x=0; x < (int) crop_info.width; x++)
5324 {
5325 q->opacity=(Quantum) TransparentOpacity;
5326 q++;
5327 }
cristy49e2d862010-11-12 02:50:30 +00005328 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005329 break;
5330 }
cristy49e2d862010-11-12 02:50:30 +00005331 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005332 /*
5333 Update image configuration.
5334 */
5335 XConfigureImageColormap(display,resource_info,windows,image);
5336 (void) XConfigureImage(display,resource_info,windows,image);
5337 return(MagickTrue);
5338}
5339
5340/*
5341%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5342% %
5343% %
5344% %
5345+ X D r a w I m a g e %
5346% %
5347% %
5348% %
5349%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5350%
5351% XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5352% the image.
5353%
5354% The format of the XDrawEditImage method is:
5355%
5356% MagickBooleanType XDrawEditImage(Display *display,
5357% XResourceInfo *resource_info,XWindows *windows,Image **image)
5358%
5359% A description of each parameter follows:
5360%
5361% o display: Specifies a connection to an X server; returned from
5362% XOpenDisplay.
5363%
5364% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5365%
5366% o windows: Specifies a pointer to a XWindows structure.
5367%
5368% o image: the image.
5369%
5370*/
5371static MagickBooleanType XDrawEditImage(Display *display,
5372 XResourceInfo *resource_info,XWindows *windows,Image **image)
5373{
5374 static const char
5375 *DrawMenu[] =
5376 {
5377 "Element",
5378 "Color",
5379 "Stipple",
5380 "Width",
5381 "Undo",
5382 "Help",
5383 "Dismiss",
5384 (char *) NULL
5385 };
5386
5387 static ElementType
5388 element = PointElement;
5389
5390 static const ModeType
5391 DrawCommands[] =
5392 {
5393 DrawElementCommand,
5394 DrawColorCommand,
5395 DrawStippleCommand,
5396 DrawWidthCommand,
5397 DrawUndoCommand,
5398 DrawHelpCommand,
5399 DrawDismissCommand
5400 };
5401
5402 static Pixmap
5403 stipple = (Pixmap) NULL;
5404
5405 static unsigned int
5406 pen_id = 0,
5407 line_width = 1;
5408
5409 char
5410 command[MaxTextExtent],
5411 text[MaxTextExtent];
5412
5413 Cursor
5414 cursor;
5415
5416 int
5417 entry,
5418 id,
5419 number_coordinates,
5420 x,
5421 y;
5422
5423 MagickRealType
5424 degrees;
5425
5426 MagickStatusType
5427 status;
5428
5429 RectangleInfo
5430 rectangle_info;
5431
5432 register int
5433 i;
5434
5435 unsigned int
5436 distance,
5437 height,
5438 max_coordinates,
5439 width;
5440
cristybb503372010-05-27 20:51:26 +00005441 size_t
cristy3ed852e2009-09-05 21:47:34 +00005442 state;
5443
5444 Window
5445 root_window;
5446
5447 XDrawInfo
5448 draw_info;
5449
5450 XEvent
5451 event;
5452
5453 XPoint
5454 *coordinate_info;
5455
5456 XSegment
5457 line_info;
5458
5459 /*
5460 Allocate polygon info.
5461 */
5462 max_coordinates=2048;
5463 coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5464 sizeof(*coordinate_info));
5465 if (coordinate_info == (XPoint *) NULL)
5466 {
5467 (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
5468 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5469 return(MagickFalse);
5470 }
5471 /*
5472 Map Command widget.
5473 */
5474 (void) CloneString(&windows->command.name,"Draw");
5475 windows->command.data=4;
5476 (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5477 (void) XMapRaised(display,windows->command.id);
5478 XClientMessage(display,windows->image.id,windows->im_protocols,
5479 windows->im_update_widget,CurrentTime);
5480 /*
5481 Wait for first button press.
5482 */
5483 root_window=XRootWindow(display,XDefaultScreen(display));
5484 draw_info.stencil=OpaqueStencil;
5485 status=MagickTrue;
5486 cursor=XCreateFontCursor(display,XC_tcross);
5487 for ( ; ; )
5488 {
5489 XQueryPosition(display,windows->image.id,&x,&y);
5490 (void) XSelectInput(display,windows->image.id,
5491 windows->image.attributes.event_mask | PointerMotionMask);
5492 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5493 state=DefaultState;
5494 do
5495 {
5496 if (windows->info.mapped != MagickFalse)
5497 {
5498 /*
5499 Display pointer position.
5500 */
5501 (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
5502 x+windows->image.x,y+windows->image.y);
5503 XInfoWidget(display,windows,text);
5504 }
5505 /*
5506 Wait for next event.
5507 */
5508 XScreenEvent(display,windows,&event);
5509 if (event.xany.window == windows->command.id)
5510 {
5511 /*
5512 Select a command from the Command widget.
5513 */
5514 id=XCommandWidget(display,windows,DrawMenu,&event);
5515 if (id < 0)
5516 continue;
5517 switch (DrawCommands[id])
5518 {
5519 case DrawElementCommand:
5520 {
5521 static const char
5522 *Elements[] =
5523 {
5524 "point",
5525 "line",
5526 "rectangle",
5527 "fill rectangle",
5528 "circle",
5529 "fill circle",
5530 "ellipse",
5531 "fill ellipse",
5532 "polygon",
5533 "fill polygon",
5534 (char *) NULL,
5535 };
5536
5537 /*
5538 Select a command from the pop-up menu.
5539 */
5540 element=(ElementType) (XMenuWidget(display,windows,
5541 DrawMenu[id],Elements,command)+1);
5542 break;
5543 }
5544 case DrawColorCommand:
5545 {
5546 const char
5547 *ColorMenu[MaxNumberPens+1];
5548
5549 int
5550 pen_number;
5551
5552 MagickBooleanType
5553 transparent;
5554
5555 XColor
5556 color;
5557
5558 /*
5559 Initialize menu selections.
5560 */
5561 for (i=0; i < (int) (MaxNumberPens-2); i++)
5562 ColorMenu[i]=resource_info->pen_colors[i];
5563 ColorMenu[MaxNumberPens-2]="transparent";
5564 ColorMenu[MaxNumberPens-1]="Browser...";
5565 ColorMenu[MaxNumberPens]=(char *) NULL;
5566 /*
5567 Select a pen color from the pop-up menu.
5568 */
5569 pen_number=XMenuWidget(display,windows,DrawMenu[id],
5570 (const char **) ColorMenu,command);
5571 if (pen_number < 0)
5572 break;
5573 transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5574 MagickFalse;
5575 if (transparent != MagickFalse)
5576 {
5577 draw_info.stencil=TransparentStencil;
5578 break;
5579 }
5580 if (pen_number == (MaxNumberPens-1))
5581 {
5582 static char
5583 color_name[MaxTextExtent] = "gray";
5584
5585 /*
5586 Select a pen color from a dialog.
5587 */
5588 resource_info->pen_colors[pen_number]=color_name;
5589 XColorBrowserWidget(display,windows,"Select",color_name);
5590 if (*color_name == '\0')
5591 break;
5592 }
5593 /*
5594 Set pen color.
5595 */
5596 (void) XParseColor(display,windows->map_info->colormap,
5597 resource_info->pen_colors[pen_number],&color);
5598 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5599 (unsigned int) MaxColors,&color);
5600 windows->pixel_info->pen_colors[pen_number]=color;
5601 pen_id=(unsigned int) pen_number;
5602 draw_info.stencil=OpaqueStencil;
5603 break;
5604 }
5605 case DrawStippleCommand:
5606 {
5607 Image
5608 *stipple_image;
5609
5610 ImageInfo
5611 *image_info;
5612
5613 int
5614 status;
5615
5616 static char
5617 filename[MaxTextExtent] = "\0";
5618
5619 static const char
5620 *StipplesMenu[] =
5621 {
5622 "Brick",
5623 "Diagonal",
5624 "Scales",
5625 "Vertical",
5626 "Wavy",
5627 "Translucent",
5628 "Opaque",
5629 (char *) NULL,
5630 (char *) NULL,
5631 };
5632
5633 /*
5634 Select a command from the pop-up menu.
5635 */
5636 StipplesMenu[7]="Open...";
5637 entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5638 command);
5639 if (entry < 0)
5640 break;
5641 if (stipple != (Pixmap) NULL)
5642 (void) XFreePixmap(display,stipple);
5643 stipple=(Pixmap) NULL;
cristy3ed852e2009-09-05 21:47:34 +00005644 if (entry != 7)
5645 {
5646 switch (entry)
5647 {
5648 case 0:
5649 {
5650 stipple=XCreateBitmapFromData(display,root_window,
5651 (char *) BricksBitmap,BricksWidth,BricksHeight);
5652 break;
5653 }
5654 case 1:
5655 {
5656 stipple=XCreateBitmapFromData(display,root_window,
5657 (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5658 break;
5659 }
5660 case 2:
5661 {
5662 stipple=XCreateBitmapFromData(display,root_window,
5663 (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5664 break;
5665 }
5666 case 3:
5667 {
5668 stipple=XCreateBitmapFromData(display,root_window,
5669 (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5670 break;
5671 }
5672 case 4:
5673 {
5674 stipple=XCreateBitmapFromData(display,root_window,
5675 (char *) WavyBitmap,WavyWidth,WavyHeight);
5676 break;
5677 }
5678 case 5:
cristy3ed852e2009-09-05 21:47:34 +00005679 {
5680 stipple=XCreateBitmapFromData(display,root_window,
5681 (char *) HighlightBitmap,HighlightWidth,
5682 HighlightHeight);
5683 break;
5684 }
cristydd05beb2010-11-21 21:23:39 +00005685 case 6:
5686 default:
5687 {
5688 stipple=XCreateBitmapFromData(display,root_window,
5689 (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5690 break;
5691 }
cristy3ed852e2009-09-05 21:47:34 +00005692 }
5693 break;
5694 }
5695 XFileBrowserWidget(display,windows,"Stipple",filename);
5696 if (*filename == '\0')
5697 break;
5698 /*
5699 Read image.
5700 */
5701 XSetCursorState(display,windows,MagickTrue);
5702 XCheckRefreshWindows(display,windows);
5703 image_info=AcquireImageInfo();
5704 (void) CopyMagickString(image_info->filename,filename,
5705 MaxTextExtent);
5706 stipple_image=ReadImage(image_info,&(*image)->exception);
5707 CatchException(&(*image)->exception);
5708 XSetCursorState(display,windows,MagickFalse);
5709 if (stipple_image == (Image *) NULL)
5710 break;
5711 (void) AcquireUniqueFileResource(filename);
5712 (void) FormatMagickString(stipple_image->filename,MaxTextExtent,
5713 "xbm:%s",filename);
5714 (void) WriteImage(image_info,stipple_image);
5715 stipple_image=DestroyImage(stipple_image);
5716 image_info=DestroyImageInfo(image_info);
5717 status=XReadBitmapFile(display,root_window,filename,&width,
5718 &height,&stipple,&x,&y);
5719 (void) RelinquishUniqueFileResource(filename);
5720 if ((status != BitmapSuccess) != 0)
5721 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5722 filename);
5723 break;
5724 }
5725 case DrawWidthCommand:
5726 {
5727 static char
5728 width[MaxTextExtent] = "0";
5729
5730 static const char
5731 *WidthsMenu[] =
5732 {
5733 "1",
5734 "2",
5735 "4",
5736 "8",
5737 "16",
5738 "Dialog...",
5739 (char *) NULL,
5740 };
5741
5742 /*
5743 Select a command from the pop-up menu.
5744 */
5745 entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5746 command);
5747 if (entry < 0)
5748 break;
5749 if (entry != 5)
5750 {
cristydd05beb2010-11-21 21:23:39 +00005751 line_width=(unsigned int) StringToUnsignedLong(
5752 WidthsMenu[entry]);
cristy3ed852e2009-09-05 21:47:34 +00005753 break;
5754 }
5755 (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5756 width);
5757 if (*width == '\0')
5758 break;
cristye27293e2009-12-18 02:53:20 +00005759 line_width=(unsigned int) StringToUnsignedLong(width);
cristy3ed852e2009-09-05 21:47:34 +00005760 break;
5761 }
5762 case DrawUndoCommand:
5763 {
5764 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5765 image);
5766 break;
5767 }
5768 case DrawHelpCommand:
5769 {
5770 XTextViewWidget(display,resource_info,windows,MagickFalse,
5771 "Help Viewer - Image Rotation",ImageDrawHelp);
5772 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5773 break;
5774 }
5775 case DrawDismissCommand:
5776 {
5777 /*
5778 Prematurely exit.
5779 */
5780 state|=EscapeState;
5781 state|=ExitState;
5782 break;
5783 }
5784 default:
5785 break;
5786 }
5787 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5788 continue;
5789 }
5790 switch (event.type)
5791 {
5792 case ButtonPress:
5793 {
5794 if (event.xbutton.button != Button1)
5795 break;
5796 if (event.xbutton.window != windows->image.id)
5797 break;
5798 /*
5799 exit loop.
5800 */
5801 x=event.xbutton.x;
5802 y=event.xbutton.y;
5803 state|=ExitState;
5804 break;
5805 }
5806 case ButtonRelease:
5807 break;
5808 case Expose:
5809 break;
5810 case KeyPress:
5811 {
5812 KeySym
5813 key_symbol;
5814
5815 if (event.xkey.window != windows->image.id)
5816 break;
5817 /*
5818 Respond to a user key press.
5819 */
5820 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5821 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5822 switch ((int) key_symbol)
5823 {
5824 case XK_Escape:
5825 case XK_F20:
5826 {
5827 /*
5828 Prematurely exit.
5829 */
5830 state|=EscapeState;
5831 state|=ExitState;
5832 break;
5833 }
5834 case XK_F1:
5835 case XK_Help:
5836 {
5837 XTextViewWidget(display,resource_info,windows,MagickFalse,
5838 "Help Viewer - Image Rotation",ImageDrawHelp);
5839 break;
5840 }
5841 default:
5842 {
5843 (void) XBell(display,0);
5844 break;
5845 }
5846 }
5847 break;
5848 }
5849 case MotionNotify:
5850 {
5851 /*
5852 Map and unmap Info widget as text cursor crosses its boundaries.
5853 */
5854 x=event.xmotion.x;
5855 y=event.xmotion.y;
5856 if (windows->info.mapped != MagickFalse)
5857 {
5858 if ((x < (int) (windows->info.x+windows->info.width)) &&
5859 (y < (int) (windows->info.y+windows->info.height)))
5860 (void) XWithdrawWindow(display,windows->info.id,
5861 windows->info.screen);
5862 }
5863 else
5864 if ((x > (int) (windows->info.x+windows->info.width)) ||
5865 (y > (int) (windows->info.y+windows->info.height)))
5866 (void) XMapWindow(display,windows->info.id);
5867 break;
5868 }
5869 }
5870 } while ((state & ExitState) == 0);
5871 (void) XSelectInput(display,windows->image.id,
5872 windows->image.attributes.event_mask);
5873 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5874 if ((state & EscapeState) != 0)
5875 break;
5876 /*
5877 Draw element as pointer moves until the button is released.
5878 */
5879 distance=0;
5880 degrees=0.0;
5881 line_info.x1=x;
5882 line_info.y1=y;
5883 line_info.x2=x;
5884 line_info.y2=y;
cristy49e2d862010-11-12 02:50:30 +00005885 rectangle_info.x=(ssize_t) x;
5886 rectangle_info.y=(ssize_t) y;
cristy3ed852e2009-09-05 21:47:34 +00005887 rectangle_info.width=0;
5888 rectangle_info.height=0;
5889 number_coordinates=1;
5890 coordinate_info->x=x;
5891 coordinate_info->y=y;
5892 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5893 state=DefaultState;
5894 do
5895 {
5896 switch (element)
5897 {
5898 case PointElement:
5899 default:
5900 {
5901 if (number_coordinates > 1)
5902 {
5903 (void) XDrawLines(display,windows->image.id,
5904 windows->image.highlight_context,coordinate_info,
5905 number_coordinates,CoordModeOrigin);
5906 (void) FormatMagickString(text,MaxTextExtent," %+d%+d",
5907 coordinate_info[number_coordinates-1].x,
5908 coordinate_info[number_coordinates-1].y);
5909 XInfoWidget(display,windows,text);
5910 }
5911 break;
5912 }
5913 case LineElement:
5914 {
5915 if (distance > 9)
5916 {
5917 /*
5918 Display angle of the line.
5919 */
5920 degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5921 line_info.y1),(double) (line_info.x2-line_info.x1)));
cristye7f51092010-01-17 00:39:37 +00005922 (void) FormatMagickString(text,MaxTextExtent," %g",
cristy3ed852e2009-09-05 21:47:34 +00005923 (double) degrees);
5924 XInfoWidget(display,windows,text);
5925 XHighlightLine(display,windows->image.id,
5926 windows->image.highlight_context,&line_info);
5927 }
5928 else
5929 if (windows->info.mapped != MagickFalse)
5930 (void) XWithdrawWindow(display,windows->info.id,
5931 windows->info.screen);
5932 break;
5933 }
5934 case RectangleElement:
5935 case FillRectangleElement:
5936 {
5937 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5938 {
5939 /*
5940 Display info and draw drawing rectangle.
5941 */
cristye8c25f92010-06-03 00:53:06 +00005942 (void) FormatMagickString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00005943 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
cristye8c25f92010-06-03 00:53:06 +00005944 (double) rectangle_info.height,(double) rectangle_info.x,
5945 (double) rectangle_info.y);
cristy3ed852e2009-09-05 21:47:34 +00005946 XInfoWidget(display,windows,text);
5947 XHighlightRectangle(display,windows->image.id,
5948 windows->image.highlight_context,&rectangle_info);
5949 }
5950 else
5951 if (windows->info.mapped != MagickFalse)
5952 (void) XWithdrawWindow(display,windows->info.id,
5953 windows->info.screen);
5954 break;
5955 }
5956 case CircleElement:
5957 case FillCircleElement:
5958 case EllipseElement:
5959 case FillEllipseElement:
5960 {
5961 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5962 {
5963 /*
5964 Display info and draw drawing rectangle.
5965 */
cristye8c25f92010-06-03 00:53:06 +00005966 (void) FormatMagickString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00005967 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
cristye8c25f92010-06-03 00:53:06 +00005968 (double) rectangle_info.height,(double) rectangle_info.x,
5969 (double) rectangle_info.y);
cristy3ed852e2009-09-05 21:47:34 +00005970 XInfoWidget(display,windows,text);
5971 XHighlightEllipse(display,windows->image.id,
5972 windows->image.highlight_context,&rectangle_info);
5973 }
5974 else
5975 if (windows->info.mapped != MagickFalse)
5976 (void) XWithdrawWindow(display,windows->info.id,
5977 windows->info.screen);
5978 break;
5979 }
5980 case PolygonElement:
5981 case FillPolygonElement:
5982 {
5983 if (number_coordinates > 1)
5984 (void) XDrawLines(display,windows->image.id,
5985 windows->image.highlight_context,coordinate_info,
5986 number_coordinates,CoordModeOrigin);
5987 if (distance > 9)
5988 {
5989 /*
5990 Display angle of the line.
5991 */
5992 degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5993 line_info.y1),(double) (line_info.x2-line_info.x1)));
cristye7f51092010-01-17 00:39:37 +00005994 (void) FormatMagickString(text,MaxTextExtent," %g",
cristy3ed852e2009-09-05 21:47:34 +00005995 (double) degrees);
5996 XInfoWidget(display,windows,text);
5997 XHighlightLine(display,windows->image.id,
5998 windows->image.highlight_context,&line_info);
5999 }
6000 else
6001 if (windows->info.mapped != MagickFalse)
6002 (void) XWithdrawWindow(display,windows->info.id,
6003 windows->info.screen);
6004 break;
6005 }
6006 }
6007 /*
6008 Wait for next event.
6009 */
6010 XScreenEvent(display,windows,&event);
6011 switch (element)
6012 {
6013 case PointElement:
6014 default:
6015 {
6016 if (number_coordinates > 1)
6017 (void) XDrawLines(display,windows->image.id,
6018 windows->image.highlight_context,coordinate_info,
6019 number_coordinates,CoordModeOrigin);
6020 break;
6021 }
6022 case LineElement:
6023 {
6024 if (distance > 9)
6025 XHighlightLine(display,windows->image.id,
6026 windows->image.highlight_context,&line_info);
6027 break;
6028 }
6029 case RectangleElement:
6030 case FillRectangleElement:
6031 {
6032 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6033 XHighlightRectangle(display,windows->image.id,
6034 windows->image.highlight_context,&rectangle_info);
6035 break;
6036 }
6037 case CircleElement:
6038 case FillCircleElement:
6039 case EllipseElement:
6040 case FillEllipseElement:
6041 {
6042 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6043 XHighlightEllipse(display,windows->image.id,
6044 windows->image.highlight_context,&rectangle_info);
6045 break;
6046 }
6047 case PolygonElement:
6048 case FillPolygonElement:
6049 {
6050 if (number_coordinates > 1)
6051 (void) XDrawLines(display,windows->image.id,
6052 windows->image.highlight_context,coordinate_info,
6053 number_coordinates,CoordModeOrigin);
6054 if (distance > 9)
6055 XHighlightLine(display,windows->image.id,
6056 windows->image.highlight_context,&line_info);
6057 break;
6058 }
6059 }
6060 switch (event.type)
6061 {
6062 case ButtonPress:
6063 break;
6064 case ButtonRelease:
6065 {
6066 /*
6067 User has committed to element.
6068 */
6069 line_info.x2=event.xbutton.x;
6070 line_info.y2=event.xbutton.y;
cristy49e2d862010-11-12 02:50:30 +00006071 rectangle_info.x=(ssize_t) event.xbutton.x;
6072 rectangle_info.y=(ssize_t) event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +00006073 coordinate_info[number_coordinates].x=event.xbutton.x;
6074 coordinate_info[number_coordinates].y=event.xbutton.y;
6075 if (((element != PolygonElement) &&
6076 (element != FillPolygonElement)) || (distance <= 9))
6077 {
6078 state|=ExitState;
6079 break;
6080 }
6081 number_coordinates++;
6082 if (number_coordinates < (int) max_coordinates)
6083 {
6084 line_info.x1=event.xbutton.x;
6085 line_info.y1=event.xbutton.y;
6086 break;
6087 }
6088 max_coordinates<<=1;
6089 coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6090 max_coordinates,sizeof(*coordinate_info));
6091 if (coordinate_info == (XPoint *) NULL)
6092 (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6093 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6094 break;
6095 }
6096 case Expose:
6097 break;
6098 case MotionNotify:
6099 {
6100 if (event.xmotion.window != windows->image.id)
6101 break;
6102 if (element != PointElement)
6103 {
6104 line_info.x2=event.xmotion.x;
6105 line_info.y2=event.xmotion.y;
cristy49e2d862010-11-12 02:50:30 +00006106 rectangle_info.x=(ssize_t) event.xmotion.x;
6107 rectangle_info.y=(ssize_t) event.xmotion.y;
cristy3ed852e2009-09-05 21:47:34 +00006108 break;
6109 }
6110 coordinate_info[number_coordinates].x=event.xbutton.x;
6111 coordinate_info[number_coordinates].y=event.xbutton.y;
6112 number_coordinates++;
6113 if (number_coordinates < (int) max_coordinates)
6114 break;
6115 max_coordinates<<=1;
6116 coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6117 max_coordinates,sizeof(*coordinate_info));
6118 if (coordinate_info == (XPoint *) NULL)
6119 (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6120 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6121 break;
6122 }
6123 default:
6124 break;
6125 }
6126 /*
6127 Check boundary conditions.
6128 */
6129 if (line_info.x2 < 0)
6130 line_info.x2=0;
6131 else
6132 if (line_info.x2 > (int) windows->image.width)
6133 line_info.x2=(short) windows->image.width;
6134 if (line_info.y2 < 0)
6135 line_info.y2=0;
6136 else
6137 if (line_info.y2 > (int) windows->image.height)
6138 line_info.y2=(short) windows->image.height;
6139 distance=(unsigned int)
6140 (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6141 ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6142 if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6143 ((state & ExitState) != 0))
6144 {
6145 if (rectangle_info.x < 0)
6146 rectangle_info.x=0;
6147 else
cristy49e2d862010-11-12 02:50:30 +00006148 if (rectangle_info.x > (ssize_t) windows->image.width)
cristybb503372010-05-27 20:51:26 +00006149 rectangle_info.x=(ssize_t) windows->image.width;
cristy3ed852e2009-09-05 21:47:34 +00006150 if ((int) rectangle_info.x < x)
6151 rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6152 else
6153 {
6154 rectangle_info.width=(unsigned int) (rectangle_info.x-x);
cristy49e2d862010-11-12 02:50:30 +00006155 rectangle_info.x=(ssize_t) x;
cristy3ed852e2009-09-05 21:47:34 +00006156 }
6157 if (rectangle_info.y < 0)
6158 rectangle_info.y=0;
6159 else
cristy49e2d862010-11-12 02:50:30 +00006160 if (rectangle_info.y > (ssize_t) windows->image.height)
cristybb503372010-05-27 20:51:26 +00006161 rectangle_info.y=(ssize_t) windows->image.height;
cristy3ed852e2009-09-05 21:47:34 +00006162 if ((int) rectangle_info.y < y)
6163 rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6164 else
6165 {
6166 rectangle_info.height=(unsigned int) (rectangle_info.y-y);
cristy49e2d862010-11-12 02:50:30 +00006167 rectangle_info.y=(ssize_t) y;
cristy3ed852e2009-09-05 21:47:34 +00006168 }
6169 }
6170 } while ((state & ExitState) == 0);
6171 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6172 if ((element == PointElement) || (element == PolygonElement) ||
6173 (element == FillPolygonElement))
6174 {
6175 /*
6176 Determine polygon bounding box.
6177 */
cristy49e2d862010-11-12 02:50:30 +00006178 rectangle_info.x=(ssize_t) coordinate_info->x;
6179 rectangle_info.y=(ssize_t) coordinate_info->y;
cristy3ed852e2009-09-05 21:47:34 +00006180 x=coordinate_info->x;
6181 y=coordinate_info->y;
6182 for (i=1; i < number_coordinates; i++)
6183 {
6184 if (coordinate_info[i].x > x)
6185 x=coordinate_info[i].x;
6186 if (coordinate_info[i].y > y)
6187 y=coordinate_info[i].y;
cristy49e2d862010-11-12 02:50:30 +00006188 if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6189 rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6190 if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6191 rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
cristy3ed852e2009-09-05 21:47:34 +00006192 }
cristybb503372010-05-27 20:51:26 +00006193 rectangle_info.width=(size_t) (x-rectangle_info.x);
6194 rectangle_info.height=(size_t) (y-rectangle_info.y);
cristy3ed852e2009-09-05 21:47:34 +00006195 for (i=0; i < number_coordinates; i++)
6196 {
6197 coordinate_info[i].x-=rectangle_info.x;
6198 coordinate_info[i].y-=rectangle_info.y;
6199 }
6200 }
6201 else
6202 if (distance <= 9)
6203 continue;
6204 else
6205 if ((element == RectangleElement) ||
6206 (element == CircleElement) || (element == EllipseElement))
6207 {
6208 rectangle_info.width--;
6209 rectangle_info.height--;
6210 }
6211 /*
6212 Drawing is relative to image configuration.
6213 */
6214 draw_info.x=(int) rectangle_info.x;
6215 draw_info.y=(int) rectangle_info.y;
6216 (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6217 image);
6218 width=(unsigned int) (*image)->columns;
6219 height=(unsigned int) (*image)->rows;
6220 x=0;
6221 y=0;
6222 if (windows->image.crop_geometry != (char *) NULL)
6223 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6224 draw_info.x+=windows->image.x-(line_width/2);
6225 if (draw_info.x < 0)
6226 draw_info.x=0;
6227 draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6228 draw_info.y+=windows->image.y-(line_width/2);
6229 if (draw_info.y < 0)
6230 draw_info.y=0;
6231 draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6232 draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6233 if (draw_info.width > (unsigned int) (*image)->columns)
6234 draw_info.width=(unsigned int) (*image)->columns;
6235 draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6236 if (draw_info.height > (unsigned int) (*image)->rows)
6237 draw_info.height=(unsigned int) (*image)->rows;
6238 (void) FormatMagickString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6239 width*draw_info.width/windows->image.ximage->width,
6240 height*draw_info.height/windows->image.ximage->height,
6241 draw_info.x+x,draw_info.y+y);
6242 /*
6243 Initialize drawing attributes.
6244 */
6245 draw_info.degrees=0.0;
6246 draw_info.element=element;
6247 draw_info.stipple=stipple;
6248 draw_info.line_width=line_width;
6249 draw_info.line_info=line_info;
6250 if (line_info.x1 > (int) (line_width/2))
6251 draw_info.line_info.x1=(short) line_width/2;
6252 if (line_info.y1 > (int) (line_width/2))
6253 draw_info.line_info.y1=(short) line_width/2;
6254 draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6255 draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6256 if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6257 {
6258 draw_info.line_info.x2=(-draw_info.line_info.x2);
6259 draw_info.line_info.y2=(-draw_info.line_info.y2);
6260 }
6261 if (draw_info.line_info.x2 < 0)
6262 {
6263 draw_info.line_info.x2=(-draw_info.line_info.x2);
6264 Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6265 }
6266 if (draw_info.line_info.y2 < 0)
6267 {
6268 draw_info.line_info.y2=(-draw_info.line_info.y2);
6269 Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6270 }
6271 draw_info.rectangle_info=rectangle_info;
cristy49e2d862010-11-12 02:50:30 +00006272 if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
cristybb503372010-05-27 20:51:26 +00006273 draw_info.rectangle_info.x=(ssize_t) line_width/2;
cristy49e2d862010-11-12 02:50:30 +00006274 if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
cristybb503372010-05-27 20:51:26 +00006275 draw_info.rectangle_info.y=(ssize_t) line_width/2;
cristy3ed852e2009-09-05 21:47:34 +00006276 draw_info.number_coordinates=(unsigned int) number_coordinates;
6277 draw_info.coordinate_info=coordinate_info;
6278 windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6279 /*
6280 Draw element on image.
6281 */
6282 XSetCursorState(display,windows,MagickTrue);
6283 XCheckRefreshWindows(display,windows);
6284 status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6285 XSetCursorState(display,windows,MagickFalse);
6286 /*
6287 Update image colormap and return to image drawing.
6288 */
6289 XConfigureImageColormap(display,resource_info,windows,*image);
6290 (void) XConfigureImage(display,resource_info,windows,*image);
6291 }
6292 XSetCursorState(display,windows,MagickFalse);
6293 coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6294 return(status != 0 ? MagickTrue : MagickFalse);
6295}
6296
6297/*
6298%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6299% %
6300% %
6301% %
6302+ X D r a w P a n R e c t a n g l e %
6303% %
6304% %
6305% %
6306%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6307%
6308% XDrawPanRectangle() draws a rectangle in the pan window. The pan window
6309% displays a zoom image and the rectangle shows which portion of the image is
6310% displayed in the Image window.
6311%
6312% The format of the XDrawPanRectangle method is:
6313%
6314% XDrawPanRectangle(Display *display,XWindows *windows)
6315%
6316% A description of each parameter follows:
6317%
6318% o display: Specifies a connection to an X server; returned from
6319% XOpenDisplay.
6320%
6321% o windows: Specifies a pointer to a XWindows structure.
6322%
6323*/
6324static void XDrawPanRectangle(Display *display,XWindows *windows)
6325{
6326 MagickRealType
6327 scale_factor;
6328
6329 RectangleInfo
6330 highlight_info;
6331
6332 /*
6333 Determine dimensions of the panning rectangle.
6334 */
6335 scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
cristy49e2d862010-11-12 02:50:30 +00006336 highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +00006337 highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6338 scale_factor=(MagickRealType)
6339 windows->pan.height/windows->image.ximage->height;
cristy49e2d862010-11-12 02:50:30 +00006340 highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00006341 highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6342 /*
6343 Display the panning rectangle.
6344 */
6345 (void) XClearWindow(display,windows->pan.id);
6346 XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6347 &highlight_info);
6348}
6349
6350/*
6351%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6352% %
6353% %
6354% %
6355+ X I m a g e C a c h e %
6356% %
6357% %
6358% %
6359%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6360%
6361% XImageCache() handles the creation, manipulation, and destruction of the
6362% image cache (undo and redo buffers).
6363%
6364% The format of the XImageCache method is:
6365%
6366% void XImageCache(Display *display,XResourceInfo *resource_info,
6367% XWindows *windows,const CommandType command,Image **image)
6368%
6369% A description of each parameter follows:
6370%
6371% o display: Specifies a connection to an X server; returned from
6372% XOpenDisplay.
6373%
6374% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6375%
6376% o windows: Specifies a pointer to a XWindows structure.
6377%
6378% o command: Specifies a command to perform.
6379%
cristya9a86bb2011-01-13 01:11:00 +00006380% o image: the image; XImageCache may transform the image and return a new
6381% image pointer.
cristy3ed852e2009-09-05 21:47:34 +00006382%
6383*/
6384static void XImageCache(Display *display,XResourceInfo *resource_info,
6385 XWindows *windows,const CommandType command,Image **image)
6386{
6387 Image
6388 *cache_image;
6389
6390 static Image
6391 *redo_image = (Image *) NULL,
6392 *undo_image = (Image *) NULL;
6393
6394 switch (command)
6395 {
6396 case FreeBuffersCommand:
6397 {
6398 /*
6399 Free memory from the undo and redo cache.
6400 */
6401 while (undo_image != (Image *) NULL)
6402 {
6403 cache_image=undo_image;
6404 undo_image=GetPreviousImageInList(undo_image);
6405 cache_image->list=DestroyImage(cache_image->list);
6406 cache_image=DestroyImage(cache_image);
6407 }
6408 undo_image=NewImageList();
6409 if (redo_image != (Image *) NULL)
6410 redo_image=DestroyImage(redo_image);
6411 redo_image=NewImageList();
6412 return;
6413 }
6414 case UndoCommand:
6415 {
cristya9a86bb2011-01-13 01:11:00 +00006416 char
6417 image_geometry[MaxTextExtent];
6418
cristy3ed852e2009-09-05 21:47:34 +00006419 /*
6420 Undo the last image transformation.
6421 */
6422 if (undo_image == (Image *) NULL)
6423 {
6424 (void) XBell(display,0);
6425 return;
6426 }
6427 cache_image=undo_image;
6428 undo_image=GetPreviousImageInList(undo_image);
6429 windows->image.window_changes.width=(int) cache_image->columns;
6430 windows->image.window_changes.height=(int) cache_image->rows;
cristya9a86bb2011-01-13 01:11:00 +00006431 (void) FormatMagickString(image_geometry,MaxTextExtent,"%dx%d!",
6432 windows->image.ximage->width,windows->image.ximage->height);
6433 (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
cristy3ed852e2009-09-05 21:47:34 +00006434 if (windows->image.crop_geometry != (char *) NULL)
6435 windows->image.crop_geometry=(char *)
6436 RelinquishMagickMemory(windows->image.crop_geometry);
6437 windows->image.crop_geometry=cache_image->geometry;
6438 if (redo_image != (Image *) NULL)
6439 redo_image=DestroyImage(redo_image);
6440 redo_image=(*image);
6441 *image=cache_image->list;
6442 cache_image=DestroyImage(cache_image);
6443 if (windows->image.orphan != MagickFalse)
6444 return;
6445 XConfigureImageColormap(display,resource_info,windows,*image);
6446 (void) XConfigureImage(display,resource_info,windows,*image);
6447 return;
6448 }
6449 case CutCommand:
6450 case PasteCommand:
6451 case ApplyCommand:
6452 case HalfSizeCommand:
6453 case OriginalSizeCommand:
6454 case DoubleSizeCommand:
6455 case ResizeCommand:
6456 case TrimCommand:
6457 case CropCommand:
6458 case ChopCommand:
6459 case FlipCommand:
6460 case FlopCommand:
6461 case RotateRightCommand:
6462 case RotateLeftCommand:
6463 case RotateCommand:
6464 case ShearCommand:
6465 case RollCommand:
6466 case NegateCommand:
6467 case ContrastStretchCommand:
6468 case SigmoidalContrastCommand:
6469 case NormalizeCommand:
6470 case EqualizeCommand:
6471 case HueCommand:
6472 case SaturationCommand:
6473 case BrightnessCommand:
6474 case GammaCommand:
6475 case SpiffCommand:
6476 case DullCommand:
6477 case GrayscaleCommand:
6478 case MapCommand:
6479 case QuantizeCommand:
6480 case DespeckleCommand:
6481 case EmbossCommand:
6482 case ReduceNoiseCommand:
6483 case AddNoiseCommand:
6484 case SharpenCommand:
6485 case BlurCommand:
6486 case ThresholdCommand:
6487 case EdgeDetectCommand:
6488 case SpreadCommand:
6489 case ShadeCommand:
6490 case RaiseCommand:
6491 case SegmentCommand:
6492 case SolarizeCommand:
6493 case SepiaToneCommand:
6494 case SwirlCommand:
6495 case ImplodeCommand:
6496 case VignetteCommand:
6497 case WaveCommand:
6498 case OilPaintCommand:
6499 case CharcoalDrawCommand:
6500 case AnnotateCommand:
6501 case AddBorderCommand:
6502 case AddFrameCommand:
6503 case CompositeCommand:
6504 case CommentCommand:
6505 case LaunchCommand:
6506 case RegionofInterestCommand:
6507 case SaveToUndoBufferCommand:
6508 case RedoCommand:
6509 {
6510 Image
6511 *previous_image;
6512
cristybb503372010-05-27 20:51:26 +00006513 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006514 bytes;
6515
cristybb503372010-05-27 20:51:26 +00006516 bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
cristy3ed852e2009-09-05 21:47:34 +00006517 if (undo_image != (Image *) NULL)
6518 {
6519 /*
cristya9a86bb2011-01-13 01:11:00 +00006520 Ensure the undo cache has enough memory available.
cristy3ed852e2009-09-05 21:47:34 +00006521 */
6522 previous_image=undo_image;
6523 while (previous_image != (Image *) NULL)
6524 {
6525 bytes+=previous_image->list->columns*previous_image->list->rows*
6526 sizeof(PixelPacket);
cristybb503372010-05-27 20:51:26 +00006527 if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
cristy3ed852e2009-09-05 21:47:34 +00006528 {
6529 previous_image=GetPreviousImageInList(previous_image);
6530 continue;
6531 }
6532 bytes-=previous_image->list->columns*previous_image->list->rows*
6533 sizeof(PixelPacket);
6534 if (previous_image == undo_image)
6535 undo_image=NewImageList();
6536 else
6537 previous_image->next->previous=NewImageList();
6538 break;
6539 }
6540 while (previous_image != (Image *) NULL)
6541 {
6542 /*
6543 Delete any excess memory from undo cache.
6544 */
6545 cache_image=previous_image;
6546 previous_image=GetPreviousImageInList(previous_image);
6547 cache_image->list=DestroyImage(cache_image->list);
6548 cache_image=DestroyImage(cache_image);
6549 }
6550 }
cristybb503372010-05-27 20:51:26 +00006551 if (bytes > (ssize_t) (resource_info->undo_cache << 20))
cristy3ed852e2009-09-05 21:47:34 +00006552 break;
6553 /*
6554 Save image before transformations are applied.
6555 */
6556 cache_image=AcquireImage((ImageInfo *) NULL);
6557 if (cache_image == (Image *) NULL)
6558 break;
6559 XSetCursorState(display,windows,MagickTrue);
6560 XCheckRefreshWindows(display,windows);
6561 cache_image->list=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
6562 XSetCursorState(display,windows,MagickFalse);
6563 if (cache_image->list == (Image *) NULL)
6564 {
6565 cache_image=DestroyImage(cache_image);
6566 break;
6567 }
cristybb503372010-05-27 20:51:26 +00006568 cache_image->columns=(size_t) windows->image.ximage->width;
6569 cache_image->rows=(size_t) windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +00006570 cache_image->geometry=windows->image.crop_geometry;
6571 if (windows->image.crop_geometry != (char *) NULL)
6572 {
6573 cache_image->geometry=AcquireString((char *) NULL);
6574 (void) CopyMagickString(cache_image->geometry,
6575 windows->image.crop_geometry,MaxTextExtent);
6576 }
6577 if (undo_image == (Image *) NULL)
6578 {
6579 undo_image=cache_image;
6580 break;
6581 }
6582 undo_image->next=cache_image;
6583 undo_image->next->previous=undo_image;
6584 undo_image=undo_image->next;
6585 break;
6586 }
6587 default:
6588 break;
6589 }
6590 if (command == RedoCommand)
6591 {
6592 /*
6593 Redo the last image transformation.
6594 */
6595 if (redo_image == (Image *) NULL)
6596 {
6597 (void) XBell(display,0);
6598 return;
6599 }
6600 windows->image.window_changes.width=(int) redo_image->columns;
6601 windows->image.window_changes.height=(int) redo_image->rows;
6602 if (windows->image.crop_geometry != (char *) NULL)
6603 windows->image.crop_geometry=(char *)
6604 RelinquishMagickMemory(windows->image.crop_geometry);
6605 windows->image.crop_geometry=redo_image->geometry;
6606 *image=DestroyImage(*image);
6607 *image=redo_image;
6608 redo_image=NewImageList();
6609 if (windows->image.orphan != MagickFalse)
6610 return;
6611 XConfigureImageColormap(display,resource_info,windows,*image);
6612 (void) XConfigureImage(display,resource_info,windows,*image);
6613 return;
6614 }
6615 if (command != InfoCommand)
6616 return;
6617 /*
6618 Display image info.
6619 */
6620 XSetCursorState(display,windows,MagickTrue);
6621 XCheckRefreshWindows(display,windows);
6622 XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6623 XSetCursorState(display,windows,MagickFalse);
6624}
6625
6626/*
6627%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6628% %
6629% %
6630% %
6631+ X I m a g e W i n d o w C o m m a n d %
6632% %
6633% %
6634% %
6635%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6636%
6637% XImageWindowCommand() makes a transform to the image or Image window as
6638% specified by a user menu button or keyboard command.
6639%
6640% The format of the XMagickCommand method is:
6641%
6642% CommandType XImageWindowCommand(Display *display,
6643% XResourceInfo *resource_info,XWindows *windows,
6644% const MagickStatusType state,KeySym key_symbol,Image **image)
6645%
6646% A description of each parameter follows:
6647%
6648% o nexus: Method XImageWindowCommand returns an image when the
6649% user chooses 'Open Image' from the command menu. Otherwise a null
6650% image is returned.
6651%
6652% o display: Specifies a connection to an X server; returned from
6653% XOpenDisplay.
6654%
6655% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6656%
6657% o windows: Specifies a pointer to a XWindows structure.
6658%
6659% o state: key mask.
6660%
6661% o key_symbol: Specifies a command to perform.
6662%
6663% o image: the image; XImageWIndowCommand
6664% may transform the image and return a new image pointer.
6665%
6666*/
6667static CommandType XImageWindowCommand(Display *display,
6668 XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6669 KeySym key_symbol,Image **image)
6670{
6671 static char
6672 delta[MaxTextExtent] = "";
6673
6674 static const char
6675 Digits[] = "01234567890";
6676
6677 static KeySym
6678 last_symbol = XK_0;
6679
6680 if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6681 {
6682 if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6683 {
6684 *delta='\0';
6685 resource_info->quantum=1;
6686 }
6687 last_symbol=key_symbol;
6688 delta[strlen(delta)+1]='\0';
6689 delta[strlen(delta)]=Digits[key_symbol-XK_0];
cristyf2f27272009-12-17 14:48:46 +00006690 resource_info->quantum=StringToLong(delta);
cristy3ed852e2009-09-05 21:47:34 +00006691 return(NullCommand);
6692 }
6693 last_symbol=key_symbol;
6694 if (resource_info->immutable)
6695 {
6696 /*
6697 Virtual image window has a restricted command set.
6698 */
6699 switch (key_symbol)
6700 {
6701 case XK_question:
6702 return(InfoCommand);
6703 case XK_p:
6704 case XK_Print:
6705 return(PrintCommand);
6706 case XK_space:
6707 return(NextCommand);
6708 case XK_q:
6709 case XK_Escape:
6710 return(QuitCommand);
6711 default:
6712 break;
6713 }
6714 return(NullCommand);
6715 }
6716 switch ((int) key_symbol)
6717 {
6718 case XK_o:
6719 {
6720 if ((state & ControlMask) == 0)
6721 break;
6722 return(OpenCommand);
6723 }
6724 case XK_space:
6725 return(NextCommand);
6726 case XK_BackSpace:
6727 return(FormerCommand);
6728 case XK_s:
6729 {
6730 if ((state & Mod1Mask) != 0)
6731 return(SwirlCommand);
6732 if ((state & ControlMask) == 0)
6733 return(ShearCommand);
6734 return(SaveCommand);
6735 }
6736 case XK_p:
6737 case XK_Print:
6738 {
6739 if ((state & Mod1Mask) != 0)
6740 return(OilPaintCommand);
6741 if ((state & Mod4Mask) != 0)
6742 return(ColorCommand);
6743 if ((state & ControlMask) == 0)
6744 return(NullCommand);
6745 return(PrintCommand);
6746 }
6747 case XK_d:
6748 {
6749 if ((state & Mod4Mask) != 0)
6750 return(DrawCommand);
6751 if ((state & ControlMask) == 0)
6752 return(NullCommand);
6753 return(DeleteCommand);
6754 }
6755 case XK_Select:
6756 {
6757 if ((state & ControlMask) == 0)
6758 return(NullCommand);
6759 return(SelectCommand);
6760 }
6761 case XK_n:
6762 {
6763 if ((state & ControlMask) == 0)
6764 return(NullCommand);
6765 return(NewCommand);
6766 }
6767 case XK_q:
6768 case XK_Escape:
6769 return(QuitCommand);
6770 case XK_z:
6771 case XK_Undo:
6772 {
6773 if ((state & ControlMask) == 0)
6774 return(NullCommand);
6775 return(UndoCommand);
6776 }
6777 case XK_r:
6778 case XK_Redo:
6779 {
6780 if ((state & ControlMask) == 0)
6781 return(RollCommand);
6782 return(RedoCommand);
6783 }
6784 case XK_x:
6785 {
6786 if ((state & ControlMask) == 0)
6787 return(NullCommand);
6788 return(CutCommand);
6789 }
6790 case XK_c:
6791 {
6792 if ((state & Mod1Mask) != 0)
6793 return(CharcoalDrawCommand);
6794 if ((state & ControlMask) == 0)
6795 return(CropCommand);
6796 return(CopyCommand);
6797 }
6798 case XK_v:
6799 case XK_Insert:
6800 {
6801 if ((state & Mod4Mask) != 0)
6802 return(CompositeCommand);
6803 if ((state & ControlMask) == 0)
6804 return(FlipCommand);
6805 return(PasteCommand);
6806 }
6807 case XK_less:
6808 return(HalfSizeCommand);
6809 case XK_minus:
6810 return(OriginalSizeCommand);
6811 case XK_greater:
6812 return(DoubleSizeCommand);
6813 case XK_percent:
6814 return(ResizeCommand);
6815 case XK_at:
6816 return(RefreshCommand);
6817 case XK_bracketleft:
6818 return(ChopCommand);
6819 case XK_h:
6820 return(FlopCommand);
6821 case XK_slash:
6822 return(RotateRightCommand);
6823 case XK_backslash:
6824 return(RotateLeftCommand);
6825 case XK_asterisk:
6826 return(RotateCommand);
6827 case XK_t:
6828 return(TrimCommand);
6829 case XK_H:
6830 return(HueCommand);
6831 case XK_S:
6832 return(SaturationCommand);
6833 case XK_L:
6834 return(BrightnessCommand);
6835 case XK_G:
6836 return(GammaCommand);
6837 case XK_C:
6838 return(SpiffCommand);
6839 case XK_Z:
6840 return(DullCommand);
6841 case XK_N:
6842 return(NormalizeCommand);
6843 case XK_equal:
6844 return(EqualizeCommand);
6845 case XK_asciitilde:
6846 return(NegateCommand);
6847 case XK_period:
6848 return(GrayscaleCommand);
6849 case XK_numbersign:
6850 return(QuantizeCommand);
6851 case XK_F2:
6852 return(DespeckleCommand);
6853 case XK_F3:
6854 return(EmbossCommand);
6855 case XK_F4:
6856 return(ReduceNoiseCommand);
6857 case XK_F5:
6858 return(AddNoiseCommand);
6859 case XK_F6:
6860 return(SharpenCommand);
6861 case XK_F7:
6862 return(BlurCommand);
6863 case XK_F8:
6864 return(ThresholdCommand);
6865 case XK_F9:
6866 return(EdgeDetectCommand);
6867 case XK_F10:
6868 return(SpreadCommand);
6869 case XK_F11:
6870 return(ShadeCommand);
6871 case XK_F12:
6872 return(RaiseCommand);
6873 case XK_F13:
6874 return(SegmentCommand);
6875 case XK_i:
6876 {
6877 if ((state & Mod1Mask) == 0)
6878 return(NullCommand);
6879 return(ImplodeCommand);
6880 }
6881 case XK_w:
6882 {
6883 if ((state & Mod1Mask) == 0)
6884 return(NullCommand);
6885 return(WaveCommand);
6886 }
6887 case XK_m:
6888 {
6889 if ((state & Mod4Mask) == 0)
6890 return(NullCommand);
6891 return(MatteCommand);
6892 }
6893 case XK_b:
6894 {
6895 if ((state & Mod4Mask) == 0)
6896 return(NullCommand);
6897 return(AddBorderCommand);
6898 }
6899 case XK_f:
6900 {
6901 if ((state & Mod4Mask) == 0)
6902 return(NullCommand);
6903 return(AddFrameCommand);
6904 }
6905 case XK_exclam:
6906 {
6907 if ((state & Mod4Mask) == 0)
6908 return(NullCommand);
6909 return(CommentCommand);
6910 }
6911 case XK_a:
6912 {
6913 if ((state & Mod1Mask) != 0)
6914 return(ApplyCommand);
6915 if ((state & Mod4Mask) != 0)
6916 return(AnnotateCommand);
6917 if ((state & ControlMask) == 0)
6918 return(NullCommand);
6919 return(RegionofInterestCommand);
6920 }
6921 case XK_question:
6922 return(InfoCommand);
6923 case XK_plus:
6924 return(ZoomCommand);
6925 case XK_P:
6926 {
6927 if ((state & ShiftMask) == 0)
6928 return(NullCommand);
6929 return(ShowPreviewCommand);
6930 }
6931 case XK_Execute:
6932 return(LaunchCommand);
6933 case XK_F1:
6934 return(HelpCommand);
6935 case XK_Find:
6936 return(BrowseDocumentationCommand);
6937 case XK_Menu:
6938 {
6939 (void) XMapRaised(display,windows->command.id);
6940 return(NullCommand);
6941 }
6942 case XK_Next:
6943 case XK_Prior:
6944 case XK_Home:
6945 case XK_KP_Home:
6946 {
6947 XTranslateImage(display,windows,*image,key_symbol);
6948 return(NullCommand);
6949 }
6950 case XK_Up:
6951 case XK_KP_Up:
6952 case XK_Down:
6953 case XK_KP_Down:
6954 case XK_Left:
6955 case XK_KP_Left:
6956 case XK_Right:
6957 case XK_KP_Right:
6958 {
6959 if ((state & Mod1Mask) != 0)
6960 {
6961 RectangleInfo
6962 crop_info;
6963
6964 /*
6965 Trim one pixel from edge of image.
6966 */
6967 crop_info.x=0;
6968 crop_info.y=0;
cristybb503372010-05-27 20:51:26 +00006969 crop_info.width=(size_t) windows->image.ximage->width;
6970 crop_info.height=(size_t) windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +00006971 if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
6972 {
6973 if (resource_info->quantum >= (int) crop_info.height)
6974 resource_info->quantum=(int) crop_info.height-1;
6975 crop_info.height-=resource_info->quantum;
6976 }
6977 if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
6978 {
6979 if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
6980 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
6981 crop_info.y+=resource_info->quantum;
6982 crop_info.height-=resource_info->quantum;
6983 }
6984 if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
6985 {
6986 if (resource_info->quantum >= (int) crop_info.width)
6987 resource_info->quantum=(int) crop_info.width-1;
6988 crop_info.width-=resource_info->quantum;
6989 }
6990 if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
6991 {
6992 if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
6993 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
6994 crop_info.x+=resource_info->quantum;
6995 crop_info.width-=resource_info->quantum;
6996 }
6997 if ((int) (windows->image.x+windows->image.width) >
6998 (int) crop_info.width)
6999 windows->image.x=(int) (crop_info.width-windows->image.width);
7000 if ((int) (windows->image.y+windows->image.height) >
7001 (int) crop_info.height)
7002 windows->image.y=(int) (crop_info.height-windows->image.height);
7003 XSetCropGeometry(display,windows,&crop_info,*image);
7004 windows->image.window_changes.width=(int) crop_info.width;
7005 windows->image.window_changes.height=(int) crop_info.height;
7006 (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7007 (void) XConfigureImage(display,resource_info,windows,*image);
7008 return(NullCommand);
7009 }
7010 XTranslateImage(display,windows,*image,key_symbol);
7011 return(NullCommand);
7012 }
7013 default:
7014 return(NullCommand);
7015 }
7016 return(NullCommand);
7017}
7018
7019/*
7020%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7021% %
7022% %
7023% %
7024+ X M a g i c k C o m m a n d %
7025% %
7026% %
7027% %
7028%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7029%
7030% XMagickCommand() makes a transform to the image or Image window as
7031% specified by a user menu button or keyboard command.
7032%
7033% The format of the XMagickCommand method is:
7034%
7035% Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7036% XWindows *windows,const CommandType command,Image **image)
7037%
7038% A description of each parameter follows:
7039%
7040% o nexus: Method XMagickCommand returns an image when the
7041% user chooses 'Load Image' from the command menu. Otherwise a null
7042% image is returned.
7043%
7044% o display: Specifies a connection to an X server; returned from
7045% XOpenDisplay.
7046%
7047% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7048%
7049% o windows: Specifies a pointer to a XWindows structure.
7050%
7051% o command: Specifies a command to perform.
7052%
7053% o image: the image; XMagickCommand
7054% may transform the image and return a new image pointer.
7055%
7056*/
7057static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7058 XWindows *windows,const CommandType command,Image **image)
7059{
7060 char
7061 filename[MaxTextExtent],
7062 geometry[MaxTextExtent],
7063 modulate_factors[MaxTextExtent];
7064
7065 GeometryInfo
7066 geometry_info;
7067
7068 Image
7069 *nexus;
7070
7071 ImageInfo
7072 *image_info;
7073
7074 int
7075 x,
7076 y;
7077
7078 MagickStatusType
7079 flags,
7080 status;
7081
7082 QuantizeInfo
7083 quantize_info;
7084
7085 RectangleInfo
7086 page_geometry;
7087
7088 register int
7089 i;
7090
7091 static char
7092 color[MaxTextExtent] = "gray";
7093
7094 unsigned int
7095 height,
7096 width;
7097
7098 /*
7099 Process user command.
7100 */
7101 XCheckRefreshWindows(display,windows);
7102 XImageCache(display,resource_info,windows,command,image);
7103 nexus=NewImageList();
7104 windows->image.window_changes.width=windows->image.ximage->width;
7105 windows->image.window_changes.height=windows->image.ximage->height;
7106 image_info=CloneImageInfo(resource_info->image_info);
7107 SetGeometryInfo(&geometry_info);
7108 GetQuantizeInfo(&quantize_info);
7109 switch (command)
7110 {
7111 case OpenCommand:
7112 {
7113 /*
7114 Load image.
7115 */
7116 nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7117 break;
7118 }
7119 case NextCommand:
7120 {
7121 /*
7122 Display next image.
7123 */
7124 for (i=0; i < resource_info->quantum; i++)
7125 XClientMessage(display,windows->image.id,windows->im_protocols,
7126 windows->im_next_image,CurrentTime);
7127 break;
7128 }
7129 case FormerCommand:
7130 {
7131 /*
7132 Display former image.
7133 */
7134 for (i=0; i < resource_info->quantum; i++)
7135 XClientMessage(display,windows->image.id,windows->im_protocols,
7136 windows->im_former_image,CurrentTime);
7137 break;
7138 }
7139 case SelectCommand:
7140 {
7141 int
7142 status;
7143
7144 /*
7145 Select image.
7146 */
7147 status=chdir(resource_info->home_directory);
7148 if (status == -1)
7149 (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
7150 FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
7151 nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7152 break;
7153 }
7154 case SaveCommand:
7155 {
7156 /*
7157 Save image.
7158 */
7159 status=XSaveImage(display,resource_info,windows,*image);
7160 if (status == MagickFalse)
7161 {
7162 XNoticeWidget(display,windows,"Unable to write X image:",
7163 (*image)->filename);
7164 break;
7165 }
7166 break;
7167 }
7168 case PrintCommand:
7169 {
7170 /*
7171 Print image.
7172 */
7173 status=XPrintImage(display,resource_info,windows,*image);
7174 if (status == MagickFalse)
7175 {
7176 XNoticeWidget(display,windows,"Unable to print X image:",
7177 (*image)->filename);
7178 break;
7179 }
7180 break;
7181 }
7182 case DeleteCommand:
7183 {
7184 static char
7185 filename[MaxTextExtent] = "\0";
7186
7187 /*
7188 Delete image file.
7189 */
7190 XFileBrowserWidget(display,windows,"Delete",filename);
7191 if (*filename == '\0')
7192 break;
7193 status=remove(filename) != 0 ? MagickTrue : MagickFalse;
7194 if (status != MagickFalse)
7195 XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7196 break;
7197 }
7198 case NewCommand:
7199 {
7200 int
7201 status;
7202
7203 static char
7204 color[MaxTextExtent] = "gray",
7205 geometry[MaxTextExtent] = "640x480";
7206
7207 static const char
7208 *format = "gradient";
7209
7210 /*
7211 Query user for canvas geometry.
7212 */
7213 status=XDialogWidget(display,windows,"New","Enter image geometry:",
7214 geometry);
7215 if (*geometry == '\0')
7216 break;
7217 if (status == 0)
7218 format="xc";
7219 XColorBrowserWidget(display,windows,"Select",color);
7220 if (*color == '\0')
7221 break;
7222 /*
7223 Create canvas.
7224 */
7225 (void) FormatMagickString(image_info->filename,MaxTextExtent,
7226 "%s:%s",format,color);
7227 (void) CloneString(&image_info->size,geometry);
7228 nexus=ReadImage(image_info,&(*image)->exception);
7229 CatchException(&(*image)->exception);
7230 XClientMessage(display,windows->image.id,windows->im_protocols,
7231 windows->im_next_image,CurrentTime);
7232 break;
7233 }
7234 case VisualDirectoryCommand:
7235 {
7236 /*
7237 Visual Image directory.
7238 */
7239 nexus=XVisualDirectoryImage(display,resource_info,windows);
7240 break;
7241 }
7242 case QuitCommand:
7243 {
7244 /*
7245 exit program.
7246 */
7247 if (resource_info->confirm_exit == MagickFalse)
7248 XClientMessage(display,windows->image.id,windows->im_protocols,
7249 windows->im_exit,CurrentTime);
7250 else
7251 {
7252 int
7253 status;
7254
7255 /*
7256 Confirm program exit.
7257 */
7258 status=XConfirmWidget(display,windows,"Do you really want to exit",
7259 resource_info->client_name);
7260 if (status > 0)
7261 XClientMessage(display,windows->image.id,windows->im_protocols,
7262 windows->im_exit,CurrentTime);
7263 }
7264 break;
7265 }
7266 case CutCommand:
7267 {
7268 /*
7269 Cut image.
7270 */
7271 (void) XCropImage(display,resource_info,windows,*image,CutMode);
7272 break;
7273 }
7274 case CopyCommand:
7275 {
7276 /*
7277 Copy image.
7278 */
7279 (void) XCropImage(display,resource_info,windows,*image,CopyMode);
7280 break;
7281 }
7282 case PasteCommand:
7283 {
7284 /*
7285 Paste image.
7286 */
7287 status=XPasteImage(display,resource_info,windows,*image);
7288 if (status == MagickFalse)
7289 {
7290 XNoticeWidget(display,windows,"Unable to paste X image",
7291 (*image)->filename);
7292 break;
7293 }
7294 break;
7295 }
7296 case HalfSizeCommand:
7297 {
7298 /*
7299 Half image size.
7300 */
7301 windows->image.window_changes.width=windows->image.ximage->width/2;
7302 windows->image.window_changes.height=windows->image.ximage->height/2;
7303 (void) XConfigureImage(display,resource_info,windows,*image);
7304 break;
7305 }
7306 case OriginalSizeCommand:
7307 {
7308 /*
7309 Original image size.
7310 */
7311 windows->image.window_changes.width=(int) (*image)->columns;
7312 windows->image.window_changes.height=(int) (*image)->rows;
7313 (void) XConfigureImage(display,resource_info,windows,*image);
7314 break;
7315 }
7316 case DoubleSizeCommand:
7317 {
7318 /*
7319 Double the image size.
7320 */
7321 windows->image.window_changes.width=windows->image.ximage->width << 1;
7322 windows->image.window_changes.height=windows->image.ximage->height << 1;
7323 (void) XConfigureImage(display,resource_info,windows,*image);
7324 break;
7325 }
7326 case ResizeCommand:
7327 {
7328 int
7329 status;
7330
cristybb503372010-05-27 20:51:26 +00007331 size_t
cristy3ed852e2009-09-05 21:47:34 +00007332 height,
7333 width;
7334
cristy9d314ff2011-03-09 01:30:28 +00007335 ssize_t
7336 x,
7337 y;
7338
cristy3ed852e2009-09-05 21:47:34 +00007339 /*
7340 Resize image.
7341 */
cristybb503372010-05-27 20:51:26 +00007342 width=(size_t) windows->image.ximage->width;
7343 height=(size_t) windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +00007344 x=0;
7345 y=0;
cristye8c25f92010-06-03 00:53:06 +00007346 (void) FormatMagickString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7347 (double) width,(double) height);
cristy3ed852e2009-09-05 21:47:34 +00007348 status=XDialogWidget(display,windows,"Resize",
7349 "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7350 if (*geometry == '\0')
7351 break;
7352 if (status == 0)
7353 (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7354 (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7355 windows->image.window_changes.width=(int) width;
7356 windows->image.window_changes.height=(int) height;
7357 (void) XConfigureImage(display,resource_info,windows,*image);
7358 break;
7359 }
7360 case ApplyCommand:
7361 {
7362 char
7363 image_geometry[MaxTextExtent];
7364
7365 if ((windows->image.crop_geometry == (char *) NULL) &&
7366 ((int) (*image)->columns == windows->image.ximage->width) &&
7367 ((int) (*image)->rows == windows->image.ximage->height))
7368 break;
7369 /*
7370 Apply size transforms to image.
7371 */
7372 XSetCursorState(display,windows,MagickTrue);
7373 XCheckRefreshWindows(display,windows);
7374 /*
7375 Crop and/or scale displayed image.
7376 */
7377 (void) FormatMagickString(image_geometry,MaxTextExtent,"%dx%d!",
7378 windows->image.ximage->width,windows->image.ximage->height);
7379 (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7380 if (windows->image.crop_geometry != (char *) NULL)
7381 windows->image.crop_geometry=(char *)
7382 RelinquishMagickMemory(windows->image.crop_geometry);
7383 windows->image.x=0;
7384 windows->image.y=0;
7385 XConfigureImageColormap(display,resource_info,windows,*image);
7386 (void) XConfigureImage(display,resource_info,windows,*image);
7387 break;
7388 }
7389 case RefreshCommand:
7390 {
7391 (void) XConfigureImage(display,resource_info,windows,*image);
7392 break;
7393 }
7394 case RestoreCommand:
7395 {
7396 /*
7397 Restore Image window to its original size.
7398 */
7399 if ((windows->image.width == (unsigned int) (*image)->columns) &&
7400 (windows->image.height == (unsigned int) (*image)->rows) &&
7401 (windows->image.crop_geometry == (char *) NULL))
7402 {
7403 (void) XBell(display,0);
7404 break;
7405 }
7406 windows->image.window_changes.width=(int) (*image)->columns;
7407 windows->image.window_changes.height=(int) (*image)->rows;
7408 if (windows->image.crop_geometry != (char *) NULL)
7409 {
7410 windows->image.crop_geometry=(char *)
7411 RelinquishMagickMemory(windows->image.crop_geometry);
7412 windows->image.crop_geometry=(char *) NULL;
7413 windows->image.x=0;
7414 windows->image.y=0;
7415 }
7416 XConfigureImageColormap(display,resource_info,windows,*image);
7417 (void) XConfigureImage(display,resource_info,windows,*image);
7418 break;
7419 }
7420 case CropCommand:
7421 {
7422 /*
7423 Crop image.
7424 */
7425 (void) XCropImage(display,resource_info,windows,*image,CropMode);
7426 break;
7427 }
7428 case ChopCommand:
7429 {
7430 /*
7431 Chop image.
7432 */
7433 status=XChopImage(display,resource_info,windows,image);
7434 if (status == MagickFalse)
7435 {
7436 XNoticeWidget(display,windows,"Unable to cut X image",
7437 (*image)->filename);
7438 break;
7439 }
7440 break;
7441 }
7442 case FlopCommand:
7443 {
7444 Image
7445 *flop_image;
7446
7447 /*
7448 Flop image scanlines.
7449 */
7450 XSetCursorState(display,windows,MagickTrue);
7451 XCheckRefreshWindows(display,windows);
7452 flop_image=FlopImage(*image,&(*image)->exception);
7453 if (flop_image != (Image *) NULL)
7454 {
7455 *image=DestroyImage(*image);
7456 *image=flop_image;
7457 }
7458 CatchException(&(*image)->exception);
7459 XSetCursorState(display,windows,MagickFalse);
7460 if (windows->image.crop_geometry != (char *) NULL)
7461 {
7462 /*
7463 Flop crop geometry.
7464 */
7465 width=(unsigned int) (*image)->columns;
7466 height=(unsigned int) (*image)->rows;
7467 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7468 &width,&height);
7469 (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
7470 "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7471 }
7472 if (windows->image.orphan != MagickFalse)
7473 break;
7474 (void) XConfigureImage(display,resource_info,windows,*image);
7475 break;
7476 }
7477 case FlipCommand:
7478 {
7479 Image
7480 *flip_image;
7481
7482 /*
7483 Flip image scanlines.
7484 */
7485 XSetCursorState(display,windows,MagickTrue);
7486 XCheckRefreshWindows(display,windows);
7487 flip_image=FlipImage(*image,&(*image)->exception);
7488 if (flip_image != (Image *) NULL)
7489 {
7490 *image=DestroyImage(*image);
7491 *image=flip_image;
7492 }
7493 CatchException(&(*image)->exception);
7494 XSetCursorState(display,windows,MagickFalse);
7495 if (windows->image.crop_geometry != (char *) NULL)
7496 {
7497 /*
7498 Flip crop geometry.
7499 */
7500 width=(unsigned int) (*image)->columns;
7501 height=(unsigned int) (*image)->rows;
7502 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7503 &width,&height);
7504 (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
7505 "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7506 }
7507 if (windows->image.orphan != MagickFalse)
7508 break;
7509 (void) XConfigureImage(display,resource_info,windows,*image);
7510 break;
7511 }
7512 case RotateRightCommand:
7513 {
7514 /*
7515 Rotate image 90 degrees clockwise.
7516 */
7517 status=XRotateImage(display,resource_info,windows,90.0,image);
7518 if (status == MagickFalse)
7519 {
7520 XNoticeWidget(display,windows,"Unable to rotate X image",
7521 (*image)->filename);
7522 break;
7523 }
7524 break;
7525 }
7526 case RotateLeftCommand:
7527 {
7528 /*
7529 Rotate image 90 degrees counter-clockwise.
7530 */
7531 status=XRotateImage(display,resource_info,windows,-90.0,image);
7532 if (status == MagickFalse)
7533 {
7534 XNoticeWidget(display,windows,"Unable to rotate X image",
7535 (*image)->filename);
7536 break;
7537 }
7538 break;
7539 }
7540 case RotateCommand:
7541 {
7542 /*
7543 Rotate image.
7544 */
7545 status=XRotateImage(display,resource_info,windows,0.0,image);
7546 if (status == MagickFalse)
7547 {
7548 XNoticeWidget(display,windows,"Unable to rotate X image",
7549 (*image)->filename);
7550 break;
7551 }
7552 break;
7553 }
7554 case ShearCommand:
7555 {
7556 Image
7557 *shear_image;
7558
7559 static char
7560 geometry[MaxTextExtent] = "45.0x45.0";
7561
7562 /*
7563 Query user for shear color and geometry.
7564 */
7565 XColorBrowserWidget(display,windows,"Select",color);
7566 if (*color == '\0')
7567 break;
7568 (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7569 geometry);
7570 if (*geometry == '\0')
7571 break;
7572 /*
7573 Shear image.
7574 */
7575 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7576 XSetCursorState(display,windows,MagickTrue);
7577 XCheckRefreshWindows(display,windows);
7578 (void) QueryColorDatabase(color,&(*image)->background_color,
7579 &(*image)->exception);
7580 flags=ParseGeometry(geometry,&geometry_info);
7581 if ((flags & SigmaValue) == 0)
7582 geometry_info.sigma=geometry_info.rho;
7583 shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7584 &(*image)->exception);
7585 if (shear_image != (Image *) NULL)
7586 {
7587 *image=DestroyImage(*image);
7588 *image=shear_image;
7589 }
7590 CatchException(&(*image)->exception);
7591 XSetCursorState(display,windows,MagickFalse);
7592 if (windows->image.orphan != MagickFalse)
7593 break;
7594 windows->image.window_changes.width=(int) (*image)->columns;
7595 windows->image.window_changes.height=(int) (*image)->rows;
7596 XConfigureImageColormap(display,resource_info,windows,*image);
7597 (void) XConfigureImage(display,resource_info,windows,*image);
7598 break;
7599 }
7600 case RollCommand:
7601 {
7602 Image
7603 *roll_image;
7604
7605 static char
7606 geometry[MaxTextExtent] = "+2+2";
7607
7608 /*
7609 Query user for the roll geometry.
7610 */
7611 (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7612 geometry);
7613 if (*geometry == '\0')
7614 break;
7615 /*
7616 Roll image.
7617 */
7618 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7619 XSetCursorState(display,windows,MagickTrue);
7620 XCheckRefreshWindows(display,windows);
7621 (void) ParsePageGeometry(*image,geometry,&page_geometry,
7622 &(*image)->exception);
7623 roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7624 &(*image)->exception);
7625 if (roll_image != (Image *) NULL)
7626 {
7627 *image=DestroyImage(*image);
7628 *image=roll_image;
7629 }
7630 CatchException(&(*image)->exception);
7631 XSetCursorState(display,windows,MagickFalse);
7632 if (windows->image.orphan != MagickFalse)
7633 break;
7634 windows->image.window_changes.width=(int) (*image)->columns;
7635 windows->image.window_changes.height=(int) (*image)->rows;
7636 XConfigureImageColormap(display,resource_info,windows,*image);
7637 (void) XConfigureImage(display,resource_info,windows,*image);
7638 break;
7639 }
7640 case TrimCommand:
7641 {
7642 static char
7643 fuzz[MaxTextExtent];
7644
7645 /*
7646 Query user for the fuzz factor.
7647 */
cristye7f51092010-01-17 00:39:37 +00007648 (void) FormatMagickString(fuzz,MaxTextExtent,"%g%%",100.0*
cristy8cd5b312010-01-07 01:10:24 +00007649 (*image)->fuzz/(QuantumRange+1.0));
cristy3ed852e2009-09-05 21:47:34 +00007650 (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7651 if (*fuzz == '\0')
7652 break;
cristyf2f27272009-12-17 14:48:46 +00007653 (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
cristy3ed852e2009-09-05 21:47:34 +00007654 /*
7655 Trim image.
7656 */
7657 status=XTrimImage(display,resource_info,windows,*image);
7658 if (status == MagickFalse)
7659 {
7660 XNoticeWidget(display,windows,"Unable to trim X image",
7661 (*image)->filename);
7662 break;
7663 }
7664 break;
7665 }
7666 case HueCommand:
7667 {
7668 static char
7669 hue_percent[MaxTextExtent] = "110";
7670
7671 /*
7672 Query user for percent hue change.
7673 */
7674 (void) XDialogWidget(display,windows,"Apply",
7675 "Enter percent change in image hue (0-200):",hue_percent);
7676 if (*hue_percent == '\0')
7677 break;
7678 /*
7679 Vary the image hue.
7680 */
7681 XSetCursorState(display,windows,MagickTrue);
7682 XCheckRefreshWindows(display,windows);
7683 (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7684 (void) ConcatenateMagickString(modulate_factors,hue_percent,
7685 MaxTextExtent);
7686 (void) ModulateImage(*image,modulate_factors);
7687 XSetCursorState(display,windows,MagickFalse);
7688 if (windows->image.orphan != MagickFalse)
7689 break;
7690 XConfigureImageColormap(display,resource_info,windows,*image);
7691 (void) XConfigureImage(display,resource_info,windows,*image);
7692 break;
7693 }
7694 case SaturationCommand:
7695 {
7696 static char
7697 saturation_percent[MaxTextExtent] = "110";
7698
7699 /*
7700 Query user for percent saturation change.
7701 */
7702 (void) XDialogWidget(display,windows,"Apply",
7703 "Enter percent change in color saturation (0-200):",saturation_percent);
7704 if (*saturation_percent == '\0')
7705 break;
7706 /*
7707 Vary color saturation.
7708 */
7709 XSetCursorState(display,windows,MagickTrue);
7710 XCheckRefreshWindows(display,windows);
7711 (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7712 (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7713 MaxTextExtent);
7714 (void) ModulateImage(*image,modulate_factors);
7715 XSetCursorState(display,windows,MagickFalse);
7716 if (windows->image.orphan != MagickFalse)
7717 break;
7718 XConfigureImageColormap(display,resource_info,windows,*image);
7719 (void) XConfigureImage(display,resource_info,windows,*image);
7720 break;
7721 }
7722 case BrightnessCommand:
7723 {
7724 static char
7725 brightness_percent[MaxTextExtent] = "110";
7726
7727 /*
7728 Query user for percent brightness change.
7729 */
7730 (void) XDialogWidget(display,windows,"Apply",
7731 "Enter percent change in color brightness (0-200):",brightness_percent);
7732 if (*brightness_percent == '\0')
7733 break;
7734 /*
7735 Vary the color brightness.
7736 */
7737 XSetCursorState(display,windows,MagickTrue);
7738 XCheckRefreshWindows(display,windows);
7739 (void) CopyMagickString(modulate_factors,brightness_percent,
7740 MaxTextExtent);
7741 (void) ModulateImage(*image,modulate_factors);
7742 XSetCursorState(display,windows,MagickFalse);
7743 if (windows->image.orphan != MagickFalse)
7744 break;
7745 XConfigureImageColormap(display,resource_info,windows,*image);
7746 (void) XConfigureImage(display,resource_info,windows,*image);
7747 break;
7748 }
7749 case GammaCommand:
7750 {
7751 static char
7752 factor[MaxTextExtent] = "1.6";
7753
7754 /*
7755 Query user for gamma value.
7756 */
7757 (void) XDialogWidget(display,windows,"Gamma",
7758 "Enter gamma value (e.g. 1.0,1.0,1.6):",factor);
7759 if (*factor == '\0')
7760 break;
7761 /*
7762 Gamma correct image.
7763 */
7764 XSetCursorState(display,windows,MagickTrue);
7765 XCheckRefreshWindows(display,windows);
7766 (void) GammaImage(*image,factor);
7767 XSetCursorState(display,windows,MagickFalse);
7768 if (windows->image.orphan != MagickFalse)
7769 break;
7770 XConfigureImageColormap(display,resource_info,windows,*image);
7771 (void) XConfigureImage(display,resource_info,windows,*image);
7772 break;
7773 }
7774 case SpiffCommand:
7775 {
7776 /*
7777 Sharpen the image contrast.
7778 */
7779 XSetCursorState(display,windows,MagickTrue);
7780 XCheckRefreshWindows(display,windows);
7781 (void) ContrastImage(*image,MagickTrue);
7782 XSetCursorState(display,windows,MagickFalse);
7783 if (windows->image.orphan != MagickFalse)
7784 break;
7785 XConfigureImageColormap(display,resource_info,windows,*image);
7786 (void) XConfigureImage(display,resource_info,windows,*image);
7787 break;
7788 }
7789 case DullCommand:
7790 {
7791 /*
7792 Dull the image contrast.
7793 */
7794 XSetCursorState(display,windows,MagickTrue);
7795 XCheckRefreshWindows(display,windows);
7796 (void) ContrastImage(*image,MagickFalse);
7797 XSetCursorState(display,windows,MagickFalse);
7798 if (windows->image.orphan != MagickFalse)
7799 break;
7800 XConfigureImageColormap(display,resource_info,windows,*image);
7801 (void) XConfigureImage(display,resource_info,windows,*image);
7802 break;
7803 }
7804 case ContrastStretchCommand:
7805 {
7806 double
7807 black_point,
7808 white_point;
7809
7810 static char
7811 levels[MaxTextExtent] = "1%";
7812
7813 /*
7814 Query user for gamma value.
7815 */
7816 (void) XDialogWidget(display,windows,"Contrast Stretch",
7817 "Enter black and white points:",levels);
7818 if (*levels == '\0')
7819 break;
7820 /*
7821 Contrast stretch image.
7822 */
7823 XSetCursorState(display,windows,MagickTrue);
7824 XCheckRefreshWindows(display,windows);
7825 flags=ParseGeometry(levels,&geometry_info);
7826 black_point=geometry_info.rho;
7827 white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7828 if ((flags & PercentValue) != 0)
7829 {
7830 black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7831 white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7832 }
7833 white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7834 (void) ContrastStretchImageChannel(*image,DefaultChannels,black_point,
7835 white_point);
7836 XSetCursorState(display,windows,MagickFalse);
7837 if (windows->image.orphan != MagickFalse)
7838 break;
7839 XConfigureImageColormap(display,resource_info,windows,*image);
7840 (void) XConfigureImage(display,resource_info,windows,*image);
7841 break;
7842 }
7843 case SigmoidalContrastCommand:
7844 {
7845 static char
7846 levels[MaxTextExtent] = "3x50%";
7847
7848 /*
7849 Query user for gamma value.
7850 */
7851 (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7852 "Enter contrast and midpoint:",levels);
7853 if (*levels == '\0')
7854 break;
7855 /*
7856 Contrast stretch image.
7857 */
7858 XSetCursorState(display,windows,MagickTrue);
7859 XCheckRefreshWindows(display,windows);
7860 (void) SigmoidalContrastImage(*image,MagickTrue,levels);
7861 XSetCursorState(display,windows,MagickFalse);
7862 if (windows->image.orphan != MagickFalse)
7863 break;
7864 XConfigureImageColormap(display,resource_info,windows,*image);
7865 (void) XConfigureImage(display,resource_info,windows,*image);
7866 break;
7867 }
7868 case NormalizeCommand:
7869 {
7870 /*
7871 Perform histogram normalization on the image.
7872 */
7873 XSetCursorState(display,windows,MagickTrue);
7874 XCheckRefreshWindows(display,windows);
7875 (void) NormalizeImage(*image);
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 EqualizeCommand:
7884 {
7885 /*
7886 Perform histogram equalization on the image.
7887 */
7888 XSetCursorState(display,windows,MagickTrue);
7889 XCheckRefreshWindows(display,windows);
7890 (void) EqualizeImage(*image);
7891 XSetCursorState(display,windows,MagickFalse);
7892 if (windows->image.orphan != MagickFalse)
7893 break;
7894 XConfigureImageColormap(display,resource_info,windows,*image);
7895 (void) XConfigureImage(display,resource_info,windows,*image);
7896 break;
7897 }
7898 case NegateCommand:
7899 {
7900 /*
7901 Negate colors in image.
7902 */
7903 XSetCursorState(display,windows,MagickTrue);
7904 XCheckRefreshWindows(display,windows);
7905 (void) NegateImage(*image,MagickFalse);
7906 XSetCursorState(display,windows,MagickFalse);
7907 if (windows->image.orphan != MagickFalse)
7908 break;
7909 XConfigureImageColormap(display,resource_info,windows,*image);
7910 (void) XConfigureImage(display,resource_info,windows,*image);
7911 break;
7912 }
7913 case GrayscaleCommand:
7914 {
7915 /*
7916 Convert image to grayscale.
7917 */
7918 XSetCursorState(display,windows,MagickTrue);
7919 XCheckRefreshWindows(display,windows);
7920 (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7921 GrayscaleType : GrayscaleMatteType);
7922 XSetCursorState(display,windows,MagickFalse);
7923 if (windows->image.orphan != MagickFalse)
7924 break;
7925 XConfigureImageColormap(display,resource_info,windows,*image);
7926 (void) XConfigureImage(display,resource_info,windows,*image);
7927 break;
7928 }
7929 case MapCommand:
7930 {
7931 Image
7932 *affinity_image;
7933
7934 static char
7935 filename[MaxTextExtent] = "\0";
7936
7937 /*
7938 Request image file name from user.
7939 */
7940 XFileBrowserWidget(display,windows,"Map",filename);
7941 if (*filename == '\0')
7942 break;
7943 /*
7944 Map image.
7945 */
7946 XSetCursorState(display,windows,MagickTrue);
7947 XCheckRefreshWindows(display,windows);
7948 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
7949 affinity_image=ReadImage(image_info,&(*image)->exception);
7950 if (affinity_image != (Image *) NULL)
7951 {
7952 (void) RemapImage(&quantize_info,*image,affinity_image);
7953 affinity_image=DestroyImage(affinity_image);
7954 }
7955 CatchException(&(*image)->exception);
7956 XSetCursorState(display,windows,MagickFalse);
7957 if (windows->image.orphan != MagickFalse)
7958 break;
7959 XConfigureImageColormap(display,resource_info,windows,*image);
7960 (void) XConfigureImage(display,resource_info,windows,*image);
7961 break;
7962 }
7963 case QuantizeCommand:
7964 {
7965 int
7966 status;
7967
7968 static char
7969 colors[MaxTextExtent] = "256";
7970
7971 /*
7972 Query user for maximum number of colors.
7973 */
7974 status=XDialogWidget(display,windows,"Quantize",
7975 "Maximum number of colors:",colors);
7976 if (*colors == '\0')
7977 break;
7978 /*
7979 Color reduce the image.
7980 */
7981 XSetCursorState(display,windows,MagickTrue);
7982 XCheckRefreshWindows(display,windows);
cristye27293e2009-12-18 02:53:20 +00007983 quantize_info.number_colors=StringToUnsignedLong(colors);
cristy3ed852e2009-09-05 21:47:34 +00007984 quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
7985 (void) QuantizeImage(&quantize_info,*image);
7986 XSetCursorState(display,windows,MagickFalse);
7987 if (windows->image.orphan != MagickFalse)
7988 break;
7989 XConfigureImageColormap(display,resource_info,windows,*image);
7990 (void) XConfigureImage(display,resource_info,windows,*image);
7991 break;
7992 }
7993 case DespeckleCommand:
7994 {
7995 Image
7996 *despeckle_image;
7997
7998 /*
7999 Despeckle image.
8000 */
8001 XSetCursorState(display,windows,MagickTrue);
8002 XCheckRefreshWindows(display,windows);
8003 despeckle_image=DespeckleImage(*image,&(*image)->exception);
8004 if (despeckle_image != (Image *) NULL)
8005 {
8006 *image=DestroyImage(*image);
8007 *image=despeckle_image;
8008 }
8009 CatchException(&(*image)->exception);
8010 XSetCursorState(display,windows,MagickFalse);
8011 if (windows->image.orphan != MagickFalse)
8012 break;
8013 XConfigureImageColormap(display,resource_info,windows,*image);
8014 (void) XConfigureImage(display,resource_info,windows,*image);
8015 break;
8016 }
8017 case EmbossCommand:
8018 {
8019 Image
8020 *emboss_image;
8021
8022 static char
8023 radius[MaxTextExtent] = "0.0x1.0";
8024
8025 /*
8026 Query user for emboss radius.
8027 */
8028 (void) XDialogWidget(display,windows,"Emboss",
8029 "Enter the emboss radius and standard deviation:",radius);
8030 if (*radius == '\0')
8031 break;
8032 /*
8033 Reduce noise in the image.
8034 */
8035 XSetCursorState(display,windows,MagickTrue);
8036 XCheckRefreshWindows(display,windows);
8037 flags=ParseGeometry(radius,&geometry_info);
8038 if ((flags & SigmaValue) == 0)
8039 geometry_info.sigma=1.0;
8040 emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8041 &(*image)->exception);
8042 if (emboss_image != (Image *) NULL)
8043 {
8044 *image=DestroyImage(*image);
8045 *image=emboss_image;
8046 }
8047 CatchException(&(*image)->exception);
8048 XSetCursorState(display,windows,MagickFalse);
8049 if (windows->image.orphan != MagickFalse)
8050 break;
8051 XConfigureImageColormap(display,resource_info,windows,*image);
8052 (void) XConfigureImage(display,resource_info,windows,*image);
8053 break;
8054 }
8055 case ReduceNoiseCommand:
8056 {
8057 Image
8058 *noise_image;
8059
8060 static char
8061 radius[MaxTextExtent] = "0";
8062
8063 /*
8064 Query user for noise radius.
8065 */
8066 (void) XDialogWidget(display,windows,"Reduce Noise",
8067 "Enter the noise radius:",radius);
8068 if (*radius == '\0')
8069 break;
8070 /*
8071 Reduce noise in the image.
8072 */
8073 XSetCursorState(display,windows,MagickTrue);
8074 XCheckRefreshWindows(display,windows);
8075 flags=ParseGeometry(radius,&geometry_info);
cristy95c38342011-03-18 22:39:51 +00008076 noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8077 geometry_info.rho,(size_t) geometry_info.rho,&(*image)->exception);
cristy3ed852e2009-09-05 21:47:34 +00008078 if (noise_image != (Image *) NULL)
8079 {
8080 *image=DestroyImage(*image);
8081 *image=noise_image;
8082 }
8083 CatchException(&(*image)->exception);
8084 XSetCursorState(display,windows,MagickFalse);
8085 if (windows->image.orphan != MagickFalse)
8086 break;
8087 XConfigureImageColormap(display,resource_info,windows,*image);
8088 (void) XConfigureImage(display,resource_info,windows,*image);
8089 break;
8090 }
8091 case AddNoiseCommand:
8092 {
8093 char
8094 **noises;
8095
8096 Image
8097 *noise_image;
8098
8099 static char
8100 noise_type[MaxTextExtent] = "Gaussian";
8101
8102 /*
8103 Add noise to the image.
8104 */
8105 noises=GetMagickOptions(MagickNoiseOptions);
8106 if (noises == (char **) NULL)
8107 break;
8108 XListBrowserWidget(display,windows,&windows->widget,
8109 (const char **) noises,"Add Noise",
8110 "Select a type of noise to add to your image:",noise_type);
8111 noises=DestroyStringList(noises);
8112 if (*noise_type == '\0')
8113 break;
8114 XSetCursorState(display,windows,MagickTrue);
8115 XCheckRefreshWindows(display,windows);
8116 noise_image=AddNoiseImage(*image,(NoiseType) ParseMagickOption(
8117 MagickNoiseOptions,MagickFalse,noise_type),&(*image)->exception);
8118 if (noise_image != (Image *) NULL)
8119 {
8120 *image=DestroyImage(*image);
8121 *image=noise_image;
8122 }
8123 CatchException(&(*image)->exception);
8124 XSetCursorState(display,windows,MagickFalse);
8125 if (windows->image.orphan != MagickFalse)
8126 break;
8127 XConfigureImageColormap(display,resource_info,windows,*image);
8128 (void) XConfigureImage(display,resource_info,windows,*image);
8129 break;
8130 }
8131 case SharpenCommand:
8132 {
8133 Image
8134 *sharp_image;
8135
8136 static char
8137 radius[MaxTextExtent] = "0.0x1.0";
8138
8139 /*
8140 Query user for sharpen radius.
8141 */
8142 (void) XDialogWidget(display,windows,"Sharpen",
8143 "Enter the sharpen radius and standard deviation:",radius);
8144 if (*radius == '\0')
8145 break;
8146 /*
8147 Sharpen image scanlines.
8148 */
8149 XSetCursorState(display,windows,MagickTrue);
8150 XCheckRefreshWindows(display,windows);
8151 flags=ParseGeometry(radius,&geometry_info);
8152 sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8153 &(*image)->exception);
8154 if (sharp_image != (Image *) NULL)
8155 {
8156 *image=DestroyImage(*image);
8157 *image=sharp_image;
8158 }
8159 CatchException(&(*image)->exception);
8160 XSetCursorState(display,windows,MagickFalse);
8161 if (windows->image.orphan != MagickFalse)
8162 break;
8163 XConfigureImageColormap(display,resource_info,windows,*image);
8164 (void) XConfigureImage(display,resource_info,windows,*image);
8165 break;
8166 }
8167 case BlurCommand:
8168 {
8169 Image
8170 *blur_image;
8171
8172 static char
8173 radius[MaxTextExtent] = "0.0x1.0";
8174
8175 /*
8176 Query user for blur radius.
8177 */
8178 (void) XDialogWidget(display,windows,"Blur",
8179 "Enter the blur radius and standard deviation:",radius);
8180 if (*radius == '\0')
8181 break;
8182 /*
8183 Blur an image.
8184 */
8185 XSetCursorState(display,windows,MagickTrue);
8186 XCheckRefreshWindows(display,windows);
8187 flags=ParseGeometry(radius,&geometry_info);
8188 blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8189 &(*image)->exception);
8190 if (blur_image != (Image *) NULL)
8191 {
8192 *image=DestroyImage(*image);
8193 *image=blur_image;
8194 }
8195 CatchException(&(*image)->exception);
8196 XSetCursorState(display,windows,MagickFalse);
8197 if (windows->image.orphan != MagickFalse)
8198 break;
8199 XConfigureImageColormap(display,resource_info,windows,*image);
8200 (void) XConfigureImage(display,resource_info,windows,*image);
8201 break;
8202 }
8203 case ThresholdCommand:
8204 {
8205 double
8206 threshold;
8207
8208 static char
8209 factor[MaxTextExtent] = "128";
8210
8211 /*
8212 Query user for threshold value.
8213 */
8214 (void) XDialogWidget(display,windows,"Threshold",
8215 "Enter threshold value:",factor);
8216 if (*factor == '\0')
8217 break;
8218 /*
8219 Gamma correct image.
8220 */
8221 XSetCursorState(display,windows,MagickTrue);
8222 XCheckRefreshWindows(display,windows);
cristyf2f27272009-12-17 14:48:46 +00008223 threshold=SiPrefixToDouble(factor,QuantumRange);
cristy3ed852e2009-09-05 21:47:34 +00008224 (void) BilevelImage(*image,threshold);
8225 XSetCursorState(display,windows,MagickFalse);
8226 if (windows->image.orphan != MagickFalse)
8227 break;
8228 XConfigureImageColormap(display,resource_info,windows,*image);
8229 (void) XConfigureImage(display,resource_info,windows,*image);
8230 break;
8231 }
8232 case EdgeDetectCommand:
8233 {
8234 Image
8235 *edge_image;
8236
8237 static char
8238 radius[MaxTextExtent] = "0";
8239
8240 /*
8241 Query user for edge factor.
8242 */
8243 (void) XDialogWidget(display,windows,"Detect Edges",
8244 "Enter the edge detect radius:",radius);
8245 if (*radius == '\0')
8246 break;
8247 /*
8248 Detect edge in image.
8249 */
8250 XSetCursorState(display,windows,MagickTrue);
8251 XCheckRefreshWindows(display,windows);
8252 flags=ParseGeometry(radius,&geometry_info);
8253 edge_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8254 if (edge_image != (Image *) NULL)
8255 {
8256 *image=DestroyImage(*image);
8257 *image=edge_image;
8258 }
8259 CatchException(&(*image)->exception);
8260 XSetCursorState(display,windows,MagickFalse);
8261 if (windows->image.orphan != MagickFalse)
8262 break;
8263 XConfigureImageColormap(display,resource_info,windows,*image);
8264 (void) XConfigureImage(display,resource_info,windows,*image);
8265 break;
8266 }
8267 case SpreadCommand:
8268 {
8269 Image
8270 *spread_image;
8271
8272 static char
8273 amount[MaxTextExtent] = "2";
8274
8275 /*
8276 Query user for spread amount.
8277 */
8278 (void) XDialogWidget(display,windows,"Spread",
8279 "Enter the displacement amount:",amount);
8280 if (*amount == '\0')
8281 break;
8282 /*
8283 Displace image pixels by a random amount.
8284 */
8285 XSetCursorState(display,windows,MagickTrue);
8286 XCheckRefreshWindows(display,windows);
8287 flags=ParseGeometry(amount,&geometry_info);
8288 spread_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8289 if (spread_image != (Image *) NULL)
8290 {
8291 *image=DestroyImage(*image);
8292 *image=spread_image;
8293 }
8294 CatchException(&(*image)->exception);
8295 XSetCursorState(display,windows,MagickFalse);
8296 if (windows->image.orphan != MagickFalse)
8297 break;
8298 XConfigureImageColormap(display,resource_info,windows,*image);
8299 (void) XConfigureImage(display,resource_info,windows,*image);
8300 break;
8301 }
8302 case ShadeCommand:
8303 {
8304 Image
8305 *shade_image;
8306
8307 int
8308 status;
8309
8310 static char
8311 geometry[MaxTextExtent] = "30x30";
8312
8313 /*
8314 Query user for the shade geometry.
8315 */
8316 status=XDialogWidget(display,windows,"Shade",
8317 "Enter the azimuth and elevation of the light source:",geometry);
8318 if (*geometry == '\0')
8319 break;
8320 /*
8321 Shade image pixels.
8322 */
8323 XSetCursorState(display,windows,MagickTrue);
8324 XCheckRefreshWindows(display,windows);
8325 flags=ParseGeometry(geometry,&geometry_info);
8326 if ((flags & SigmaValue) == 0)
8327 geometry_info.sigma=1.0;
8328 shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8329 geometry_info.rho,geometry_info.sigma,&(*image)->exception);
8330 if (shade_image != (Image *) NULL)
8331 {
8332 *image=DestroyImage(*image);
8333 *image=shade_image;
8334 }
8335 CatchException(&(*image)->exception);
8336 XSetCursorState(display,windows,MagickFalse);
8337 if (windows->image.orphan != MagickFalse)
8338 break;
8339 XConfigureImageColormap(display,resource_info,windows,*image);
8340 (void) XConfigureImage(display,resource_info,windows,*image);
8341 break;
8342 }
8343 case RaiseCommand:
8344 {
8345 static char
8346 bevel_width[MaxTextExtent] = "10";
8347
8348 /*
8349 Query user for bevel width.
8350 */
8351 (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8352 if (*bevel_width == '\0')
8353 break;
8354 /*
8355 Raise an image.
8356 */
8357 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8358 XSetCursorState(display,windows,MagickTrue);
8359 XCheckRefreshWindows(display,windows);
8360 (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8361 &(*image)->exception);
8362 (void) RaiseImage(*image,&page_geometry,MagickTrue);
8363 XSetCursorState(display,windows,MagickFalse);
8364 if (windows->image.orphan != MagickFalse)
8365 break;
8366 XConfigureImageColormap(display,resource_info,windows,*image);
8367 (void) XConfigureImage(display,resource_info,windows,*image);
8368 break;
8369 }
8370 case SegmentCommand:
8371 {
8372 static char
8373 threshold[MaxTextExtent] = "1.0x1.5";
8374
8375 /*
8376 Query user for smoothing threshold.
8377 */
8378 (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8379 threshold);
8380 if (*threshold == '\0')
8381 break;
8382 /*
8383 Segment an image.
8384 */
8385 XSetCursorState(display,windows,MagickTrue);
8386 XCheckRefreshWindows(display,windows);
8387 flags=ParseGeometry(threshold,&geometry_info);
8388 if ((flags & SigmaValue) == 0)
8389 geometry_info.sigma=1.0;
8390 (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8391 geometry_info.sigma);
8392 XSetCursorState(display,windows,MagickFalse);
8393 if (windows->image.orphan != MagickFalse)
8394 break;
8395 XConfigureImageColormap(display,resource_info,windows,*image);
8396 (void) XConfigureImage(display,resource_info,windows,*image);
8397 break;
8398 }
8399 case SepiaToneCommand:
8400 {
8401 double
8402 threshold;
8403
8404 Image
8405 *sepia_image;
8406
8407 static char
8408 factor[MaxTextExtent] = "80%";
8409
8410 /*
8411 Query user for sepia-tone factor.
8412 */
8413 (void) XDialogWidget(display,windows,"Sepia Tone",
8414 "Enter the sepia tone factor (0 - 99.9%):",factor);
8415 if (*factor == '\0')
8416 break;
8417 /*
8418 Sepia tone image pixels.
8419 */
8420 XSetCursorState(display,windows,MagickTrue);
8421 XCheckRefreshWindows(display,windows);
cristyf2f27272009-12-17 14:48:46 +00008422 threshold=SiPrefixToDouble(factor,QuantumRange);
cristy3ed852e2009-09-05 21:47:34 +00008423 sepia_image=SepiaToneImage(*image,threshold,&(*image)->exception);
8424 if (sepia_image != (Image *) NULL)
8425 {
8426 *image=DestroyImage(*image);
8427 *image=sepia_image;
8428 }
8429 CatchException(&(*image)->exception);
8430 XSetCursorState(display,windows,MagickFalse);
8431 if (windows->image.orphan != MagickFalse)
8432 break;
8433 XConfigureImageColormap(display,resource_info,windows,*image);
8434 (void) XConfigureImage(display,resource_info,windows,*image);
8435 break;
8436 }
8437 case SolarizeCommand:
8438 {
8439 double
8440 threshold;
8441
8442 static char
8443 factor[MaxTextExtent] = "60%";
8444
8445 /*
8446 Query user for solarize factor.
8447 */
8448 (void) XDialogWidget(display,windows,"Solarize",
8449 "Enter the solarize factor (0 - 99.9%):",factor);
8450 if (*factor == '\0')
8451 break;
8452 /*
8453 Solarize image pixels.
8454 */
8455 XSetCursorState(display,windows,MagickTrue);
8456 XCheckRefreshWindows(display,windows);
cristyf2f27272009-12-17 14:48:46 +00008457 threshold=SiPrefixToDouble(factor,QuantumRange);
cristy3ed852e2009-09-05 21:47:34 +00008458 (void) SolarizeImage(*image,threshold);
8459 XSetCursorState(display,windows,MagickFalse);
8460 if (windows->image.orphan != MagickFalse)
8461 break;
8462 XConfigureImageColormap(display,resource_info,windows,*image);
8463 (void) XConfigureImage(display,resource_info,windows,*image);
8464 break;
8465 }
8466 case SwirlCommand:
8467 {
8468 Image
8469 *swirl_image;
8470
8471 static char
8472 degrees[MaxTextExtent] = "60";
8473
8474 /*
8475 Query user for swirl angle.
8476 */
8477 (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8478 degrees);
8479 if (*degrees == '\0')
8480 break;
8481 /*
8482 Swirl image pixels about the center.
8483 */
8484 XSetCursorState(display,windows,MagickTrue);
8485 XCheckRefreshWindows(display,windows);
8486 flags=ParseGeometry(degrees,&geometry_info);
8487 swirl_image=SwirlImage(*image,geometry_info.rho,&(*image)->exception);
8488 if (swirl_image != (Image *) NULL)
8489 {
8490 *image=DestroyImage(*image);
8491 *image=swirl_image;
8492 }
8493 CatchException(&(*image)->exception);
8494 XSetCursorState(display,windows,MagickFalse);
8495 if (windows->image.orphan != MagickFalse)
8496 break;
8497 XConfigureImageColormap(display,resource_info,windows,*image);
8498 (void) XConfigureImage(display,resource_info,windows,*image);
8499 break;
8500 }
8501 case ImplodeCommand:
8502 {
8503 Image
8504 *implode_image;
8505
8506 static char
8507 factor[MaxTextExtent] = "0.3";
8508
8509 /*
8510 Query user for implode factor.
8511 */
8512 (void) XDialogWidget(display,windows,"Implode",
8513 "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8514 if (*factor == '\0')
8515 break;
8516 /*
8517 Implode image pixels about the center.
8518 */
8519 XSetCursorState(display,windows,MagickTrue);
8520 XCheckRefreshWindows(display,windows);
8521 flags=ParseGeometry(factor,&geometry_info);
8522 implode_image=ImplodeImage(*image,geometry_info.rho,&(*image)->exception);
8523 if (implode_image != (Image *) NULL)
8524 {
8525 *image=DestroyImage(*image);
8526 *image=implode_image;
8527 }
8528 CatchException(&(*image)->exception);
8529 XSetCursorState(display,windows,MagickFalse);
8530 if (windows->image.orphan != MagickFalse)
8531 break;
8532 XConfigureImageColormap(display,resource_info,windows,*image);
8533 (void) XConfigureImage(display,resource_info,windows,*image);
8534 break;
8535 }
8536 case VignetteCommand:
8537 {
8538 Image
8539 *vignette_image;
8540
8541 static char
8542 geometry[MaxTextExtent] = "0x20";
8543
8544 /*
8545 Query user for the vignette geometry.
8546 */
8547 (void) XDialogWidget(display,windows,"Vignette",
8548 "Enter the radius, sigma, and x and y offsets:",geometry);
8549 if (*geometry == '\0')
8550 break;
8551 /*
8552 Soften the edges of the image in vignette style
8553 */
8554 XSetCursorState(display,windows,MagickTrue);
8555 XCheckRefreshWindows(display,windows);
8556 flags=ParseGeometry(geometry,&geometry_info);
8557 if ((flags & SigmaValue) == 0)
8558 geometry_info.sigma=1.0;
8559 if ((flags & XiValue) == 0)
8560 geometry_info.xi=0.1*(*image)->columns;
8561 if ((flags & PsiValue) == 0)
8562 geometry_info.psi=0.1*(*image)->rows;
8563 vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
cristyb70a4862010-06-18 01:18:44 +00008564 (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8565 0.5),&(*image)->exception);
cristy3ed852e2009-09-05 21:47:34 +00008566 if (vignette_image != (Image *) NULL)
8567 {
8568 *image=DestroyImage(*image);
8569 *image=vignette_image;
8570 }
8571 CatchException(&(*image)->exception);
8572 XSetCursorState(display,windows,MagickFalse);
8573 if (windows->image.orphan != MagickFalse)
8574 break;
8575 XConfigureImageColormap(display,resource_info,windows,*image);
8576 (void) XConfigureImage(display,resource_info,windows,*image);
8577 break;
8578 }
8579 case WaveCommand:
8580 {
8581 Image
8582 *wave_image;
8583
8584 static char
8585 geometry[MaxTextExtent] = "25x150";
8586
8587 /*
8588 Query user for the wave geometry.
8589 */
8590 (void) XDialogWidget(display,windows,"Wave",
8591 "Enter the amplitude and length of the wave:",geometry);
8592 if (*geometry == '\0')
8593 break;
8594 /*
cristycee97112010-05-28 00:44:52 +00008595 Alter an image along a sine wave.
cristy3ed852e2009-09-05 21:47:34 +00008596 */
8597 XSetCursorState(display,windows,MagickTrue);
8598 XCheckRefreshWindows(display,windows);
8599 flags=ParseGeometry(geometry,&geometry_info);
8600 if ((flags & SigmaValue) == 0)
8601 geometry_info.sigma=1.0;
8602 wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8603 &(*image)->exception);
8604 if (wave_image != (Image *) NULL)
8605 {
8606 *image=DestroyImage(*image);
8607 *image=wave_image;
8608 }
8609 CatchException(&(*image)->exception);
8610 XSetCursorState(display,windows,MagickFalse);
8611 if (windows->image.orphan != MagickFalse)
8612 break;
8613 XConfigureImageColormap(display,resource_info,windows,*image);
8614 (void) XConfigureImage(display,resource_info,windows,*image);
8615 break;
8616 }
8617 case OilPaintCommand:
8618 {
8619 Image
8620 *paint_image;
8621
8622 static char
8623 radius[MaxTextExtent] = "0";
8624
8625 /*
8626 Query user for circular neighborhood radius.
8627 */
8628 (void) XDialogWidget(display,windows,"Oil Paint",
8629 "Enter the mask radius:",radius);
8630 if (*radius == '\0')
8631 break;
8632 /*
8633 OilPaint image scanlines.
8634 */
8635 XSetCursorState(display,windows,MagickTrue);
8636 XCheckRefreshWindows(display,windows);
8637 flags=ParseGeometry(radius,&geometry_info);
8638 paint_image=OilPaintImage(*image,geometry_info.rho,&(*image)->exception);
8639 if (paint_image != (Image *) NULL)
8640 {
8641 *image=DestroyImage(*image);
8642 *image=paint_image;
8643 }
8644 CatchException(&(*image)->exception);
8645 XSetCursorState(display,windows,MagickFalse);
8646 if (windows->image.orphan != MagickFalse)
8647 break;
8648 XConfigureImageColormap(display,resource_info,windows,*image);
8649 (void) XConfigureImage(display,resource_info,windows,*image);
8650 break;
8651 }
8652 case CharcoalDrawCommand:
8653 {
8654 Image
8655 *charcoal_image;
8656
8657 static char
8658 radius[MaxTextExtent] = "0x1";
8659
8660 /*
8661 Query user for charcoal radius.
8662 */
8663 (void) XDialogWidget(display,windows,"Charcoal Draw",
8664 "Enter the charcoal radius and sigma:",radius);
8665 if (*radius == '\0')
8666 break;
8667 /*
8668 Charcoal the image.
8669 */
8670 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8671 XSetCursorState(display,windows,MagickTrue);
8672 XCheckRefreshWindows(display,windows);
8673 flags=ParseGeometry(radius,&geometry_info);
8674 if ((flags & SigmaValue) == 0)
8675 geometry_info.sigma=geometry_info.rho;
8676 charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8677 &(*image)->exception);
8678 if (charcoal_image != (Image *) NULL)
8679 {
8680 *image=DestroyImage(*image);
8681 *image=charcoal_image;
8682 }
8683 CatchException(&(*image)->exception);
8684 XSetCursorState(display,windows,MagickFalse);
8685 if (windows->image.orphan != MagickFalse)
8686 break;
8687 XConfigureImageColormap(display,resource_info,windows,*image);
8688 (void) XConfigureImage(display,resource_info,windows,*image);
8689 break;
8690 }
8691 case AnnotateCommand:
8692 {
8693 /*
8694 Annotate the image with text.
8695 */
8696 status=XAnnotateEditImage(display,resource_info,windows,*image);
8697 if (status == MagickFalse)
8698 {
8699 XNoticeWidget(display,windows,"Unable to annotate X image",
8700 (*image)->filename);
8701 break;
8702 }
8703 break;
8704 }
8705 case DrawCommand:
8706 {
8707 /*
8708 Draw image.
8709 */
8710 status=XDrawEditImage(display,resource_info,windows,image);
8711 if (status == MagickFalse)
8712 {
8713 XNoticeWidget(display,windows,"Unable to draw on the X image",
8714 (*image)->filename);
8715 break;
8716 }
8717 break;
8718 }
8719 case ColorCommand:
8720 {
8721 /*
8722 Color edit.
8723 */
8724 status=XColorEditImage(display,resource_info,windows,image);
8725 if (status == MagickFalse)
8726 {
8727 XNoticeWidget(display,windows,"Unable to pixel edit X image",
8728 (*image)->filename);
8729 break;
8730 }
8731 break;
8732 }
8733 case MatteCommand:
8734 {
8735 /*
8736 Matte edit.
8737 */
8738 status=XMatteEditImage(display,resource_info,windows,image);
8739 if (status == MagickFalse)
8740 {
8741 XNoticeWidget(display,windows,"Unable to matte edit X image",
8742 (*image)->filename);
8743 break;
8744 }
8745 break;
8746 }
8747 case CompositeCommand:
8748 {
8749 /*
8750 Composite image.
8751 */
8752 status=XCompositeImage(display,resource_info,windows,*image);
8753 if (status == MagickFalse)
8754 {
8755 XNoticeWidget(display,windows,"Unable to composite X image",
8756 (*image)->filename);
8757 break;
8758 }
8759 break;
8760 }
8761 case AddBorderCommand:
8762 {
8763 Image
8764 *border_image;
8765
8766 static char
8767 geometry[MaxTextExtent] = "6x6";
8768
8769 /*
8770 Query user for border color and geometry.
8771 */
8772 XColorBrowserWidget(display,windows,"Select",color);
8773 if (*color == '\0')
8774 break;
8775 (void) XDialogWidget(display,windows,"Add Border",
8776 "Enter border geometry:",geometry);
8777 if (*geometry == '\0')
8778 break;
8779 /*
8780 Add a border to the image.
8781 */
8782 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8783 XSetCursorState(display,windows,MagickTrue);
8784 XCheckRefreshWindows(display,windows);
8785 (void) QueryColorDatabase(color,&(*image)->border_color,
8786 &(*image)->exception);
8787 (void) ParsePageGeometry(*image,geometry,&page_geometry,
8788 &(*image)->exception);
8789 border_image=BorderImage(*image,&page_geometry,&(*image)->exception);
8790 if (border_image != (Image *) NULL)
8791 {
8792 *image=DestroyImage(*image);
8793 *image=border_image;
8794 }
8795 CatchException(&(*image)->exception);
8796 XSetCursorState(display,windows,MagickFalse);
8797 if (windows->image.orphan != MagickFalse)
8798 break;
8799 windows->image.window_changes.width=(int) (*image)->columns;
8800 windows->image.window_changes.height=(int) (*image)->rows;
8801 XConfigureImageColormap(display,resource_info,windows,*image);
8802 (void) XConfigureImage(display,resource_info,windows,*image);
8803 break;
8804 }
8805 case AddFrameCommand:
8806 {
8807 FrameInfo
8808 frame_info;
8809
8810 Image
8811 *frame_image;
8812
8813 static char
8814 geometry[MaxTextExtent] = "6x6";
8815
8816 /*
8817 Query user for frame color and geometry.
8818 */
8819 XColorBrowserWidget(display,windows,"Select",color);
8820 if (*color == '\0')
8821 break;
8822 (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8823 geometry);
8824 if (*geometry == '\0')
8825 break;
8826 /*
8827 Surround image with an ornamental border.
8828 */
8829 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8830 XSetCursorState(display,windows,MagickTrue);
8831 XCheckRefreshWindows(display,windows);
8832 (void) QueryColorDatabase(color,&(*image)->matte_color,
8833 &(*image)->exception);
8834 (void) ParsePageGeometry(*image,geometry,&page_geometry,
8835 &(*image)->exception);
8836 frame_info.width=page_geometry.width;
8837 frame_info.height=page_geometry.height;
8838 frame_info.outer_bevel=page_geometry.x;
8839 frame_info.inner_bevel=page_geometry.y;
cristybb503372010-05-27 20:51:26 +00008840 frame_info.x=(ssize_t) frame_info.width;
8841 frame_info.y=(ssize_t) frame_info.height;
cristy3ed852e2009-09-05 21:47:34 +00008842 frame_info.width=(*image)->columns+2*frame_info.width;
8843 frame_info.height=(*image)->rows+2*frame_info.height;
8844 frame_image=FrameImage(*image,&frame_info,&(*image)->exception);
8845 if (frame_image != (Image *) NULL)
8846 {
8847 *image=DestroyImage(*image);
8848 *image=frame_image;
8849 }
8850 CatchException(&(*image)->exception);
8851 XSetCursorState(display,windows,MagickFalse);
8852 if (windows->image.orphan != MagickFalse)
8853 break;
8854 windows->image.window_changes.width=(int) (*image)->columns;
8855 windows->image.window_changes.height=(int) (*image)->rows;
8856 XConfigureImageColormap(display,resource_info,windows,*image);
8857 (void) XConfigureImage(display,resource_info,windows,*image);
8858 break;
8859 }
8860 case CommentCommand:
8861 {
8862 const char
8863 *value;
8864
8865 FILE
8866 *file;
8867
8868 int
8869 unique_file;
8870
8871 /*
8872 Edit image comment.
8873 */
8874 unique_file=AcquireUniqueFileResource(image_info->filename);
8875 if (unique_file == -1)
8876 XNoticeWidget(display,windows,"Unable to edit image comment",
8877 image_info->filename);
8878 value=GetImageProperty(*image,"comment");
8879 if (value == (char *) NULL)
8880 unique_file=close(unique_file)-1;
8881 else
8882 {
8883 register const char
8884 *p;
8885
8886 file=fdopen(unique_file,"w");
8887 if (file == (FILE *) NULL)
8888 {
8889 XNoticeWidget(display,windows,"Unable to edit image comment",
8890 image_info->filename);
8891 break;
8892 }
8893 for (p=value; *p != '\0'; p++)
8894 (void) fputc((int) *p,file);
8895 (void) fputc('\n',file);
8896 (void) fclose(file);
8897 }
8898 XSetCursorState(display,windows,MagickTrue);
8899 XCheckRefreshWindows(display,windows);
8900 status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8901 &(*image)->exception);
8902 if (status == MagickFalse)
8903 XNoticeWidget(display,windows,"Unable to edit image comment",
8904 (char *) NULL);
8905 else
8906 {
8907 char
8908 *comment;
8909
8910 comment=FileToString(image_info->filename,~0UL,&(*image)->exception);
8911 if (comment != (char *) NULL)
8912 {
8913 (void) SetImageProperty(*image,"comment",comment);
8914 (*image)->taint=MagickTrue;
8915 }
8916 }
8917 (void) RelinquishUniqueFileResource(image_info->filename);
8918 XSetCursorState(display,windows,MagickFalse);
8919 break;
8920 }
8921 case LaunchCommand:
8922 {
8923 /*
8924 Launch program.
8925 */
8926 XSetCursorState(display,windows,MagickTrue);
8927 XCheckRefreshWindows(display,windows);
8928 (void) AcquireUniqueFilename(filename);
8929 (void) FormatMagickString((*image)->filename,MaxTextExtent,"launch:%s",
8930 filename);
8931 status=WriteImage(image_info,*image);
8932 if (status == MagickFalse)
8933 XNoticeWidget(display,windows,"Unable to launch image editor",
8934 (char *) NULL);
8935 else
8936 {
8937 nexus=ReadImage(resource_info->image_info,&(*image)->exception);
8938 CatchException(&(*image)->exception);
8939 XClientMessage(display,windows->image.id,windows->im_protocols,
8940 windows->im_next_image,CurrentTime);
8941 }
8942 (void) RelinquishUniqueFileResource(filename);
8943 XSetCursorState(display,windows,MagickFalse);
8944 break;
8945 }
8946 case RegionofInterestCommand:
8947 {
8948 /*
8949 Apply an image processing technique to a region of interest.
8950 */
8951 (void) XROIImage(display,resource_info,windows,image);
8952 break;
8953 }
8954 case InfoCommand:
8955 break;
8956 case ZoomCommand:
8957 {
8958 /*
8959 Zoom image.
8960 */
8961 if (windows->magnify.mapped != MagickFalse)
8962 (void) XRaiseWindow(display,windows->magnify.id);
8963 else
8964 {
8965 /*
8966 Make magnify image.
8967 */
8968 XSetCursorState(display,windows,MagickTrue);
8969 (void) XMapRaised(display,windows->magnify.id);
8970 XSetCursorState(display,windows,MagickFalse);
8971 }
8972 break;
8973 }
8974 case ShowPreviewCommand:
8975 {
8976 char
8977 **previews;
8978
8979 Image
8980 *preview_image;
8981
8982 static char
8983 preview_type[MaxTextExtent] = "Gamma";
8984
8985 /*
8986 Select preview type from menu.
8987 */
8988 previews=GetMagickOptions(MagickPreviewOptions);
8989 if (previews == (char **) NULL)
8990 break;
8991 XListBrowserWidget(display,windows,&windows->widget,
8992 (const char **) previews,"Preview",
8993 "Select an enhancement, effect, or F/X:",preview_type);
8994 previews=DestroyStringList(previews);
8995 if (*preview_type == '\0')
8996 break;
8997 /*
8998 Show image preview.
8999 */
9000 XSetCursorState(display,windows,MagickTrue);
9001 XCheckRefreshWindows(display,windows);
9002 image_info->preview_type=(PreviewType)
9003 ParseMagickOption(MagickPreviewOptions,MagickFalse,preview_type);
cristybb503372010-05-27 20:51:26 +00009004 image_info->group=(ssize_t) windows->image.id;
cristy3ed852e2009-09-05 21:47:34 +00009005 (void) DeleteImageProperty(*image,"label");
9006 (void) SetImageProperty(*image,"label","Preview");
9007 (void) AcquireUniqueFilename(filename);
9008 (void) FormatMagickString((*image)->filename,MaxTextExtent,"preview:%s",
9009 filename);
9010 status=WriteImage(image_info,*image);
9011 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9012 preview_image=ReadImage(image_info,&(*image)->exception);
9013 (void) RelinquishUniqueFileResource(filename);
9014 if (preview_image == (Image *) NULL)
9015 break;
9016 (void) FormatMagickString(preview_image->filename,MaxTextExtent,"show:%s",
9017 filename);
9018 status=WriteImage(image_info,preview_image);
9019 preview_image=DestroyImage(preview_image);
9020 if (status == MagickFalse)
9021 XNoticeWidget(display,windows,"Unable to show image preview",
9022 (*image)->filename);
9023 XDelay(display,1500);
9024 XSetCursorState(display,windows,MagickFalse);
9025 break;
9026 }
9027 case ShowHistogramCommand:
9028 {
9029 Image
9030 *histogram_image;
9031
9032 /*
9033 Show image histogram.
9034 */
9035 XSetCursorState(display,windows,MagickTrue);
9036 XCheckRefreshWindows(display,windows);
cristybb503372010-05-27 20:51:26 +00009037 image_info->group=(ssize_t) windows->image.id;
cristy3ed852e2009-09-05 21:47:34 +00009038 (void) DeleteImageProperty(*image,"label");
9039 (void) SetImageProperty(*image,"label","Histogram");
9040 (void) AcquireUniqueFilename(filename);
9041 (void) FormatMagickString((*image)->filename,MaxTextExtent,"histogram:%s",
9042 filename);
9043 status=WriteImage(image_info,*image);
9044 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9045 histogram_image=ReadImage(image_info,&(*image)->exception);
9046 (void) RelinquishUniqueFileResource(filename);
9047 if (histogram_image == (Image *) NULL)
9048 break;
9049 (void) FormatMagickString(histogram_image->filename,MaxTextExtent,
9050 "show:%s",filename);
9051 status=WriteImage(image_info,histogram_image);
9052 histogram_image=DestroyImage(histogram_image);
9053 if (status == MagickFalse)
9054 XNoticeWidget(display,windows,"Unable to show histogram",
9055 (*image)->filename);
9056 XDelay(display,1500);
9057 XSetCursorState(display,windows,MagickFalse);
9058 break;
9059 }
9060 case ShowMatteCommand:
9061 {
9062 Image
9063 *matte_image;
9064
9065 if ((*image)->matte == MagickFalse)
9066 {
9067 XNoticeWidget(display,windows,
9068 "Image does not have any matte information",(*image)->filename);
9069 break;
9070 }
9071 /*
9072 Show image matte.
9073 */
9074 XSetCursorState(display,windows,MagickTrue);
9075 XCheckRefreshWindows(display,windows);
cristybb503372010-05-27 20:51:26 +00009076 image_info->group=(ssize_t) windows->image.id;
cristy3ed852e2009-09-05 21:47:34 +00009077 (void) DeleteImageProperty(*image,"label");
9078 (void) SetImageProperty(*image,"label","Matte");
9079 (void) AcquireUniqueFilename(filename);
9080 (void) FormatMagickString((*image)->filename,MaxTextExtent,"matte:%s",
9081 filename);
9082 status=WriteImage(image_info,*image);
9083 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9084 matte_image=ReadImage(image_info,&(*image)->exception);
9085 (void) RelinquishUniqueFileResource(filename);
9086 if (matte_image == (Image *) NULL)
9087 break;
9088 (void) FormatMagickString(matte_image->filename,MaxTextExtent,"show:%s",
9089 filename);
9090 status=WriteImage(image_info,matte_image);
9091 matte_image=DestroyImage(matte_image);
9092 if (status == MagickFalse)
9093 XNoticeWidget(display,windows,"Unable to show matte",
9094 (*image)->filename);
9095 XDelay(display,1500);
9096 XSetCursorState(display,windows,MagickFalse);
9097 break;
9098 }
9099 case BackgroundCommand:
9100 {
9101 /*
9102 Background image.
9103 */
9104 status=XBackgroundImage(display,resource_info,windows,image);
9105 if (status == MagickFalse)
9106 break;
9107 nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9108 if (nexus != (Image *) NULL)
9109 XClientMessage(display,windows->image.id,windows->im_protocols,
9110 windows->im_next_image,CurrentTime);
9111 break;
9112 }
9113 case SlideShowCommand:
9114 {
9115 static char
9116 delay[MaxTextExtent] = "5";
9117
9118 /*
9119 Display next image after pausing.
9120 */
9121 (void) XDialogWidget(display,windows,"Slide Show",
9122 "Pause how many 1/100ths of a second between images:",delay);
9123 if (*delay == '\0')
9124 break;
cristye27293e2009-12-18 02:53:20 +00009125 resource_info->delay=StringToUnsignedLong(delay);
cristy3ed852e2009-09-05 21:47:34 +00009126 XClientMessage(display,windows->image.id,windows->im_protocols,
9127 windows->im_next_image,CurrentTime);
9128 break;
9129 }
9130 case PreferencesCommand:
9131 {
9132 /*
9133 Set user preferences.
9134 */
9135 status=XPreferencesWidget(display,resource_info,windows);
9136 if (status == MagickFalse)
9137 break;
9138 nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9139 if (nexus != (Image *) NULL)
9140 XClientMessage(display,windows->image.id,windows->im_protocols,
9141 windows->im_next_image,CurrentTime);
9142 break;
9143 }
9144 case HelpCommand:
9145 {
9146 /*
9147 User requested help.
9148 */
9149 XTextViewWidget(display,resource_info,windows,MagickFalse,
9150 "Help Viewer - Display",DisplayHelp);
9151 break;
9152 }
9153 case BrowseDocumentationCommand:
9154 {
9155 Atom
9156 mozilla_atom;
9157
9158 Window
9159 mozilla_window,
9160 root_window;
9161
9162 /*
9163 Browse the ImageMagick documentation.
9164 */
9165 root_window=XRootWindow(display,XDefaultScreen(display));
9166 mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9167 mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9168 if (mozilla_window != (Window) NULL)
9169 {
9170 char
9171 command[MaxTextExtent],
9172 *url;
9173
9174 /*
9175 Display documentation using Netscape remote control.
9176 */
9177 url=GetMagickHomeURL();
9178 (void) FormatMagickString(command,MaxTextExtent,
9179 "openurl(%s,new-tab)",url);
9180 url=DestroyString(url);
9181 mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9182 (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9183 8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9184 XSetCursorState(display,windows,MagickFalse);
9185 break;
9186 }
9187 XSetCursorState(display,windows,MagickTrue);
9188 XCheckRefreshWindows(display,windows);
9189 status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9190 &(*image)->exception);
9191 if (status == MagickFalse)
9192 XNoticeWidget(display,windows,"Unable to browse documentation",
9193 (char *) NULL);
9194 XDelay(display,1500);
9195 XSetCursorState(display,windows,MagickFalse);
9196 break;
9197 }
9198 case VersionCommand:
9199 {
cristybb503372010-05-27 20:51:26 +00009200 XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
cristy3ed852e2009-09-05 21:47:34 +00009201 GetMagickCopyright());
9202 break;
9203 }
9204 case SaveToUndoBufferCommand:
9205 break;
9206 default:
9207 {
9208 (void) XBell(display,0);
9209 break;
9210 }
9211 }
9212 image_info=DestroyImageInfo(image_info);
9213 return(nexus);
9214}
9215
9216/*
9217%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9218% %
9219% %
9220% %
9221+ X M a g n i f y I m a g e %
9222% %
9223% %
9224% %
9225%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9226%
9227% XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9228% The magnified portion is displayed in a separate window.
9229%
9230% The format of the XMagnifyImage method is:
9231%
9232% void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9233%
9234% A description of each parameter follows:
9235%
9236% o display: Specifies a connection to an X server; returned from
9237% XOpenDisplay.
9238%
9239% o windows: Specifies a pointer to a XWindows structure.
9240%
9241% o event: Specifies a pointer to a XEvent structure. If it is NULL,
9242% the entire image is refreshed.
9243%
9244*/
9245static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9246{
9247 char
9248 text[MaxTextExtent];
9249
9250 register int
9251 x,
9252 y;
9253
cristybb503372010-05-27 20:51:26 +00009254 size_t
cristy3ed852e2009-09-05 21:47:34 +00009255 state;
9256
9257 /*
9258 Update magnified image until the mouse button is released.
9259 */
9260 (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9261 state=DefaultState;
9262 x=event->xbutton.x;
9263 y=event->xbutton.y;
cristy49e2d862010-11-12 02:50:30 +00009264 windows->magnify.x=(int) windows->image.x+x;
9265 windows->magnify.y=(int) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +00009266 do
9267 {
9268 /*
9269 Map and unmap Info widget as text cursor crosses its boundaries.
9270 */
9271 if (windows->info.mapped != MagickFalse)
9272 {
9273 if ((x < (int) (windows->info.x+windows->info.width)) &&
9274 (y < (int) (windows->info.y+windows->info.height)))
9275 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9276 }
9277 else
9278 if ((x > (int) (windows->info.x+windows->info.width)) ||
9279 (y > (int) (windows->info.y+windows->info.height)))
9280 (void) XMapWindow(display,windows->info.id);
9281 if (windows->info.mapped != MagickFalse)
9282 {
9283 /*
9284 Display pointer position.
9285 */
9286 (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
9287 windows->magnify.x,windows->magnify.y);
9288 XInfoWidget(display,windows,text);
9289 }
9290 /*
9291 Wait for next event.
9292 */
9293 XScreenEvent(display,windows,event);
9294 switch (event->type)
9295 {
9296 case ButtonPress:
9297 break;
9298 case ButtonRelease:
9299 {
9300 /*
9301 User has finished magnifying image.
9302 */
9303 x=event->xbutton.x;
9304 y=event->xbutton.y;
9305 state|=ExitState;
9306 break;
9307 }
9308 case Expose:
9309 break;
9310 case MotionNotify:
9311 {
9312 x=event->xmotion.x;
9313 y=event->xmotion.y;
9314 break;
9315 }
9316 default:
9317 break;
9318 }
9319 /*
9320 Check boundary conditions.
9321 */
9322 if (x < 0)
9323 x=0;
9324 else
9325 if (x >= (int) windows->image.width)
9326 x=(int) windows->image.width-1;
9327 if (y < 0)
9328 y=0;
9329 else
9330 if (y >= (int) windows->image.height)
9331 y=(int) windows->image.height-1;
9332 } while ((state & ExitState) == 0);
9333 /*
9334 Display magnified image.
9335 */
9336 XSetCursorState(display,windows,MagickFalse);
9337}
9338
9339/*
9340%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9341% %
9342% %
9343% %
9344+ X M a g n i f y W i n d o w C o m m a n d %
9345% %
9346% %
9347% %
9348%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9349%
9350% XMagnifyWindowCommand() moves the image within an Magnify window by one
9351% pixel as specified by the key symbol.
9352%
9353% The format of the XMagnifyWindowCommand method is:
9354%
9355% void XMagnifyWindowCommand(Display *display,XWindows *windows,
9356% const MagickStatusType state,const KeySym key_symbol)
9357%
9358% A description of each parameter follows:
9359%
9360% o display: Specifies a connection to an X server; returned from
9361% XOpenDisplay.
9362%
9363% o windows: Specifies a pointer to a XWindows structure.
9364%
9365% o state: key mask.
9366%
9367% o key_symbol: Specifies a KeySym which indicates which side of the image
9368% to trim.
9369%
9370*/
9371static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9372 const MagickStatusType state,const KeySym key_symbol)
9373{
9374 unsigned int
9375 quantum;
9376
9377 /*
9378 User specified a magnify factor or position.
9379 */
9380 quantum=1;
9381 if ((state & Mod1Mask) != 0)
9382 quantum=10;
9383 switch ((int) key_symbol)
9384 {
9385 case QuitCommand:
9386 {
9387 (void) XWithdrawWindow(display,windows->magnify.id,
9388 windows->magnify.screen);
9389 break;
9390 }
9391 case XK_Home:
9392 case XK_KP_Home:
9393 {
9394 windows->magnify.x=(int) windows->image.width/2;
9395 windows->magnify.y=(int) windows->image.height/2;
9396 break;
9397 }
9398 case XK_Left:
9399 case XK_KP_Left:
9400 {
9401 if (windows->magnify.x > 0)
9402 windows->magnify.x-=quantum;
9403 break;
9404 }
9405 case XK_Up:
9406 case XK_KP_Up:
9407 {
9408 if (windows->magnify.y > 0)
9409 windows->magnify.y-=quantum;
9410 break;
9411 }
9412 case XK_Right:
9413 case XK_KP_Right:
9414 {
9415 if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9416 windows->magnify.x+=quantum;
9417 break;
9418 }
9419 case XK_Down:
9420 case XK_KP_Down:
9421 {
9422 if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9423 windows->magnify.y+=quantum;
9424 break;
9425 }
9426 case XK_0:
9427 case XK_1:
9428 case XK_2:
9429 case XK_3:
9430 case XK_4:
9431 case XK_5:
9432 case XK_6:
9433 case XK_7:
9434 case XK_8:
9435 case XK_9:
9436 {
9437 windows->magnify.data=(key_symbol-XK_0);
9438 break;
9439 }
9440 case XK_KP_0:
9441 case XK_KP_1:
9442 case XK_KP_2:
9443 case XK_KP_3:
9444 case XK_KP_4:
9445 case XK_KP_5:
9446 case XK_KP_6:
9447 case XK_KP_7:
9448 case XK_KP_8:
9449 case XK_KP_9:
9450 {
9451 windows->magnify.data=(key_symbol-XK_KP_0);
9452 break;
9453 }
9454 default:
9455 break;
9456 }
9457 XMakeMagnifyImage(display,windows);
9458}
9459
9460/*
9461%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9462% %
9463% %
9464% %
9465+ X M a k e P a n I m a g e %
9466% %
9467% %
9468% %
9469%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9470%
9471% XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9472% icon window.
9473%
9474% The format of the XMakePanImage method is:
9475%
9476% void XMakePanImage(Display *display,XResourceInfo *resource_info,
9477% XWindows *windows,Image *image)
9478%
9479% A description of each parameter follows:
9480%
9481% o display: Specifies a connection to an X server; returned from
9482% XOpenDisplay.
9483%
9484% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9485%
9486% o windows: Specifies a pointer to a XWindows structure.
9487%
9488% o image: the image.
9489%
9490*/
9491static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9492 XWindows *windows,Image *image)
9493{
9494 MagickStatusType
9495 status;
9496
9497 /*
9498 Create and display image for panning icon.
9499 */
9500 XSetCursorState(display,windows,MagickTrue);
9501 XCheckRefreshWindows(display,windows);
cristy49e2d862010-11-12 02:50:30 +00009502 windows->pan.x=(int) windows->image.x;
9503 windows->pan.y=(int) windows->image.y;
cristy3ed852e2009-09-05 21:47:34 +00009504 status=XMakeImage(display,resource_info,&windows->pan,image,
9505 windows->pan.width,windows->pan.height);
9506 if (status == MagickFalse)
9507 ThrowXWindowFatalException(XServerError,image->exception.reason,
9508 image->exception.description);
9509 (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9510 windows->pan.pixmap);
9511 (void) XClearWindow(display,windows->pan.id);
9512 XDrawPanRectangle(display,windows);
9513 XSetCursorState(display,windows,MagickFalse);
9514}
9515
9516/*
9517%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9518% %
9519% %
9520% %
9521+ X M a t t a E d i t I m a g e %
9522% %
9523% %
9524% %
9525%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9526%
9527% XMatteEditImage() allows the user to interactively change the Matte channel
9528% of an image. If the image is PseudoClass it is promoted to DirectClass
9529% before the matte information is stored.
9530%
9531% The format of the XMatteEditImage method is:
9532%
9533% MagickBooleanType XMatteEditImage(Display *display,
9534% XResourceInfo *resource_info,XWindows *windows,Image **image)
9535%
9536% A description of each parameter follows:
9537%
9538% o display: Specifies a connection to an X server; returned from
9539% XOpenDisplay.
9540%
9541% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9542%
9543% o windows: Specifies a pointer to a XWindows structure.
9544%
9545% o image: the image; returned from ReadImage.
9546%
9547*/
9548static MagickBooleanType XMatteEditImage(Display *display,
9549 XResourceInfo *resource_info,XWindows *windows,Image **image)
9550{
9551 static char
9552 matte[MaxTextExtent] = "0";
9553
9554 static const char
9555 *MatteEditMenu[] =
9556 {
9557 "Method",
9558 "Border Color",
9559 "Fuzz",
9560 "Matte Value",
9561 "Undo",
9562 "Help",
9563 "Dismiss",
9564 (char *) NULL
9565 };
9566
9567 static const ModeType
9568 MatteEditCommands[] =
9569 {
9570 MatteEditMethod,
9571 MatteEditBorderCommand,
9572 MatteEditFuzzCommand,
9573 MatteEditValueCommand,
9574 MatteEditUndoCommand,
9575 MatteEditHelpCommand,
9576 MatteEditDismissCommand
9577 };
9578
9579 static PaintMethod
9580 method = PointMethod;
9581
9582 static XColor
9583 border_color = { 0, 0, 0, 0, 0, 0 };
9584
9585 char
9586 command[MaxTextExtent],
9587 text[MaxTextExtent];
9588
9589 Cursor
9590 cursor;
9591
9592 int
9593 entry,
9594 id,
9595 x,
9596 x_offset,
9597 y,
9598 y_offset;
9599
9600 register int
9601 i;
9602
9603 register PixelPacket
9604 *q;
9605
9606 unsigned int
9607 height,
9608 width;
9609
cristybb503372010-05-27 20:51:26 +00009610 size_t
cristy3ed852e2009-09-05 21:47:34 +00009611 state;
9612
9613 XEvent
9614 event;
9615
9616 /*
9617 Map Command widget.
9618 */
9619 (void) CloneString(&windows->command.name,"Matte Edit");
9620 windows->command.data=4;
9621 (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9622 (void) XMapRaised(display,windows->command.id);
9623 XClientMessage(display,windows->image.id,windows->im_protocols,
9624 windows->im_update_widget,CurrentTime);
9625 /*
9626 Make cursor.
9627 */
9628 cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9629 resource_info->background_color,resource_info->foreground_color);
9630 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9631 /*
9632 Track pointer until button 1 is pressed.
9633 */
9634 XQueryPosition(display,windows->image.id,&x,&y);
9635 (void) XSelectInput(display,windows->image.id,
9636 windows->image.attributes.event_mask | PointerMotionMask);
9637 state=DefaultState;
9638 do
9639 {
9640 if (windows->info.mapped != MagickFalse)
9641 {
9642 /*
9643 Display pointer position.
9644 */
9645 (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
9646 x+windows->image.x,y+windows->image.y);
9647 XInfoWidget(display,windows,text);
9648 }
9649 /*
9650 Wait for next event.
9651 */
9652 XScreenEvent(display,windows,&event);
9653 if (event.xany.window == windows->command.id)
9654 {
9655 /*
9656 Select a command from the Command widget.
9657 */
9658 id=XCommandWidget(display,windows,MatteEditMenu,&event);
9659 if (id < 0)
9660 {
9661 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9662 continue;
9663 }
9664 switch (MatteEditCommands[id])
9665 {
9666 case MatteEditMethod:
9667 {
9668 char
9669 **methods;
9670
9671 /*
9672 Select a method from the pop-up menu.
9673 */
9674 methods=GetMagickOptions(MagickMethodOptions);
9675 if (methods == (char **) NULL)
9676 break;
9677 entry=XMenuWidget(display,windows,MatteEditMenu[id],
9678 (const char **) methods,command);
9679 if (entry >= 0)
9680 method=(PaintMethod) ParseMagickOption(MagickMethodOptions,
9681 MagickFalse,methods[entry]);
9682 methods=DestroyStringList(methods);
9683 break;
9684 }
9685 case MatteEditBorderCommand:
9686 {
9687 const char
9688 *ColorMenu[MaxNumberPens];
9689
9690 int
9691 pen_number;
9692
9693 /*
9694 Initialize menu selections.
9695 */
9696 for (i=0; i < (int) (MaxNumberPens-2); i++)
9697 ColorMenu[i]=resource_info->pen_colors[i];
9698 ColorMenu[MaxNumberPens-2]="Browser...";
9699 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9700 /*
9701 Select a pen color from the pop-up menu.
9702 */
9703 pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9704 (const char **) ColorMenu,command);
9705 if (pen_number < 0)
9706 break;
9707 if (pen_number == (MaxNumberPens-2))
9708 {
9709 static char
9710 color_name[MaxTextExtent] = "gray";
9711
9712 /*
9713 Select a pen color from a dialog.
9714 */
9715 resource_info->pen_colors[pen_number]=color_name;
9716 XColorBrowserWidget(display,windows,"Select",color_name);
9717 if (*color_name == '\0')
9718 break;
9719 }
9720 /*
9721 Set border color.
9722 */
9723 (void) XParseColor(display,windows->map_info->colormap,
9724 resource_info->pen_colors[pen_number],&border_color);
9725 break;
9726 }
9727 case MatteEditFuzzCommand:
9728 {
9729 static char
9730 fuzz[MaxTextExtent];
9731
9732 static const char
9733 *FuzzMenu[] =
9734 {
9735 "0%",
9736 "2%",
9737 "5%",
9738 "10%",
9739 "15%",
9740 "Dialog...",
9741 (char *) NULL,
9742 };
9743
9744 /*
9745 Select a command from the pop-up menu.
9746 */
9747 entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9748 command);
9749 if (entry < 0)
9750 break;
9751 if (entry != 5)
9752 {
cristyf2f27272009-12-17 14:48:46 +00009753 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*QuantumRange+
cristy3ed852e2009-09-05 21:47:34 +00009754 1.0);
9755 break;
9756 }
9757 (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9758 (void) XDialogWidget(display,windows,"Ok",
9759 "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9760 if (*fuzz == '\0')
9761 break;
9762 (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
cristyf2f27272009-12-17 14:48:46 +00009763 (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
cristy3ed852e2009-09-05 21:47:34 +00009764 break;
9765 }
9766 case MatteEditValueCommand:
9767 {
9768 static char
9769 message[MaxTextExtent];
9770
9771 static const char
9772 *MatteMenu[] =
9773 {
9774 "Opaque",
9775 "Transparent",
9776 "Dialog...",
9777 (char *) NULL,
9778 };
9779
9780 /*
9781 Select a command from the pop-up menu.
9782 */
9783 entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9784 command);
9785 if (entry < 0)
9786 break;
9787 if (entry != 2)
9788 {
9789 (void) FormatMagickString(matte,MaxTextExtent,QuantumFormat,
9790 OpaqueOpacity);
9791 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9792 (void) FormatMagickString(matte,MaxTextExtent,QuantumFormat,
9793 (Quantum) TransparentOpacity);
9794 break;
9795 }
9796 (void) FormatMagickString(message,MaxTextExtent,
9797 "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9798 QuantumRange);
9799 (void) XDialogWidget(display,windows,"Matte",message,matte);
9800 if (*matte == '\0')
9801 break;
9802 break;
9803 }
9804 case MatteEditUndoCommand:
9805 {
9806 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9807 image);
9808 break;
9809 }
9810 case MatteEditHelpCommand:
9811 {
9812 XTextViewWidget(display,resource_info,windows,MagickFalse,
9813 "Help Viewer - Matte Edit",ImageMatteEditHelp);
9814 break;
9815 }
9816 case MatteEditDismissCommand:
9817 {
9818 /*
9819 Prematurely exit.
9820 */
9821 state|=EscapeState;
9822 state|=ExitState;
9823 break;
9824 }
9825 default:
9826 break;
9827 }
9828 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9829 continue;
9830 }
9831 switch (event.type)
9832 {
9833 case ButtonPress:
9834 {
9835 if (event.xbutton.button != Button1)
9836 break;
9837 if ((event.xbutton.window != windows->image.id) &&
9838 (event.xbutton.window != windows->magnify.id))
9839 break;
9840 /*
9841 Update matte data.
9842 */
9843 x=event.xbutton.x;
9844 y=event.xbutton.y;
9845 (void) XMagickCommand(display,resource_info,windows,
9846 SaveToUndoBufferCommand,image);
9847 state|=UpdateConfigurationState;
9848 break;
9849 }
9850 case ButtonRelease:
9851 {
9852 if (event.xbutton.button != Button1)
9853 break;
9854 if ((event.xbutton.window != windows->image.id) &&
9855 (event.xbutton.window != windows->magnify.id))
9856 break;
9857 /*
9858 Update colormap information.
9859 */
9860 x=event.xbutton.x;
9861 y=event.xbutton.y;
9862 XConfigureImageColormap(display,resource_info,windows,*image);
9863 (void) XConfigureImage(display,resource_info,windows,*image);
9864 XInfoWidget(display,windows,text);
9865 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9866 state&=(~UpdateConfigurationState);
9867 break;
9868 }
9869 case Expose:
9870 break;
9871 case KeyPress:
9872 {
9873 char
9874 command[MaxTextExtent];
9875
9876 KeySym
9877 key_symbol;
9878
9879 if (event.xkey.window == windows->magnify.id)
9880 {
9881 Window
9882 window;
9883
9884 window=windows->magnify.id;
9885 while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9886 }
9887 if (event.xkey.window != windows->image.id)
9888 break;
9889 /*
9890 Respond to a user key press.
9891 */
9892 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9893 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9894 switch ((int) key_symbol)
9895 {
9896 case XK_Escape:
9897 case XK_F20:
9898 {
9899 /*
9900 Prematurely exit.
9901 */
9902 state|=ExitState;
9903 break;
9904 }
9905 case XK_F1:
9906 case XK_Help:
9907 {
9908 XTextViewWidget(display,resource_info,windows,MagickFalse,
9909 "Help Viewer - Matte Edit",ImageMatteEditHelp);
9910 break;
9911 }
9912 default:
9913 {
9914 (void) XBell(display,0);
9915 break;
9916 }
9917 }
9918 break;
9919 }
9920 case MotionNotify:
9921 {
9922 /*
9923 Map and unmap Info widget as cursor crosses its boundaries.
9924 */
9925 x=event.xmotion.x;
9926 y=event.xmotion.y;
9927 if (windows->info.mapped != MagickFalse)
9928 {
9929 if ((x < (int) (windows->info.x+windows->info.width)) &&
9930 (y < (int) (windows->info.y+windows->info.height)))
9931 (void) XWithdrawWindow(display,windows->info.id,
9932 windows->info.screen);
9933 }
9934 else
9935 if ((x > (int) (windows->info.x+windows->info.width)) ||
9936 (y > (int) (windows->info.y+windows->info.height)))
9937 (void) XMapWindow(display,windows->info.id);
9938 break;
9939 }
9940 default:
9941 break;
9942 }
9943 if (event.xany.window == windows->magnify.id)
9944 {
9945 x=windows->magnify.x-windows->image.x;
9946 y=windows->magnify.y-windows->image.y;
9947 }
9948 x_offset=x;
9949 y_offset=y;
9950 if ((state & UpdateConfigurationState) != 0)
9951 {
cristy49e2d862010-11-12 02:50:30 +00009952 CacheView
9953 *image_view;
9954
cristy3ed852e2009-09-05 21:47:34 +00009955 ExceptionInfo
9956 *exception;
9957
9958 int
9959 x,
9960 y;
9961
9962 /*
9963 Matte edit is relative to image configuration.
9964 */
9965 (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
9966 MagickTrue);
9967 XPutPixel(windows->image.ximage,x_offset,y_offset,
9968 windows->pixel_info->background_color.pixel);
9969 width=(unsigned int) (*image)->columns;
9970 height=(unsigned int) (*image)->rows;
9971 x=0;
9972 y=0;
9973 if (windows->image.crop_geometry != (char *) NULL)
9974 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
9975 &width,&height);
9976 x_offset=(int)
9977 (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
9978 y_offset=(int)
9979 (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
9980 if ((x_offset < 0) || (y_offset < 0))
9981 continue;
9982 if ((x_offset >= (int) (*image)->columns) ||
9983 (y_offset >= (int) (*image)->rows))
9984 continue;
9985 if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
9986 return(MagickFalse);
9987 (*image)->matte=MagickTrue;
9988 exception=(&(*image)->exception);
cristy49e2d862010-11-12 02:50:30 +00009989 image_view=AcquireCacheView(*image);
cristy3ed852e2009-09-05 21:47:34 +00009990 switch (method)
9991 {
9992 case PointMethod:
9993 default:
9994 {
9995 /*
9996 Update matte information using point algorithm.
9997 */
cristy49e2d862010-11-12 02:50:30 +00009998 q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
9999 (ssize_t) y_offset,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +000010000 if (q == (PixelPacket *) NULL)
10001 break;
cristyf2f27272009-12-17 14:48:46 +000010002 q->opacity=(Quantum) StringToLong(matte);
cristy49e2d862010-11-12 02:50:30 +000010003 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +000010004 break;
10005 }
10006 case ReplaceMethod:
10007 {
10008 PixelPacket
10009 target;
10010
10011 /*
10012 Update matte information using replace algorithm.
10013 */
cristy49e2d862010-11-12 02:50:30 +000010014 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10015 (ssize_t) y_offset,&target,exception);
10016 for (y=0; y < (int) (*image)->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010017 {
cristy49e2d862010-11-12 02:50:30 +000010018 q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10019 (*image)->columns,1,&(*image)->exception);
cristy3ed852e2009-09-05 21:47:34 +000010020 if (q == (PixelPacket *) NULL)
10021 break;
10022 for (x=0; x < (int) (*image)->columns; x++)
10023 {
10024 if (IsColorSimilar(*image,q,&target))
cristyf2f27272009-12-17 14:48:46 +000010025 q->opacity=(Quantum) StringToLong(matte);
cristy3ed852e2009-09-05 21:47:34 +000010026 q++;
10027 }
cristy49e2d862010-11-12 02:50:30 +000010028 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010029 break;
10030 }
10031 break;
10032 }
10033 case FloodfillMethod:
10034 case FillToBorderMethod:
10035 {
10036 DrawInfo
10037 *draw_info;
10038
10039 MagickPixelPacket
10040 target;
10041
10042 /*
10043 Update matte information using floodfill algorithm.
10044 */
cristy49e2d862010-11-12 02:50:30 +000010045 (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10046 (ssize_t) y_offset,&target,exception);
cristy3ed852e2009-09-05 21:47:34 +000010047 if (method == FillToBorderMethod)
10048 {
10049 target.red=(MagickRealType)
10050 ScaleShortToQuantum(border_color.red);
10051 target.green=(MagickRealType)
10052 ScaleShortToQuantum(border_color.green);
10053 target.blue=(MagickRealType)
10054 ScaleShortToQuantum(border_color.blue);
10055 }
10056 draw_info=CloneDrawInfo(resource_info->image_info,
10057 (DrawInfo *) NULL);
cristyce70c172010-01-07 17:15:30 +000010058 draw_info->fill.opacity=ClampToQuantum(StringToDouble(matte));
cristy3ed852e2009-09-05 21:47:34 +000010059 (void) FloodfillPaintImage(*image,OpacityChannel,draw_info,&target,
cristy49e2d862010-11-12 02:50:30 +000010060 (ssize_t) x_offset,(ssize_t) y_offset,
10061 method == FloodfillMethod ? MagickFalse : MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +000010062 draw_info=DestroyDrawInfo(draw_info);
10063 break;
10064 }
10065 case ResetMethod:
10066 {
10067 /*
10068 Update matte information using reset algorithm.
10069 */
10070 if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
10071 return(MagickFalse);
cristy49e2d862010-11-12 02:50:30 +000010072 for (y=0; y < (int) (*image)->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010073 {
cristy49e2d862010-11-12 02:50:30 +000010074 q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10075 (*image)->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +000010076 if (q == (PixelPacket *) NULL)
10077 break;
10078 for (x=0; x < (int) (*image)->columns; x++)
10079 {
cristyf2f27272009-12-17 14:48:46 +000010080 q->opacity=(Quantum) StringToLong(matte);
cristy3ed852e2009-09-05 21:47:34 +000010081 q++;
10082 }
cristy49e2d862010-11-12 02:50:30 +000010083 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010084 break;
10085 }
cristyf2f27272009-12-17 14:48:46 +000010086 if (StringToLong(matte) == OpaqueOpacity)
cristy3ed852e2009-09-05 21:47:34 +000010087 (*image)->matte=MagickFalse;
10088 break;
10089 }
10090 }
cristy49e2d862010-11-12 02:50:30 +000010091 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +000010092 state&=(~UpdateConfigurationState);
10093 }
10094 } while ((state & ExitState) == 0);
10095 (void) XSelectInput(display,windows->image.id,
10096 windows->image.attributes.event_mask);
10097 XSetCursorState(display,windows,MagickFalse);
10098 (void) XFreeCursor(display,cursor);
10099 return(MagickTrue);
10100}
10101
10102/*
10103%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10104% %
10105% %
10106% %
10107+ X O p e n I m a g e %
10108% %
10109% %
10110% %
10111%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10112%
10113% XOpenImage() loads an image from a file.
10114%
10115% The format of the XOpenImage method is:
10116%
10117% Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10118% XWindows *windows,const unsigned int command)
10119%
10120% A description of each parameter follows:
10121%
10122% o display: Specifies a connection to an X server; returned from
10123% XOpenDisplay.
10124%
10125% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10126%
10127% o windows: Specifies a pointer to a XWindows structure.
10128%
10129% o command: A value other than zero indicates that the file is selected
10130% from the command line argument list.
10131%
10132*/
10133static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10134 XWindows *windows,const MagickBooleanType command)
10135{
10136 const MagickInfo
10137 *magick_info;
10138
10139 ExceptionInfo
10140 *exception;
10141
10142 Image
10143 *nexus;
10144
10145 ImageInfo
10146 *image_info;
10147
10148 static char
10149 filename[MaxTextExtent] = "\0";
10150
10151 /*
10152 Request file name from user.
10153 */
10154 if (command == MagickFalse)
10155 XFileBrowserWidget(display,windows,"Open",filename);
10156 else
10157 {
10158 char
10159 **filelist,
10160 **files;
10161
10162 int
10163 count,
10164 status;
10165
10166 register int
10167 i,
10168 j;
10169
10170 /*
10171 Select next image from the command line.
10172 */
10173 status=XGetCommand(display,windows->image.id,&files,&count);
10174 if (status == 0)
10175 {
10176 ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10177 return((Image *) NULL);
10178 }
10179 filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10180 if (filelist == (char **) NULL)
10181 {
10182 ThrowXWindowFatalException(ResourceLimitError,
10183 "MemoryAllocationFailed","...");
10184 (void) XFreeStringList(files);
10185 return((Image *) NULL);
10186 }
10187 j=0;
10188 for (i=1; i < count; i++)
10189 if (*files[i] != '-')
10190 filelist[j++]=files[i];
10191 filelist[j]=(char *) NULL;
10192 XListBrowserWidget(display,windows,&windows->widget,
10193 (const char **) filelist,"Load","Select Image to Load:",filename);
10194 filelist=(char **) RelinquishMagickMemory(filelist);
10195 (void) XFreeStringList(files);
10196 }
10197 if (*filename == '\0')
10198 return((Image *) NULL);
10199 image_info=CloneImageInfo(resource_info->image_info);
10200 (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10201 (void *) NULL);
10202 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10203 exception=AcquireExceptionInfo();
cristyd965a422010-03-03 17:47:35 +000010204 (void) SetImageInfo(image_info,0,exception);
cristy3ed852e2009-09-05 21:47:34 +000010205 if (LocaleCompare(image_info->magick,"X") == 0)
10206 {
10207 char
10208 seconds[MaxTextExtent];
10209
10210 /*
10211 User may want to delay the X server screen grab.
10212 */
10213 (void) CopyMagickString(seconds,"0",MaxTextExtent);
10214 (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10215 seconds);
10216 if (*seconds == '\0')
10217 return((Image *) NULL);
cristybb503372010-05-27 20:51:26 +000010218 XDelay(display,(size_t) (1000*StringToLong(seconds)));
cristy3ed852e2009-09-05 21:47:34 +000010219 }
10220 magick_info=GetMagickInfo(image_info->magick,exception);
10221 if ((magick_info != (const MagickInfo *) NULL) &&
10222 (magick_info->raw != MagickFalse))
10223 {
10224 char
10225 geometry[MaxTextExtent];
10226
10227 /*
10228 Request image size from the user.
10229 */
10230 (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10231 if (image_info->size != (char *) NULL)
10232 (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10233 (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10234 geometry);
10235 (void) CloneString(&image_info->size,geometry);
10236 }
10237 /*
10238 Load the image.
10239 */
10240 XSetCursorState(display,windows,MagickTrue);
10241 XCheckRefreshWindows(display,windows);
10242 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10243 nexus=ReadImage(image_info,exception);
10244 CatchException(exception);
10245 XSetCursorState(display,windows,MagickFalse);
10246 if (nexus != (Image *) NULL)
10247 XClientMessage(display,windows->image.id,windows->im_protocols,
10248 windows->im_next_image,CurrentTime);
10249 else
10250 {
10251 char
10252 *text,
10253 **textlist;
10254
10255 /*
10256 Unknown image format.
10257 */
10258 text=FileToString(filename,~0,exception);
10259 if (text == (char *) NULL)
10260 return((Image *) NULL);
10261 textlist=StringToList(text);
10262 if (textlist != (char **) NULL)
10263 {
10264 char
10265 title[MaxTextExtent];
10266
10267 register int
10268 i;
10269
10270 (void) FormatMagickString(title,MaxTextExtent,
10271 "Unknown format: %s",filename);
10272 XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10273 (const char **) textlist);
10274 for (i=0; textlist[i] != (char *) NULL; i++)
10275 textlist[i]=DestroyString(textlist[i]);
10276 textlist=(char **) RelinquishMagickMemory(textlist);
10277 }
10278 text=DestroyString(text);
10279 }
10280 exception=DestroyExceptionInfo(exception);
10281 image_info=DestroyImageInfo(image_info);
10282 return(nexus);
10283}
10284
10285/*
10286%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10287% %
10288% %
10289% %
10290+ X P a n I m a g e %
10291% %
10292% %
10293% %
10294%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10295%
10296% XPanImage() pans the image until the mouse button is released.
10297%
10298% The format of the XPanImage method is:
10299%
10300% void XPanImage(Display *display,XWindows *windows,XEvent *event)
10301%
10302% A description of each parameter follows:
10303%
10304% o display: Specifies a connection to an X server; returned from
10305% XOpenDisplay.
10306%
10307% o windows: Specifies a pointer to a XWindows structure.
10308%
10309% o event: Specifies a pointer to a XEvent structure. If it is NULL,
10310% the entire image is refreshed.
10311%
10312*/
10313static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10314{
10315 char
10316 text[MaxTextExtent];
10317
10318 Cursor
10319 cursor;
10320
10321 MagickRealType
10322 x_factor,
10323 y_factor;
10324
10325 RectangleInfo
10326 pan_info;
10327
cristybb503372010-05-27 20:51:26 +000010328 size_t
cristy3ed852e2009-09-05 21:47:34 +000010329 state;
10330
10331 /*
10332 Define cursor.
10333 */
10334 if ((windows->image.ximage->width > (int) windows->image.width) &&
10335 (windows->image.ximage->height > (int) windows->image.height))
10336 cursor=XCreateFontCursor(display,XC_fleur);
10337 else
10338 if (windows->image.ximage->width > (int) windows->image.width)
10339 cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10340 else
10341 if (windows->image.ximage->height > (int) windows->image.height)
10342 cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10343 else
10344 cursor=XCreateFontCursor(display,XC_arrow);
10345 (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10346 /*
10347 Pan image as pointer moves until the mouse button is released.
10348 */
10349 x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10350 y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10351 pan_info.width=windows->pan.width*windows->image.width/
10352 windows->image.ximage->width;
10353 pan_info.height=windows->pan.height*windows->image.height/
10354 windows->image.ximage->height;
10355 pan_info.x=0;
10356 pan_info.y=0;
10357 state=UpdateConfigurationState;
10358 do
10359 {
10360 switch (event->type)
10361 {
10362 case ButtonPress:
10363 {
10364 /*
10365 User choose an initial pan location.
10366 */
cristy49e2d862010-11-12 02:50:30 +000010367 pan_info.x=(ssize_t) event->xbutton.x;
10368 pan_info.y=(ssize_t) event->xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000010369 state|=UpdateConfigurationState;
10370 break;
10371 }
10372 case ButtonRelease:
10373 {
10374 /*
10375 User has finished panning the image.
10376 */
cristy49e2d862010-11-12 02:50:30 +000010377 pan_info.x=(ssize_t) event->xbutton.x;
10378 pan_info.y=(ssize_t) event->xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000010379 state|=UpdateConfigurationState | ExitState;
10380 break;
10381 }
10382 case MotionNotify:
10383 {
cristy49e2d862010-11-12 02:50:30 +000010384 pan_info.x=(ssize_t) event->xmotion.x;
10385 pan_info.y=(ssize_t) event->xmotion.y;
cristy3ed852e2009-09-05 21:47:34 +000010386 state|=UpdateConfigurationState;
10387 }
10388 default:
10389 break;
10390 }
10391 if ((state & UpdateConfigurationState) != 0)
10392 {
10393 /*
10394 Check boundary conditions.
10395 */
cristy49e2d862010-11-12 02:50:30 +000010396 if (pan_info.x < (ssize_t) (pan_info.width/2))
cristy3ed852e2009-09-05 21:47:34 +000010397 pan_info.x=0;
10398 else
cristy49e2d862010-11-12 02:50:30 +000010399 pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
cristy3ed852e2009-09-05 21:47:34 +000010400 if (pan_info.x < 0)
10401 pan_info.x=0;
10402 else
10403 if ((int) (pan_info.x+windows->image.width) >
10404 windows->image.ximage->width)
cristybb503372010-05-27 20:51:26 +000010405 pan_info.x=(ssize_t)
cristy3ed852e2009-09-05 21:47:34 +000010406 (windows->image.ximage->width-windows->image.width);
cristybb503372010-05-27 20:51:26 +000010407 if (pan_info.y < (ssize_t) (pan_info.height/2))
cristy3ed852e2009-09-05 21:47:34 +000010408 pan_info.y=0;
10409 else
cristybb503372010-05-27 20:51:26 +000010410 pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
cristy3ed852e2009-09-05 21:47:34 +000010411 if (pan_info.y < 0)
10412 pan_info.y=0;
10413 else
10414 if ((int) (pan_info.y+windows->image.height) >
10415 windows->image.ximage->height)
cristybb503372010-05-27 20:51:26 +000010416 pan_info.y=(ssize_t)
cristy3ed852e2009-09-05 21:47:34 +000010417 (windows->image.ximage->height-windows->image.height);
10418 if ((windows->image.x != (int) pan_info.x) ||
10419 (windows->image.y != (int) pan_info.y))
10420 {
10421 /*
10422 Display image pan offset.
10423 */
10424 windows->image.x=(int) pan_info.x;
10425 windows->image.y=(int) pan_info.y;
10426 (void) FormatMagickString(text,MaxTextExtent," %ux%u%+d%+d ",
10427 windows->image.width,windows->image.height,windows->image.x,
10428 windows->image.y);
10429 XInfoWidget(display,windows,text);
10430 /*
10431 Refresh Image window.
10432 */
10433 XDrawPanRectangle(display,windows);
10434 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10435 }
10436 state&=(~UpdateConfigurationState);
10437 }
10438 /*
10439 Wait for next event.
10440 */
10441 if ((state & ExitState) == 0)
10442 XScreenEvent(display,windows,event);
10443 } while ((state & ExitState) == 0);
10444 /*
10445 Restore cursor.
10446 */
10447 (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10448 (void) XFreeCursor(display,cursor);
10449 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10450}
10451
10452/*
10453%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10454% %
10455% %
10456% %
10457+ X P a s t e I m a g e %
10458% %
10459% %
10460% %
10461%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10462%
10463% XPasteImage() pastes an image previously saved with XCropImage in the X
10464% window image at a location the user chooses with the pointer.
10465%
10466% The format of the XPasteImage method is:
10467%
10468% MagickBooleanType XPasteImage(Display *display,
10469% XResourceInfo *resource_info,XWindows *windows,Image *image)
10470%
10471% A description of each parameter follows:
10472%
10473% o display: Specifies a connection to an X server; returned from
10474% XOpenDisplay.
10475%
10476% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10477%
10478% o windows: Specifies a pointer to a XWindows structure.
10479%
10480% o image: the image; returned from ReadImage.
10481%
10482*/
10483static MagickBooleanType XPasteImage(Display *display,
10484 XResourceInfo *resource_info,XWindows *windows,Image *image)
10485{
10486 static const char
10487 *PasteMenu[] =
10488 {
10489 "Operator",
10490 "Help",
10491 "Dismiss",
10492 (char *) NULL
10493 };
10494
10495 static const ModeType
10496 PasteCommands[] =
10497 {
10498 PasteOperatorsCommand,
10499 PasteHelpCommand,
10500 PasteDismissCommand
10501 };
10502
10503 static CompositeOperator
10504 compose = CopyCompositeOp;
10505
10506 char
10507 text[MaxTextExtent];
10508
10509 Cursor
10510 cursor;
10511
10512 Image
10513 *paste_image;
10514
10515 int
10516 entry,
10517 id,
10518 x,
10519 y;
10520
10521 MagickRealType
10522 scale_factor;
10523
10524 RectangleInfo
10525 highlight_info,
10526 paste_info;
10527
10528 unsigned int
10529 height,
10530 width;
10531
cristybb503372010-05-27 20:51:26 +000010532 size_t
cristy3ed852e2009-09-05 21:47:34 +000010533 state;
10534
10535 XEvent
10536 event;
10537
10538 /*
10539 Copy image.
10540 */
10541 if (resource_info->copy_image == (Image *) NULL)
10542 return(MagickFalse);
10543 paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,
10544 &image->exception);
10545 /*
10546 Map Command widget.
10547 */
10548 (void) CloneString(&windows->command.name,"Paste");
10549 windows->command.data=1;
10550 (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10551 (void) XMapRaised(display,windows->command.id);
10552 XClientMessage(display,windows->image.id,windows->im_protocols,
10553 windows->im_update_widget,CurrentTime);
10554 /*
10555 Track pointer until button 1 is pressed.
10556 */
10557 XSetCursorState(display,windows,MagickFalse);
10558 XQueryPosition(display,windows->image.id,&x,&y);
10559 (void) XSelectInput(display,windows->image.id,
10560 windows->image.attributes.event_mask | PointerMotionMask);
cristy49e2d862010-11-12 02:50:30 +000010561 paste_info.x=(ssize_t) windows->image.x+x;
10562 paste_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +000010563 paste_info.width=0;
10564 paste_info.height=0;
10565 cursor=XCreateFontCursor(display,XC_ul_angle);
10566 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10567 state=DefaultState;
10568 do
10569 {
10570 if (windows->info.mapped != MagickFalse)
10571 {
10572 /*
10573 Display pointer position.
10574 */
10575 (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
cristyf2faecf2010-05-28 19:19:36 +000010576 (long) paste_info.x,(long) paste_info.y);
cristy3ed852e2009-09-05 21:47:34 +000010577 XInfoWidget(display,windows,text);
10578 }
10579 highlight_info=paste_info;
10580 highlight_info.x=paste_info.x-windows->image.x;
10581 highlight_info.y=paste_info.y-windows->image.y;
10582 XHighlightRectangle(display,windows->image.id,
10583 windows->image.highlight_context,&highlight_info);
10584 /*
10585 Wait for next event.
10586 */
10587 XScreenEvent(display,windows,&event);
10588 XHighlightRectangle(display,windows->image.id,
10589 windows->image.highlight_context,&highlight_info);
10590 if (event.xany.window == windows->command.id)
10591 {
10592 /*
10593 Select a command from the Command widget.
10594 */
10595 id=XCommandWidget(display,windows,PasteMenu,&event);
10596 if (id < 0)
10597 continue;
10598 switch (PasteCommands[id])
10599 {
10600 case PasteOperatorsCommand:
10601 {
10602 char
10603 command[MaxTextExtent],
10604 **operators;
10605
10606 /*
10607 Select a command from the pop-up menu.
10608 */
10609 operators=GetMagickOptions(MagickComposeOptions);
10610 if (operators == (char **) NULL)
10611 break;
10612 entry=XMenuWidget(display,windows,PasteMenu[id],
10613 (const char **) operators,command);
10614 if (entry >= 0)
10615 compose=(CompositeOperator) ParseMagickOption(
10616 MagickComposeOptions,MagickFalse,operators[entry]);
10617 operators=DestroyStringList(operators);
10618 break;
10619 }
10620 case PasteHelpCommand:
10621 {
10622 XTextViewWidget(display,resource_info,windows,MagickFalse,
10623 "Help Viewer - Image Composite",ImagePasteHelp);
10624 break;
10625 }
10626 case PasteDismissCommand:
10627 {
10628 /*
10629 Prematurely exit.
10630 */
10631 state|=EscapeState;
10632 state|=ExitState;
10633 break;
10634 }
10635 default:
10636 break;
10637 }
10638 continue;
10639 }
10640 switch (event.type)
10641 {
10642 case ButtonPress:
10643 {
10644 if (image->debug != MagickFalse)
10645 (void) LogMagickEvent(X11Event,GetMagickModule(),
10646 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10647 event.xbutton.button,event.xbutton.x,event.xbutton.y);
10648 if (event.xbutton.button != Button1)
10649 break;
10650 if (event.xbutton.window != windows->image.id)
10651 break;
10652 /*
10653 Paste rectangle is relative to image configuration.
10654 */
10655 width=(unsigned int) image->columns;
10656 height=(unsigned int) image->rows;
10657 x=0;
10658 y=0;
10659 if (windows->image.crop_geometry != (char *) NULL)
10660 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10661 &width,&height);
10662 scale_factor=(MagickRealType) windows->image.ximage->width/width;
10663 paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10664 scale_factor=(MagickRealType) windows->image.ximage->height/height;
10665 paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10666 (void) XCheckDefineCursor(display,windows->image.id,cursor);
cristy49e2d862010-11-12 02:50:30 +000010667 paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10668 paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000010669 break;
10670 }
10671 case ButtonRelease:
10672 {
10673 if (image->debug != MagickFalse)
10674 (void) LogMagickEvent(X11Event,GetMagickModule(),
10675 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10676 event.xbutton.button,event.xbutton.x,event.xbutton.y);
10677 if (event.xbutton.button != Button1)
10678 break;
10679 if (event.xbutton.window != windows->image.id)
10680 break;
10681 if ((paste_info.width != 0) && (paste_info.height != 0))
10682 {
10683 /*
10684 User has selected the location of the paste image.
10685 */
cristy49e2d862010-11-12 02:50:30 +000010686 paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10687 paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000010688 state|=ExitState;
10689 }
10690 break;
10691 }
10692 case Expose:
10693 break;
10694 case KeyPress:
10695 {
10696 char
10697 command[MaxTextExtent];
10698
10699 KeySym
10700 key_symbol;
10701
10702 int
10703 length;
10704
10705 if (event.xkey.window != windows->image.id)
10706 break;
10707 /*
10708 Respond to a user key press.
10709 */
10710 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10711 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10712 *(command+length)='\0';
10713 if (image->debug != MagickFalse)
10714 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010715 "Key press: 0x%lx (%s)",(long) key_symbol,command);
cristy3ed852e2009-09-05 21:47:34 +000010716 switch ((int) key_symbol)
10717 {
10718 case XK_Escape:
10719 case XK_F20:
10720 {
10721 /*
10722 Prematurely exit.
10723 */
10724 paste_image=DestroyImage(paste_image);
10725 state|=EscapeState;
10726 state|=ExitState;
10727 break;
10728 }
10729 case XK_F1:
10730 case XK_Help:
10731 {
10732 (void) XSetFunction(display,windows->image.highlight_context,
10733 GXcopy);
10734 XTextViewWidget(display,resource_info,windows,MagickFalse,
10735 "Help Viewer - Image Composite",ImagePasteHelp);
10736 (void) XSetFunction(display,windows->image.highlight_context,
10737 GXinvert);
10738 break;
10739 }
10740 default:
10741 {
10742 (void) XBell(display,0);
10743 break;
10744 }
10745 }
10746 break;
10747 }
10748 case MotionNotify:
10749 {
10750 /*
10751 Map and unmap Info widget as text cursor crosses its boundaries.
10752 */
10753 x=event.xmotion.x;
10754 y=event.xmotion.y;
10755 if (windows->info.mapped != MagickFalse)
10756 {
10757 if ((x < (int) (windows->info.x+windows->info.width)) &&
10758 (y < (int) (windows->info.y+windows->info.height)))
10759 (void) XWithdrawWindow(display,windows->info.id,
10760 windows->info.screen);
10761 }
10762 else
10763 if ((x > (int) (windows->info.x+windows->info.width)) ||
10764 (y > (int) (windows->info.y+windows->info.height)))
10765 (void) XMapWindow(display,windows->info.id);
cristy49e2d862010-11-12 02:50:30 +000010766 paste_info.x=(ssize_t) windows->image.x+x;
10767 paste_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +000010768 break;
10769 }
10770 default:
10771 {
10772 if (image->debug != MagickFalse)
10773 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10774 event.type);
10775 break;
10776 }
10777 }
10778 } while ((state & ExitState) == 0);
10779 (void) XSelectInput(display,windows->image.id,
10780 windows->image.attributes.event_mask);
10781 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10782 XSetCursorState(display,windows,MagickFalse);
10783 (void) XFreeCursor(display,cursor);
10784 if ((state & EscapeState) != 0)
10785 return(MagickTrue);
10786 /*
10787 Image pasting is relative to image configuration.
10788 */
10789 XSetCursorState(display,windows,MagickTrue);
10790 XCheckRefreshWindows(display,windows);
10791 width=(unsigned int) image->columns;
10792 height=(unsigned int) image->rows;
10793 x=0;
10794 y=0;
10795 if (windows->image.crop_geometry != (char *) NULL)
10796 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10797 scale_factor=(MagickRealType) width/windows->image.ximage->width;
10798 paste_info.x+=x;
cristy49e2d862010-11-12 02:50:30 +000010799 paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +000010800 paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10801 scale_factor=(MagickRealType) height/windows->image.ximage->height;
10802 paste_info.y+=y;
cristy49e2d862010-11-12 02:50:30 +000010803 paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
cristy3ed852e2009-09-05 21:47:34 +000010804 paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10805 /*
10806 Paste image with X Image window.
10807 */
10808 (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10809 paste_image=DestroyImage(paste_image);
10810 XSetCursorState(display,windows,MagickFalse);
10811 /*
10812 Update image colormap.
10813 */
10814 XConfigureImageColormap(display,resource_info,windows,image);
10815 (void) XConfigureImage(display,resource_info,windows,image);
10816 return(MagickTrue);
10817}
10818
10819/*
10820%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10821% %
10822% %
10823% %
10824+ X P r i n t I m a g e %
10825% %
10826% %
10827% %
10828%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10829%
10830% XPrintImage() prints an image to a Postscript printer.
10831%
10832% The format of the XPrintImage method is:
10833%
10834% MagickBooleanType XPrintImage(Display *display,
10835% XResourceInfo *resource_info,XWindows *windows,Image *image)
10836%
10837% A description of each parameter follows:
10838%
10839% o display: Specifies a connection to an X server; returned from
10840% XOpenDisplay.
10841%
10842% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10843%
10844% o windows: Specifies a pointer to a XWindows structure.
10845%
10846% o image: the image.
10847%
10848*/
10849static MagickBooleanType XPrintImage(Display *display,
10850 XResourceInfo *resource_info,XWindows *windows,Image *image)
10851{
10852 char
10853 filename[MaxTextExtent],
10854 geometry[MaxTextExtent];
10855
10856 Image
10857 *print_image;
10858
10859 ImageInfo
10860 *image_info;
10861
10862 MagickStatusType
10863 status;
10864
10865 /*
10866 Request Postscript page geometry from user.
10867 */
10868 image_info=CloneImageInfo(resource_info->image_info);
10869 (void) FormatMagickString(geometry,MaxTextExtent,"Letter");
10870 if (image_info->page != (char *) NULL)
10871 (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10872 XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10873 "Select Postscript Page Geometry:",geometry);
10874 if (*geometry == '\0')
10875 return(MagickTrue);
10876 image_info->page=GetPageGeometry(geometry);
10877 /*
10878 Apply image transforms.
10879 */
10880 XSetCursorState(display,windows,MagickTrue);
10881 XCheckRefreshWindows(display,windows);
10882 print_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10883 if (print_image == (Image *) NULL)
10884 return(MagickFalse);
10885 (void) FormatMagickString(geometry,MaxTextExtent,"%dx%d!",
10886 windows->image.ximage->width,windows->image.ximage->height);
10887 (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10888 /*
10889 Print image.
10890 */
10891 (void) AcquireUniqueFilename(filename);
10892 (void) FormatMagickString(print_image->filename,MaxTextExtent,"print:%s",
10893 filename);
10894 status=WriteImage(image_info,print_image);
10895 (void) RelinquishUniqueFileResource(filename);
10896 print_image=DestroyImage(print_image);
10897 image_info=DestroyImageInfo(image_info);
10898 XSetCursorState(display,windows,MagickFalse);
10899 return(status != 0 ? MagickTrue : MagickFalse);
10900}
10901
10902/*
10903%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10904% %
10905% %
10906% %
10907+ X R O I I m a g e %
10908% %
10909% %
10910% %
10911%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10912%
10913% XROIImage() applies an image processing technique to a region of interest.
10914%
10915% The format of the XROIImage method is:
10916%
10917% MagickBooleanType XROIImage(Display *display,
10918% XResourceInfo *resource_info,XWindows *windows,Image **image)
10919%
10920% A description of each parameter follows:
10921%
10922% o display: Specifies a connection to an X server; returned from
10923% XOpenDisplay.
10924%
10925% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10926%
10927% o windows: Specifies a pointer to a XWindows structure.
10928%
10929% o image: the image; returned from ReadImage.
10930%
10931*/
10932static MagickBooleanType XROIImage(Display *display,
10933 XResourceInfo *resource_info,XWindows *windows,Image **image)
10934{
10935#define ApplyMenus 7
10936
10937 static const char
10938 *ROIMenu[] =
10939 {
10940 "Help",
10941 "Dismiss",
10942 (char *) NULL
10943 },
10944 *ApplyMenu[] =
10945 {
10946 "File",
10947 "Edit",
10948 "Transform",
10949 "Enhance",
10950 "Effects",
10951 "F/X",
10952 "Miscellany",
10953 "Help",
10954 "Dismiss",
10955 (char *) NULL
10956 },
10957 *FileMenu[] =
10958 {
10959 "Save...",
10960 "Print...",
10961 (char *) NULL
10962 },
10963 *EditMenu[] =
10964 {
10965 "Undo",
10966 "Redo",
10967 (char *) NULL
10968 },
10969 *TransformMenu[] =
10970 {
10971 "Flop",
10972 "Flip",
10973 "Rotate Right",
10974 "Rotate Left",
10975 (char *) NULL
10976 },
10977 *EnhanceMenu[] =
10978 {
10979 "Hue...",
10980 "Saturation...",
10981 "Brightness...",
10982 "Gamma...",
10983 "Spiff",
10984 "Dull",
10985 "Contrast Stretch...",
10986 "Sigmoidal Contrast...",
10987 "Normalize",
10988 "Equalize",
10989 "Negate",
10990 "Grayscale",
10991 "Map...",
10992 "Quantize...",
10993 (char *) NULL
10994 },
10995 *EffectsMenu[] =
10996 {
10997 "Despeckle",
10998 "Emboss",
10999 "Reduce Noise",
11000 "Add Noise",
11001 "Sharpen...",
11002 "Blur...",
11003 "Threshold...",
11004 "Edge Detect...",
11005 "Spread...",
11006 "Shade...",
11007 "Raise...",
11008 "Segment...",
11009 (char *) NULL
11010 },
11011 *FXMenu[] =
11012 {
11013 "Solarize...",
11014 "Sepia Tone...",
11015 "Swirl...",
11016 "Implode...",
11017 "Vignette...",
11018 "Wave...",
11019 "Oil Paint...",
11020 "Charcoal Draw...",
11021 (char *) NULL
11022 },
11023 *MiscellanyMenu[] =
11024 {
11025 "Image Info",
11026 "Zoom Image",
11027 "Show Preview...",
11028 "Show Histogram",
11029 "Show Matte",
11030 (char *) NULL
11031 };
11032
11033 static const char
11034 **Menus[ApplyMenus] =
11035 {
11036 FileMenu,
11037 EditMenu,
11038 TransformMenu,
11039 EnhanceMenu,
11040 EffectsMenu,
11041 FXMenu,
11042 MiscellanyMenu
11043 };
11044
11045 static const CommandType
11046 ApplyCommands[] =
11047 {
11048 NullCommand,
11049 NullCommand,
11050 NullCommand,
11051 NullCommand,
11052 NullCommand,
11053 NullCommand,
11054 NullCommand,
11055 HelpCommand,
11056 QuitCommand
11057 },
11058 FileCommands[] =
11059 {
11060 SaveCommand,
11061 PrintCommand
11062 },
11063 EditCommands[] =
11064 {
11065 UndoCommand,
11066 RedoCommand
11067 },
11068 TransformCommands[] =
11069 {
11070 FlopCommand,
11071 FlipCommand,
11072 RotateRightCommand,
11073 RotateLeftCommand
11074 },
11075 EnhanceCommands[] =
11076 {
11077 HueCommand,
11078 SaturationCommand,
11079 BrightnessCommand,
11080 GammaCommand,
11081 SpiffCommand,
11082 DullCommand,
11083 ContrastStretchCommand,
11084 SigmoidalContrastCommand,
11085 NormalizeCommand,
11086 EqualizeCommand,
11087 NegateCommand,
11088 GrayscaleCommand,
11089 MapCommand,
11090 QuantizeCommand
11091 },
11092 EffectsCommands[] =
11093 {
11094 DespeckleCommand,
11095 EmbossCommand,
11096 ReduceNoiseCommand,
11097 AddNoiseCommand,
11098 SharpenCommand,
11099 BlurCommand,
11100 EdgeDetectCommand,
11101 SpreadCommand,
11102 ShadeCommand,
11103 RaiseCommand,
11104 SegmentCommand
11105 },
11106 FXCommands[] =
11107 {
11108 SolarizeCommand,
11109 SepiaToneCommand,
11110 SwirlCommand,
11111 ImplodeCommand,
11112 VignetteCommand,
11113 WaveCommand,
11114 OilPaintCommand,
11115 CharcoalDrawCommand
11116 },
11117 MiscellanyCommands[] =
11118 {
11119 InfoCommand,
11120 ZoomCommand,
11121 ShowPreviewCommand,
11122 ShowHistogramCommand,
11123 ShowMatteCommand
11124 },
11125 ROICommands[] =
11126 {
11127 ROIHelpCommand,
11128 ROIDismissCommand
11129 };
11130
11131 static const CommandType
11132 *Commands[ApplyMenus] =
11133 {
11134 FileCommands,
11135 EditCommands,
11136 TransformCommands,
11137 EnhanceCommands,
11138 EffectsCommands,
11139 FXCommands,
11140 MiscellanyCommands
11141 };
11142
11143 char
11144 command[MaxTextExtent],
11145 text[MaxTextExtent];
11146
11147 CommandType
11148 command_type;
11149
11150 Cursor
11151 cursor;
11152
11153 Image
11154 *roi_image;
11155
11156 int
11157 entry,
11158 id,
11159 x,
11160 y;
11161
11162 MagickRealType
11163 scale_factor;
11164
11165 MagickProgressMonitor
11166 progress_monitor;
11167
11168 RectangleInfo
11169 crop_info,
11170 highlight_info,
11171 roi_info;
11172
11173 unsigned int
11174 height,
11175 width;
11176
cristybb503372010-05-27 20:51:26 +000011177 size_t
cristy3ed852e2009-09-05 21:47:34 +000011178 state;
11179
11180 XEvent
11181 event;
11182
11183 /*
11184 Map Command widget.
11185 */
11186 (void) CloneString(&windows->command.name,"ROI");
11187 windows->command.data=0;
11188 (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11189 (void) XMapRaised(display,windows->command.id);
11190 XClientMessage(display,windows->image.id,windows->im_protocols,
11191 windows->im_update_widget,CurrentTime);
11192 /*
11193 Track pointer until button 1 is pressed.
11194 */
11195 XQueryPosition(display,windows->image.id,&x,&y);
11196 (void) XSelectInput(display,windows->image.id,
11197 windows->image.attributes.event_mask | PointerMotionMask);
cristy49e2d862010-11-12 02:50:30 +000011198 roi_info.x=(ssize_t) windows->image.x+x;
11199 roi_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +000011200 roi_info.width=0;
11201 roi_info.height=0;
11202 cursor=XCreateFontCursor(display,XC_fleur);
11203 state=DefaultState;
11204 do
11205 {
11206 if (windows->info.mapped != MagickFalse)
11207 {
11208 /*
11209 Display pointer position.
11210 */
11211 (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
cristyf2faecf2010-05-28 19:19:36 +000011212 (long) roi_info.x,(long) roi_info.y);
cristy3ed852e2009-09-05 21:47:34 +000011213 XInfoWidget(display,windows,text);
11214 }
11215 /*
11216 Wait for next event.
11217 */
11218 XScreenEvent(display,windows,&event);
11219 if (event.xany.window == windows->command.id)
11220 {
11221 /*
11222 Select a command from the Command widget.
11223 */
11224 id=XCommandWidget(display,windows,ROIMenu,&event);
11225 if (id < 0)
11226 continue;
11227 switch (ROICommands[id])
11228 {
11229 case ROIHelpCommand:
11230 {
11231 XTextViewWidget(display,resource_info,windows,MagickFalse,
11232 "Help Viewer - Region of Interest",ImageROIHelp);
11233 break;
11234 }
11235 case ROIDismissCommand:
11236 {
11237 /*
11238 Prematurely exit.
11239 */
11240 state|=EscapeState;
11241 state|=ExitState;
11242 break;
11243 }
11244 default:
11245 break;
11246 }
11247 continue;
11248 }
11249 switch (event.type)
11250 {
11251 case ButtonPress:
11252 {
11253 if (event.xbutton.button != Button1)
11254 break;
11255 if (event.xbutton.window != windows->image.id)
11256 break;
11257 /*
11258 Note first corner of region of interest rectangle-- exit loop.
11259 */
11260 (void) XCheckDefineCursor(display,windows->image.id,cursor);
cristy49e2d862010-11-12 02:50:30 +000011261 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11262 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000011263 state|=ExitState;
11264 break;
11265 }
11266 case ButtonRelease:
11267 break;
11268 case Expose:
11269 break;
11270 case KeyPress:
11271 {
11272 KeySym
11273 key_symbol;
11274
11275 if (event.xkey.window != windows->image.id)
11276 break;
11277 /*
11278 Respond to a user key press.
11279 */
11280 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11281 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11282 switch ((int) key_symbol)
11283 {
11284 case XK_Escape:
11285 case XK_F20:
11286 {
11287 /*
11288 Prematurely exit.
11289 */
11290 state|=EscapeState;
11291 state|=ExitState;
11292 break;
11293 }
11294 case XK_F1:
11295 case XK_Help:
11296 {
11297 XTextViewWidget(display,resource_info,windows,MagickFalse,
11298 "Help Viewer - Region of Interest",ImageROIHelp);
11299 break;
11300 }
11301 default:
11302 {
11303 (void) XBell(display,0);
11304 break;
11305 }
11306 }
11307 break;
11308 }
11309 case MotionNotify:
11310 {
11311 /*
11312 Map and unmap Info widget as text cursor crosses its boundaries.
11313 */
11314 x=event.xmotion.x;
11315 y=event.xmotion.y;
11316 if (windows->info.mapped != MagickFalse)
11317 {
11318 if ((x < (int) (windows->info.x+windows->info.width)) &&
11319 (y < (int) (windows->info.y+windows->info.height)))
11320 (void) XWithdrawWindow(display,windows->info.id,
11321 windows->info.screen);
11322 }
11323 else
11324 if ((x > (int) (windows->info.x+windows->info.width)) ||
11325 (y > (int) (windows->info.y+windows->info.height)))
11326 (void) XMapWindow(display,windows->info.id);
cristy49e2d862010-11-12 02:50:30 +000011327 roi_info.x=(ssize_t) windows->image.x+x;
11328 roi_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +000011329 break;
11330 }
11331 default:
11332 break;
11333 }
11334 } while ((state & ExitState) == 0);
11335 (void) XSelectInput(display,windows->image.id,
11336 windows->image.attributes.event_mask);
11337 if ((state & EscapeState) != 0)
11338 {
11339 /*
11340 User want to exit without region of interest.
11341 */
11342 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11343 (void) XFreeCursor(display,cursor);
11344 return(MagickTrue);
11345 }
11346 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11347 do
11348 {
11349 /*
11350 Size rectangle as pointer moves until the mouse button is released.
11351 */
11352 x=(int) roi_info.x;
11353 y=(int) roi_info.y;
11354 roi_info.width=0;
11355 roi_info.height=0;
11356 state=DefaultState;
11357 do
11358 {
11359 highlight_info=roi_info;
11360 highlight_info.x=roi_info.x-windows->image.x;
11361 highlight_info.y=roi_info.y-windows->image.y;
11362 if ((highlight_info.width > 3) && (highlight_info.height > 3))
11363 {
11364 /*
11365 Display info and draw region of interest rectangle.
11366 */
11367 if (windows->info.mapped == MagickFalse)
11368 (void) XMapWindow(display,windows->info.id);
cristye8c25f92010-06-03 00:53:06 +000011369 (void) FormatMagickString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +000011370 " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +000011371 roi_info.height,(double) roi_info.x,(double) roi_info.y);
cristy3ed852e2009-09-05 21:47:34 +000011372 XInfoWidget(display,windows,text);
11373 XHighlightRectangle(display,windows->image.id,
11374 windows->image.highlight_context,&highlight_info);
11375 }
11376 else
11377 if (windows->info.mapped != MagickFalse)
11378 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11379 /*
11380 Wait for next event.
11381 */
11382 XScreenEvent(display,windows,&event);
11383 if ((highlight_info.width > 3) && (highlight_info.height > 3))
11384 XHighlightRectangle(display,windows->image.id,
11385 windows->image.highlight_context,&highlight_info);
11386 switch (event.type)
11387 {
11388 case ButtonPress:
11389 {
cristy49e2d862010-11-12 02:50:30 +000011390 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11391 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000011392 break;
11393 }
11394 case ButtonRelease:
11395 {
11396 /*
11397 User has committed to region of interest rectangle.
11398 */
cristy49e2d862010-11-12 02:50:30 +000011399 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11400 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000011401 XSetCursorState(display,windows,MagickFalse);
11402 state|=ExitState;
11403 if (LocaleCompare(windows->command.name,"Apply") == 0)
11404 break;
11405 (void) CloneString(&windows->command.name,"Apply");
11406 windows->command.data=ApplyMenus;
11407 (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11408 break;
11409 }
11410 case Expose:
11411 break;
11412 case MotionNotify:
11413 {
cristy49e2d862010-11-12 02:50:30 +000011414 roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11415 roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
cristy3ed852e2009-09-05 21:47:34 +000011416 }
11417 default:
11418 break;
11419 }
11420 if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11421 ((state & ExitState) != 0))
11422 {
11423 /*
11424 Check boundary conditions.
11425 */
11426 if (roi_info.x < 0)
11427 roi_info.x=0;
11428 else
cristy49e2d862010-11-12 02:50:30 +000011429 if (roi_info.x > (ssize_t) windows->image.ximage->width)
11430 roi_info.x=(ssize_t) windows->image.ximage->width;
cristy3ed852e2009-09-05 21:47:34 +000011431 if ((int) roi_info.x < x)
11432 roi_info.width=(unsigned int) (x-roi_info.x);
11433 else
11434 {
11435 roi_info.width=(unsigned int) (roi_info.x-x);
cristy49e2d862010-11-12 02:50:30 +000011436 roi_info.x=(ssize_t) x;
cristy3ed852e2009-09-05 21:47:34 +000011437 }
11438 if (roi_info.y < 0)
11439 roi_info.y=0;
11440 else
cristy49e2d862010-11-12 02:50:30 +000011441 if (roi_info.y > (ssize_t) windows->image.ximage->height)
11442 roi_info.y=(ssize_t) windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +000011443 if ((int) roi_info.y < y)
11444 roi_info.height=(unsigned int) (y-roi_info.y);
11445 else
11446 {
11447 roi_info.height=(unsigned int) (roi_info.y-y);
cristy49e2d862010-11-12 02:50:30 +000011448 roi_info.y=(ssize_t) y;
cristy3ed852e2009-09-05 21:47:34 +000011449 }
11450 }
11451 } while ((state & ExitState) == 0);
11452 /*
11453 Wait for user to grab a corner of the rectangle or press return.
11454 */
11455 state=DefaultState;
11456 command_type=NullCommand;
11457 (void) XMapWindow(display,windows->info.id);
11458 do
11459 {
11460 if (windows->info.mapped != MagickFalse)
11461 {
11462 /*
11463 Display pointer position.
11464 */
cristye8c25f92010-06-03 00:53:06 +000011465 (void) FormatMagickString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +000011466 " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +000011467 roi_info.height,(double) roi_info.x,(double) roi_info.y);
cristy3ed852e2009-09-05 21:47:34 +000011468 XInfoWidget(display,windows,text);
11469 }
11470 highlight_info=roi_info;
11471 highlight_info.x=roi_info.x-windows->image.x;
11472 highlight_info.y=roi_info.y-windows->image.y;
11473 if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11474 {
11475 state|=EscapeState;
11476 state|=ExitState;
11477 break;
11478 }
11479 if ((state & UpdateRegionState) != 0)
11480 {
11481 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11482 switch (command_type)
11483 {
11484 case UndoCommand:
11485 case RedoCommand:
11486 {
11487 (void) XMagickCommand(display,resource_info,windows,command_type,
11488 image);
11489 break;
11490 }
11491 default:
11492 {
11493 /*
11494 Region of interest is relative to image configuration.
11495 */
11496 progress_monitor=SetImageProgressMonitor(*image,
11497 (MagickProgressMonitor) NULL,(*image)->client_data);
11498 crop_info=roi_info;
11499 width=(unsigned int) (*image)->columns;
11500 height=(unsigned int) (*image)->rows;
11501 x=0;
11502 y=0;
11503 if (windows->image.crop_geometry != (char *) NULL)
11504 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11505 &width,&height);
11506 scale_factor=(MagickRealType) width/windows->image.ximage->width;
11507 crop_info.x+=x;
cristy49e2d862010-11-12 02:50:30 +000011508 crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +000011509 crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11510 scale_factor=(MagickRealType)
11511 height/windows->image.ximage->height;
11512 crop_info.y+=y;
cristy49e2d862010-11-12 02:50:30 +000011513 crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +000011514 crop_info.height=(unsigned int)
11515 (scale_factor*crop_info.height+0.5);
11516 roi_image=CropImage(*image,&crop_info,&(*image)->exception);
11517 (void) SetImageProgressMonitor(*image,progress_monitor,
11518 (*image)->client_data);
11519 if (roi_image == (Image *) NULL)
11520 continue;
11521 /*
11522 Apply image processing technique to the region of interest.
11523 */
11524 windows->image.orphan=MagickTrue;
11525 (void) XMagickCommand(display,resource_info,windows,command_type,
11526 &roi_image);
11527 progress_monitor=SetImageProgressMonitor(*image,
11528 (MagickProgressMonitor) NULL,(*image)->client_data);
11529 (void) XMagickCommand(display,resource_info,windows,
11530 SaveToUndoBufferCommand,image);
11531 windows->image.orphan=MagickFalse;
11532 (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11533 crop_info.x,crop_info.y);
11534 roi_image=DestroyImage(roi_image);
11535 (void) SetImageProgressMonitor(*image,progress_monitor,
11536 (*image)->client_data);
11537 break;
11538 }
11539 }
11540 if (command_type != InfoCommand)
11541 {
11542 XConfigureImageColormap(display,resource_info,windows,*image);
11543 (void) XConfigureImage(display,resource_info,windows,*image);
11544 }
11545 XCheckRefreshWindows(display,windows);
11546 XInfoWidget(display,windows,text);
11547 (void) XSetFunction(display,windows->image.highlight_context,
11548 GXinvert);
11549 state&=(~UpdateRegionState);
11550 }
11551 XHighlightRectangle(display,windows->image.id,
11552 windows->image.highlight_context,&highlight_info);
11553 XScreenEvent(display,windows,&event);
11554 if (event.xany.window == windows->command.id)
11555 {
11556 /*
11557 Select a command from the Command widget.
11558 */
11559 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11560 command_type=NullCommand;
11561 id=XCommandWidget(display,windows,ApplyMenu,&event);
11562 if (id >= 0)
11563 {
11564 (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11565 command_type=ApplyCommands[id];
11566 if (id < ApplyMenus)
11567 {
11568 /*
11569 Select a command from a pop-up menu.
11570 */
11571 entry=XMenuWidget(display,windows,ApplyMenu[id],
11572 (const char **) Menus[id],command);
11573 if (entry >= 0)
11574 {
11575 (void) CopyMagickString(command,Menus[id][entry],
11576 MaxTextExtent);
11577 command_type=Commands[id][entry];
11578 }
11579 }
11580 }
11581 (void) XSetFunction(display,windows->image.highlight_context,
11582 GXinvert);
11583 XHighlightRectangle(display,windows->image.id,
11584 windows->image.highlight_context,&highlight_info);
11585 if (command_type == HelpCommand)
11586 {
11587 (void) XSetFunction(display,windows->image.highlight_context,
11588 GXcopy);
11589 XTextViewWidget(display,resource_info,windows,MagickFalse,
11590 "Help Viewer - Region of Interest",ImageROIHelp);
11591 (void) XSetFunction(display,windows->image.highlight_context,
11592 GXinvert);
11593 continue;
11594 }
11595 if (command_type == QuitCommand)
11596 {
11597 /*
11598 exit.
11599 */
11600 state|=EscapeState;
11601 state|=ExitState;
11602 continue;
11603 }
11604 if (command_type != NullCommand)
11605 state|=UpdateRegionState;
11606 continue;
11607 }
11608 XHighlightRectangle(display,windows->image.id,
11609 windows->image.highlight_context,&highlight_info);
11610 switch (event.type)
11611 {
11612 case ButtonPress:
11613 {
11614 x=windows->image.x;
11615 y=windows->image.y;
11616 if (event.xbutton.button != Button1)
11617 break;
11618 if (event.xbutton.window != windows->image.id)
11619 break;
11620 x=windows->image.x+event.xbutton.x;
11621 y=windows->image.y+event.xbutton.y;
11622 if ((x < (int) (roi_info.x+RoiDelta)) &&
11623 (x > (int) (roi_info.x-RoiDelta)) &&
11624 (y < (int) (roi_info.y+RoiDelta)) &&
11625 (y > (int) (roi_info.y-RoiDelta)))
11626 {
cristybb503372010-05-27 20:51:26 +000011627 roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11628 roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
cristy3ed852e2009-09-05 21:47:34 +000011629 state|=UpdateConfigurationState;
11630 break;
11631 }
11632 if ((x < (int) (roi_info.x+RoiDelta)) &&
11633 (x > (int) (roi_info.x-RoiDelta)) &&
11634 (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11635 (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11636 {
cristybb503372010-05-27 20:51:26 +000011637 roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
cristy3ed852e2009-09-05 21:47:34 +000011638 state|=UpdateConfigurationState;
11639 break;
11640 }
11641 if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11642 (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11643 (y < (int) (roi_info.y+RoiDelta)) &&
11644 (y > (int) (roi_info.y-RoiDelta)))
11645 {
cristybb503372010-05-27 20:51:26 +000011646 roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
cristy3ed852e2009-09-05 21:47:34 +000011647 state|=UpdateConfigurationState;
11648 break;
11649 }
11650 if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11651 (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11652 (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11653 (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11654 {
11655 state|=UpdateConfigurationState;
11656 break;
11657 }
11658 }
11659 case ButtonRelease:
11660 {
11661 if (event.xbutton.window == windows->pan.id)
11662 if ((highlight_info.x != crop_info.x-windows->image.x) ||
11663 (highlight_info.y != crop_info.y-windows->image.y))
11664 XHighlightRectangle(display,windows->image.id,
11665 windows->image.highlight_context,&highlight_info);
11666 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11667 event.xbutton.time);
11668 break;
11669 }
11670 case Expose:
11671 {
11672 if (event.xexpose.window == windows->image.id)
11673 if (event.xexpose.count == 0)
11674 {
11675 event.xexpose.x=(int) highlight_info.x;
11676 event.xexpose.y=(int) highlight_info.y;
11677 event.xexpose.width=(int) highlight_info.width;
11678 event.xexpose.height=(int) highlight_info.height;
11679 XRefreshWindow(display,&windows->image,&event);
11680 }
11681 if (event.xexpose.window == windows->info.id)
11682 if (event.xexpose.count == 0)
11683 XInfoWidget(display,windows,text);
11684 break;
11685 }
11686 case KeyPress:
11687 {
11688 KeySym
11689 key_symbol;
11690
11691 if (event.xkey.window != windows->image.id)
11692 break;
11693 /*
11694 Respond to a user key press.
11695 */
11696 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11697 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11698 switch ((int) key_symbol)
11699 {
11700 case XK_Shift_L:
11701 case XK_Shift_R:
11702 break;
11703 case XK_Escape:
11704 case XK_F20:
11705 state|=EscapeState;
11706 case XK_Return:
11707 {
11708 state|=ExitState;
11709 break;
11710 }
11711 case XK_Home:
11712 case XK_KP_Home:
11713 {
cristybb503372010-05-27 20:51:26 +000011714 roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
cristy49e2d862010-11-12 02:50:30 +000011715 roi_info.y=(ssize_t) (windows->image.height/2L-
11716 roi_info.height/2L);
cristy3ed852e2009-09-05 21:47:34 +000011717 break;
11718 }
11719 case XK_Left:
11720 case XK_KP_Left:
11721 {
11722 roi_info.x--;
11723 break;
11724 }
11725 case XK_Up:
11726 case XK_KP_Up:
11727 case XK_Next:
11728 {
11729 roi_info.y--;
11730 break;
11731 }
11732 case XK_Right:
11733 case XK_KP_Right:
11734 {
11735 roi_info.x++;
11736 break;
11737 }
11738 case XK_Prior:
11739 case XK_Down:
11740 case XK_KP_Down:
11741 {
11742 roi_info.y++;
11743 break;
11744 }
11745 case XK_F1:
11746 case XK_Help:
11747 {
11748 (void) XSetFunction(display,windows->image.highlight_context,
11749 GXcopy);
11750 XTextViewWidget(display,resource_info,windows,MagickFalse,
11751 "Help Viewer - Region of Interest",ImageROIHelp);
11752 (void) XSetFunction(display,windows->image.highlight_context,
11753 GXinvert);
11754 break;
11755 }
11756 default:
11757 {
11758 command_type=XImageWindowCommand(display,resource_info,windows,
11759 event.xkey.state,key_symbol,image);
11760 if (command_type != NullCommand)
11761 state|=UpdateRegionState;
11762 break;
11763 }
11764 }
11765 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11766 event.xkey.time);
11767 break;
11768 }
11769 case KeyRelease:
11770 break;
11771 case MotionNotify:
11772 {
11773 if (event.xbutton.window != windows->image.id)
11774 break;
11775 /*
11776 Map and unmap Info widget as text cursor crosses its boundaries.
11777 */
11778 x=event.xmotion.x;
11779 y=event.xmotion.y;
11780 if (windows->info.mapped != MagickFalse)
11781 {
11782 if ((x < (int) (windows->info.x+windows->info.width)) &&
11783 (y < (int) (windows->info.y+windows->info.height)))
11784 (void) XWithdrawWindow(display,windows->info.id,
11785 windows->info.screen);
11786 }
11787 else
11788 if ((x > (int) (windows->info.x+windows->info.width)) ||
11789 (y > (int) (windows->info.y+windows->info.height)))
11790 (void) XMapWindow(display,windows->info.id);
cristy49e2d862010-11-12 02:50:30 +000011791 roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11792 roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
cristy3ed852e2009-09-05 21:47:34 +000011793 break;
11794 }
11795 case SelectionRequest:
11796 {
11797 XSelectionEvent
11798 notify;
11799
11800 XSelectionRequestEvent
11801 *request;
11802
11803 /*
11804 Set primary selection.
11805 */
cristye8c25f92010-06-03 00:53:06 +000011806 (void) FormatMagickString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +000011807 "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +000011808 roi_info.height,(double) roi_info.x,(double) roi_info.y);
cristy3ed852e2009-09-05 21:47:34 +000011809 request=(&(event.xselectionrequest));
11810 (void) XChangeProperty(request->display,request->requestor,
11811 request->property,request->target,8,PropModeReplace,
11812 (unsigned char *) text,(int) strlen(text));
11813 notify.type=SelectionNotify;
11814 notify.display=request->display;
11815 notify.requestor=request->requestor;
11816 notify.selection=request->selection;
11817 notify.target=request->target;
11818 notify.time=request->time;
11819 if (request->property == None)
11820 notify.property=request->target;
11821 else
11822 notify.property=request->property;
11823 (void) XSendEvent(request->display,request->requestor,False,0,
11824 (XEvent *) &notify);
11825 }
11826 default:
11827 break;
11828 }
11829 if ((state & UpdateConfigurationState) != 0)
11830 {
11831 (void) XPutBackEvent(display,&event);
11832 (void) XCheckDefineCursor(display,windows->image.id,cursor);
11833 break;
11834 }
11835 } while ((state & ExitState) == 0);
11836 } while ((state & ExitState) == 0);
11837 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11838 XSetCursorState(display,windows,MagickFalse);
11839 if ((state & EscapeState) != 0)
11840 return(MagickTrue);
11841 return(MagickTrue);
11842}
11843
11844/*
11845%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11846% %
11847% %
11848% %
11849+ X R o t a t e I m a g e %
11850% %
11851% %
11852% %
11853%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11854%
11855% XRotateImage() rotates the X image. If the degrees parameter if zero, the
11856% rotation angle is computed from the slope of a line drawn by the user.
11857%
11858% The format of the XRotateImage method is:
11859%
11860% MagickBooleanType XRotateImage(Display *display,
11861% XResourceInfo *resource_info,XWindows *windows,double degrees,
11862% Image **image)
11863%
11864% A description of each parameter follows:
11865%
11866% o display: Specifies a connection to an X server; returned from
11867% XOpenDisplay.
11868%
11869% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11870%
11871% o windows: Specifies a pointer to a XWindows structure.
11872%
11873% o degrees: Specifies the number of degrees to rotate the image.
11874%
11875% o image: the image.
11876%
11877*/
11878static MagickBooleanType XRotateImage(Display *display,
11879 XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image)
11880{
11881 static const char
11882 *RotateMenu[] =
11883 {
11884 "Pixel Color",
11885 "Direction",
11886 "Help",
11887 "Dismiss",
11888 (char *) NULL
11889 };
11890
11891 static ModeType
11892 direction = HorizontalRotateCommand;
11893
11894 static const ModeType
11895 DirectionCommands[] =
11896 {
11897 HorizontalRotateCommand,
11898 VerticalRotateCommand
11899 },
11900 RotateCommands[] =
11901 {
11902 RotateColorCommand,
11903 RotateDirectionCommand,
11904 RotateHelpCommand,
11905 RotateDismissCommand
11906 };
11907
11908 static unsigned int
11909 pen_id = 0;
11910
11911 char
11912 command[MaxTextExtent],
11913 text[MaxTextExtent];
11914
11915 Image
11916 *rotate_image;
11917
11918 int
11919 id,
11920 x,
11921 y;
11922
11923 MagickRealType
11924 normalized_degrees;
11925
11926 register int
11927 i;
11928
11929 unsigned int
11930 height,
11931 rotations,
11932 width;
11933
11934 if (degrees == 0.0)
11935 {
11936 unsigned int
11937 distance;
11938
cristybb503372010-05-27 20:51:26 +000011939 size_t
cristy3ed852e2009-09-05 21:47:34 +000011940 state;
11941
11942 XEvent
11943 event;
11944
11945 XSegment
11946 rotate_info;
11947
11948 /*
11949 Map Command widget.
11950 */
11951 (void) CloneString(&windows->command.name,"Rotate");
11952 windows->command.data=2;
11953 (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
11954 (void) XMapRaised(display,windows->command.id);
11955 XClientMessage(display,windows->image.id,windows->im_protocols,
11956 windows->im_update_widget,CurrentTime);
11957 /*
11958 Wait for first button press.
11959 */
11960 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11961 XQueryPosition(display,windows->image.id,&x,&y);
11962 rotate_info.x1=x;
11963 rotate_info.y1=y;
11964 rotate_info.x2=x;
11965 rotate_info.y2=y;
11966 state=DefaultState;
11967 do
11968 {
11969 XHighlightLine(display,windows->image.id,
11970 windows->image.highlight_context,&rotate_info);
11971 /*
11972 Wait for next event.
11973 */
11974 XScreenEvent(display,windows,&event);
11975 XHighlightLine(display,windows->image.id,
11976 windows->image.highlight_context,&rotate_info);
11977 if (event.xany.window == windows->command.id)
11978 {
11979 /*
11980 Select a command from the Command widget.
11981 */
11982 id=XCommandWidget(display,windows,RotateMenu,&event);
11983 if (id < 0)
11984 continue;
11985 (void) XSetFunction(display,windows->image.highlight_context,
11986 GXcopy);
11987 switch (RotateCommands[id])
11988 {
11989 case RotateColorCommand:
11990 {
11991 const char
11992 *ColorMenu[MaxNumberPens];
11993
11994 int
11995 pen_number;
11996
11997 XColor
11998 color;
11999
12000 /*
12001 Initialize menu selections.
12002 */
12003 for (i=0; i < (int) (MaxNumberPens-2); i++)
12004 ColorMenu[i]=resource_info->pen_colors[i];
12005 ColorMenu[MaxNumberPens-2]="Browser...";
12006 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12007 /*
12008 Select a pen color from the pop-up menu.
12009 */
12010 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12011 (const char **) ColorMenu,command);
12012 if (pen_number < 0)
12013 break;
12014 if (pen_number == (MaxNumberPens-2))
12015 {
12016 static char
12017 color_name[MaxTextExtent] = "gray";
12018
12019 /*
12020 Select a pen color from a dialog.
12021 */
12022 resource_info->pen_colors[pen_number]=color_name;
12023 XColorBrowserWidget(display,windows,"Select",color_name);
12024 if (*color_name == '\0')
12025 break;
12026 }
12027 /*
12028 Set pen color.
12029 */
12030 (void) XParseColor(display,windows->map_info->colormap,
12031 resource_info->pen_colors[pen_number],&color);
12032 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12033 (unsigned int) MaxColors,&color);
12034 windows->pixel_info->pen_colors[pen_number]=color;
12035 pen_id=(unsigned int) pen_number;
12036 break;
12037 }
12038 case RotateDirectionCommand:
12039 {
12040 static const char
12041 *Directions[] =
12042 {
12043 "horizontal",
12044 "vertical",
12045 (char *) NULL,
12046 };
12047
12048 /*
12049 Select a command from the pop-up menu.
12050 */
12051 id=XMenuWidget(display,windows,RotateMenu[id],
12052 Directions,command);
12053 if (id >= 0)
12054 direction=DirectionCommands[id];
12055 break;
12056 }
12057 case RotateHelpCommand:
12058 {
12059 XTextViewWidget(display,resource_info,windows,MagickFalse,
12060 "Help Viewer - Image Rotation",ImageRotateHelp);
12061 break;
12062 }
12063 case RotateDismissCommand:
12064 {
12065 /*
12066 Prematurely exit.
12067 */
12068 state|=EscapeState;
12069 state|=ExitState;
12070 break;
12071 }
12072 default:
12073 break;
12074 }
12075 (void) XSetFunction(display,windows->image.highlight_context,
12076 GXinvert);
12077 continue;
12078 }
12079 switch (event.type)
12080 {
12081 case ButtonPress:
12082 {
12083 if (event.xbutton.button != Button1)
12084 break;
12085 if (event.xbutton.window != windows->image.id)
12086 break;
12087 /*
12088 exit loop.
12089 */
12090 (void) XSetFunction(display,windows->image.highlight_context,
12091 GXcopy);
12092 rotate_info.x1=event.xbutton.x;
12093 rotate_info.y1=event.xbutton.y;
12094 state|=ExitState;
12095 break;
12096 }
12097 case ButtonRelease:
12098 break;
12099 case Expose:
12100 break;
12101 case KeyPress:
12102 {
12103 char
12104 command[MaxTextExtent];
12105
12106 KeySym
12107 key_symbol;
12108
12109 if (event.xkey.window != windows->image.id)
12110 break;
12111 /*
12112 Respond to a user key press.
12113 */
12114 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12115 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12116 switch ((int) key_symbol)
12117 {
12118 case XK_Escape:
12119 case XK_F20:
12120 {
12121 /*
12122 Prematurely exit.
12123 */
12124 state|=EscapeState;
12125 state|=ExitState;
12126 break;
12127 }
12128 case XK_F1:
12129 case XK_Help:
12130 {
12131 (void) XSetFunction(display,windows->image.highlight_context,
12132 GXcopy);
12133 XTextViewWidget(display,resource_info,windows,MagickFalse,
12134 "Help Viewer - Image Rotation",ImageRotateHelp);
12135 (void) XSetFunction(display,windows->image.highlight_context,
12136 GXinvert);
12137 break;
12138 }
12139 default:
12140 {
12141 (void) XBell(display,0);
12142 break;
12143 }
12144 }
12145 break;
12146 }
12147 case MotionNotify:
12148 {
12149 rotate_info.x1=event.xmotion.x;
12150 rotate_info.y1=event.xmotion.y;
12151 }
12152 }
12153 rotate_info.x2=rotate_info.x1;
12154 rotate_info.y2=rotate_info.y1;
12155 if (direction == HorizontalRotateCommand)
12156 rotate_info.x2+=32;
12157 else
12158 rotate_info.y2-=32;
12159 } while ((state & ExitState) == 0);
12160 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12161 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12162 if ((state & EscapeState) != 0)
12163 return(MagickTrue);
12164 /*
12165 Draw line as pointer moves until the mouse button is released.
12166 */
12167 distance=0;
12168 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12169 state=DefaultState;
12170 do
12171 {
12172 if (distance > 9)
12173 {
12174 /*
12175 Display info and draw rotation line.
12176 */
12177 if (windows->info.mapped == MagickFalse)
12178 (void) XMapWindow(display,windows->info.id);
cristye7f51092010-01-17 00:39:37 +000012179 (void) FormatMagickString(text,MaxTextExtent," %g",
cristy3ed852e2009-09-05 21:47:34 +000012180 direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12181 XInfoWidget(display,windows,text);
12182 XHighlightLine(display,windows->image.id,
12183 windows->image.highlight_context,&rotate_info);
12184 }
12185 else
12186 if (windows->info.mapped != MagickFalse)
12187 (void) XWithdrawWindow(display,windows->info.id,
12188 windows->info.screen);
12189 /*
12190 Wait for next event.
12191 */
12192 XScreenEvent(display,windows,&event);
12193 if (distance > 9)
12194 XHighlightLine(display,windows->image.id,
12195 windows->image.highlight_context,&rotate_info);
12196 switch (event.type)
12197 {
12198 case ButtonPress:
12199 break;
12200 case ButtonRelease:
12201 {
12202 /*
12203 User has committed to rotation line.
12204 */
12205 rotate_info.x2=event.xbutton.x;
12206 rotate_info.y2=event.xbutton.y;
12207 state|=ExitState;
12208 break;
12209 }
12210 case Expose:
12211 break;
12212 case MotionNotify:
12213 {
12214 rotate_info.x2=event.xmotion.x;
12215 rotate_info.y2=event.xmotion.y;
12216 }
12217 default:
12218 break;
12219 }
12220 /*
12221 Check boundary conditions.
12222 */
12223 if (rotate_info.x2 < 0)
12224 rotate_info.x2=0;
12225 else
12226 if (rotate_info.x2 > (int) windows->image.width)
12227 rotate_info.x2=(short) windows->image.width;
12228 if (rotate_info.y2 < 0)
12229 rotate_info.y2=0;
12230 else
12231 if (rotate_info.y2 > (int) windows->image.height)
12232 rotate_info.y2=(short) windows->image.height;
12233 /*
12234 Compute rotation angle from the slope of the line.
12235 */
12236 degrees=0.0;
12237 distance=(unsigned int)
12238 ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12239 ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12240 if (distance > 9)
12241 degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12242 rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12243 } while ((state & ExitState) == 0);
12244 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12245 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12246 if (distance <= 9)
12247 return(MagickTrue);
12248 }
12249 if (direction == VerticalRotateCommand)
12250 degrees-=90.0;
12251 if (degrees == 0.0)
12252 return(MagickTrue);
12253 /*
12254 Rotate image.
12255 */
12256 normalized_degrees=degrees;
12257 while (normalized_degrees < -45.0)
12258 normalized_degrees+=360.0;
12259 for (rotations=0; normalized_degrees > 45.0; rotations++)
12260 normalized_degrees-=90.0;
12261 if (normalized_degrees != 0.0)
12262 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
12263 XSetCursorState(display,windows,MagickTrue);
12264 XCheckRefreshWindows(display,windows);
12265 (*image)->background_color.red=ScaleShortToQuantum(
12266 windows->pixel_info->pen_colors[pen_id].red);
12267 (*image)->background_color.green=ScaleShortToQuantum(
12268 windows->pixel_info->pen_colors[pen_id].green);
12269 (*image)->background_color.blue=ScaleShortToQuantum(
12270 windows->pixel_info->pen_colors[pen_id].blue);
12271 rotate_image=RotateImage(*image,degrees,&(*image)->exception);
12272 XSetCursorState(display,windows,MagickFalse);
12273 if (rotate_image == (Image *) NULL)
12274 return(MagickFalse);
12275 *image=DestroyImage(*image);
12276 *image=rotate_image;
12277 if (windows->image.crop_geometry != (char *) NULL)
12278 {
12279 /*
12280 Rotate crop geometry.
12281 */
12282 width=(unsigned int) (*image)->columns;
12283 height=(unsigned int) (*image)->rows;
12284 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12285 switch (rotations % 4)
12286 {
12287 default:
12288 case 0:
12289 break;
12290 case 1:
12291 {
12292 /*
12293 Rotate 90 degrees.
12294 */
12295 (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
12296 "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12297 (int) height-y,x);
12298 break;
12299 }
12300 case 2:
12301 {
12302 /*
12303 Rotate 180 degrees.
12304 */
12305 (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
12306 "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12307 break;
12308 }
12309 case 3:
12310 {
12311 /*
12312 Rotate 270 degrees.
12313 */
12314 (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
12315 "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12316 break;
12317 }
12318 }
12319 }
12320 if (windows->image.orphan != MagickFalse)
12321 return(MagickTrue);
12322 if (normalized_degrees != 0.0)
12323 {
12324 /*
12325 Update image colormap.
12326 */
12327 windows->image.window_changes.width=(int) (*image)->columns;
12328 windows->image.window_changes.height=(int) (*image)->rows;
12329 if (windows->image.crop_geometry != (char *) NULL)
12330 {
12331 /*
12332 Obtain dimensions of image from crop geometry.
12333 */
12334 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12335 &width,&height);
12336 windows->image.window_changes.width=(int) width;
12337 windows->image.window_changes.height=(int) height;
12338 }
12339 XConfigureImageColormap(display,resource_info,windows,*image);
12340 }
12341 else
12342 if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12343 {
12344 windows->image.window_changes.width=windows->image.ximage->height;
12345 windows->image.window_changes.height=windows->image.ximage->width;
12346 }
12347 /*
12348 Update image configuration.
12349 */
12350 (void) XConfigureImage(display,resource_info,windows,*image);
12351 return(MagickTrue);
12352}
12353
12354/*
12355%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12356% %
12357% %
12358% %
12359+ X S a v e I m a g e %
12360% %
12361% %
12362% %
12363%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12364%
12365% XSaveImage() saves an image to a file.
12366%
12367% The format of the XSaveImage method is:
12368%
12369% MagickBooleanType XSaveImage(Display *display,
12370% XResourceInfo *resource_info,XWindows *windows,Image *image)
12371%
12372% A description of each parameter follows:
12373%
12374% o display: Specifies a connection to an X server; returned from
12375% XOpenDisplay.
12376%
12377% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12378%
12379% o windows: Specifies a pointer to a XWindows structure.
12380%
12381% o image: the image.
12382%
12383*/
12384static MagickBooleanType XSaveImage(Display *display,
12385 XResourceInfo *resource_info,XWindows *windows,Image *image)
12386{
12387 char
12388 filename[MaxTextExtent],
12389 geometry[MaxTextExtent];
12390
12391 Image
12392 *save_image;
12393
12394 ImageInfo
12395 *image_info;
12396
12397 MagickStatusType
12398 status;
12399
12400 /*
12401 Request file name from user.
12402 */
12403 if (resource_info->write_filename != (char *) NULL)
12404 (void) CopyMagickString(filename,resource_info->write_filename,
12405 MaxTextExtent);
12406 else
12407 {
12408 char
12409 path[MaxTextExtent];
12410
12411 int
12412 status;
12413
12414 GetPathComponent(image->filename,HeadPath,path);
12415 GetPathComponent(image->filename,TailPath,filename);
12416 status=chdir(path);
12417 if (status == -1)
12418 (void) ThrowMagickException(&image->exception,GetMagickModule(),
12419 FileOpenError,"UnableToOpenFile","%s",path);
12420 }
12421 XFileBrowserWidget(display,windows,"Save",filename);
12422 if (*filename == '\0')
12423 return(MagickTrue);
12424 if (IsPathAccessible(filename) != MagickFalse)
12425 {
12426 int
12427 status;
12428
12429 /*
12430 File exists-- seek user's permission before overwriting.
12431 */
12432 status=XConfirmWidget(display,windows,"Overwrite",filename);
12433 if (status <= 0)
12434 return(MagickTrue);
12435 }
12436 image_info=CloneImageInfo(resource_info->image_info);
12437 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
cristyd965a422010-03-03 17:47:35 +000012438 (void) SetImageInfo(image_info,1,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000012439 if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12440 (LocaleCompare(image_info->magick,"JPG") == 0))
12441 {
12442 char
12443 quality[MaxTextExtent];
12444
12445 int
12446 status;
12447
12448 /*
12449 Request JPEG quality from user.
12450 */
cristye8c25f92010-06-03 00:53:06 +000012451 (void) FormatMagickString(quality,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +000012452 image->quality);
cristy3ed852e2009-09-05 21:47:34 +000012453 status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12454 quality);
12455 if (*quality == '\0')
12456 return(MagickTrue);
cristye27293e2009-12-18 02:53:20 +000012457 image->quality=StringToUnsignedLong(quality);
cristy3ed852e2009-09-05 21:47:34 +000012458 image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12459 }
12460 if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12461 (LocaleCompare(image_info->magick,"PDF") == 0) ||
12462 (LocaleCompare(image_info->magick,"PS") == 0) ||
12463 (LocaleCompare(image_info->magick,"PS2") == 0))
12464 {
12465 char
12466 geometry[MaxTextExtent];
12467
12468 /*
12469 Request page geometry from user.
12470 */
cristyb93d9e72009-09-12 20:02:21 +000012471 (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +000012472 if (LocaleCompare(image_info->magick,"PDF") == 0)
cristyb93d9e72009-09-12 20:02:21 +000012473 (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +000012474 if (image_info->page != (char *) NULL)
12475 (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12476 XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12477 "Select page geometry:",geometry);
12478 if (*geometry != '\0')
12479 image_info->page=GetPageGeometry(geometry);
12480 }
12481 /*
12482 Apply image transforms.
12483 */
12484 XSetCursorState(display,windows,MagickTrue);
12485 XCheckRefreshWindows(display,windows);
12486 save_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12487 if (save_image == (Image *) NULL)
12488 return(MagickFalse);
12489 (void) FormatMagickString(geometry,MaxTextExtent,"%dx%d!",
12490 windows->image.ximage->width,windows->image.ximage->height);
12491 (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12492 /*
12493 Write image.
12494 */
12495 (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12496 status=WriteImage(image_info,save_image);
12497 if (status != MagickFalse)
12498 image->taint=MagickFalse;
12499 save_image=DestroyImage(save_image);
12500 image_info=DestroyImageInfo(image_info);
12501 XSetCursorState(display,windows,MagickFalse);
12502 return(status != 0 ? MagickTrue : MagickFalse);
12503}
12504
12505/*
12506%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12507% %
12508% %
12509% %
12510+ X S c r e e n E v e n t %
12511% %
12512% %
12513% %
12514%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12515%
12516% XScreenEvent() handles global events associated with the Pan and Magnify
12517% windows.
12518%
12519% The format of the XScreenEvent function is:
12520%
12521% void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12522%
12523% A description of each parameter follows:
12524%
12525% o display: Specifies a pointer to the Display structure; returned from
12526% XOpenDisplay.
12527%
12528% o windows: Specifies a pointer to a XWindows structure.
12529%
12530% o event: Specifies a pointer to a X11 XEvent structure.
12531%
12532%
12533*/
12534
12535#if defined(__cplusplus) || defined(c_plusplus)
12536extern "C" {
12537#endif
12538
12539static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12540{
12541 register XWindows
12542 *windows;
12543
12544 windows=(XWindows *) data;
12545 if ((event->type == ClientMessage) &&
12546 (event->xclient.window == windows->image.id))
12547 return(MagickFalse);
12548 return(MagickTrue);
12549}
12550
12551#if defined(__cplusplus) || defined(c_plusplus)
12552}
12553#endif
12554
12555static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12556{
12557 register int
12558 x,
12559 y;
12560
12561 (void) XIfEvent(display,event,XPredicate,(char *) windows);
12562 if (event->xany.window == windows->command.id)
12563 return;
12564 switch (event->type)
12565 {
12566 case ButtonPress:
12567 case ButtonRelease:
12568 {
12569 if ((event->xbutton.button == Button3) &&
12570 (event->xbutton.state & Mod1Mask))
12571 {
12572 /*
12573 Convert Alt-Button3 to Button2.
12574 */
12575 event->xbutton.button=Button2;
12576 event->xbutton.state&=(~Mod1Mask);
12577 }
12578 if (event->xbutton.window == windows->backdrop.id)
12579 {
12580 (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12581 event->xbutton.time);
12582 break;
12583 }
12584 if (event->xbutton.window == windows->pan.id)
12585 {
12586 XPanImage(display,windows,event);
12587 break;
12588 }
12589 if (event->xbutton.window == windows->image.id)
12590 if (event->xbutton.button == Button2)
12591 {
12592 /*
12593 Update magnified image.
12594 */
12595 x=event->xbutton.x;
12596 y=event->xbutton.y;
12597 if (x < 0)
12598 x=0;
12599 else
12600 if (x >= (int) windows->image.width)
12601 x=(int) (windows->image.width-1);
cristy49e2d862010-11-12 02:50:30 +000012602 windows->magnify.x=(int) windows->image.x+x;
cristy3ed852e2009-09-05 21:47:34 +000012603 if (y < 0)
12604 y=0;
12605 else
12606 if (y >= (int) windows->image.height)
12607 y=(int) (windows->image.height-1);
12608 windows->magnify.y=windows->image.y+y;
12609 if (windows->magnify.mapped == MagickFalse)
12610 (void) XMapRaised(display,windows->magnify.id);
12611 XMakeMagnifyImage(display,windows);
12612 if (event->type == ButtonRelease)
12613 (void) XWithdrawWindow(display,windows->info.id,
12614 windows->info.screen);
12615 break;
12616 }
12617 break;
12618 }
12619 case ClientMessage:
12620 {
12621 /*
12622 If client window delete message, exit.
12623 */
12624 if (event->xclient.message_type != windows->wm_protocols)
12625 break;
cristyecd0ab52010-05-30 14:59:20 +000012626 if (*event->xclient.data.l != (long) windows->wm_delete_window)
cristy3ed852e2009-09-05 21:47:34 +000012627 break;
12628 if (event->xclient.window == windows->magnify.id)
12629 {
12630 (void) XWithdrawWindow(display,windows->magnify.id,
12631 windows->magnify.screen);
12632 break;
12633 }
12634 break;
12635 }
12636 case ConfigureNotify:
12637 {
12638 if (event->xconfigure.window == windows->magnify.id)
12639 {
12640 unsigned int
12641 magnify;
12642
12643 /*
12644 Magnify window has a new configuration.
12645 */
12646 windows->magnify.width=(unsigned int) event->xconfigure.width;
12647 windows->magnify.height=(unsigned int) event->xconfigure.height;
12648 if (windows->magnify.mapped == MagickFalse)
12649 break;
12650 magnify=1;
12651 while ((int) magnify <= event->xconfigure.width)
12652 magnify<<=1;
12653 while ((int) magnify <= event->xconfigure.height)
12654 magnify<<=1;
12655 magnify>>=1;
12656 if (((int) magnify != event->xconfigure.width) ||
12657 ((int) magnify != event->xconfigure.height))
12658 {
12659 XWindowChanges
12660 window_changes;
12661
12662 window_changes.width=(int) magnify;
12663 window_changes.height=(int) magnify;
12664 (void) XReconfigureWMWindow(display,windows->magnify.id,
12665 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12666 &window_changes);
12667 break;
12668 }
12669 XMakeMagnifyImage(display,windows);
12670 break;
12671 }
12672 break;
12673 }
12674 case Expose:
12675 {
12676 if (event->xexpose.window == windows->image.id)
12677 {
12678 XRefreshWindow(display,&windows->image,event);
12679 break;
12680 }
12681 if (event->xexpose.window == windows->pan.id)
12682 if (event->xexpose.count == 0)
12683 {
12684 XDrawPanRectangle(display,windows);
12685 break;
12686 }
12687 if (event->xexpose.window == windows->magnify.id)
12688 if (event->xexpose.count == 0)
12689 {
12690 XMakeMagnifyImage(display,windows);
12691 break;
12692 }
12693 break;
12694 }
12695 case KeyPress:
12696 {
12697 char
12698 command[MaxTextExtent];
12699
12700 KeySym
12701 key_symbol;
12702
12703 if (event->xkey.window != windows->magnify.id)
12704 break;
12705 /*
12706 Respond to a user key press.
12707 */
12708 (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12709 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12710 XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12711 break;
12712 }
12713 case MapNotify:
12714 {
12715 if (event->xmap.window == windows->magnify.id)
12716 {
12717 windows->magnify.mapped=MagickTrue;
12718 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12719 break;
12720 }
12721 if (event->xmap.window == windows->info.id)
12722 {
12723 windows->info.mapped=MagickTrue;
12724 break;
12725 }
12726 break;
12727 }
12728 case MotionNotify:
12729 {
12730 while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12731 if (event->xmotion.window == windows->image.id)
12732 if (windows->magnify.mapped != MagickFalse)
12733 {
12734 /*
12735 Update magnified image.
12736 */
12737 x=event->xmotion.x;
12738 y=event->xmotion.y;
12739 if (x < 0)
12740 x=0;
12741 else
12742 if (x >= (int) windows->image.width)
12743 x=(int) (windows->image.width-1);
cristy49e2d862010-11-12 02:50:30 +000012744 windows->magnify.x=(int) windows->image.x+x;
cristy3ed852e2009-09-05 21:47:34 +000012745 if (y < 0)
12746 y=0;
12747 else
12748 if (y >= (int) windows->image.height)
12749 y=(int) (windows->image.height-1);
12750 windows->magnify.y=windows->image.y+y;
12751 XMakeMagnifyImage(display,windows);
12752 }
12753 break;
12754 }
12755 case UnmapNotify:
12756 {
12757 if (event->xunmap.window == windows->magnify.id)
12758 {
12759 windows->magnify.mapped=MagickFalse;
12760 break;
12761 }
12762 if (event->xunmap.window == windows->info.id)
12763 {
12764 windows->info.mapped=MagickFalse;
12765 break;
12766 }
12767 break;
12768 }
12769 default:
12770 break;
12771 }
12772}
12773
12774/*
12775%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12776% %
12777% %
12778% %
12779+ X S e t C r o p G e o m e t r y %
12780% %
12781% %
12782% %
12783%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12784%
12785% XSetCropGeometry() accepts a cropping geometry relative to the Image window
12786% and translates it to a cropping geometry relative to the image.
12787%
12788% The format of the XSetCropGeometry method is:
12789%
12790% void XSetCropGeometry(Display *display,XWindows *windows,
12791% RectangleInfo *crop_info,Image *image)
12792%
12793% A description of each parameter follows:
12794%
12795% o display: Specifies a connection to an X server; returned from
12796% XOpenDisplay.
12797%
12798% o windows: Specifies a pointer to a XWindows structure.
12799%
12800% o crop_info: A pointer to a RectangleInfo that defines a region of the
12801% Image window to crop.
12802%
12803% o image: the image.
12804%
12805*/
12806static void XSetCropGeometry(Display *display,XWindows *windows,
12807 RectangleInfo *crop_info,Image *image)
12808{
12809 char
12810 text[MaxTextExtent];
12811
12812 int
12813 x,
12814 y;
12815
12816 MagickRealType
12817 scale_factor;
12818
12819 unsigned int
12820 height,
12821 width;
12822
12823 if (windows->info.mapped != MagickFalse)
12824 {
12825 /*
12826 Display info on cropping rectangle.
12827 */
cristy6d8abba2010-06-03 01:10:47 +000012828 (void) FormatMagickString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
cristye8c25f92010-06-03 00:53:06 +000012829 (double) crop_info->width,(double) crop_info->height,(double)
12830 crop_info->x,(double) crop_info->y);
cristy3ed852e2009-09-05 21:47:34 +000012831 XInfoWidget(display,windows,text);
12832 }
12833 /*
12834 Cropping geometry is relative to any previous crop geometry.
12835 */
12836 x=0;
12837 y=0;
12838 width=(unsigned int) image->columns;
12839 height=(unsigned int) image->rows;
12840 if (windows->image.crop_geometry != (char *) NULL)
12841 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12842 else
12843 windows->image.crop_geometry=AcquireString((char *) NULL);
12844 /*
12845 Define the crop geometry string from the cropping rectangle.
12846 */
12847 scale_factor=(MagickRealType) width/windows->image.ximage->width;
12848 if (crop_info->x > 0)
12849 x+=(int) (scale_factor*crop_info->x+0.5);
12850 width=(unsigned int) (scale_factor*crop_info->width+0.5);
12851 if (width == 0)
12852 width=1;
12853 scale_factor=(MagickRealType) height/windows->image.ximage->height;
12854 if (crop_info->y > 0)
12855 y+=(int) (scale_factor*crop_info->y+0.5);
12856 height=(unsigned int) (scale_factor*crop_info->height+0.5);
12857 if (height == 0)
12858 height=1;
12859 (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
12860 "%ux%u%+d%+d",width,height,x,y);
12861}
12862
12863/*
12864%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12865% %
12866% %
12867% %
12868+ X T i l e I m a g e %
12869% %
12870% %
12871% %
12872%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12873%
12874% XTileImage() loads or deletes a selected tile from a visual image directory.
12875% The load or delete command is chosen from a menu.
12876%
12877% The format of the XTileImage method is:
12878%
12879% Image *XTileImage(Display *display,XResourceInfo *resource_info,
12880% XWindows *windows,Image *image,XEvent *event)
12881%
12882% A description of each parameter follows:
12883%
12884% o tile_image: XTileImage reads or deletes the tile image
12885% and returns it. A null image is returned if an error occurs.
12886%
12887% o display: Specifies a connection to an X server; returned from
12888% XOpenDisplay.
12889%
12890% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12891%
12892% o windows: Specifies a pointer to a XWindows structure.
12893%
12894% o image: the image; returned from ReadImage.
12895%
12896% o event: Specifies a pointer to a XEvent structure. If it is NULL,
12897% the entire image is refreshed.
12898%
12899*/
12900static Image *XTileImage(Display *display,XResourceInfo *resource_info,
12901 XWindows *windows,Image *image,XEvent *event)
12902{
12903 static const char
12904 *VerbMenu[] =
12905 {
12906 "Load",
12907 "Next",
12908 "Former",
12909 "Delete",
12910 "Update",
12911 (char *) NULL,
12912 };
12913
12914 static const ModeType
12915 TileCommands[] =
12916 {
12917 TileLoadCommand,
12918 TileNextCommand,
12919 TileFormerCommand,
12920 TileDeleteCommand,
12921 TileUpdateCommand
12922 };
12923
12924 char
12925 command[MaxTextExtent],
12926 filename[MaxTextExtent];
12927
12928 Image
12929 *tile_image;
12930
12931 int
12932 id,
12933 status,
12934 tile,
12935 x,
12936 y;
12937
12938 MagickRealType
12939 scale_factor;
12940
12941 register char
12942 *p,
12943 *q;
12944
12945 register int
12946 i;
12947
12948 unsigned int
12949 height,
12950 width;
12951
12952 /*
12953 Tile image is relative to montage image configuration.
12954 */
12955 x=0;
12956 y=0;
12957 width=(unsigned int) image->columns;
12958 height=(unsigned int) image->rows;
12959 if (windows->image.crop_geometry != (char *) NULL)
12960 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12961 scale_factor=(MagickRealType) width/windows->image.ximage->width;
12962 event->xbutton.x+=windows->image.x;
12963 event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
12964 scale_factor=(MagickRealType) height/windows->image.ximage->height;
12965 event->xbutton.y+=windows->image.y;
12966 event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
12967 /*
12968 Determine size and location of each tile in the visual image directory.
12969 */
12970 width=(unsigned int) image->columns;
12971 height=(unsigned int) image->rows;
12972 x=0;
12973 y=0;
12974 (void) XParseGeometry(image->montage,&x,&y,&width,&height);
12975 tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
12976 (event->xbutton.x-x)/width;
12977 if (tile < 0)
12978 {
12979 /*
12980 Button press is outside any tile.
12981 */
12982 (void) XBell(display,0);
12983 return((Image *) NULL);
12984 }
12985 /*
12986 Determine file name from the tile directory.
12987 */
12988 p=image->directory;
12989 for (i=tile; (i != 0) && (*p != '\0'); )
12990 {
12991 if (*p == '\n')
12992 i--;
12993 p++;
12994 }
12995 if (*p == '\0')
12996 {
12997 /*
12998 Button press is outside any tile.
12999 */
13000 (void) XBell(display,0);
13001 return((Image *) NULL);
13002 }
13003 /*
13004 Select a command from the pop-up menu.
13005 */
13006 id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13007 if (id < 0)
13008 return((Image *) NULL);
13009 q=p;
13010 while ((*q != '\n') && (*q != '\0'))
13011 q++;
13012 (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13013 /*
13014 Perform command for the selected tile.
13015 */
13016 XSetCursorState(display,windows,MagickTrue);
13017 XCheckRefreshWindows(display,windows);
13018 tile_image=NewImageList();
13019 switch (TileCommands[id])
13020 {
13021 case TileLoadCommand:
13022 {
13023 /*
13024 Load tile image.
13025 */
13026 XCheckRefreshWindows(display,windows);
13027 (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13028 MaxTextExtent);
13029 (void) CopyMagickString(resource_info->image_info->filename,filename,
13030 MaxTextExtent);
13031 tile_image=ReadImage(resource_info->image_info,&image->exception);
13032 CatchException(&image->exception);
13033 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13034 break;
13035 }
13036 case TileNextCommand:
13037 {
13038 /*
13039 Display next image.
13040 */
13041 XClientMessage(display,windows->image.id,windows->im_protocols,
13042 windows->im_next_image,CurrentTime);
13043 break;
13044 }
13045 case TileFormerCommand:
13046 {
13047 /*
13048 Display former image.
13049 */
13050 XClientMessage(display,windows->image.id,windows->im_protocols,
13051 windows->im_former_image,CurrentTime);
13052 break;
13053 }
13054 case TileDeleteCommand:
13055 {
13056 /*
13057 Delete tile image.
13058 */
13059 if (IsPathAccessible(filename) == MagickFalse)
13060 {
13061 XNoticeWidget(display,windows,"Image file does not exist:",filename);
13062 break;
13063 }
13064 status=XConfirmWidget(display,windows,"Really delete tile",filename);
13065 if (status <= 0)
13066 break;
13067 status=remove(filename) != 0 ? MagickTrue : MagickFalse;
13068 if (status != MagickFalse)
13069 {
13070 XNoticeWidget(display,windows,"Unable to delete image file:",
13071 filename);
13072 break;
13073 }
13074 }
13075 case TileUpdateCommand:
13076 {
13077 ExceptionInfo
13078 *exception;
13079
13080 int
13081 x_offset,
13082 y_offset;
13083
13084 PixelPacket
13085 pixel;
13086
13087 register int
13088 j;
13089
13090 register PixelPacket
13091 *s;
13092
13093 /*
13094 Ensure all the images exist.
13095 */
13096 tile=0;
13097 for (p=image->directory; *p != '\0'; p++)
13098 {
cristyf7c25522010-11-15 01:25:14 +000013099 CacheView
13100 *image_view;
13101
cristy3ed852e2009-09-05 21:47:34 +000013102 q=p;
13103 while ((*q != '\n') && (*q != '\0'))
13104 q++;
13105 (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13106 p=q;
13107 if (IsPathAccessible(filename) != MagickFalse)
13108 {
13109 tile++;
13110 continue;
13111 }
13112 /*
13113 Overwrite tile with background color.
13114 */
13115 x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13116 y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13117 exception=(&image->exception);
cristy49e2d862010-11-12 02:50:30 +000013118 image_view=AcquireCacheView(image);
13119 (void) GetOneCacheViewVirtualPixel(image_view,0,0,&pixel,exception);
cristy3ed852e2009-09-05 21:47:34 +000013120 for (i=0; i < (int) height; i++)
13121 {
cristy49e2d862010-11-12 02:50:30 +000013122 s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13123 y_offset+i,width,1,exception);
cristy3ed852e2009-09-05 21:47:34 +000013124 if (s == (PixelPacket *) NULL)
13125 break;
13126 for (j=0; j < (int) width; j++)
13127 *s++=pixel;
cristy49e2d862010-11-12 02:50:30 +000013128 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000013129 break;
13130 }
cristyca1628f2010-11-15 01:17:49 +000013131 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +000013132 tile++;
13133 }
13134 windows->image.window_changes.width=(int) image->columns;
13135 windows->image.window_changes.height=(int) image->rows;
13136 XConfigureImageColormap(display,resource_info,windows,image);
13137 (void) XConfigureImage(display,resource_info,windows,image);
13138 break;
13139 }
13140 default:
13141 break;
13142 }
13143 XSetCursorState(display,windows,MagickFalse);
13144 return(tile_image);
13145}
13146
13147/*
13148%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13149% %
13150% %
13151% %
13152+ X T r a n s l a t e I m a g e %
13153% %
13154% %
13155% %
13156%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13157%
13158% XTranslateImage() translates the image within an Image window by one pixel
13159% as specified by the key symbol. If the image has a `montage string the
13160% translation is respect to the width and height contained within the string.
13161%
13162% The format of the XTranslateImage method is:
13163%
13164% void XTranslateImage(Display *display,XWindows *windows,
13165% Image *image,const KeySym key_symbol)
13166%
13167% A description of each parameter follows:
13168%
13169% o display: Specifies a connection to an X server; returned from
13170% XOpenDisplay.
13171%
13172% o windows: Specifies a pointer to a XWindows structure.
13173%
13174% o image: the image.
13175%
13176% o key_symbol: Specifies a KeySym which indicates which side of the image
13177% to trim.
13178%
13179*/
13180static void XTranslateImage(Display *display,XWindows *windows,
13181 Image *image,const KeySym key_symbol)
13182{
13183 char
13184 text[MaxTextExtent];
13185
13186 int
13187 x,
13188 y;
13189
13190 unsigned int
13191 x_offset,
13192 y_offset;
13193
13194 /*
13195 User specified a pan position offset.
13196 */
13197 x_offset=windows->image.width;
13198 y_offset=windows->image.height;
13199 if (image->montage != (char *) NULL)
13200 (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13201 switch ((int) key_symbol)
13202 {
13203 case XK_Home:
13204 case XK_KP_Home:
13205 {
13206 windows->image.x=(int) windows->image.width/2;
13207 windows->image.y=(int) windows->image.height/2;
13208 break;
13209 }
13210 case XK_Left:
13211 case XK_KP_Left:
13212 {
13213 windows->image.x-=x_offset;
13214 break;
13215 }
13216 case XK_Next:
13217 case XK_Up:
13218 case XK_KP_Up:
13219 {
13220 windows->image.y-=y_offset;
13221 break;
13222 }
13223 case XK_Right:
13224 case XK_KP_Right:
13225 {
13226 windows->image.x+=x_offset;
13227 break;
13228 }
13229 case XK_Prior:
13230 case XK_Down:
13231 case XK_KP_Down:
13232 {
13233 windows->image.y+=y_offset;
13234 break;
13235 }
13236 default:
13237 return;
13238 }
13239 /*
13240 Check boundary conditions.
13241 */
13242 if (windows->image.x < 0)
13243 windows->image.x=0;
13244 else
13245 if ((int) (windows->image.x+windows->image.width) >
13246 windows->image.ximage->width)
cristy49e2d862010-11-12 02:50:30 +000013247 windows->image.x=(int) windows->image.ximage->width-windows->image.width;
cristy3ed852e2009-09-05 21:47:34 +000013248 if (windows->image.y < 0)
13249 windows->image.y=0;
13250 else
13251 if ((int) (windows->image.y+windows->image.height) >
13252 windows->image.ximage->height)
cristy49e2d862010-11-12 02:50:30 +000013253 windows->image.y=(int) windows->image.ximage->height-windows->image.height;
cristy3ed852e2009-09-05 21:47:34 +000013254 /*
13255 Refresh Image window.
13256 */
13257 (void) FormatMagickString(text,MaxTextExtent," %ux%u%+d%+d ",
13258 windows->image.width,windows->image.height,windows->image.x,
13259 windows->image.y);
13260 XInfoWidget(display,windows,text);
13261 XCheckRefreshWindows(display,windows);
13262 XDrawPanRectangle(display,windows);
13263 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13264 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13265}
13266
13267/*
13268%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13269% %
13270% %
13271% %
13272+ X T r i m I m a g e %
13273% %
13274% %
13275% %
13276%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13277%
13278% XTrimImage() trims the edges from the Image window.
13279%
13280% The format of the XTrimImage method is:
13281%
13282% MagickBooleanType XTrimImage(Display *display,
13283% XResourceInfo *resource_info,XWindows *windows,Image *image)
13284%
13285% A description of each parameter follows:
13286%
13287% o display: Specifies a connection to an X server; returned from
13288% XOpenDisplay.
13289%
13290% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13291%
13292% o windows: Specifies a pointer to a XWindows structure.
13293%
13294% o image: the image.
13295%
13296*/
13297static MagickBooleanType XTrimImage(Display *display,
13298 XResourceInfo *resource_info,XWindows *windows,Image *image)
13299{
13300 RectangleInfo
13301 trim_info;
13302
13303 register int
13304 x,
13305 y;
13306
cristybb503372010-05-27 20:51:26 +000013307 size_t
cristy3ed852e2009-09-05 21:47:34 +000013308 background,
13309 pixel;
13310
13311 /*
13312 Trim edges from image.
13313 */
13314 XSetCursorState(display,windows,MagickTrue);
13315 XCheckRefreshWindows(display,windows);
13316 /*
13317 Crop the left edge.
13318 */
13319 background=XGetPixel(windows->image.ximage,0,0);
cristybb503372010-05-27 20:51:26 +000013320 trim_info.width=(size_t) windows->image.ximage->width;
cristy3ed852e2009-09-05 21:47:34 +000013321 for (x=0; x < windows->image.ximage->width; x++)
13322 {
13323 for (y=0; y < windows->image.ximage->height; y++)
13324 {
13325 pixel=XGetPixel(windows->image.ximage,x,y);
13326 if (pixel != background)
13327 break;
13328 }
13329 if (y < windows->image.ximage->height)
13330 break;
13331 }
cristy49e2d862010-11-12 02:50:30 +000013332 trim_info.x=(ssize_t) x;
13333 if (trim_info.x == (ssize_t) windows->image.ximage->width)
cristy3ed852e2009-09-05 21:47:34 +000013334 {
13335 XSetCursorState(display,windows,MagickFalse);
13336 return(MagickFalse);
13337 }
13338 /*
13339 Crop the right edge.
13340 */
13341 background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13342 for (x=windows->image.ximage->width-1; x != 0; x--)
13343 {
13344 for (y=0; y < windows->image.ximage->height; y++)
13345 {
13346 pixel=XGetPixel(windows->image.ximage,x,y);
13347 if (pixel != background)
13348 break;
13349 }
13350 if (y < windows->image.ximage->height)
13351 break;
13352 }
cristybb503372010-05-27 20:51:26 +000013353 trim_info.width=(size_t) (x-trim_info.x+1);
cristy3ed852e2009-09-05 21:47:34 +000013354 /*
13355 Crop the top edge.
13356 */
13357 background=XGetPixel(windows->image.ximage,0,0);
cristybb503372010-05-27 20:51:26 +000013358 trim_info.height=(size_t) windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +000013359 for (y=0; y < windows->image.ximage->height; y++)
13360 {
13361 for (x=0; x < windows->image.ximage->width; x++)
13362 {
13363 pixel=XGetPixel(windows->image.ximage,x,y);
13364 if (pixel != background)
13365 break;
13366 }
13367 if (x < windows->image.ximage->width)
13368 break;
13369 }
cristy49e2d862010-11-12 02:50:30 +000013370 trim_info.y=(ssize_t) y;
cristy3ed852e2009-09-05 21:47:34 +000013371 /*
13372 Crop the bottom edge.
13373 */
13374 background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13375 for (y=windows->image.ximage->height-1; y != 0; y--)
13376 {
13377 for (x=0; x < windows->image.ximage->width; x++)
13378 {
13379 pixel=XGetPixel(windows->image.ximage,x,y);
13380 if (pixel != background)
13381 break;
13382 }
13383 if (x < windows->image.ximage->width)
13384 break;
13385 }
cristybb503372010-05-27 20:51:26 +000013386 trim_info.height=(size_t) y-trim_info.y+1;
cristy3ed852e2009-09-05 21:47:34 +000013387 if (((unsigned int) trim_info.width != windows->image.width) ||
13388 ((unsigned int) trim_info.height != windows->image.height))
13389 {
13390 /*
13391 Reconfigure Image window as defined by the trimming rectangle.
13392 */
13393 XSetCropGeometry(display,windows,&trim_info,image);
13394 windows->image.window_changes.width=(int) trim_info.width;
13395 windows->image.window_changes.height=(int) trim_info.height;
13396 (void) XConfigureImage(display,resource_info,windows,image);
13397 }
13398 XSetCursorState(display,windows,MagickFalse);
13399 return(MagickTrue);
13400}
13401
13402/*
13403%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13404% %
13405% %
13406% %
13407+ X V i s u a l D i r e c t o r y I m a g e %
13408% %
13409% %
13410% %
13411%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13412%
13413% XVisualDirectoryImage() creates a Visual Image Directory.
13414%
13415% The format of the XVisualDirectoryImage method is:
13416%
13417% Image *XVisualDirectoryImage(Display *display,
13418% XResourceInfo *resource_info,XWindows *windows)
13419%
13420% A description of each parameter follows:
13421%
13422% o nexus: Method XVisualDirectoryImage returns a visual image
13423% directory if it can be created successfully. Otherwise a null image
13424% is returned.
13425%
13426% o display: Specifies a connection to an X server; returned from
13427% XOpenDisplay.
13428%
13429% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13430%
13431% o windows: Specifies a pointer to a XWindows structure.
13432%
13433*/
13434static Image *XVisualDirectoryImage(Display *display,
13435 XResourceInfo *resource_info,XWindows *windows)
13436{
13437#define TileImageTag "Scale/Image"
13438#define XClientName "montage"
13439
13440 char
13441 **filelist;
13442
13443 ExceptionInfo
13444 *exception;
13445
13446 Image
13447 *images,
13448 *montage_image,
13449 *next_image,
13450 *thumbnail_image;
13451
13452 ImageInfo
13453 *read_info;
13454
13455 int
13456 number_files;
13457
13458 MagickBooleanType
13459 backdrop;
13460
13461 MagickStatusType
13462 status;
13463
13464 MontageInfo
13465 *montage_info;
13466
13467 RectangleInfo
13468 geometry;
13469
13470 register int
13471 i;
13472
13473 static char
13474 filename[MaxTextExtent] = "\0",
13475 filenames[MaxTextExtent] = "*";
13476
13477 XResourceInfo
13478 background_resources;
13479
13480 /*
13481 Request file name from user.
13482 */
13483 XFileBrowserWidget(display,windows,"Directory",filenames);
13484 if (*filenames == '\0')
13485 return((Image *) NULL);
13486 /*
13487 Expand the filenames.
13488 */
cristy73bd4a52010-10-05 11:24:23 +000013489 filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
cristy3ed852e2009-09-05 21:47:34 +000013490 if (filelist == (char **) NULL)
13491 {
13492 ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13493 filenames);
13494 return((Image *) NULL);
13495 }
13496 number_files=1;
13497 filelist[0]=filenames;
13498 status=ExpandFilenames(&number_files,&filelist);
13499 if ((status == MagickFalse) || (number_files == 0))
13500 {
13501 if (number_files == 0)
13502 ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13503 else
13504 ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13505 filenames);
13506 return((Image *) NULL);
13507 }
13508 /*
13509 Set image background resources.
13510 */
13511 background_resources=(*resource_info);
13512 background_resources.window_id=AcquireString("");
13513 (void) FormatMagickString(background_resources.window_id,MaxTextExtent,
13514 "0x%lx",windows->image.id);
13515 background_resources.backdrop=MagickTrue;
13516 /*
13517 Read each image and convert them to a tile.
13518 */
13519 backdrop=(windows->visual_info->klass == TrueColor) ||
13520 (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13521 read_info=CloneImageInfo(resource_info->image_info);
cristy9ce61202010-11-24 00:38:37 +000013522 (void) SetImageOption(read_info,"jpeg:size","120x120");
13523 (void) CloneString(&read_info->size,DefaultTileGeometry);
cristy3ed852e2009-09-05 21:47:34 +000013524 (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13525 (void *) NULL);
13526 images=NewImageList();
13527 exception=AcquireExceptionInfo();
13528 XSetCursorState(display,windows,MagickTrue);
13529 XCheckRefreshWindows(display,windows);
cristy49e2d862010-11-12 02:50:30 +000013530 for (i=0; i < (int) number_files; i++)
cristy3ed852e2009-09-05 21:47:34 +000013531 {
13532 (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13533 filelist[i]=DestroyString(filelist[i]);
13534 *read_info->magick='\0';
cristy3ed852e2009-09-05 21:47:34 +000013535 next_image=ReadImage(read_info,exception);
13536 CatchException(exception);
13537 if (next_image != (Image *) NULL)
13538 {
13539 (void) DeleteImageProperty(next_image,"label");
cristy77619442010-11-24 00:27:23 +000013540 (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13541 read_info,next_image,DefaultTileLabel));
cristy3ed852e2009-09-05 21:47:34 +000013542 (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13543 exception);
13544 thumbnail_image=ThumbnailImage(next_image,geometry.width,
13545 geometry.height,exception);
13546 if (thumbnail_image != (Image *) NULL)
13547 {
13548 next_image=DestroyImage(next_image);
13549 next_image=thumbnail_image;
13550 }
13551 if (backdrop)
13552 {
13553 (void) XDisplayBackgroundImage(display,&background_resources,
13554 next_image);
13555 XSetCursorState(display,windows,MagickTrue);
13556 }
13557 AppendImageToList(&images,next_image);
13558 if (images->progress_monitor != (MagickProgressMonitor) NULL)
13559 {
13560 MagickBooleanType
13561 proceed;
13562
13563 proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13564 (MagickSizeType) number_files);
13565 if (proceed == MagickFalse)
13566 break;
13567 }
13568 }
13569 }
13570 exception=DestroyExceptionInfo(exception);
13571 filelist=(char **) RelinquishMagickMemory(filelist);
cristy3ed852e2009-09-05 21:47:34 +000013572 if (images == (Image *) NULL)
13573 {
cristy8d52fca2010-11-24 00:45:05 +000013574 read_info=DestroyImageInfo(read_info);
cristy3ed852e2009-09-05 21:47:34 +000013575 XSetCursorState(display,windows,MagickFalse);
13576 ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13577 return((Image *) NULL);
13578 }
13579 /*
13580 Create the Visual Image Directory.
13581 */
cristy8d52fca2010-11-24 00:45:05 +000013582 montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13583 montage_info->pointsize=10;
cristy3ed852e2009-09-05 21:47:34 +000013584 if (resource_info->font != (char *) NULL)
13585 (void) CloneString(&montage_info->font,resource_info->font);
13586 (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
cristy8d52fca2010-11-24 00:45:05 +000013587 montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13588 images),&images->exception);
cristy3ed852e2009-09-05 21:47:34 +000013589 images=DestroyImageList(images);
cristy8d52fca2010-11-24 00:45:05 +000013590 montage_info=DestroyMontageInfo(montage_info);
13591 read_info=DestroyImageInfo(read_info);
cristy3ed852e2009-09-05 21:47:34 +000013592 XSetCursorState(display,windows,MagickFalse);
13593 if (montage_image == (Image *) NULL)
13594 return(montage_image);
13595 XClientMessage(display,windows->image.id,windows->im_protocols,
13596 windows->im_next_image,CurrentTime);
13597 return(montage_image);
13598}
13599
13600/*
13601%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13602% %
13603% %
13604% %
13605% X D i s p l a y B a c k g r o u n d I m a g e %
13606% %
13607% %
13608% %
13609%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13610%
13611% XDisplayBackgroundImage() displays an image in the background of a window.
13612%
13613% The format of the XDisplayBackgroundImage method is:
13614%
13615% MagickBooleanType XDisplayBackgroundImage(Display *display,
13616% XResourceInfo *resource_info,Image *image)
13617%
13618% A description of each parameter follows:
13619%
13620% o display: Specifies a connection to an X server; returned from
13621% XOpenDisplay.
13622%
13623% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13624%
13625% o image: the image.
13626%
13627*/
13628MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13629 XResourceInfo *resource_info,Image *image)
13630{
13631 char
13632 geometry[MaxTextExtent],
13633 visual_type[MaxTextExtent];
13634
13635 int
13636 height,
13637 status,
13638 width;
13639
13640 RectangleInfo
13641 geometry_info;
13642
13643 static XPixelInfo
13644 pixel;
13645
13646 static XStandardColormap
13647 *map_info;
13648
13649 static XVisualInfo
13650 *visual_info = (XVisualInfo *) NULL;
13651
13652 static XWindowInfo
13653 window_info;
13654
cristybb503372010-05-27 20:51:26 +000013655 size_t
cristy3ed852e2009-09-05 21:47:34 +000013656 delay;
13657
13658 Window
13659 root_window;
13660
13661 XGCValues
13662 context_values;
13663
13664 XResourceInfo
13665 resources;
13666
13667 XWindowAttributes
13668 window_attributes;
13669
13670 /*
13671 Determine target window.
13672 */
13673 assert(image != (Image *) NULL);
13674 assert(image->signature == MagickSignature);
13675 if (image->debug != MagickFalse)
13676 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13677 resources=(*resource_info);
13678 window_info.id=(Window) NULL;
13679 root_window=XRootWindow(display,XDefaultScreen(display));
13680 if (LocaleCompare(resources.window_id,"root") == 0)
13681 window_info.id=root_window;
13682 else
13683 {
13684 if (isdigit((unsigned char) *resources.window_id) != 0)
13685 window_info.id=XWindowByID(display,root_window,
13686 (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13687 if (window_info.id == (Window) NULL)
13688 window_info.id=XWindowByName(display,root_window,resources.window_id);
13689 }
13690 if (window_info.id == (Window) NULL)
13691 {
13692 ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13693 resources.window_id);
13694 return(MagickFalse);
13695 }
13696 /*
13697 Determine window visual id.
13698 */
13699 window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13700 window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13701 (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13702 status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13703 if (status != 0)
13704 (void) FormatMagickString(visual_type,MaxTextExtent,"0x%lx",
13705 XVisualIDFromVisual(window_attributes.visual));
13706 if (visual_info == (XVisualInfo *) NULL)
13707 {
13708 /*
13709 Allocate standard colormap.
13710 */
13711 map_info=XAllocStandardColormap();
13712 if (map_info == (XStandardColormap *) NULL)
13713 ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13714 image->filename);
13715 map_info->colormap=(Colormap) NULL;
cristyf2faecf2010-05-28 19:19:36 +000013716 pixel.pixels=(unsigned long *) NULL;
cristy3ed852e2009-09-05 21:47:34 +000013717 /*
13718 Initialize visual info.
13719 */
13720 resources.map_type=(char *) NULL;
13721 resources.visual_type=visual_type;
13722 visual_info=XBestVisualInfo(display,map_info,&resources);
13723 if (visual_info == (XVisualInfo *) NULL)
13724 ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13725 resources.visual_type);
13726 /*
13727 Initialize window info.
13728 */
13729 window_info.ximage=(XImage *) NULL;
13730 window_info.matte_image=(XImage *) NULL;
13731 window_info.pixmap=(Pixmap) NULL;
13732 window_info.matte_pixmap=(Pixmap) NULL;
13733 }
13734 /*
13735 Free previous root colors.
13736 */
13737 if (window_info.id == root_window)
13738 (void) XDestroyWindowColors(display,root_window);
13739 /*
13740 Initialize Standard Colormap.
13741 */
13742 resources.colormap=SharedColormap;
13743 XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13744 /*
13745 Graphic context superclass.
13746 */
13747 context_values.background=pixel.background_color.pixel;
13748 context_values.foreground=pixel.foreground_color.pixel;
13749 pixel.annotate_context=XCreateGC(display,window_info.id,
cristybb503372010-05-27 20:51:26 +000013750 (size_t) (GCBackground | GCForeground),&context_values);
cristy3ed852e2009-09-05 21:47:34 +000013751 if (pixel.annotate_context == (GC) NULL)
13752 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13753 image->filename);
13754 /*
13755 Initialize Image window attributes.
13756 */
13757 window_info.name=AcquireString("\0");
13758 window_info.icon_name=AcquireString("\0");
13759 XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13760 &resources,&window_info);
13761 /*
13762 Create the X image.
13763 */
13764 window_info.width=(unsigned int) image->columns;
13765 window_info.height=(unsigned int) image->rows;
13766 if ((image->columns != window_info.width) ||
13767 (image->rows != window_info.height))
13768 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13769 image->filename);
13770 (void) FormatMagickString(geometry,MaxTextExtent,"%ux%u+0+0>",
13771 window_attributes.width,window_attributes.height);
13772 geometry_info.width=window_info.width;
13773 geometry_info.height=window_info.height;
cristyecd0ab52010-05-30 14:59:20 +000013774 geometry_info.x=(ssize_t) window_info.x;
13775 geometry_info.y=(ssize_t) window_info.y;
cristy3ed852e2009-09-05 21:47:34 +000013776 (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13777 &geometry_info.width,&geometry_info.height);
13778 window_info.width=(unsigned int) geometry_info.width;
13779 window_info.height=(unsigned int) geometry_info.height;
13780 window_info.x=(int) geometry_info.x;
13781 window_info.y=(int) geometry_info.y;
13782 status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13783 window_info.height);
13784 if (status == MagickFalse)
13785 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13786 image->filename);
13787 window_info.x=0;
13788 window_info.y=0;
13789 if (image->debug != MagickFalse)
13790 {
13791 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000013792 "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13793 (double) image->columns,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000013794 if (image->colors != 0)
cristye8c25f92010-06-03 00:53:06 +000013795 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13796 image->colors);
cristy3ed852e2009-09-05 21:47:34 +000013797 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13798 }
13799 /*
13800 Adjust image dimensions as specified by backdrop or geometry options.
13801 */
13802 width=(int) window_info.width;
13803 height=(int) window_info.height;
13804 if (resources.backdrop != MagickFalse)
13805 {
13806 /*
13807 Center image on window.
13808 */
13809 window_info.x=(window_attributes.width/2)-
13810 (window_info.ximage->width/2);
13811 window_info.y=(window_attributes.height/2)-
13812 (window_info.ximage->height/2);
13813 width=window_attributes.width;
13814 height=window_attributes.height;
13815 }
13816 if ((resources.image_geometry != (char *) NULL) &&
13817 (*resources.image_geometry != '\0'))
13818 {
13819 char
13820 default_geometry[MaxTextExtent];
13821
13822 int
13823 flags,
13824 gravity;
13825
13826 XSizeHints
13827 *size_hints;
13828
13829 /*
13830 User specified geometry.
13831 */
13832 size_hints=XAllocSizeHints();
13833 if (size_hints == (XSizeHints *) NULL)
13834 ThrowXWindowFatalException(ResourceLimitFatalError,
13835 "MemoryAllocationFailed",image->filename);
13836 size_hints->flags=0L;
13837 (void) FormatMagickString(default_geometry,MaxTextExtent,"%dx%d",
13838 width,height);
13839 flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13840 default_geometry,window_info.border_width,size_hints,&window_info.x,
13841 &window_info.y,&width,&height,&gravity);
13842 if (flags & (XValue | YValue))
13843 {
13844 width=window_attributes.width;
13845 height=window_attributes.height;
13846 }
13847 (void) XFree((void *) size_hints);
13848 }
13849 /*
13850 Create the X pixmap.
13851 */
13852 window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13853 (unsigned int) height,window_info.depth);
13854 if (window_info.pixmap == (Pixmap) NULL)
13855 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13856 image->filename);
13857 /*
13858 Display pixmap on the window.
13859 */
13860 if (((unsigned int) width > window_info.width) ||
13861 ((unsigned int) height > window_info.height))
13862 (void) XFillRectangle(display,window_info.pixmap,
13863 window_info.annotate_context,0,0,(unsigned int) width,
13864 (unsigned int) height);
13865 (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13866 window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13867 window_info.width,(unsigned int) window_info.height);
13868 (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13869 (void) XClearWindow(display,window_info.id);
13870 delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13871 XDelay(display,delay == 0UL ? 10UL : delay);
13872 (void) XSync(display,MagickFalse);
13873 return(window_info.id == root_window ? MagickTrue : MagickFalse);
13874}
13875
13876/*
13877%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13878% %
13879% %
13880% %
13881+ X D i s p l a y I m a g e %
13882% %
13883% %
13884% %
13885%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13886%
13887% XDisplayImage() displays an image via X11. A new image is created and
13888% returned if the user interactively transforms the displayed image.
13889%
13890% The format of the XDisplayImage method is:
13891%
13892% Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
cristybb503372010-05-27 20:51:26 +000013893% char **argv,int argc,Image **image,size_t *state)
cristy3ed852e2009-09-05 21:47:34 +000013894%
13895% A description of each parameter follows:
13896%
13897% o nexus: Method XDisplayImage returns an image when the
13898% user chooses 'Open Image' from the command menu or picks a tile
13899% from the image directory. Otherwise a null image is returned.
13900%
13901% o display: Specifies a connection to an X server; returned from
13902% XOpenDisplay.
13903%
13904% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13905%
13906% o argv: Specifies the application's argument list.
13907%
13908% o argc: Specifies the number of arguments.
13909%
13910% o image: Specifies an address to an address of an Image structure;
13911%
13912*/
13913MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
cristybb503372010-05-27 20:51:26 +000013914 char **argv,int argc,Image **image,size_t *state)
cristy3ed852e2009-09-05 21:47:34 +000013915{
13916#define MagnifySize 256 /* must be a power of 2 */
13917#define MagickMenus 10
13918#define MagickTitle "Commands"
13919
13920 static const char
13921 *CommandMenu[] =
13922 {
13923 "File",
13924 "Edit",
13925 "View",
13926 "Transform",
13927 "Enhance",
13928 "Effects",
13929 "F/X",
13930 "Image Edit",
13931 "Miscellany",
13932 "Help",
13933 (char *) NULL
13934 },
13935 *FileMenu[] =
13936 {
13937 "Open...",
13938 "Next",
13939 "Former",
13940 "Select...",
13941 "Save...",
13942 "Print...",
13943 "Delete...",
13944 "New...",
13945 "Visual Directory...",
13946 "Quit",
13947 (char *) NULL
13948 },
13949 *EditMenu[] =
13950 {
13951 "Undo",
13952 "Redo",
13953 "Cut",
13954 "Copy",
13955 "Paste",
13956 (char *) NULL
13957 },
13958 *ViewMenu[] =
13959 {
13960 "Half Size",
13961 "Original Size",
13962 "Double Size",
13963 "Resize...",
13964 "Apply",
13965 "Refresh",
13966 "Restore",
13967 (char *) NULL
13968 },
13969 *TransformMenu[] =
13970 {
13971 "Crop",
13972 "Chop",
13973 "Flop",
13974 "Flip",
13975 "Rotate Right",
13976 "Rotate Left",
13977 "Rotate...",
13978 "Shear...",
13979 "Roll...",
13980 "Trim Edges",
13981 (char *) NULL
13982 },
13983 *EnhanceMenu[] =
13984 {
13985 "Hue...",
13986 "Saturation...",
13987 "Brightness...",
13988 "Gamma...",
13989 "Spiff",
13990 "Dull",
13991 "Contrast Stretch...",
13992 "Sigmoidal Contrast...",
13993 "Normalize",
13994 "Equalize",
13995 "Negate",
13996 "Grayscale",
13997 "Map...",
13998 "Quantize...",
13999 (char *) NULL
14000 },
14001 *EffectsMenu[] =
14002 {
14003 "Despeckle",
14004 "Emboss",
14005 "Reduce Noise",
14006 "Add Noise...",
14007 "Sharpen...",
14008 "Blur...",
14009 "Threshold...",
14010 "Edge Detect...",
14011 "Spread...",
14012 "Shade...",
14013 "Raise...",
14014 "Segment...",
14015 (char *) NULL
14016 },
14017 *FXMenu[] =
14018 {
14019 "Solarize...",
14020 "Sepia Tone...",
14021 "Swirl...",
14022 "Implode...",
14023 "Vignette...",
14024 "Wave...",
14025 "Oil Paint...",
14026 "Charcoal Draw...",
14027 (char *) NULL
14028 },
14029 *ImageEditMenu[] =
14030 {
14031 "Annotate...",
14032 "Draw...",
14033 "Color...",
14034 "Matte...",
14035 "Composite...",
14036 "Add Border...",
14037 "Add Frame...",
14038 "Comment...",
14039 "Launch...",
14040 "Region of Interest...",
14041 (char *) NULL
14042 },
14043 *MiscellanyMenu[] =
14044 {
14045 "Image Info",
14046 "Zoom Image",
14047 "Show Preview...",
14048 "Show Histogram",
14049 "Show Matte",
14050 "Background...",
14051 "Slide Show...",
14052 "Preferences...",
14053 (char *) NULL
14054 },
14055 *HelpMenu[] =
14056 {
14057 "Overview",
14058 "Browse Documentation",
14059 "About Display",
14060 (char *) NULL
14061 },
14062 *ShortCutsMenu[] =
14063 {
14064 "Next",
14065 "Former",
14066 "Open...",
14067 "Save...",
14068 "Print...",
14069 "Undo",
14070 "Restore",
14071 "Image Info",
14072 "Quit",
14073 (char *) NULL
14074 },
14075 *VirtualMenu[] =
14076 {
14077 "Image Info",
14078 "Print",
14079 "Next",
14080 "Quit",
14081 (char *) NULL
14082 };
14083
14084 static const char
14085 **Menus[MagickMenus] =
14086 {
14087 FileMenu,
14088 EditMenu,
14089 ViewMenu,
14090 TransformMenu,
14091 EnhanceMenu,
14092 EffectsMenu,
14093 FXMenu,
14094 ImageEditMenu,
14095 MiscellanyMenu,
14096 HelpMenu
14097 };
14098
14099 static CommandType
14100 CommandMenus[] =
14101 {
14102 NullCommand,
14103 NullCommand,
14104 NullCommand,
14105 NullCommand,
14106 NullCommand,
14107 NullCommand,
14108 NullCommand,
14109 NullCommand,
14110 NullCommand,
14111 NullCommand,
14112 },
14113 FileCommands[] =
14114 {
14115 OpenCommand,
14116 NextCommand,
14117 FormerCommand,
14118 SelectCommand,
14119 SaveCommand,
14120 PrintCommand,
14121 DeleteCommand,
14122 NewCommand,
14123 VisualDirectoryCommand,
14124 QuitCommand
14125 },
14126 EditCommands[] =
14127 {
14128 UndoCommand,
14129 RedoCommand,
14130 CutCommand,
14131 CopyCommand,
14132 PasteCommand
14133 },
14134 ViewCommands[] =
14135 {
14136 HalfSizeCommand,
14137 OriginalSizeCommand,
14138 DoubleSizeCommand,
14139 ResizeCommand,
14140 ApplyCommand,
14141 RefreshCommand,
14142 RestoreCommand
14143 },
14144 TransformCommands[] =
14145 {
14146 CropCommand,
14147 ChopCommand,
14148 FlopCommand,
14149 FlipCommand,
14150 RotateRightCommand,
14151 RotateLeftCommand,
14152 RotateCommand,
14153 ShearCommand,
14154 RollCommand,
14155 TrimCommand
14156 },
14157 EnhanceCommands[] =
14158 {
14159 HueCommand,
14160 SaturationCommand,
14161 BrightnessCommand,
14162 GammaCommand,
14163 SpiffCommand,
14164 DullCommand,
14165 ContrastStretchCommand,
14166 SigmoidalContrastCommand,
14167 NormalizeCommand,
14168 EqualizeCommand,
14169 NegateCommand,
14170 GrayscaleCommand,
14171 MapCommand,
14172 QuantizeCommand
14173 },
14174 EffectsCommands[] =
14175 {
14176 DespeckleCommand,
14177 EmbossCommand,
14178 ReduceNoiseCommand,
14179 AddNoiseCommand,
14180 SharpenCommand,
14181 BlurCommand,
14182 ThresholdCommand,
14183 EdgeDetectCommand,
14184 SpreadCommand,
14185 ShadeCommand,
14186 RaiseCommand,
14187 SegmentCommand
14188 },
14189 FXCommands[] =
14190 {
14191 SolarizeCommand,
14192 SepiaToneCommand,
14193 SwirlCommand,
14194 ImplodeCommand,
14195 VignetteCommand,
14196 WaveCommand,
14197 OilPaintCommand,
14198 CharcoalDrawCommand
14199 },
14200 ImageEditCommands[] =
14201 {
14202 AnnotateCommand,
14203 DrawCommand,
14204 ColorCommand,
14205 MatteCommand,
14206 CompositeCommand,
14207 AddBorderCommand,
14208 AddFrameCommand,
14209 CommentCommand,
14210 LaunchCommand,
14211 RegionofInterestCommand
14212 },
14213 MiscellanyCommands[] =
14214 {
14215 InfoCommand,
14216 ZoomCommand,
14217 ShowPreviewCommand,
14218 ShowHistogramCommand,
14219 ShowMatteCommand,
14220 BackgroundCommand,
14221 SlideShowCommand,
14222 PreferencesCommand
14223 },
14224 HelpCommands[] =
14225 {
14226 HelpCommand,
14227 BrowseDocumentationCommand,
14228 VersionCommand
14229 },
14230 ShortCutsCommands[] =
14231 {
14232 NextCommand,
14233 FormerCommand,
14234 OpenCommand,
14235 SaveCommand,
14236 PrintCommand,
14237 UndoCommand,
14238 RestoreCommand,
14239 InfoCommand,
14240 QuitCommand
14241 },
14242 VirtualCommands[] =
14243 {
14244 InfoCommand,
14245 PrintCommand,
14246 NextCommand,
14247 QuitCommand
14248 };
14249
14250 static CommandType
14251 *Commands[MagickMenus] =
14252 {
14253 FileCommands,
14254 EditCommands,
14255 ViewCommands,
14256 TransformCommands,
14257 EnhanceCommands,
14258 EffectsCommands,
14259 FXCommands,
14260 ImageEditCommands,
14261 MiscellanyCommands,
14262 HelpCommands
14263 };
14264
14265 char
14266 command[MaxTextExtent],
cristy00976d82011-02-20 20:31:28 +000014267 *directory,
cristy3ed852e2009-09-05 21:47:34 +000014268 geometry[MaxTextExtent],
14269 resource_name[MaxTextExtent];
14270
14271 CommandType
14272 command_type;
14273
14274 Image
14275 *display_image,
14276 *nexus;
14277
14278 int
14279 entry,
14280 id;
14281
14282 KeySym
14283 key_symbol;
14284
14285 MagickStatusType
14286 context_mask,
14287 status;
14288
14289 RectangleInfo
14290 geometry_info;
14291
14292 register int
14293 i;
14294
14295 static char
14296 working_directory[MaxTextExtent];
14297
14298 static XPoint
14299 vid_info;
14300
14301 static XWindowInfo
14302 *magick_windows[MaxXWindows];
14303
14304 static unsigned int
14305 number_windows;
14306
14307 struct stat
14308 attributes;
14309
14310 time_t
14311 timer,
14312 timestamp,
14313 update_time;
14314
14315 unsigned int
14316 height,
14317 width;
14318
cristybb503372010-05-27 20:51:26 +000014319 size_t
cristy3ed852e2009-09-05 21:47:34 +000014320 delay;
14321
14322 WarningHandler
14323 warning_handler;
14324
14325 Window
14326 root_window;
14327
14328 XClassHint
14329 *class_hints;
14330
14331 XEvent
14332 event;
14333
14334 XFontStruct
14335 *font_info;
14336
14337 XGCValues
14338 context_values;
14339
14340 XPixelInfo
14341 *icon_pixel,
14342 *pixel;
14343
14344 XResourceInfo
14345 *icon_resources;
14346
14347 XStandardColormap
14348 *icon_map,
14349 *map_info;
14350
14351 XVisualInfo
14352 *icon_visual,
14353 *visual_info;
14354
14355 XWindowChanges
14356 window_changes;
14357
14358 XWindows
14359 *windows;
14360
14361 XWMHints
14362 *manager_hints;
14363
14364 assert(image != (Image **) NULL);
14365 assert((*image)->signature == MagickSignature);
14366 if ((*image)->debug != MagickFalse)
14367 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14368 display_image=(*image);
14369 warning_handler=(WarningHandler) NULL;
14370 windows=XSetWindows((XWindows *) ~0);
14371 if (windows != (XWindows *) NULL)
14372 {
14373 int
14374 status;
14375
14376 status=chdir(working_directory);
14377 if (status == -1)
14378 (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
14379 FileOpenError,"UnableToOpenFile","%s",working_directory);
14380 warning_handler=resource_info->display_warnings ?
14381 SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14382 warning_handler=resource_info->display_warnings ?
14383 SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14384 }
14385 else
14386 {
14387 /*
14388 Allocate windows structure.
14389 */
14390 resource_info->colors=display_image->colors;
14391 windows=XSetWindows(XInitializeWindows(display,resource_info));
14392 if (windows == (XWindows *) NULL)
14393 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14394 (*image)->filename);
14395 /*
14396 Initialize window id's.
14397 */
14398 number_windows=0;
14399 magick_windows[number_windows++]=(&windows->icon);
14400 magick_windows[number_windows++]=(&windows->backdrop);
14401 magick_windows[number_windows++]=(&windows->image);
14402 magick_windows[number_windows++]=(&windows->info);
14403 magick_windows[number_windows++]=(&windows->command);
14404 magick_windows[number_windows++]=(&windows->widget);
14405 magick_windows[number_windows++]=(&windows->popup);
14406 magick_windows[number_windows++]=(&windows->magnify);
14407 magick_windows[number_windows++]=(&windows->pan);
14408 for (i=0; i < (int) number_windows; i++)
14409 magick_windows[i]->id=(Window) NULL;
14410 vid_info.x=0;
14411 vid_info.y=0;
14412 }
14413 /*
14414 Initialize font info.
14415 */
14416 if (windows->font_info != (XFontStruct *) NULL)
14417 (void) XFreeFont(display,windows->font_info);
14418 windows->font_info=XBestFont(display,resource_info,MagickFalse);
14419 if (windows->font_info == (XFontStruct *) NULL)
14420 ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14421 resource_info->font);
14422 /*
14423 Initialize Standard Colormap.
14424 */
14425 map_info=windows->map_info;
14426 icon_map=windows->icon_map;
14427 visual_info=windows->visual_info;
14428 icon_visual=windows->icon_visual;
14429 pixel=windows->pixel_info;
14430 icon_pixel=windows->icon_pixel;
14431 font_info=windows->font_info;
14432 icon_resources=windows->icon_resources;
14433 class_hints=windows->class_hints;
14434 manager_hints=windows->manager_hints;
14435 root_window=XRootWindow(display,visual_info->screen);
14436 nexus=NewImageList();
14437 if (display_image->debug != MagickFalse)
14438 {
14439 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000014440 "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14441 (double) display_image->scene,(double) display_image->columns,
14442 (double) display_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000014443 if (display_image->colors != 0)
cristye8c25f92010-06-03 00:53:06 +000014444 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14445 display_image->colors);
cristy3ed852e2009-09-05 21:47:34 +000014446 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14447 display_image->magick);
14448 }
14449 XMakeStandardColormap(display,visual_info,resource_info,display_image,
14450 map_info,pixel);
14451 display_image->taint=MagickFalse;
14452 /*
14453 Initialize graphic context.
14454 */
14455 windows->context.id=(Window) NULL;
14456 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14457 resource_info,&windows->context);
14458 (void) CloneString(&class_hints->res_name,resource_info->client_name);
14459 (void) CloneString(&class_hints->res_class,resource_info->client_name);
14460 class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14461 manager_hints->flags=InputHint | StateHint;
14462 manager_hints->input=MagickFalse;
14463 manager_hints->initial_state=WithdrawnState;
14464 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14465 &windows->context);
14466 if (display_image->debug != MagickFalse)
14467 (void) LogMagickEvent(X11Event,GetMagickModule(),
14468 "Window id: 0x%lx (context)",windows->context.id);
14469 context_values.background=pixel->background_color.pixel;
14470 context_values.font=font_info->fid;
14471 context_values.foreground=pixel->foreground_color.pixel;
14472 context_values.graphics_exposures=MagickFalse;
14473 context_mask=(MagickStatusType)
14474 (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14475 if (pixel->annotate_context != (GC) NULL)
14476 (void) XFreeGC(display,pixel->annotate_context);
14477 pixel->annotate_context=XCreateGC(display,windows->context.id,
14478 context_mask,&context_values);
14479 if (pixel->annotate_context == (GC) NULL)
14480 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14481 display_image->filename);
14482 context_values.background=pixel->depth_color.pixel;
14483 if (pixel->widget_context != (GC) NULL)
14484 (void) XFreeGC(display,pixel->widget_context);
14485 pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14486 &context_values);
14487 if (pixel->widget_context == (GC) NULL)
14488 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14489 display_image->filename);
14490 context_values.background=pixel->foreground_color.pixel;
14491 context_values.foreground=pixel->background_color.pixel;
14492 context_values.plane_mask=context_values.background ^
14493 context_values.foreground;
14494 if (pixel->highlight_context != (GC) NULL)
14495 (void) XFreeGC(display,pixel->highlight_context);
14496 pixel->highlight_context=XCreateGC(display,windows->context.id,
cristybb503372010-05-27 20:51:26 +000014497 (size_t) (context_mask | GCPlaneMask),&context_values);
cristy3ed852e2009-09-05 21:47:34 +000014498 if (pixel->highlight_context == (GC) NULL)
14499 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14500 display_image->filename);
14501 (void) XDestroyWindow(display,windows->context.id);
14502 /*
14503 Initialize icon window.
14504 */
14505 XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14506 icon_resources,&windows->icon);
14507 windows->icon.geometry=resource_info->icon_geometry;
14508 XBestIconSize(display,&windows->icon,display_image);
14509 windows->icon.attributes.colormap=XDefaultColormap(display,
14510 icon_visual->screen);
14511 windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14512 manager_hints->flags=InputHint | StateHint;
14513 manager_hints->input=MagickFalse;
14514 manager_hints->initial_state=IconicState;
14515 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14516 &windows->icon);
14517 if (display_image->debug != MagickFalse)
14518 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14519 windows->icon.id);
14520 /*
14521 Initialize graphic context for icon window.
14522 */
14523 if (icon_pixel->annotate_context != (GC) NULL)
14524 (void) XFreeGC(display,icon_pixel->annotate_context);
14525 context_values.background=icon_pixel->background_color.pixel;
14526 context_values.foreground=icon_pixel->foreground_color.pixel;
14527 icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
cristybb503372010-05-27 20:51:26 +000014528 (size_t) (GCBackground | GCForeground),&context_values);
cristy3ed852e2009-09-05 21:47:34 +000014529 if (icon_pixel->annotate_context == (GC) NULL)
14530 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14531 display_image->filename);
14532 windows->icon.annotate_context=icon_pixel->annotate_context;
14533 /*
14534 Initialize Image window.
14535 */
14536 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14537 &windows->image);
14538 windows->image.shape=MagickTrue; /* non-rectangular shape hint */
14539 if (resource_info->use_shared_memory == MagickFalse)
14540 windows->image.shared_memory=MagickFalse;
14541 if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14542 {
14543 char
14544 *title;
14545
14546 title=InterpretImageProperties(resource_info->image_info,display_image,
14547 resource_info->title);
14548 (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14549 (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14550 title=DestroyString(title);
14551 }
14552 else
14553 {
14554 char
14555 filename[MaxTextExtent];
14556
14557 /*
14558 Window name is the base of the filename.
14559 */
14560 GetPathComponent(display_image->magick_filename,TailPath,filename);
14561 if (GetImageListLength(display_image) == 1)
14562 (void) FormatMagickString(windows->image.name,MaxTextExtent,
cristy40a08ad2010-02-09 02:27:44 +000014563 "%s: %s",MagickPackageName,filename);
cristy3ed852e2009-09-05 21:47:34 +000014564 else
14565 (void) FormatMagickString(windows->image.name,MaxTextExtent,
cristy04d55062011-03-15 18:58:50 +000014566 "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14567 (double) display_image->scene,(double) GetImageListLength(
14568 display_image));
cristy3ed852e2009-09-05 21:47:34 +000014569 (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14570 }
14571 if (resource_info->immutable)
14572 windows->image.immutable=MagickTrue;
14573 windows->image.use_pixmap=resource_info->use_pixmap;
14574 windows->image.geometry=resource_info->image_geometry;
14575 (void) FormatMagickString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14576 XDisplayWidth(display,visual_info->screen),
14577 XDisplayHeight(display,visual_info->screen));
14578 geometry_info.width=display_image->columns;
14579 geometry_info.height=display_image->rows;
14580 geometry_info.x=0;
14581 geometry_info.y=0;
14582 (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14583 &geometry_info.width,&geometry_info.height);
14584 windows->image.width=(unsigned int) geometry_info.width;
14585 windows->image.height=(unsigned int) geometry_info.height;
14586 windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14587 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14588 KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14589 PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14590 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14591 resource_info,&windows->backdrop);
14592 if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14593 {
14594 /*
14595 Initialize backdrop window.
14596 */
14597 windows->backdrop.x=0;
14598 windows->backdrop.y=0;
cristy40a08ad2010-02-09 02:27:44 +000014599 (void) CloneString(&windows->backdrop.name,"Backdrop");
cristybb503372010-05-27 20:51:26 +000014600 windows->backdrop.flags=(size_t) (USSize | USPosition);
cristy3ed852e2009-09-05 21:47:34 +000014601 windows->backdrop.width=(unsigned int)
14602 XDisplayWidth(display,visual_info->screen);
14603 windows->backdrop.height=(unsigned int)
14604 XDisplayHeight(display,visual_info->screen);
14605 windows->backdrop.border_width=0;
14606 windows->backdrop.immutable=MagickTrue;
14607 windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14608 ButtonReleaseMask;
14609 windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14610 StructureNotifyMask;
14611 manager_hints->flags=IconWindowHint | InputHint | StateHint;
14612 manager_hints->icon_window=windows->icon.id;
14613 manager_hints->input=MagickTrue;
14614 manager_hints->initial_state=resource_info->iconic ? IconicState :
14615 NormalState;
14616 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14617 &windows->backdrop);
14618 if (display_image->debug != MagickFalse)
14619 (void) LogMagickEvent(X11Event,GetMagickModule(),
14620 "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14621 (void) XMapWindow(display,windows->backdrop.id);
14622 (void) XClearWindow(display,windows->backdrop.id);
14623 if (windows->image.id != (Window) NULL)
14624 {
14625 (void) XDestroyWindow(display,windows->image.id);
14626 windows->image.id=(Window) NULL;
14627 }
14628 /*
14629 Position image in the center the backdrop.
14630 */
14631 windows->image.flags|=USPosition;
14632 windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14633 (windows->image.width/2);
14634 windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14635 (windows->image.height/2);
14636 }
14637 manager_hints->flags=IconWindowHint | InputHint | StateHint;
14638 manager_hints->icon_window=windows->icon.id;
14639 manager_hints->input=MagickTrue;
14640 manager_hints->initial_state=resource_info->iconic ? IconicState :
14641 NormalState;
14642 if (windows->group_leader.id != (Window) NULL)
14643 {
14644 /*
14645 Follow the leader.
14646 */
14647 manager_hints->flags|=WindowGroupHint;
14648 manager_hints->window_group=windows->group_leader.id;
14649 (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14650 if (display_image->debug != MagickFalse)
14651 (void) LogMagickEvent(X11Event,GetMagickModule(),
14652 "Window id: 0x%lx (group leader)",windows->group_leader.id);
14653 }
14654 XMakeWindow(display,
14655 (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14656 argv,argc,class_hints,manager_hints,&windows->image);
14657 (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14658 XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14659 if (windows->group_leader.id != (Window) NULL)
14660 (void) XSetTransientForHint(display,windows->image.id,
14661 windows->group_leader.id);
14662 if (display_image->debug != MagickFalse)
14663 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14664 windows->image.id);
14665 /*
14666 Initialize Info widget.
14667 */
14668 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14669 &windows->info);
14670 (void) CloneString(&windows->info.name,"Info");
14671 (void) CloneString(&windows->info.icon_name,"Info");
14672 windows->info.border_width=1;
14673 windows->info.x=2;
14674 windows->info.y=2;
14675 windows->info.flags|=PPosition;
14676 windows->info.attributes.win_gravity=UnmapGravity;
14677 windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14678 StructureNotifyMask;
14679 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14680 manager_hints->input=MagickFalse;
14681 manager_hints->initial_state=NormalState;
14682 manager_hints->window_group=windows->image.id;
14683 XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14684 &windows->info);
14685 windows->info.highlight_stipple=XCreateBitmapFromData(display,
14686 windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14687 windows->info.shadow_stipple=XCreateBitmapFromData(display,
14688 windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14689 (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14690 if (windows->image.mapped != MagickFalse)
14691 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14692 if (display_image->debug != MagickFalse)
14693 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14694 windows->info.id);
14695 /*
14696 Initialize Command widget.
14697 */
14698 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14699 resource_info,&windows->command);
14700 windows->command.data=MagickMenus;
14701 (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14702 (void) FormatMagickString(resource_name,MaxTextExtent,"%s.command",
14703 resource_info->client_name);
14704 windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14705 resource_name,"geometry",(char *) NULL);
14706 (void) CloneString(&windows->command.name,MagickTitle);
14707 windows->command.border_width=0;
14708 windows->command.flags|=PPosition;
14709 windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14710 ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14711 OwnerGrabButtonMask | StructureNotifyMask;
14712 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14713 manager_hints->input=MagickTrue;
14714 manager_hints->initial_state=NormalState;
14715 manager_hints->window_group=windows->image.id;
14716 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14717 &windows->command);
14718 windows->command.highlight_stipple=XCreateBitmapFromData(display,
14719 windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14720 HighlightHeight);
14721 windows->command.shadow_stipple=XCreateBitmapFromData(display,
14722 windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14723 (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14724 if (windows->command.mapped != MagickFalse)
14725 (void) XMapRaised(display,windows->command.id);
14726 if (display_image->debug != MagickFalse)
14727 (void) LogMagickEvent(X11Event,GetMagickModule(),
14728 "Window id: 0x%lx (command)",windows->command.id);
14729 /*
14730 Initialize Widget window.
14731 */
14732 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14733 resource_info,&windows->widget);
14734 (void) FormatMagickString(resource_name,MaxTextExtent,"%s.widget",
14735 resource_info->client_name);
14736 windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14737 resource_name,"geometry",(char *) NULL);
14738 windows->widget.border_width=0;
14739 windows->widget.flags|=PPosition;
14740 windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14741 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14742 KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14743 StructureNotifyMask;
14744 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14745 manager_hints->input=MagickTrue;
14746 manager_hints->initial_state=NormalState;
14747 manager_hints->window_group=windows->image.id;
14748 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14749 &windows->widget);
14750 windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14751 windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14752 windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14753 windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14754 (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14755 if (display_image->debug != MagickFalse)
14756 (void) LogMagickEvent(X11Event,GetMagickModule(),
14757 "Window id: 0x%lx (widget)",windows->widget.id);
14758 /*
14759 Initialize popup window.
14760 */
14761 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14762 resource_info,&windows->popup);
14763 windows->popup.border_width=0;
14764 windows->popup.flags|=PPosition;
14765 windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14766 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14767 KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14768 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14769 manager_hints->input=MagickTrue;
14770 manager_hints->initial_state=NormalState;
14771 manager_hints->window_group=windows->image.id;
14772 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14773 &windows->popup);
14774 windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14775 windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14776 windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14777 windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14778 (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14779 if (display_image->debug != MagickFalse)
14780 (void) LogMagickEvent(X11Event,GetMagickModule(),
14781 "Window id: 0x%lx (pop up)",windows->popup.id);
14782 /*
14783 Initialize Magnify window and cursor.
14784 */
14785 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14786 resource_info,&windows->magnify);
14787 if (resource_info->use_shared_memory == MagickFalse)
14788 windows->magnify.shared_memory=MagickFalse;
14789 (void) FormatMagickString(resource_name,MaxTextExtent,"%s.magnify",
14790 resource_info->client_name);
14791 windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14792 resource_name,"geometry",(char *) NULL);
14793 (void) FormatMagickString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14794 resource_info->magnify);
14795 if (windows->magnify.cursor != (Cursor) NULL)
14796 (void) XFreeCursor(display,windows->magnify.cursor);
14797 windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14798 map_info->colormap,resource_info->background_color,
14799 resource_info->foreground_color);
14800 if (windows->magnify.cursor == (Cursor) NULL)
14801 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14802 display_image->filename);
14803 windows->magnify.width=MagnifySize;
14804 windows->magnify.height=MagnifySize;
14805 windows->magnify.flags|=PPosition;
14806 windows->magnify.min_width=MagnifySize;
14807 windows->magnify.min_height=MagnifySize;
14808 windows->magnify.width_inc=MagnifySize;
14809 windows->magnify.height_inc=MagnifySize;
14810 windows->magnify.data=resource_info->magnify;
14811 windows->magnify.attributes.cursor=windows->magnify.cursor;
14812 windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14813 ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14814 StructureNotifyMask;
14815 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14816 manager_hints->input=MagickTrue;
14817 manager_hints->initial_state=NormalState;
14818 manager_hints->window_group=windows->image.id;
14819 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14820 &windows->magnify);
14821 if (display_image->debug != MagickFalse)
14822 (void) LogMagickEvent(X11Event,GetMagickModule(),
14823 "Window id: 0x%lx (magnify)",windows->magnify.id);
14824 (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14825 /*
14826 Initialize panning window.
14827 */
14828 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14829 resource_info,&windows->pan);
14830 (void) CloneString(&windows->pan.name,"Pan Icon");
14831 windows->pan.width=windows->icon.width;
14832 windows->pan.height=windows->icon.height;
14833 (void) FormatMagickString(resource_name,MaxTextExtent,"%s.pan",
14834 resource_info->client_name);
14835 windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14836 resource_name,"geometry",(char *) NULL);
14837 (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14838 &windows->pan.width,&windows->pan.height);
14839 windows->pan.flags|=PPosition;
14840 windows->pan.immutable=MagickTrue;
14841 windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14842 ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14843 StructureNotifyMask;
14844 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14845 manager_hints->input=MagickFalse;
14846 manager_hints->initial_state=NormalState;
14847 manager_hints->window_group=windows->image.id;
14848 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14849 &windows->pan);
14850 if (display_image->debug != MagickFalse)
14851 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14852 windows->pan.id);
14853 (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14854 if (windows->info.mapped != MagickFalse)
14855 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14856 if ((windows->image.mapped == MagickFalse) ||
14857 (windows->backdrop.id != (Window) NULL))
14858 (void) XMapWindow(display,windows->image.id);
14859 /*
14860 Set our progress monitor and warning handlers.
14861 */
14862 if (warning_handler == (WarningHandler) NULL)
14863 {
14864 warning_handler=resource_info->display_warnings ?
14865 SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14866 warning_handler=resource_info->display_warnings ?
14867 SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14868 }
14869 /*
14870 Initialize Image and Magnify X images.
14871 */
14872 windows->image.x=0;
14873 windows->image.y=0;
14874 windows->magnify.shape=MagickFalse;
14875 width=(unsigned int) display_image->columns;
14876 height=(unsigned int) display_image->rows;
14877 if ((display_image->columns != width) || (display_image->rows != height))
14878 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14879 display_image->filename);
14880 status=XMakeImage(display,resource_info,&windows->image,display_image,
14881 width,height);
14882 if (status == MagickFalse)
14883 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14884 display_image->filename);
14885 status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14886 windows->magnify.width,windows->magnify.height);
14887 if (status == MagickFalse)
14888 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14889 display_image->filename);
14890 if (windows->magnify.mapped != MagickFalse)
14891 (void) XMapRaised(display,windows->magnify.id);
14892 if (windows->pan.mapped != MagickFalse)
14893 (void) XMapRaised(display,windows->pan.id);
14894 windows->image.window_changes.width=(int) display_image->columns;
14895 windows->image.window_changes.height=(int) display_image->rows;
14896 (void) XConfigureImage(display,resource_info,windows,display_image);
14897 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14898 (void) XSync(display,MagickFalse);
14899 /*
14900 Respond to events.
14901 */
14902 delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
14903 timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
14904 update_time=0;
14905 if (resource_info->update != MagickFalse)
14906 {
14907 MagickBooleanType
14908 status;
14909
14910 /*
14911 Determine when file data was last modified.
14912 */
14913 status=GetPathAttributes(display_image->filename,&attributes);
14914 if (status != MagickFalse)
14915 update_time=attributes.st_mtime;
14916 }
14917 *state&=(~FormerImageState);
14918 *state&=(~MontageImageState);
14919 *state&=(~NextImageState);
14920 do
14921 {
14922 /*
14923 Handle a window event.
14924 */
14925 if (windows->image.mapped != MagickFalse)
14926 if ((display_image->delay != 0) || (resource_info->update != 0))
14927 {
14928 if (timer < time((time_t *) NULL))
14929 {
14930 if (resource_info->update == MagickFalse)
14931 *state|=NextImageState | ExitState;
14932 else
14933 {
14934 MagickBooleanType
14935 status;
14936
14937 /*
14938 Determine if image file was modified.
14939 */
14940 status=GetPathAttributes(display_image->filename,&attributes);
14941 if (status != MagickFalse)
14942 if (update_time != attributes.st_mtime)
14943 {
14944 /*
14945 Redisplay image.
14946 */
14947 (void) FormatMagickString(
14948 resource_info->image_info->filename,MaxTextExtent,
14949 "%s:%s",display_image->magick,
14950 display_image->filename);
14951 nexus=ReadImage(resource_info->image_info,
14952 &display_image->exception);
14953 if (nexus != (Image *) NULL)
14954 {
14955 nexus=DestroyImage(nexus);
14956 *state|=NextImageState | ExitState;
14957 }
14958 }
14959 delay=display_image->delay/MagickMax(
14960 display_image->ticks_per_second,1L);
14961 timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
14962 }
14963 }
14964 if (XEventsQueued(display,QueuedAfterFlush) == 0)
14965 {
14966 /*
14967 Do not block if delay > 0.
14968 */
14969 XDelay(display,SuspendTime << 2);
14970 continue;
14971 }
14972 }
14973 timestamp=time((time_t *) NULL);
14974 (void) XNextEvent(display,&event);
14975 if (windows->image.stasis == MagickFalse)
14976 windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
14977 MagickTrue : MagickFalse;
14978 if (windows->magnify.stasis == MagickFalse)
14979 windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
14980 MagickTrue : MagickFalse;
14981 if (event.xany.window == windows->command.id)
14982 {
14983 /*
14984 Select a command from the Command widget.
14985 */
14986 id=XCommandWidget(display,windows,CommandMenu,&event);
14987 if (id < 0)
14988 continue;
14989 (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
14990 command_type=CommandMenus[id];
14991 if (id < MagickMenus)
14992 {
14993 /*
14994 Select a command from a pop-up menu.
14995 */
14996 entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
14997 command);
14998 if (entry < 0)
14999 continue;
15000 (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15001 command_type=Commands[id][entry];
15002 }
15003 if (command_type != NullCommand)
15004 nexus=XMagickCommand(display,resource_info,windows,command_type,
15005 &display_image);
15006 continue;
15007 }
15008 switch (event.type)
15009 {
15010 case ButtonPress:
15011 {
15012 if (display_image->debug != MagickFalse)
15013 (void) LogMagickEvent(X11Event,GetMagickModule(),
15014 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15015 event.xbutton.button,event.xbutton.x,event.xbutton.y);
15016 if ((event.xbutton.button == Button3) &&
15017 (event.xbutton.state & Mod1Mask))
15018 {
15019 /*
15020 Convert Alt-Button3 to Button2.
15021 */
15022 event.xbutton.button=Button2;
15023 event.xbutton.state&=(~Mod1Mask);
15024 }
15025 if (event.xbutton.window == windows->backdrop.id)
15026 {
15027 (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15028 event.xbutton.time);
15029 break;
15030 }
15031 if (event.xbutton.window == windows->image.id)
15032 {
15033 switch (event.xbutton.button)
15034 {
15035 case Button1:
15036 {
15037 if (resource_info->immutable)
15038 {
15039 /*
15040 Select a command from the Virtual menu.
15041 */
15042 entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15043 command);
15044 if (entry >= 0)
15045 nexus=XMagickCommand(display,resource_info,windows,
15046 VirtualCommands[entry],&display_image);
15047 break;
15048 }
15049 /*
15050 Map/unmap Command widget.
15051 */
15052 if (windows->command.mapped != MagickFalse)
15053 (void) XWithdrawWindow(display,windows->command.id,
15054 windows->command.screen);
15055 else
15056 {
15057 (void) XCommandWidget(display,windows,CommandMenu,
15058 (XEvent *) NULL);
15059 (void) XMapRaised(display,windows->command.id);
15060 }
15061 break;
15062 }
15063 case Button2:
15064 {
15065 /*
15066 User pressed the image magnify button.
15067 */
15068 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15069 &display_image);
15070 XMagnifyImage(display,windows,&event);
15071 break;
15072 }
15073 case Button3:
15074 {
15075 if (resource_info->immutable)
15076 {
15077 /*
15078 Select a command from the Virtual menu.
15079 */
15080 entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15081 command);
15082 if (entry >= 0)
15083 nexus=XMagickCommand(display,resource_info,windows,
15084 VirtualCommands[entry],&display_image);
15085 break;
15086 }
15087 if (display_image->montage != (char *) NULL)
15088 {
15089 /*
15090 Open or delete a tile from a visual image directory.
15091 */
15092 nexus=XTileImage(display,resource_info,windows,
15093 display_image,&event);
15094 if (nexus != (Image *) NULL)
15095 *state|=MontageImageState | NextImageState | ExitState;
cristy49e2d862010-11-12 02:50:30 +000015096 vid_info.x=(short int) windows->image.x;
15097 vid_info.y=(short int) windows->image.y;
cristy3ed852e2009-09-05 21:47:34 +000015098 break;
15099 }
15100 /*
15101 Select a command from the Short Cuts menu.
15102 */
15103 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15104 command);
15105 if (entry >= 0)
15106 nexus=XMagickCommand(display,resource_info,windows,
15107 ShortCutsCommands[entry],&display_image);
15108 break;
15109 }
15110 case Button4:
15111 {
15112 /*
15113 Wheel up.
15114 */
15115 XTranslateImage(display,windows,*image,XK_Up);
15116 break;
15117 }
15118 case Button5:
15119 {
15120 /*
15121 Wheel down.
15122 */
15123 XTranslateImage(display,windows,*image,XK_Down);
15124 break;
15125 }
15126 default:
15127 break;
15128 }
15129 break;
15130 }
15131 if (event.xbutton.window == windows->magnify.id)
15132 {
15133 int
15134 factor;
15135
15136 static const char
15137 *MagnifyMenu[] =
15138 {
15139 "2",
15140 "4",
15141 "5",
15142 "6",
15143 "7",
15144 "8",
15145 "9",
15146 "3",
15147 (char *) NULL,
15148 };
15149
15150 static KeySym
15151 MagnifyCommands[] =
15152 {
15153 XK_2,
15154 XK_4,
15155 XK_5,
15156 XK_6,
15157 XK_7,
15158 XK_8,
15159 XK_9,
15160 XK_3
15161 };
15162
15163 /*
15164 Select a magnify factor from the pop-up menu.
15165 */
15166 factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15167 if (factor >= 0)
15168 XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15169 break;
15170 }
15171 if (event.xbutton.window == windows->pan.id)
15172 {
15173 switch (event.xbutton.button)
15174 {
15175 case Button4:
15176 {
15177 /*
15178 Wheel up.
15179 */
15180 XTranslateImage(display,windows,*image,XK_Up);
15181 break;
15182 }
15183 case Button5:
15184 {
15185 /*
15186 Wheel down.
15187 */
15188 XTranslateImage(display,windows,*image,XK_Down);
15189 break;
15190 }
15191 default:
15192 {
15193 XPanImage(display,windows,&event);
15194 break;
15195 }
15196 }
15197 break;
15198 }
15199 delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15200 1L);
15201 timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15202 break;
15203 }
15204 case ButtonRelease:
15205 {
15206 if (display_image->debug != MagickFalse)
15207 (void) LogMagickEvent(X11Event,GetMagickModule(),
15208 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15209 event.xbutton.button,event.xbutton.x,event.xbutton.y);
15210 break;
15211 }
15212 case ClientMessage:
15213 {
15214 if (display_image->debug != MagickFalse)
15215 (void) LogMagickEvent(X11Event,GetMagickModule(),
15216 "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
cristyf2faecf2010-05-28 19:19:36 +000015217 event.xclient.message_type,event.xclient.format,(unsigned long)
cristy3ed852e2009-09-05 21:47:34 +000015218 event.xclient.data.l[0]);
15219 if (event.xclient.message_type == windows->im_protocols)
15220 {
cristyecd0ab52010-05-30 14:59:20 +000015221 if (*event.xclient.data.l == (long) windows->im_update_widget)
cristy3ed852e2009-09-05 21:47:34 +000015222 {
15223 (void) CloneString(&windows->command.name,MagickTitle);
15224 windows->command.data=MagickMenus;
15225 (void) XCommandWidget(display,windows,CommandMenu,
15226 (XEvent *) NULL);
15227 break;
15228 }
cristyecd0ab52010-05-30 14:59:20 +000015229 if (*event.xclient.data.l == (long) windows->im_update_colormap)
cristy3ed852e2009-09-05 21:47:34 +000015230 {
15231 /*
15232 Update graphic context and window colormap.
15233 */
15234 for (i=0; i < (int) number_windows; i++)
15235 {
15236 if (magick_windows[i]->id == windows->icon.id)
15237 continue;
15238 context_values.background=pixel->background_color.pixel;
15239 context_values.foreground=pixel->foreground_color.pixel;
15240 (void) XChangeGC(display,magick_windows[i]->annotate_context,
15241 context_mask,&context_values);
15242 (void) XChangeGC(display,magick_windows[i]->widget_context,
15243 context_mask,&context_values);
15244 context_values.background=pixel->foreground_color.pixel;
15245 context_values.foreground=pixel->background_color.pixel;
15246 context_values.plane_mask=context_values.background ^
15247 context_values.foreground;
15248 (void) XChangeGC(display,magick_windows[i]->highlight_context,
cristybb503372010-05-27 20:51:26 +000015249 (size_t) (context_mask | GCPlaneMask),
cristy3ed852e2009-09-05 21:47:34 +000015250 &context_values);
15251 magick_windows[i]->attributes.background_pixel=
15252 pixel->background_color.pixel;
15253 magick_windows[i]->attributes.border_pixel=
15254 pixel->border_color.pixel;
15255 magick_windows[i]->attributes.colormap=map_info->colormap;
15256 (void) XChangeWindowAttributes(display,magick_windows[i]->id,
cristy49e2d862010-11-12 02:50:30 +000015257 (unsigned long) magick_windows[i]->mask,
15258 &magick_windows[i]->attributes);
cristy3ed852e2009-09-05 21:47:34 +000015259 }
15260 if (windows->pan.mapped != MagickFalse)
15261 {
15262 (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15263 windows->pan.pixmap);
15264 (void) XClearWindow(display,windows->pan.id);
15265 XDrawPanRectangle(display,windows);
15266 }
15267 if (windows->backdrop.id != (Window) NULL)
15268 (void) XInstallColormap(display,map_info->colormap);
15269 break;
15270 }
cristyecd0ab52010-05-30 14:59:20 +000015271 if (*event.xclient.data.l == (long) windows->im_former_image)
cristy3ed852e2009-09-05 21:47:34 +000015272 {
15273 *state|=FormerImageState | ExitState;
15274 break;
15275 }
cristyecd0ab52010-05-30 14:59:20 +000015276 if (*event.xclient.data.l == (long) windows->im_next_image)
cristy3ed852e2009-09-05 21:47:34 +000015277 {
15278 *state|=NextImageState | ExitState;
15279 break;
15280 }
cristyecd0ab52010-05-30 14:59:20 +000015281 if (*event.xclient.data.l == (long) windows->im_retain_colors)
cristy3ed852e2009-09-05 21:47:34 +000015282 {
15283 *state|=RetainColorsState;
15284 break;
15285 }
cristyecd0ab52010-05-30 14:59:20 +000015286 if (*event.xclient.data.l == (long) windows->im_exit)
cristy3ed852e2009-09-05 21:47:34 +000015287 {
15288 *state|=ExitState;
15289 break;
15290 }
15291 break;
15292 }
15293 if (event.xclient.message_type == windows->dnd_protocols)
15294 {
15295 Atom
15296 selection,
15297 type;
15298
15299 int
15300 format,
15301 status;
15302
15303 unsigned char
15304 *data;
15305
cristyf2faecf2010-05-28 19:19:36 +000015306 unsigned long
cristy3ed852e2009-09-05 21:47:34 +000015307 after,
15308 length;
15309
15310 /*
15311 Display image named by the Drag-and-Drop selection.
15312 */
15313 if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15314 break;
15315 selection=XInternAtom(display,"DndSelection",MagickFalse);
cristyecd0ab52010-05-30 14:59:20 +000015316 status=XGetWindowProperty(display,root_window,selection,0L,(long)
cristy3ed852e2009-09-05 21:47:34 +000015317 MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15318 &length,&after,&data);
15319 if ((status != Success) || (length == 0))
15320 break;
15321 if (*event.xclient.data.l == 2)
15322 {
15323 /*
15324 Offix DND.
15325 */
15326 (void) CopyMagickString(resource_info->image_info->filename,
15327 (char *) data,MaxTextExtent);
15328 }
15329 else
15330 {
15331 /*
15332 XDND.
15333 */
15334 if (strncmp((char *) data, "file:", 5) != 0)
15335 {
15336 (void) XFree((void *) data);
15337 break;
15338 }
15339 (void) CopyMagickString(resource_info->image_info->filename,
15340 ((char *) data)+5,MaxTextExtent);
15341 }
15342 nexus=ReadImage(resource_info->image_info,
15343 &display_image->exception);
15344 CatchException(&display_image->exception);
15345 if (nexus != (Image *) NULL)
15346 *state|=NextImageState | ExitState;
15347 (void) XFree((void *) data);
15348 break;
15349 }
15350 /*
15351 If client window delete message, exit.
15352 */
15353 if (event.xclient.message_type != windows->wm_protocols)
15354 break;
cristyecd0ab52010-05-30 14:59:20 +000015355 if (*event.xclient.data.l != (long) windows->wm_delete_window)
cristy3ed852e2009-09-05 21:47:34 +000015356 break;
15357 (void) XWithdrawWindow(display,event.xclient.window,
15358 visual_info->screen);
15359 if (event.xclient.window == windows->image.id)
15360 {
15361 *state|=ExitState;
15362 break;
15363 }
15364 if (event.xclient.window == windows->pan.id)
15365 {
15366 /*
15367 Restore original image size when pan window is deleted.
15368 */
15369 windows->image.window_changes.width=windows->image.ximage->width;
15370 windows->image.window_changes.height=windows->image.ximage->height;
15371 (void) XConfigureImage(display,resource_info,windows,
15372 display_image);
15373 }
15374 break;
15375 }
15376 case ConfigureNotify:
15377 {
15378 if (display_image->debug != MagickFalse)
15379 (void) LogMagickEvent(X11Event,GetMagickModule(),
15380 "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15381 event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15382 event.xconfigure.y,event.xconfigure.send_event);
15383 if (event.xconfigure.window == windows->image.id)
15384 {
15385 /*
15386 Image window has a new configuration.
15387 */
15388 if (event.xconfigure.send_event != 0)
15389 {
15390 XWindowChanges
15391 window_changes;
15392
15393 /*
15394 Position the transient windows relative of the Image window.
15395 */
15396 if (windows->command.geometry == (char *) NULL)
15397 if (windows->command.mapped == MagickFalse)
15398 {
15399 windows->command.x=event.xconfigure.x-
15400 windows->command.width-25;
15401 windows->command.y=event.xconfigure.y;
15402 XConstrainWindowPosition(display,&windows->command);
15403 window_changes.x=windows->command.x;
15404 window_changes.y=windows->command.y;
15405 (void) XReconfigureWMWindow(display,windows->command.id,
15406 windows->command.screen,(unsigned int) (CWX | CWY),
15407 &window_changes);
15408 }
15409 if (windows->widget.geometry == (char *) NULL)
15410 if (windows->widget.mapped == MagickFalse)
15411 {
15412 windows->widget.x=event.xconfigure.x+
15413 event.xconfigure.width/10;
15414 windows->widget.y=event.xconfigure.y+
15415 event.xconfigure.height/10;
15416 XConstrainWindowPosition(display,&windows->widget);
15417 window_changes.x=windows->widget.x;
15418 window_changes.y=windows->widget.y;
15419 (void) XReconfigureWMWindow(display,windows->widget.id,
15420 windows->widget.screen,(unsigned int) (CWX | CWY),
15421 &window_changes);
15422 }
15423 if (windows->magnify.geometry == (char *) NULL)
15424 if (windows->magnify.mapped == MagickFalse)
15425 {
15426 windows->magnify.x=event.xconfigure.x+
15427 event.xconfigure.width+25;
15428 windows->magnify.y=event.xconfigure.y;
15429 XConstrainWindowPosition(display,&windows->magnify);
15430 window_changes.x=windows->magnify.x;
15431 window_changes.y=windows->magnify.y;
15432 (void) XReconfigureWMWindow(display,windows->magnify.id,
15433 windows->magnify.screen,(unsigned int) (CWX | CWY),
15434 &window_changes);
15435 }
15436 if (windows->pan.geometry == (char *) NULL)
15437 if (windows->pan.mapped == MagickFalse)
15438 {
15439 windows->pan.x=event.xconfigure.x+
15440 event.xconfigure.width+25;
15441 windows->pan.y=event.xconfigure.y+
15442 windows->magnify.height+50;
15443 XConstrainWindowPosition(display,&windows->pan);
15444 window_changes.x=windows->pan.x;
15445 window_changes.y=windows->pan.y;
15446 (void) XReconfigureWMWindow(display,windows->pan.id,
15447 windows->pan.screen,(unsigned int) (CWX | CWY),
15448 &window_changes);
15449 }
15450 }
cristyecd0ab52010-05-30 14:59:20 +000015451 if ((event.xconfigure.width == (int) windows->image.width) &&
15452 (event.xconfigure.height == (int) windows->image.height))
cristy3ed852e2009-09-05 21:47:34 +000015453 break;
15454 windows->image.width=(unsigned int) event.xconfigure.width;
15455 windows->image.height=(unsigned int) event.xconfigure.height;
15456 windows->image.x=0;
15457 windows->image.y=0;
15458 if (display_image->montage != (char *) NULL)
15459 {
15460 windows->image.x=vid_info.x;
15461 windows->image.y=vid_info.y;
15462 }
cristy34b9f452010-01-06 20:04:29 +000015463 if ((windows->image.mapped != MagickFalse) &&
15464 (windows->image.stasis != MagickFalse))
15465 {
15466 /*
15467 Update image window configuration.
15468 */
15469 windows->image.window_changes.width=event.xconfigure.width;
15470 windows->image.window_changes.height=event.xconfigure.height;
15471 (void) XConfigureImage(display,resource_info,windows,
15472 display_image);
15473 }
cristy3ed852e2009-09-05 21:47:34 +000015474 /*
15475 Update pan window configuration.
15476 */
15477 if ((event.xconfigure.width < windows->image.ximage->width) ||
15478 (event.xconfigure.height < windows->image.ximage->height))
15479 {
15480 (void) XMapRaised(display,windows->pan.id);
15481 XDrawPanRectangle(display,windows);
15482 }
15483 else
15484 if (windows->pan.mapped != MagickFalse)
15485 (void) XWithdrawWindow(display,windows->pan.id,
15486 windows->pan.screen);
15487 break;
15488 }
15489 if (event.xconfigure.window == windows->magnify.id)
15490 {
15491 unsigned int
15492 magnify;
15493
15494 /*
15495 Magnify window has a new configuration.
15496 */
15497 windows->magnify.width=(unsigned int) event.xconfigure.width;
15498 windows->magnify.height=(unsigned int) event.xconfigure.height;
15499 if (windows->magnify.mapped == MagickFalse)
15500 break;
15501 magnify=1;
15502 while ((int) magnify <= event.xconfigure.width)
15503 magnify<<=1;
15504 while ((int) magnify <= event.xconfigure.height)
15505 magnify<<=1;
15506 magnify>>=1;
15507 if (((int) magnify != event.xconfigure.width) ||
15508 ((int) magnify != event.xconfigure.height))
15509 {
15510 window_changes.width=(int) magnify;
15511 window_changes.height=(int) magnify;
15512 (void) XReconfigureWMWindow(display,windows->magnify.id,
15513 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15514 &window_changes);
15515 break;
15516 }
15517 if ((windows->magnify.mapped != MagickFalse) &&
15518 (windows->magnify.stasis != MagickFalse))
15519 {
15520 status=XMakeImage(display,resource_info,&windows->magnify,
15521 display_image,windows->magnify.width,windows->magnify.height);
15522 XMakeMagnifyImage(display,windows);
15523 }
15524 break;
15525 }
15526 if ((windows->magnify.mapped != MagickFalse) &&
15527 (event.xconfigure.window == windows->pan.id))
15528 {
15529 /*
15530 Pan icon window has a new configuration.
15531 */
15532 if (event.xconfigure.send_event != 0)
15533 {
15534 windows->pan.x=event.xconfigure.x;
15535 windows->pan.y=event.xconfigure.y;
15536 }
15537 windows->pan.width=(unsigned int) event.xconfigure.width;
15538 windows->pan.height=(unsigned int) event.xconfigure.height;
15539 break;
15540 }
15541 if (event.xconfigure.window == windows->icon.id)
15542 {
15543 /*
15544 Icon window has a new configuration.
15545 */
15546 windows->icon.width=(unsigned int) event.xconfigure.width;
15547 windows->icon.height=(unsigned int) event.xconfigure.height;
15548 break;
15549 }
15550 break;
15551 }
15552 case DestroyNotify:
15553 {
15554 /*
15555 Group leader has exited.
15556 */
15557 if (display_image->debug != MagickFalse)
15558 (void) LogMagickEvent(X11Event,GetMagickModule(),
15559 "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15560 if (event.xdestroywindow.window == windows->group_leader.id)
15561 {
15562 *state|=ExitState;
15563 break;
15564 }
15565 break;
15566 }
15567 case EnterNotify:
15568 {
15569 /*
15570 Selectively install colormap.
15571 */
15572 if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15573 if (event.xcrossing.mode != NotifyUngrab)
15574 XInstallColormap(display,map_info->colormap);
15575 break;
15576 }
15577 case Expose:
15578 {
15579 if (display_image->debug != MagickFalse)
15580 (void) LogMagickEvent(X11Event,GetMagickModule(),
15581 "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15582 event.xexpose.width,event.xexpose.height,event.xexpose.x,
15583 event.xexpose.y);
15584 /*
15585 Refresh windows that are now exposed.
15586 */
cristy6bee4042010-01-30 15:27:14 +000015587 if ((event.xexpose.window == windows->image.id) &&
15588 (windows->image.mapped != MagickFalse))
15589 {
15590 XRefreshWindow(display,&windows->image,&event);
15591 delay=display_image->delay/MagickMax(
15592 display_image->ticks_per_second,1L);
15593 timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15594 break;
15595 }
15596 if ((event.xexpose.window == windows->magnify.id) &&
15597 (windows->magnify.mapped != MagickFalse))
15598 {
15599 XMakeMagnifyImage(display,windows);
15600 break;
15601 }
cristy3ed852e2009-09-05 21:47:34 +000015602 if (event.xexpose.window == windows->pan.id)
cristy6bee4042010-01-30 15:27:14 +000015603 {
15604 XDrawPanRectangle(display,windows);
15605 break;
15606 }
cristy3ed852e2009-09-05 21:47:34 +000015607 if (event.xexpose.window == windows->icon.id)
cristy6bee4042010-01-30 15:27:14 +000015608 {
15609 XRefreshWindow(display,&windows->icon,&event);
15610 break;
15611 }
cristy3ed852e2009-09-05 21:47:34 +000015612 break;
15613 }
15614 case KeyPress:
15615 {
15616 int
15617 length;
15618
15619 /*
15620 Respond to a user key press.
15621 */
15622 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15623 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15624 *(command+length)='\0';
15625 if (display_image->debug != MagickFalse)
15626 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000015627 "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
cristy3ed852e2009-09-05 21:47:34 +000015628 key_symbol,command);
15629 if (event.xkey.window == windows->image.id)
15630 {
15631 command_type=XImageWindowCommand(display,resource_info,windows,
15632 event.xkey.state,key_symbol,&display_image);
15633 if (command_type != NullCommand)
15634 nexus=XMagickCommand(display,resource_info,windows,command_type,
15635 &display_image);
15636 }
15637 if (event.xkey.window == windows->magnify.id)
15638 XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15639 if (event.xkey.window == windows->pan.id)
15640 {
15641 if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15642 (void) XWithdrawWindow(display,windows->pan.id,
15643 windows->pan.screen);
15644 else
15645 if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15646 XTextViewWidget(display,resource_info,windows,MagickFalse,
15647 "Help Viewer - Image Pan",ImagePanHelp);
15648 else
15649 XTranslateImage(display,windows,*image,key_symbol);
15650 }
15651 delay=display_image->delay/MagickMax(
15652 display_image->ticks_per_second,1L);
15653 timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15654 break;
15655 }
15656 case KeyRelease:
15657 {
15658 /*
15659 Respond to a user key release.
15660 */
15661 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15662 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15663 if (display_image->debug != MagickFalse)
15664 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000015665 "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
cristy3ed852e2009-09-05 21:47:34 +000015666 break;
15667 }
15668 case LeaveNotify:
15669 {
15670 /*
15671 Selectively uninstall colormap.
15672 */
15673 if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15674 if (event.xcrossing.mode != NotifyUngrab)
15675 XUninstallColormap(display,map_info->colormap);
15676 break;
15677 }
15678 case MapNotify:
15679 {
15680 if (display_image->debug != MagickFalse)
15681 (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15682 event.xmap.window);
15683 if (event.xmap.window == windows->backdrop.id)
15684 {
15685 (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15686 CurrentTime);
15687 windows->backdrop.mapped=MagickTrue;
15688 break;
15689 }
15690 if (event.xmap.window == windows->image.id)
15691 {
15692 if (windows->backdrop.id != (Window) NULL)
15693 (void) XInstallColormap(display,map_info->colormap);
15694 if (LocaleCompare(display_image->magick,"LOGO") == 0)
15695 {
15696 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15697 nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15698 }
15699 if (((int) windows->image.width < windows->image.ximage->width) ||
15700 ((int) windows->image.height < windows->image.ximage->height))
15701 (void) XMapRaised(display,windows->pan.id);
15702 windows->image.mapped=MagickTrue;
15703 break;
15704 }
15705 if (event.xmap.window == windows->magnify.id)
15706 {
15707 XMakeMagnifyImage(display,windows);
15708 windows->magnify.mapped=MagickTrue;
15709 (void) XWithdrawWindow(display,windows->info.id,
15710 windows->info.screen);
15711 break;
15712 }
15713 if (event.xmap.window == windows->pan.id)
15714 {
15715 XMakePanImage(display,resource_info,windows,display_image);
15716 windows->pan.mapped=MagickTrue;
15717 break;
15718 }
15719 if (event.xmap.window == windows->info.id)
15720 {
15721 windows->info.mapped=MagickTrue;
15722 break;
15723 }
15724 if (event.xmap.window == windows->icon.id)
15725 {
15726 MagickBooleanType
15727 taint;
15728
15729 /*
15730 Create an icon image.
15731 */
15732 taint=display_image->taint;
15733 XMakeStandardColormap(display,icon_visual,icon_resources,
15734 display_image,icon_map,icon_pixel);
15735 (void) XMakeImage(display,icon_resources,&windows->icon,
15736 display_image,windows->icon.width,windows->icon.height);
15737 display_image->taint=taint;
15738 (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15739 windows->icon.pixmap);
15740 (void) XClearWindow(display,windows->icon.id);
15741 (void) XWithdrawWindow(display,windows->info.id,
15742 windows->info.screen);
15743 windows->icon.mapped=MagickTrue;
15744 break;
15745 }
15746 if (event.xmap.window == windows->command.id)
15747 {
15748 windows->command.mapped=MagickTrue;
15749 break;
15750 }
15751 if (event.xmap.window == windows->popup.id)
15752 {
15753 windows->popup.mapped=MagickTrue;
15754 break;
15755 }
15756 if (event.xmap.window == windows->widget.id)
15757 {
15758 windows->widget.mapped=MagickTrue;
15759 break;
15760 }
15761 break;
15762 }
15763 case MappingNotify:
15764 {
15765 (void) XRefreshKeyboardMapping(&event.xmapping);
15766 break;
15767 }
15768 case NoExpose:
15769 break;
15770 case PropertyNotify:
15771 {
15772 Atom
15773 type;
15774
15775 int
15776 format,
15777 status;
15778
15779 unsigned char
15780 *data;
15781
cristyf2faecf2010-05-28 19:19:36 +000015782 unsigned long
cristy3ed852e2009-09-05 21:47:34 +000015783 after,
15784 length;
15785
15786 if (display_image->debug != MagickFalse)
15787 (void) LogMagickEvent(X11Event,GetMagickModule(),
15788 "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15789 event.xproperty.atom,event.xproperty.state);
15790 if (event.xproperty.atom != windows->im_remote_command)
15791 break;
15792 /*
15793 Display image named by the remote command protocol.
15794 */
15795 status=XGetWindowProperty(display,event.xproperty.window,
cristyecd0ab52010-05-30 14:59:20 +000015796 event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
cristy3ed852e2009-09-05 21:47:34 +000015797 AnyPropertyType,&type,&format,&length,&after,&data);
15798 if ((status != Success) || (length == 0))
15799 break;
15800 if (LocaleCompare((char *) data,"-quit") == 0)
15801 {
15802 XClientMessage(display,windows->image.id,windows->im_protocols,
15803 windows->im_exit,CurrentTime);
15804 (void) XFree((void *) data);
15805 break;
15806 }
15807 (void) CopyMagickString(resource_info->image_info->filename,
15808 (char *) data,MaxTextExtent);
15809 (void) XFree((void *) data);
15810 nexus=ReadImage(resource_info->image_info,&display_image->exception);
15811 CatchException(&display_image->exception);
15812 if (nexus != (Image *) NULL)
15813 *state|=NextImageState | ExitState;
15814 break;
15815 }
15816 case ReparentNotify:
15817 {
15818 if (display_image->debug != MagickFalse)
15819 (void) LogMagickEvent(X11Event,GetMagickModule(),
15820 "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15821 event.xreparent.window);
15822 break;
15823 }
15824 case UnmapNotify:
15825 {
15826 if (display_image->debug != MagickFalse)
15827 (void) LogMagickEvent(X11Event,GetMagickModule(),
15828 "Unmap Notify: 0x%lx",event.xunmap.window);
15829 if (event.xunmap.window == windows->backdrop.id)
15830 {
15831 windows->backdrop.mapped=MagickFalse;
15832 break;
15833 }
15834 if (event.xunmap.window == windows->image.id)
15835 {
15836 windows->image.mapped=MagickFalse;
15837 break;
15838 }
15839 if (event.xunmap.window == windows->magnify.id)
15840 {
15841 windows->magnify.mapped=MagickFalse;
15842 break;
15843 }
15844 if (event.xunmap.window == windows->pan.id)
15845 {
15846 windows->pan.mapped=MagickFalse;
15847 break;
15848 }
15849 if (event.xunmap.window == windows->info.id)
15850 {
15851 windows->info.mapped=MagickFalse;
15852 break;
15853 }
15854 if (event.xunmap.window == windows->icon.id)
15855 {
15856 if (map_info->colormap == icon_map->colormap)
15857 XConfigureImageColormap(display,resource_info,windows,
15858 display_image);
15859 (void) XFreeStandardColormap(display,icon_visual,icon_map,
15860 icon_pixel);
15861 windows->icon.mapped=MagickFalse;
15862 break;
15863 }
15864 if (event.xunmap.window == windows->command.id)
15865 {
15866 windows->command.mapped=MagickFalse;
15867 break;
15868 }
15869 if (event.xunmap.window == windows->popup.id)
15870 {
15871 if (windows->backdrop.id != (Window) NULL)
15872 (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15873 CurrentTime);
15874 windows->popup.mapped=MagickFalse;
15875 break;
15876 }
15877 if (event.xunmap.window == windows->widget.id)
15878 {
15879 if (windows->backdrop.id != (Window) NULL)
15880 (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15881 CurrentTime);
15882 windows->widget.mapped=MagickFalse;
15883 break;
15884 }
15885 break;
15886 }
15887 default:
15888 {
15889 if (display_image->debug != MagickFalse)
15890 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
15891 event.type);
15892 break;
15893 }
15894 }
15895 } while (!(*state & ExitState));
15896 if ((*state & ExitState) == 0)
15897 (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
15898 &display_image);
15899 else
15900 if (resource_info->confirm_edit != MagickFalse)
15901 {
15902 /*
15903 Query user if image has changed.
15904 */
15905 if ((resource_info->immutable == MagickFalse) &&
15906 (display_image->taint != MagickFalse))
15907 {
15908 int
15909 status;
15910
15911 status=XConfirmWidget(display,windows,"Your image changed.",
15912 "Do you want to save it");
15913 if (status == 0)
15914 *state&=(~ExitState);
15915 else
15916 if (status > 0)
15917 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
15918 &display_image);
15919 }
15920 }
15921 if ((windows->visual_info->klass == GrayScale) ||
15922 (windows->visual_info->klass == PseudoColor) ||
15923 (windows->visual_info->klass == DirectColor))
15924 {
15925 /*
15926 Withdraw pan and Magnify window.
15927 */
15928 if (windows->info.mapped != MagickFalse)
15929 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15930 if (windows->magnify.mapped != MagickFalse)
15931 (void) XWithdrawWindow(display,windows->magnify.id,
15932 windows->magnify.screen);
15933 if (windows->command.mapped != MagickFalse)
15934 (void) XWithdrawWindow(display,windows->command.id,
15935 windows->command.screen);
15936 }
15937 if (windows->pan.mapped != MagickFalse)
15938 (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
15939 if (resource_info->backdrop == MagickFalse)
15940 if (windows->backdrop.mapped)
15941 {
15942 (void) XWithdrawWindow(display,windows->backdrop.id,
15943 windows->backdrop.screen);
15944 (void) XDestroyWindow(display,windows->backdrop.id);
15945 windows->backdrop.id=(Window) NULL;
15946 (void) XWithdrawWindow(display,windows->image.id,
15947 windows->image.screen);
15948 (void) XDestroyWindow(display,windows->image.id);
15949 windows->image.id=(Window) NULL;
15950 }
15951 XSetCursorState(display,windows,MagickTrue);
15952 XCheckRefreshWindows(display,windows);
15953 if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
15954 *state&=(~ExitState);
15955 if (*state & ExitState)
15956 {
15957 /*
15958 Free Standard Colormap.
15959 */
15960 (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
15961 if (resource_info->map_type == (char *) NULL)
15962 (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
15963 /*
15964 Free X resources.
15965 */
15966 if (resource_info->copy_image != (Image *) NULL)
15967 {
15968 resource_info->copy_image=DestroyImage(resource_info->copy_image);
15969 resource_info->copy_image=NewImageList();
15970 }
15971 DestroyXResources();
15972 }
15973 (void) XSync(display,MagickFalse);
15974 /*
15975 Restore our progress monitor and warning handlers.
15976 */
15977 (void) SetErrorHandler(warning_handler);
15978 (void) SetWarningHandler(warning_handler);
15979 /*
15980 Change to home directory.
15981 */
cristy00976d82011-02-20 20:31:28 +000015982 directory=getcwd(working_directory,MaxTextExtent);
15983 (void) directory;
cristy3ed852e2009-09-05 21:47:34 +000015984 {
15985 int
15986 status;
15987
15988 status=chdir(resource_info->home_directory);
15989 if (status == -1)
15990 (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
15991 FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
15992 }
15993 *image=display_image;
15994 return(nexus);
15995}
15996#else
15997
15998/*
15999%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16000% %
16001% %
16002% %
16003+ D i s p l a y I m a g e s %
16004% %
16005% %
16006% %
16007%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16008%
16009% DisplayImages() displays an image sequence to any X window screen. It
16010% returns a value other than 0 if successful. Check the exception member
16011% of image to determine the reason for any failure.
16012%
16013% The format of the DisplayImages method is:
16014%
16015% MagickBooleanType DisplayImages(const ImageInfo *image_info,
16016% Image *images)
16017%
16018% A description of each parameter follows:
16019%
16020% o image_info: the image info.
16021%
16022% o image: the image.
16023%
16024*/
16025MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16026 Image *image)
16027{
16028 assert(image_info != (const ImageInfo *) NULL);
16029 assert(image_info->signature == MagickSignature);
16030 assert(image != (Image *) NULL);
16031 assert(image->signature == MagickSignature);
16032 if (image->debug != MagickFalse)
16033 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16034 (void) ThrowMagickException(&image->exception,GetMagickModule(),
16035 MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
16036 image->filename);
16037 return(MagickFalse);
16038}
16039
16040/*
16041%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16042% %
16043% %
16044% %
16045+ R e m o t e D i s p l a y C o m m a n d %
16046% %
16047% %
16048% %
16049%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16050%
16051% RemoteDisplayCommand() encourages a remote display program to display the
16052% specified image filename.
16053%
16054% The format of the RemoteDisplayCommand method is:
16055%
16056% MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16057% const char *window,const char *filename,ExceptionInfo *exception)
16058%
16059% A description of each parameter follows:
16060%
16061% o image_info: the image info.
16062%
16063% o window: Specifies the name or id of an X window.
16064%
16065% o filename: the name of the image filename to display.
16066%
16067% o exception: return any errors or warnings in this structure.
16068%
16069*/
16070MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16071 const char *window,const char *filename,ExceptionInfo *exception)
16072{
16073 assert(image_info != (const ImageInfo *) NULL);
16074 assert(image_info->signature == MagickSignature);
16075 assert(filename != (char *) NULL);
16076 (void) window;
16077 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16078 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16079 "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16080 return(MagickFalse);
16081}
16082#endif