blob: 84c09bbc9c2c384df0282d8c94f03ff267211b92 [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*/
cristy4c08aed2011-07-01 19:47:50 +000042#include "MagickCore/studio.h"
43#include "MagickCore/artifact.h"
44#include "MagickCore/blob.h"
45#include "MagickCore/cache.h"
46#include "MagickCore/client.h"
47#include "MagickCore/color.h"
48#include "MagickCore/colorspace.h"
49#include "MagickCore/composite.h"
50#include "MagickCore/constitute.h"
51#include "MagickCore/decorate.h"
52#include "MagickCore/delegate.h"
53#include "MagickCore/display.h"
54#include "MagickCore/display-private.h"
55#include "MagickCore/draw.h"
56#include "MagickCore/effect.h"
57#include "MagickCore/enhance.h"
58#include "MagickCore/exception.h"
59#include "MagickCore/exception-private.h"
60#include "MagickCore/fx.h"
61#include "MagickCore/geometry.h"
62#include "MagickCore/image.h"
63#include "MagickCore/image-private.h"
64#include "MagickCore/list.h"
65#include "MagickCore/log.h"
66#include "MagickCore/magick.h"
67#include "MagickCore/memory_.h"
68#include "MagickCore/monitor.h"
69#include "MagickCore/monitor-private.h"
70#include "MagickCore/montage.h"
71#include "MagickCore/option.h"
72#include "MagickCore/paint.h"
73#include "MagickCore/pixel.h"
74#include "MagickCore/pixel-accessor.h"
75#include "MagickCore/PreRvIcccm.h"
76#include "MagickCore/property.h"
77#include "MagickCore/quantum.h"
78#include "MagickCore/quantum-private.h"
79#include "MagickCore/resize.h"
80#include "MagickCore/resource_.h"
81#include "MagickCore/shear.h"
82#include "MagickCore/segment.h"
83#include "MagickCore/string_.h"
84#include "MagickCore/string-private.h"
85#include "MagickCore/transform.h"
86#include "MagickCore/threshold.h"
87#include "MagickCore/utility.h"
cristyd1dd6e42011-09-04 01:46:08 +000088#include "MagickCore/utility-private.h"
cristy4c08aed2011-07-01 19:47:50 +000089#include "MagickCore/version.h"
90#include "MagickCore/widget.h"
cristybcbda3f2011-09-03 13:01:22 +000091#include "MagickCore/widget-private.h"
92#include "MagickCore/xwindow.h"
cristy4c08aed2011-07-01 19:47:50 +000093#include "MagickCore/xwindow-private.h"
cristy3ed852e2009-09-05 21:47:34 +000094
95#if defined(MAGICKCORE_X11_DELEGATE)
96/*
97 Define declarations.
98*/
cristy49e2d862010-11-12 02:50:30 +000099#define MaxColors MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
cristy3ed852e2009-09-05 21:47:34 +0000100
101/*
102 Constant declarations.
103*/
104static const unsigned char
105 HighlightBitmap[8] =
106 {
107 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
108 },
cristydd05beb2010-11-21 21:23:39 +0000109 OpaqueBitmap[8] =
110 {
111 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
112 },
cristy3ed852e2009-09-05 21:47:34 +0000113 ShadowBitmap[8] =
114 {
115 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
116 };
117
118static const char
119 *PageSizes[] =
120 {
121 "Letter",
122 "Tabloid",
123 "Ledger",
124 "Legal",
125 "Statement",
126 "Executive",
127 "A3",
128 "A4",
129 "A5",
130 "B4",
131 "B5",
132 "Folio",
133 "Quarto",
134 "10x14",
135 (char *) NULL
136 };
137
138/*
139 Help widget declarations.
140*/
141static const char
142 *ImageAnnotateHelp[] =
143 {
144 "In annotate mode, the Command widget has these options:",
145 "",
146 " Font Name",
147 " fixed",
148 " variable",
149 " 5x8",
150 " 6x10",
151 " 7x13bold",
152 " 8x13bold",
153 " 9x15bold",
154 " 10x20",
155 " 12x24",
156 " Browser...",
157 " Font Color",
158 " black",
159 " blue",
160 " cyan",
161 " green",
162 " gray",
163 " red",
164 " magenta",
165 " yellow",
166 " white",
167 " transparent",
168 " Browser...",
169 " Font Color",
170 " black",
171 " blue",
172 " cyan",
173 " green",
174 " gray",
175 " red",
176 " magenta",
177 " yellow",
178 " white",
179 " transparent",
180 " Browser...",
181 " Rotate Text",
182 " -90",
183 " -45",
184 " -30",
185 " 0",
186 " 30",
187 " 45",
188 " 90",
189 " 180",
190 " Dialog...",
191 " Help",
192 " Dismiss",
193 "",
194 "Choose a font name from the Font Name sub-menu. Additional",
195 "font names can be specified with the font browser. You can",
196 "change the menu names by setting the X resources font1",
197 "through font9.",
198 "",
199 "Choose a font color from the Font Color sub-menu.",
200 "Additional font colors can be specified with the color",
201 "browser. You can change the menu colors by setting the X",
202 "resources pen1 through pen9.",
203 "",
204 "If you select the color browser and press Grab, you can",
205 "choose the font color by moving the pointer to the desired",
206 "color on the screen and press any button.",
207 "",
208 "If you choose to rotate the text, choose Rotate Text from the",
209 "menu and select an angle. Typically you will only want to",
210 "rotate one line of text at a time. Depending on the angle you",
211 "choose, subsequent lines may end up overwriting each other.",
212 "",
213 "Choosing a font and its color is optional. The default font",
214 "is fixed and the default color is black. However, you must",
215 "choose a location to begin entering text and press button 1.",
216 "An underscore character will appear at the location of the",
217 "pointer. The cursor changes to a pencil to indicate you are",
218 "in text mode. To exit immediately, press Dismiss.",
219 "",
220 "In text mode, any key presses will display the character at",
221 "the location of the underscore and advance the underscore",
222 "cursor. Enter your text and once completed press Apply to",
223 "finish your image annotation. To correct errors press BACK",
224 "SPACE. To delete an entire line of text, press DELETE. Any",
225 "text that exceeds the boundaries of the image window is",
226 "automagically continued onto the next line.",
227 "",
228 "The actual color you request for the font is saved in the",
229 "image. However, the color that appears in your image window",
230 "may be different. For example, on a monochrome screen the",
231 "text will appear black or white even if you choose the color",
232 "red as the font color. However, the image saved to a file",
233 "with -write is written with red lettering. To assure the",
234 "correct color text in the final image, any PseudoClass image",
235 "is promoted to DirectClass (see miff(5)). To force a",
236 "PseudoClass image to remain PseudoClass, use -colors.",
237 (char *) NULL,
238 },
239 *ImageChopHelp[] =
240 {
241 "In chop mode, the Command widget has these options:",
242 "",
243 " Direction",
244 " horizontal",
245 " vertical",
246 " Help",
247 " Dismiss",
248 "",
249 "If the you choose the horizontal direction (this the",
250 "default), the area of the image between the two horizontal",
251 "endpoints of the chop line is removed. Otherwise, the area",
252 "of the image between the two vertical endpoints of the chop",
253 "line is removed.",
254 "",
255 "Select a location within the image window to begin your chop,",
256 "press and hold any button. Next, move the pointer to",
257 "another location in the image. As you move a line will",
258 "connect the initial location and the pointer. When you",
259 "release the button, the area within the image to chop is",
260 "determined by which direction you choose from the Command",
261 "widget.",
262 "",
263 "To cancel the image chopping, move the pointer back to the",
264 "starting point of the line and release the button.",
265 (char *) NULL,
266 },
267 *ImageColorEditHelp[] =
268 {
269 "In color edit mode, the Command widget has these options:",
270 "",
271 " Method",
272 " point",
273 " replace",
274 " floodfill",
275 " filltoborder",
276 " reset",
277 " Pixel Color",
278 " black",
279 " blue",
280 " cyan",
281 " green",
282 " gray",
283 " red",
284 " magenta",
285 " yellow",
286 " white",
287 " Browser...",
288 " Border Color",
289 " black",
290 " blue",
291 " cyan",
292 " green",
293 " gray",
294 " red",
295 " magenta",
296 " yellow",
297 " white",
298 " Browser...",
299 " Fuzz",
300 " 0%",
301 " 2%",
302 " 5%",
303 " 10%",
304 " 15%",
305 " Dialog...",
306 " Undo",
307 " Help",
308 " Dismiss",
309 "",
310 "Choose a color editing method from the Method sub-menu",
311 "of the Command widget. The point method recolors any pixel",
312 "selected with the pointer until the button is released. The",
313 "replace method recolors any pixel that matches the color of",
314 "the pixel you select with a button press. Floodfill recolors",
315 "any pixel that matches the color of the pixel you select with",
316 "a button press and is a neighbor. Whereas filltoborder recolors",
317 "any neighbor pixel that is not the border color. Finally reset",
318 "changes the entire image to the designated color.",
319 "",
320 "Next, choose a pixel color from the Pixel Color sub-menu.",
321 "Additional pixel colors can be specified with the color",
322 "browser. You can change the menu colors by setting the X",
323 "resources pen1 through pen9.",
324 "",
325 "Now press button 1 to select a pixel within the image window",
326 "to change its color. Additional pixels may be recolored as",
327 "prescribed by the method you choose.",
328 "",
329 "If the Magnify widget is mapped, it can be helpful in positioning",
330 "your pointer within the image (refer to button 2).",
331 "",
332 "The actual color you request for the pixels is saved in the",
333 "image. However, the color that appears in your image window",
334 "may be different. For example, on a monochrome screen the",
335 "pixel will appear black or white even if you choose the",
336 "color red as the pixel color. However, the image saved to a",
337 "file with -write is written with red pixels. To assure the",
338 "correct color text in the final image, any PseudoClass image",
339 "is promoted to DirectClass (see miff(5)). To force a",
340 "PseudoClass image to remain PseudoClass, use -colors.",
341 (char *) NULL,
342 },
343 *ImageCompositeHelp[] =
344 {
345 "First a widget window is displayed requesting you to enter an",
346 "image name. Press Composite, Grab or type a file name.",
347 "Press Cancel if you choose not to create a composite image.",
348 "When you choose Grab, move the pointer to the desired window",
349 "and press any button.",
350 "",
351 "If the Composite image does not have any matte information,",
352 "you are informed and the file browser is displayed again.",
353 "Enter the name of a mask image. The image is typically",
354 "grayscale and the same size as the composite image. If the",
355 "image is not grayscale, it is converted to grayscale and the",
356 "resulting intensities are used as matte information.",
357 "",
358 "A small window appears showing the location of the cursor in",
359 "the image window. You are now in composite mode. To exit",
360 "immediately, press Dismiss. In composite mode, the Command",
361 "widget has these options:",
362 "",
363 " Operators",
364 " Over",
365 " In",
366 " Out",
367 " Atop",
368 " Xor",
369 " Plus",
370 " Minus",
371 " Add",
372 " Subtract",
373 " Difference",
374 " Multiply",
375 " Bumpmap",
376 " Copy",
377 " CopyRed",
378 " CopyGreen",
379 " CopyBlue",
380 " CopyOpacity",
381 " Clear",
382 " Dissolve",
383 " Displace",
384 " Help",
385 " Dismiss",
386 "",
387 "Choose a composite operation from the Operators sub-menu of",
388 "the Command widget. How each operator behaves is described",
389 "below. Image window is the image currently displayed on",
390 "your X server and image is the image obtained with the File",
391 "Browser widget.",
392 "",
393 "Over The result is the union of the two image shapes,",
394 " with image obscuring image window in the region of",
395 " overlap.",
396 "",
397 "In The result is simply image cut by the shape of",
398 " image window. None of the image data of image",
399 " window is in the result.",
400 "",
401 "Out The resulting image is image with the shape of",
402 " image window cut out.",
403 "",
404 "Atop The result is the same shape as image image window,",
405 " with image obscuring image window where the image",
406 " shapes overlap. Note this differs from over",
407 " because the portion of image outside image window's",
408 " shape does not appear in the result.",
409 "",
410 "Xor The result is the image data from both image and",
411 " image window that is outside the overlap region.",
412 " The overlap region is blank.",
413 "",
414 "Plus The result is just the sum of the image data.",
415 " Output values are cropped to QuantumRange (no overflow).",
416 "",
417 "Minus The result of image - image window, with underflow",
418 " cropped to zero.",
419 "",
420 "Add The result of image + image window, with overflow",
421 " wrapping around (mod 256).",
422 "",
423 "Subtract The result of image - image window, with underflow",
424 " wrapping around (mod 256). The add and subtract",
425 " operators can be used to perform reversible",
426 " transformations.",
427 "",
428 "Difference",
429 " The result of abs(image - image window). This",
430 " useful for comparing two very similar images.",
431 "",
432 "Multiply",
433 " The result of image * image window. This",
434 " useful for the creation of drop-shadows.",
435 "",
436 "Bumpmap The result of surface normals from image * image",
437 " window.",
438 "",
439 "Copy The resulting image is image window replaced with",
440 " image. Here the matte information is ignored.",
441 "",
442 "CopyRed The red layer of the image window is replace with",
443 " the red layer of the image. The other layers are",
444 " untouched.",
445 "",
446 "CopyGreen",
447 " The green layer of the image window is replace with",
448 " the green layer of the image. The other layers are",
449 " untouched.",
450 "",
451 "CopyBlue The blue layer of the image window is replace with",
452 " the blue layer of the image. The other layers are",
453 " untouched.",
454 "",
455 "CopyOpacity",
456 " The matte layer of the image window is replace with",
457 " the matte layer of the image. The other layers are",
458 " untouched.",
459 "",
460 "The image compositor requires a matte, or alpha channel in",
461 "the image for some operations. This extra channel usually",
462 "defines a mask which represents a sort of a cookie-cutter",
463 "for the image. This the case when matte is opaque (full",
464 "coverage) for pixels inside the shape, zero outside, and",
465 "between 0 and QuantumRange on the boundary. If image does not",
466 "have a matte channel, it is initialized with 0 for any pixel",
467 "matching in color to pixel location (0,0), otherwise QuantumRange.",
468 "",
469 "If you choose Dissolve, the composite operator becomes Over. The",
470 "image matte channel percent transparency is initialized to factor.",
471 "The image window is initialized to (100-factor). Where factor is the",
472 "value you specify in the Dialog widget.",
473 "",
474 "Displace shifts the image pixels as defined by a displacement",
475 "map. With this option, image is used as a displacement map.",
476 "Black, within the displacement map, is a maximum positive",
477 "displacement. White is a maximum negative displacement and",
478 "middle gray is neutral. The displacement is scaled to determine",
479 "the pixel shift. By default, the displacement applies in both the",
480 "horizontal and vertical directions. However, if you specify a mask,",
481 "image is the horizontal X displacement and mask the vertical Y",
482 "displacement.",
483 "",
484 "Note that matte information for image window is not retained",
485 "for colormapped X server visuals (e.g. StaticColor,",
486 "StaticColor, GrayScale, PseudoColor). Correct compositing",
487 "behavior may require a TrueColor or DirectColor visual or a",
488 "Standard Colormap.",
489 "",
490 "Choosing a composite operator is optional. The default",
491 "operator is replace. However, you must choose a location to",
492 "composite your image and press button 1. Press and hold the",
493 "button before releasing and an outline of the image will",
494 "appear to help you identify your location.",
495 "",
496 "The actual colors of the composite image is saved. However,",
497 "the color that appears in image window may be different.",
498 "For example, on a monochrome screen image window will appear",
499 "black or white even though your composited image may have",
500 "many colors. If the image is saved to a file it is written",
501 "with the correct colors. To assure the correct colors are",
502 "saved in the final image, any PseudoClass image is promoted",
503 "to DirectClass (see miff(5)). To force a PseudoClass image",
504 "to remain PseudoClass, use -colors.",
505 (char *) NULL,
506 },
507 *ImageCutHelp[] =
508 {
509 "In cut mode, the Command widget has these options:",
510 "",
511 " Help",
512 " Dismiss",
513 "",
514 "To define a cut region, press button 1 and drag. The",
515 "cut region is defined by a highlighted rectangle that",
516 "expands or contracts as it follows the pointer. Once you",
517 "are satisfied with the cut region, release the button.",
518 "You are now in rectify mode. In rectify mode, the Command",
519 "widget has these options:",
520 "",
521 " Cut",
522 " Help",
523 " Dismiss",
524 "",
525 "You can make adjustments by moving the pointer to one of the",
526 "cut rectangle corners, pressing a button, and dragging.",
527 "Finally, press Cut to commit your copy region. To",
528 "exit without cutting the image, press Dismiss.",
529 (char *) NULL,
530 },
531 *ImageCopyHelp[] =
532 {
533 "In copy mode, the Command widget has these options:",
534 "",
535 " Help",
536 " Dismiss",
537 "",
538 "To define a copy region, press button 1 and drag. The",
539 "copy region is defined by a highlighted rectangle that",
540 "expands or contracts as it follows the pointer. Once you",
541 "are satisfied with the copy region, release the button.",
542 "You are now in rectify mode. In rectify mode, the Command",
543 "widget has these options:",
544 "",
545 " Copy",
546 " Help",
547 " Dismiss",
548 "",
549 "You can make adjustments by moving the pointer to one of the",
550 "copy rectangle corners, pressing a button, and dragging.",
551 "Finally, press Copy to commit your copy region. To",
552 "exit without copying the image, press Dismiss.",
553 (char *) NULL,
554 },
555 *ImageCropHelp[] =
556 {
557 "In crop mode, the Command widget has these options:",
558 "",
559 " Help",
560 " Dismiss",
561 "",
562 "To define a cropping region, press button 1 and drag. The",
563 "cropping region is defined by a highlighted rectangle that",
564 "expands or contracts as it follows the pointer. Once you",
565 "are satisfied with the cropping region, release the button.",
566 "You are now in rectify mode. In rectify mode, the Command",
567 "widget has these options:",
568 "",
569 " Crop",
570 " Help",
571 " Dismiss",
572 "",
573 "You can make adjustments by moving the pointer to one of the",
574 "cropping rectangle corners, pressing a button, and dragging.",
575 "Finally, press Crop to commit your cropping region. To",
576 "exit without cropping the image, press Dismiss.",
577 (char *) NULL,
578 },
579 *ImageDrawHelp[] =
580 {
581 "The cursor changes to a crosshair to indicate you are in",
582 "draw mode. To exit immediately, press Dismiss. In draw mode,",
583 "the Command widget has these options:",
584 "",
585 " Element",
586 " point",
587 " line",
588 " rectangle",
589 " fill rectangle",
590 " circle",
591 " fill circle",
592 " ellipse",
593 " fill ellipse",
594 " polygon",
595 " fill polygon",
596 " Color",
597 " black",
598 " blue",
599 " cyan",
600 " green",
601 " gray",
602 " red",
603 " magenta",
604 " yellow",
605 " white",
606 " transparent",
607 " Browser...",
608 " Stipple",
609 " Brick",
610 " Diagonal",
611 " Scales",
612 " Vertical",
613 " Wavy",
614 " Translucent",
615 " Opaque",
616 " Open...",
617 " Width",
618 " 1",
619 " 2",
620 " 4",
621 " 8",
622 " 16",
623 " Dialog...",
624 " Undo",
625 " Help",
626 " Dismiss",
627 "",
628 "Choose a drawing primitive from the Element sub-menu.",
629 "",
630 "Choose a color from the Color sub-menu. Additional",
631 "colors can be specified with the color browser.",
632 "",
633 "If you choose the color browser and press Grab, you can",
634 "select the color by moving the pointer to the desired",
635 "color on the screen and press any button. The transparent",
636 "color updates the image matte channel and is useful for",
637 "image compositing.",
638 "",
639 "Choose a stipple, if appropriate, from the Stipple sub-menu.",
640 "Additional stipples can be specified with the file browser.",
641 "Stipples obtained from the file browser must be on disk in the",
642 "X11 bitmap format.",
643 "",
644 "Choose a width, if appropriate, from the Width sub-menu. To",
645 "choose a specific width select the Dialog widget.",
646 "",
647 "Choose a point in the Image window and press button 1 and",
648 "hold. Next, move the pointer to another location in the",
649 "image. As you move, a line connects the initial location and",
650 "the pointer. When you release the button, the image is",
651 "updated with the primitive you just drew. For polygons, the",
652 "image is updated when you press and release the button without",
653 "moving the pointer.",
654 "",
655 "To cancel image drawing, move the pointer back to the",
656 "starting point of the line and release the button.",
657 (char *) NULL,
658 },
659 *DisplayHelp[] =
660 {
661 "BUTTONS",
662 " The effects of each button press is described below. Three",
663 " buttons are required. If you have a two button mouse,",
664 " button 1 and 3 are returned. Press ALT and button 3 to",
665 " simulate button 2.",
666 "",
667 " 1 Press this button to map or unmap the Command widget.",
668 "",
669 " 2 Press and drag to define a region of the image to",
670 " magnify.",
671 "",
672 " 3 Press and drag to choose from a select set of commands.",
673 " This button behaves differently if the image being",
674 " displayed is a visual image directory. Here, choose a",
675 " particular tile of the directory and press this button and",
676 " drag to select a command from a pop-up menu. Choose from",
677 " these menu items:",
678 "",
679 " Open",
680 " Next",
681 " Former",
682 " Delete",
683 " Update",
684 "",
685 " If you choose Open, the image represented by the tile is",
686 " displayed. To return to the visual image directory, choose",
687 " Next from the Command widget. Next and Former moves to the",
688 " next or former image respectively. Choose Delete to delete",
689 " a particular image tile. Finally, choose Update to",
690 " synchronize all the image tiles with their respective",
691 " images.",
692 "",
693 "COMMAND WIDGET",
694 " The Command widget lists a number of sub-menus and commands.",
695 " They are",
696 "",
697 " File",
698 " Open...",
699 " Next",
700 " Former",
701 " Select...",
702 " Save...",
703 " Print...",
704 " Delete...",
705 " New...",
706 " Visual Directory...",
707 " Quit",
708 " Edit",
709 " Undo",
710 " Redo",
711 " Cut",
712 " Copy",
713 " Paste",
714 " View",
715 " Half Size",
716 " Original Size",
717 " Double Size",
718 " Resize...",
719 " Apply",
720 " Refresh",
721 " Restore",
722 " Transform",
723 " Crop",
724 " Chop",
725 " Flop",
726 " Flip",
727 " Rotate Right",
728 " Rotate Left",
729 " Rotate...",
730 " Shear...",
731 " Roll...",
732 " Trim Edges",
733 " Enhance",
734 " Brightness...",
735 " Saturation...",
736 " Hue...",
737 " Gamma...",
738 " Sharpen...",
739 " Dull",
740 " Contrast Stretch...",
741 " Sigmoidal Contrast...",
742 " Normalize",
743 " Equalize",
744 " Negate",
745 " Grayscale",
746 " Map...",
747 " Quantize...",
748 " Effects",
749 " Despeckle",
750 " Emboss",
751 " Reduce Noise",
752 " Add Noise",
753 " Sharpen...",
754 " Blur...",
755 " Threshold...",
756 " Edge Detect...",
757 " Spread...",
758 " Shade...",
759 " Painting...",
760 " Segment...",
761 " F/X",
762 " Solarize...",
763 " Sepia Tone...",
764 " Swirl...",
765 " Implode...",
766 " Vignette...",
767 " Wave...",
768 " Oil Painting...",
769 " Charcoal Drawing...",
770 " Image Edit",
771 " Annotate...",
772 " Draw...",
773 " Color...",
774 " Matte...",
775 " Composite...",
776 " Add Border...",
777 " Add Frame...",
778 " Comment...",
779 " Launch...",
780 " Region of Interest...",
781 " Miscellany",
782 " Image Info",
783 " Zoom Image",
784 " Show Preview...",
785 " Show Histogram",
786 " Show Matte",
787 " Background...",
788 " Slide Show",
789 " Preferences...",
790 " Help",
791 " Overview",
792 " Browse Documentation",
793 " About Display",
794 "",
795 " Menu items with a indented triangle have a sub-menu. They",
796 " are represented above as the indented items. To access a",
797 " sub-menu item, move the pointer to the appropriate menu and",
798 " press a button and drag. When you find the desired sub-menu",
799 " item, release the button and the command is executed. Move",
800 " the pointer away from the sub-menu if you decide not to",
801 " execute a particular command.",
802 "",
803 "KEYBOARD ACCELERATORS",
804 " Accelerators are one or two key presses that effect a",
805 " particular command. The keyboard accelerators that",
806 " display(1) understands is:",
807 "",
808 " Ctl+O Press to open an image from a file.",
809 "",
810 " space Press to display the next image.",
811 "",
812 " If the image is a multi-paged document such as a Postscript",
813 " document, you can skip ahead several pages by preceding",
814 " this command with a number. For example to display the",
815 " third page beyond the current page, press 3<space>.",
816 "",
817 " backspace Press to display the former image.",
818 "",
819 " If the image is a multi-paged document such as a Postscript",
820 " document, you can skip behind several pages by preceding",
821 " this command with a number. For example to display the",
822 " third page preceding the current page, press 3<backspace>.",
823 "",
824 " Ctl+S Press to write the image to a file.",
825 "",
826 " Ctl+P Press to print the image to a Postscript printer.",
827 "",
828 " Ctl+D Press to delete an image file.",
829 "",
830 " Ctl+N Press to create a blank canvas.",
831 "",
832 " Ctl+Q Press to discard all images and exit program.",
833 "",
834 " Ctl+Z Press to undo last image transformation.",
835 "",
836 " Ctl+R Press to redo last image transformation.",
837 "",
838 " Ctl+X Press to cut a region of the image.",
839 "",
840 " Ctl+C Press to copy a region of the image.",
841 "",
842 " Ctl+V Press to paste a region to the image.",
843 "",
844 " < Press to half the image size.",
845 "",
846 " - Press to return to the original image size.",
847 "",
848 " > Press to double the image size.",
849 "",
850 " % Press to resize the image to a width and height you",
851 " specify.",
852 "",
853 "Cmd-A Press to make any image transformations permanent."
854 "",
855 " By default, any image size transformations are applied",
856 " to the original image to create the image displayed on",
857 " the X server. However, the transformations are not",
858 " permanent (i.e. the original image does not change",
859 " size only the X image does). For example, if you",
860 " press > the X image will appear to double in size,",
861 " but the original image will in fact remain the same size.",
862 " To force the original image to double in size, press >",
863 " followed by Cmd-A.",
864 "",
865 " @ Press to refresh the image window.",
866 "",
867 " C Press to cut out a rectangular region of the image.",
868 "",
869 " [ Press to chop the image.",
870 "",
871 " H Press to flop image in the horizontal direction.",
872 "",
873 " V Press to flip image in the vertical direction.",
874 "",
875 " / Press to rotate the image 90 degrees clockwise.",
876 "",
877 " \\ Press to rotate the image 90 degrees counter-clockwise.",
878 "",
879 " * Press to rotate the image the number of degrees you",
880 " specify.",
881 "",
882 " S Press to shear the image the number of degrees you",
883 " specify.",
884 "",
885 " R Press to roll the image.",
886 "",
887 " T Press to trim the image edges.",
888 "",
889 " Shft-H Press to vary the image hue.",
890 "",
891 " Shft-S Press to vary the color saturation.",
892 "",
893 " Shft-L Press to vary the color brightness.",
894 "",
895 " Shft-G Press to gamma correct the image.",
896 "",
897 " Shft-C Press to sharpen the image contrast.",
898 "",
899 " Shft-Z Press to dull the image contrast.",
900 "",
901 " = Press to perform histogram equalization on the image.",
902 "",
903 " Shft-N Press to perform histogram normalization on the image.",
904 "",
905 " Shft-~ Press to negate the colors of the image.",
906 "",
907 " . Press to convert the image colors to gray.",
908 "",
909 " Shft-# Press to set the maximum number of unique colors in the",
910 " image.",
911 "",
912 " F2 Press to reduce the speckles in an image.",
913 "",
914 " F3 Press to eliminate peak noise from an image.",
915 "",
916 " F4 Press to add noise to an image.",
917 "",
918 " F5 Press to sharpen an image.",
919 "",
920 " F6 Press to delete an image file.",
921 "",
922 " F7 Press to threshold the image.",
923 "",
924 " F8 Press to detect edges within an image.",
925 "",
926 " F9 Press to emboss an image.",
927 "",
928 " F10 Press to displace pixels by a random amount.",
929 "",
930 " F11 Press to negate all pixels above the threshold level.",
931 "",
932 " F12 Press to shade the image using a distant light source.",
933 "",
934 " F13 Press to lighten or darken image edges to create a 3-D effect.",
935 "",
936 " F14 Press to segment the image by color.",
937 "",
938 " Meta-S Press to swirl image pixels about the center.",
939 "",
940 " Meta-I Press to implode image pixels about the center.",
941 "",
cristycee97112010-05-28 00:44:52 +0000942 " Meta-W Press to alter an image along a sine wave.",
cristy3ed852e2009-09-05 21:47:34 +0000943 "",
944 " Meta-P Press to simulate an oil painting.",
945 "",
946 " Meta-C Press to simulate a charcoal drawing.",
947 "",
948 " Alt-A Press to annotate the image with text.",
949 "",
950 " Alt-D Press to draw on an image.",
951 "",
952 " Alt-P Press to edit an image pixel color.",
953 "",
954 " Alt-M Press to edit the image matte information.",
955 "",
956 " Alt-V Press to composite the image with another.",
957 "",
958 " Alt-B Press to add a border to the image.",
959 "",
960 " Alt-F Press to add an ornamental border to the image.",
961 "",
962 " Alt-Shft-!",
963 " Press to add an image comment.",
964 "",
965 " Ctl-A Press to apply image processing techniques to a region",
966 " of interest.",
967 "",
968 " Shft-? Press to display information about the image.",
969 "",
970 " Shft-+ Press to map the zoom image window.",
971 "",
972 " Shft-P Press to preview an image enhancement, effect, or f/x.",
973 "",
974 " F1 Press to display helpful information about display(1).",
975 "",
976 " Find Press to browse documentation about ImageMagick.",
977 "",
978 " 1-9 Press to change the level of magnification.",
979 "",
980 " Use the arrow keys to move the image one pixel up, down,",
981 " left, or right within the magnify window. Be sure to first",
982 " map the magnify window by pressing button 2.",
983 "",
984 " Press ALT and one of the arrow keys to trim off one pixel",
985 " from any side of the image.",
986 (char *) NULL,
987 },
988 *ImageMatteEditHelp[] =
989 {
990 "Matte information within an image is useful for some",
991 "operations such as image compositing (See IMAGE",
992 "COMPOSITING). This extra channel usually defines a mask",
993 "which represents a sort of a cookie-cutter for the image.",
994 "This the case when matte is opaque (full coverage) for",
995 "pixels inside the shape, zero outside, and between 0 and",
996 "QuantumRange on the boundary.",
997 "",
998 "A small window appears showing the location of the cursor in",
999 "the image window. You are now in matte edit mode. To exit",
1000 "immediately, press Dismiss. In matte edit mode, the Command",
1001 "widget has these options:",
1002 "",
1003 " Method",
1004 " point",
1005 " replace",
1006 " floodfill",
1007 " filltoborder",
1008 " reset",
1009 " Border Color",
1010 " black",
1011 " blue",
1012 " cyan",
1013 " green",
1014 " gray",
1015 " red",
1016 " magenta",
1017 " yellow",
1018 " white",
1019 " Browser...",
1020 " Fuzz",
1021 " 0%",
1022 " 2%",
1023 " 5%",
1024 " 10%",
1025 " 15%",
1026 " Dialog...",
1027 " Matte",
1028 " Opaque",
1029 " Transparent",
1030 " Dialog...",
1031 " Undo",
1032 " Help",
1033 " Dismiss",
1034 "",
1035 "Choose a matte editing method from the Method sub-menu of",
1036 "the Command widget. The point method changes the matte value",
1037 "of any pixel selected with the pointer until the button is",
1038 "is released. The replace method changes the matte value of",
1039 "any pixel that matches the color of the pixel you select with",
1040 "a button press. Floodfill changes the matte value of any pixel",
1041 "that matches the color of the pixel you select with a button",
1042 "press and is a neighbor. Whereas filltoborder changes the matte",
1043 "value any neighbor pixel that is not the border color. Finally",
1044 "reset changes the entire image to the designated matte value.",
1045 "",
1046 "Choose Matte Value and pick Opaque or Transarent. For other values",
1047 "select the Dialog entry. Here a dialog appears requesting a matte",
1048 "value. The value you select is assigned as the opacity value of the",
1049 "selected pixel or pixels.",
1050 "",
1051 "Now, press any button to select a pixel within the image",
1052 "window to change its matte value.",
1053 "",
1054 "If the Magnify widget is mapped, it can be helpful in positioning",
1055 "your pointer within the image (refer to button 2).",
1056 "",
1057 "Matte information is only valid in a DirectClass image.",
1058 "Therefore, any PseudoClass image is promoted to DirectClass",
1059 "(see miff(5)). Note that matte information for PseudoClass",
1060 "is not retained for colormapped X server visuals (e.g.",
1061 "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1062 "immediately save your image to a file (refer to Write).",
1063 "Correct matte editing behavior may require a TrueColor or",
1064 "DirectColor visual or a Standard Colormap.",
1065 (char *) NULL,
1066 },
1067 *ImagePanHelp[] =
1068 {
1069 "When an image exceeds the width or height of the X server",
1070 "screen, display maps a small panning icon. The rectangle",
1071 "within the panning icon shows the area that is currently",
1072 "displayed in the image window. To pan about the image,",
1073 "press any button and drag the pointer within the panning",
1074 "icon. The pan rectangle moves with the pointer and the",
1075 "image window is updated to reflect the location of the",
1076 "rectangle within the panning icon. When you have selected",
1077 "the area of the image you wish to view, release the button.",
1078 "",
1079 "Use the arrow keys to pan the image one pixel up, down,",
1080 "left, or right within the image window.",
1081 "",
1082 "The panning icon is withdrawn if the image becomes smaller",
1083 "than the dimensions of the X server screen.",
1084 (char *) NULL,
1085 },
1086 *ImagePasteHelp[] =
1087 {
1088 "A small window appears showing the location of the cursor in",
1089 "the image window. You are now in paste mode. To exit",
1090 "immediately, press Dismiss. In paste mode, the Command",
1091 "widget has these options:",
1092 "",
1093 " Operators",
1094 " over",
1095 " in",
1096 " out",
1097 " atop",
1098 " xor",
1099 " plus",
1100 " minus",
1101 " add",
1102 " subtract",
1103 " difference",
1104 " replace",
1105 " Help",
1106 " Dismiss",
1107 "",
1108 "Choose a composite operation from the Operators sub-menu of",
1109 "the Command widget. How each operator behaves is described",
1110 "below. Image window is the image currently displayed on",
1111 "your X server and image is the image obtained with the File",
1112 "Browser widget.",
1113 "",
1114 "Over The result is the union of the two image shapes,",
1115 " with image obscuring image window in the region of",
1116 " overlap.",
1117 "",
1118 "In The result is simply image cut by the shape of",
1119 " image window. None of the image data of image",
1120 " window is in the result.",
1121 "",
1122 "Out The resulting image is image with the shape of",
1123 " image window cut out.",
1124 "",
1125 "Atop The result is the same shape as image image window,",
1126 " with image obscuring image window where the image",
1127 " shapes overlap. Note this differs from over",
1128 " because the portion of image outside image window's",
1129 " shape does not appear in the result.",
1130 "",
1131 "Xor The result is the image data from both image and",
1132 " image window that is outside the overlap region.",
1133 " The overlap region is blank.",
1134 "",
1135 "Plus The result is just the sum of the image data.",
1136 " Output values are cropped to QuantumRange (no overflow).",
1137 " This operation is independent of the matte",
1138 " channels.",
1139 "",
1140 "Minus The result of image - image window, with underflow",
1141 " cropped to zero.",
1142 "",
1143 "Add The result of image + image window, with overflow",
1144 " wrapping around (mod 256).",
1145 "",
1146 "Subtract The result of image - image window, with underflow",
1147 " wrapping around (mod 256). The add and subtract",
1148 " operators can be used to perform reversible",
1149 " transformations.",
1150 "",
1151 "Difference",
1152 " The result of abs(image - image window). This",
1153 " useful for comparing two very similar images.",
1154 "",
1155 "Copy The resulting image is image window replaced with",
1156 " image. Here the matte information is ignored.",
1157 "",
1158 "CopyRed The red layer of the image window is replace with",
1159 " the red layer of the image. The other layers are",
1160 " untouched.",
1161 "",
1162 "CopyGreen",
1163 " The green layer of the image window is replace with",
1164 " the green layer of the image. The other layers are",
1165 " untouched.",
1166 "",
1167 "CopyBlue The blue layer of the image window is replace with",
1168 " the blue layer of the image. The other layers are",
1169 " untouched.",
1170 "",
1171 "CopyOpacity",
1172 " The matte layer of the image window is replace with",
1173 " the matte layer of the image. The other layers are",
1174 " untouched.",
1175 "",
1176 "The image compositor requires a matte, or alpha channel in",
1177 "the image for some operations. This extra channel usually",
1178 "defines a mask which represents a sort of a cookie-cutter",
1179 "for the image. This the case when matte is opaque (full",
1180 "coverage) for pixels inside the shape, zero outside, and",
1181 "between 0 and QuantumRange on the boundary. If image does not",
1182 "have a matte channel, it is initialized with 0 for any pixel",
1183 "matching in color to pixel location (0,0), otherwise QuantumRange.",
1184 "",
1185 "Note that matte information for image window is not retained",
1186 "for colormapped X server visuals (e.g. StaticColor,",
1187 "StaticColor, GrayScale, PseudoColor). Correct compositing",
1188 "behavior may require a TrueColor or DirectColor visual or a",
1189 "Standard Colormap.",
1190 "",
1191 "Choosing a composite operator is optional. The default",
1192 "operator is replace. However, you must choose a location to",
1193 "paste your image and press button 1. Press and hold the",
1194 "button before releasing and an outline of the image will",
1195 "appear to help you identify your location.",
1196 "",
1197 "The actual colors of the pasted image is saved. However,",
1198 "the color that appears in image window may be different.",
1199 "For example, on a monochrome screen image window will appear",
1200 "black or white even though your pasted image may have",
1201 "many colors. If the image is saved to a file it is written",
1202 "with the correct colors. To assure the correct colors are",
1203 "saved in the final image, any PseudoClass image is promoted",
1204 "to DirectClass (see miff(5)). To force a PseudoClass image",
1205 "to remain PseudoClass, use -colors.",
1206 (char *) NULL,
1207 },
1208 *ImageROIHelp[] =
1209 {
1210 "In region of interest mode, the Command widget has these",
1211 "options:",
1212 "",
1213 " Help",
1214 " Dismiss",
1215 "",
1216 "To define a region of interest, press button 1 and drag.",
1217 "The region of interest is defined by a highlighted rectangle",
1218 "that expands or contracts as it follows the pointer. Once",
1219 "you are satisfied with the region of interest, release the",
1220 "button. You are now in apply mode. In apply mode the",
1221 "Command widget has these options:",
1222 "",
1223 " File",
1224 " Save...",
1225 " Print...",
1226 " Edit",
1227 " Undo",
1228 " Redo",
1229 " Transform",
1230 " Flop",
1231 " Flip",
1232 " Rotate Right",
1233 " Rotate Left",
1234 " Enhance",
1235 " Hue...",
1236 " Saturation...",
1237 " Brightness...",
1238 " Gamma...",
1239 " Spiff",
1240 " Dull",
1241 " Contrast Stretch",
1242 " Sigmoidal Contrast...",
1243 " Normalize",
1244 " Equalize",
1245 " Negate",
1246 " Grayscale",
1247 " Map...",
1248 " Quantize...",
1249 " Effects",
1250 " Despeckle",
1251 " Emboss",
1252 " Reduce Noise",
1253 " Sharpen...",
1254 " Blur...",
1255 " Threshold...",
1256 " Edge Detect...",
1257 " Spread...",
1258 " Shade...",
1259 " Raise...",
1260 " Segment...",
1261 " F/X",
1262 " Solarize...",
1263 " Sepia Tone...",
1264 " Swirl...",
1265 " Implode...",
1266 " Vignette...",
1267 " Wave...",
1268 " Oil Painting...",
1269 " Charcoal Drawing...",
1270 " Miscellany",
1271 " Image Info",
1272 " Zoom Image",
1273 " Show Preview...",
1274 " Show Histogram",
1275 " Show Matte",
1276 " Help",
1277 " Dismiss",
1278 "",
1279 "You can make adjustments to the region of interest by moving",
1280 "the pointer to one of the rectangle corners, pressing a",
1281 "button, and dragging. Finally, choose an image processing",
1282 "technique from the Command widget. You can choose more than",
1283 "one image processing technique to apply to an area.",
1284 "Alternatively, you can move the region of interest before",
1285 "applying another image processing technique. To exit, press",
1286 "Dismiss.",
1287 (char *) NULL,
1288 },
1289 *ImageRotateHelp[] =
1290 {
1291 "In rotate mode, the Command widget has these options:",
1292 "",
1293 " Pixel Color",
1294 " black",
1295 " blue",
1296 " cyan",
1297 " green",
1298 " gray",
1299 " red",
1300 " magenta",
1301 " yellow",
1302 " white",
1303 " Browser...",
1304 " Direction",
1305 " horizontal",
1306 " vertical",
1307 " Help",
1308 " Dismiss",
1309 "",
1310 "Choose a background color from the Pixel Color sub-menu.",
1311 "Additional background colors can be specified with the color",
1312 "browser. You can change the menu colors by setting the X",
1313 "resources pen1 through pen9.",
1314 "",
1315 "If you choose the color browser and press Grab, you can",
1316 "select the background color by moving the pointer to the",
1317 "desired color on the screen and press any button.",
1318 "",
1319 "Choose a point in the image window and press this button and",
1320 "hold. Next, move the pointer to another location in the",
1321 "image. As you move a line connects the initial location and",
1322 "the pointer. When you release the button, the degree of",
1323 "image rotation is determined by the slope of the line you",
1324 "just drew. The slope is relative to the direction you",
1325 "choose from the Direction sub-menu of the Command widget.",
1326 "",
1327 "To cancel the image rotation, move the pointer back to the",
1328 "starting point of the line and release the button.",
1329 (char *) NULL,
1330 };
1331
1332/*
1333 Enumeration declarations.
1334*/
1335typedef enum
1336{
1337 CopyMode,
1338 CropMode,
1339 CutMode
1340} ClipboardMode;
1341
1342typedef enum
1343{
1344 OpenCommand,
1345 NextCommand,
1346 FormerCommand,
1347 SelectCommand,
1348 SaveCommand,
1349 PrintCommand,
1350 DeleteCommand,
1351 NewCommand,
1352 VisualDirectoryCommand,
1353 QuitCommand,
1354 UndoCommand,
1355 RedoCommand,
1356 CutCommand,
1357 CopyCommand,
1358 PasteCommand,
1359 HalfSizeCommand,
1360 OriginalSizeCommand,
1361 DoubleSizeCommand,
1362 ResizeCommand,
1363 ApplyCommand,
1364 RefreshCommand,
1365 RestoreCommand,
1366 CropCommand,
1367 ChopCommand,
1368 FlopCommand,
1369 FlipCommand,
1370 RotateRightCommand,
1371 RotateLeftCommand,
1372 RotateCommand,
1373 ShearCommand,
1374 RollCommand,
1375 TrimCommand,
1376 HueCommand,
1377 SaturationCommand,
1378 BrightnessCommand,
1379 GammaCommand,
1380 SpiffCommand,
1381 DullCommand,
1382 ContrastStretchCommand,
1383 SigmoidalContrastCommand,
1384 NormalizeCommand,
1385 EqualizeCommand,
1386 NegateCommand,
1387 GrayscaleCommand,
1388 MapCommand,
1389 QuantizeCommand,
1390 DespeckleCommand,
1391 EmbossCommand,
1392 ReduceNoiseCommand,
1393 AddNoiseCommand,
1394 SharpenCommand,
1395 BlurCommand,
1396 ThresholdCommand,
1397 EdgeDetectCommand,
1398 SpreadCommand,
1399 ShadeCommand,
1400 RaiseCommand,
1401 SegmentCommand,
1402 SolarizeCommand,
1403 SepiaToneCommand,
1404 SwirlCommand,
1405 ImplodeCommand,
1406 VignetteCommand,
1407 WaveCommand,
1408 OilPaintCommand,
1409 CharcoalDrawCommand,
1410 AnnotateCommand,
1411 DrawCommand,
1412 ColorCommand,
1413 MatteCommand,
1414 CompositeCommand,
1415 AddBorderCommand,
1416 AddFrameCommand,
1417 CommentCommand,
1418 LaunchCommand,
1419 RegionofInterestCommand,
1420 ROIHelpCommand,
1421 ROIDismissCommand,
1422 InfoCommand,
1423 ZoomCommand,
1424 ShowPreviewCommand,
1425 ShowHistogramCommand,
1426 ShowMatteCommand,
1427 BackgroundCommand,
1428 SlideShowCommand,
1429 PreferencesCommand,
1430 HelpCommand,
1431 BrowseDocumentationCommand,
1432 VersionCommand,
1433 SaveToUndoBufferCommand,
1434 FreeBuffersCommand,
1435 NullCommand
1436} CommandType;
1437
1438typedef enum
1439{
1440 AnnotateNameCommand,
1441 AnnotateFontColorCommand,
1442 AnnotateBackgroundColorCommand,
1443 AnnotateRotateCommand,
1444 AnnotateHelpCommand,
1445 AnnotateDismissCommand,
1446 TextHelpCommand,
1447 TextApplyCommand,
1448 ChopDirectionCommand,
1449 ChopHelpCommand,
1450 ChopDismissCommand,
1451 HorizontalChopCommand,
1452 VerticalChopCommand,
1453 ColorEditMethodCommand,
1454 ColorEditColorCommand,
1455 ColorEditBorderCommand,
1456 ColorEditFuzzCommand,
1457 ColorEditUndoCommand,
1458 ColorEditHelpCommand,
1459 ColorEditDismissCommand,
1460 CompositeOperatorsCommand,
1461 CompositeDissolveCommand,
1462 CompositeDisplaceCommand,
1463 CompositeHelpCommand,
1464 CompositeDismissCommand,
1465 CropHelpCommand,
1466 CropDismissCommand,
1467 RectifyCopyCommand,
1468 RectifyHelpCommand,
1469 RectifyDismissCommand,
1470 DrawElementCommand,
1471 DrawColorCommand,
1472 DrawStippleCommand,
1473 DrawWidthCommand,
1474 DrawUndoCommand,
1475 DrawHelpCommand,
1476 DrawDismissCommand,
1477 MatteEditMethod,
1478 MatteEditBorderCommand,
1479 MatteEditFuzzCommand,
1480 MatteEditValueCommand,
1481 MatteEditUndoCommand,
1482 MatteEditHelpCommand,
1483 MatteEditDismissCommand,
1484 PasteOperatorsCommand,
1485 PasteHelpCommand,
1486 PasteDismissCommand,
1487 RotateColorCommand,
1488 RotateDirectionCommand,
1489 RotateCropCommand,
1490 RotateSharpenCommand,
1491 RotateHelpCommand,
1492 RotateDismissCommand,
1493 HorizontalRotateCommand,
1494 VerticalRotateCommand,
1495 TileLoadCommand,
1496 TileNextCommand,
1497 TileFormerCommand,
1498 TileDeleteCommand,
1499 TileUpdateCommand
1500} ModeType;
1501
1502/*
1503 Stipples.
1504*/
1505#define BricksWidth 20
1506#define BricksHeight 20
1507#define DiagonalWidth 16
1508#define DiagonalHeight 16
1509#define HighlightWidth 8
1510#define HighlightHeight 8
cristydd05beb2010-11-21 21:23:39 +00001511#define OpaqueWidth 8
1512#define OpaqueHeight 8
cristy3ed852e2009-09-05 21:47:34 +00001513#define ScalesWidth 16
1514#define ScalesHeight 16
1515#define ShadowWidth 8
1516#define ShadowHeight 8
1517#define VerticalWidth 16
1518#define VerticalHeight 16
1519#define WavyWidth 16
1520#define WavyHeight 16
1521
1522/*
1523 Constant declaration.
1524*/
1525static const int
1526 RoiDelta = 8;
1527
1528static const unsigned char
1529 BricksBitmap[] =
1530 {
1531 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1532 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1533 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1534 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1535 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1536 },
1537 DiagonalBitmap[] =
1538 {
1539 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1540 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1541 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1542 },
1543 ScalesBitmap[] =
1544 {
1545 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1546 0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1547 0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1548 },
1549 VerticalBitmap[] =
1550 {
1551 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1552 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1553 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1554 },
1555 WavyBitmap[] =
1556 {
1557 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1558 0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1559 0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1560 };
1561
1562/*
1563 Function prototypes.
1564*/
1565static CommandType
1566 XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
cristy051718b2011-08-28 22:49:25 +00001567 const MagickStatusType,KeySym,Image **,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +00001568
1569static Image
1570 *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
cristy051718b2011-08-28 22:49:25 +00001571 Image **,ExceptionInfo *),
cristy3ed852e2009-09-05 21:47:34 +00001572 *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
cristy051718b2011-08-28 22:49:25 +00001573 *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1574 ExceptionInfo *),
1575 *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1576 ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +00001577
1578static MagickBooleanType
cristy051718b2011-08-28 22:49:25 +00001579 XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1580 ExceptionInfo *),
1581 XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1582 ExceptionInfo *),
1583 XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1584 ExceptionInfo *),
1585 XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1586 ExceptionInfo *),
1587 XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1588 ExceptionInfo *),
1589 XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1590 ExceptionInfo *),
1591 XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1592 XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1593 ExceptionInfo *),
1594 XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1595 ExceptionInfo *),
1596 XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1597 XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1598 XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1599 ExceptionInfo *),
1600 XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1601 XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1602 XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +00001603
1604static void
1605 XDrawPanRectangle(Display *,XWindows *),
cristy051718b2011-08-28 22:49:25 +00001606 XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1607 ExceptionInfo *),
cristy3ed852e2009-09-05 21:47:34 +00001608 XMagnifyImage(Display *,XWindows *,XEvent *),
cristy051718b2011-08-28 22:49:25 +00001609 XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
cristy3ed852e2009-09-05 21:47:34 +00001610 XPanImage(Display *,XWindows *,XEvent *),
1611 XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1612 const KeySym),
1613 XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1614 XScreenEvent(Display *,XWindows *,XEvent *),
1615 XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1616
1617/*
1618%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1619% %
1620% %
1621% %
1622% D i s p l a y I m a g e s %
1623% %
1624% %
1625% %
1626%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1627%
1628% DisplayImages() displays an image sequence to any X window screen. It
1629% returns a value other than 0 if successful. Check the exception member
1630% of image to determine the reason for any failure.
1631%
1632% The format of the DisplayImages method is:
1633%
1634% MagickBooleanType DisplayImages(const ImageInfo *image_info,
cristy051718b2011-08-28 22:49:25 +00001635% Image *images,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001636%
1637% A description of each parameter follows:
1638%
1639% o image_info: the image info.
1640%
1641% o image: the image.
1642%
cristy051718b2011-08-28 22:49:25 +00001643% o exception: return any errors or warnings in this structure.
1644%
cristy3ed852e2009-09-05 21:47:34 +00001645*/
1646MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
cristy051718b2011-08-28 22:49:25 +00001647 Image *images,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001648{
1649 char
1650 *argv[1];
1651
1652 Display
1653 *display;
1654
1655 Image
1656 *image;
1657
cristybb503372010-05-27 20:51:26 +00001658 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001659 i;
1660
cristybb503372010-05-27 20:51:26 +00001661 size_t
cristy3ed852e2009-09-05 21:47:34 +00001662 state;
1663
1664 XrmDatabase
1665 resource_database;
1666
1667 XResourceInfo
1668 resource_info;
1669
1670 assert(image_info != (const ImageInfo *) NULL);
1671 assert(image_info->signature == MagickSignature);
1672 assert(images != (Image *) NULL);
1673 assert(images->signature == MagickSignature);
1674 if (images->debug != MagickFalse)
1675 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1676 display=XOpenDisplay(image_info->server_name);
1677 if (display == (Display *) NULL)
1678 {
cristy051718b2011-08-28 22:49:25 +00001679 (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1680 "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
cristy3ed852e2009-09-05 21:47:34 +00001681 return(MagickFalse);
1682 }
cristy051718b2011-08-28 22:49:25 +00001683 if (exception->severity != UndefinedException)
1684 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00001685 (void) XSetErrorHandler(XError);
1686 resource_database=XGetResourceDatabase(display,GetClientName());
1687 (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1688 XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1689 if (image_info->page != (char *) NULL)
1690 resource_info.image_geometry=AcquireString(image_info->page);
1691 resource_info.immutable=MagickTrue;
1692 argv[0]=AcquireString(GetClientName());
1693 state=DefaultState;
1694 for (i=0; (state & ExitState) == 0; i++)
1695 {
cristybb503372010-05-27 20:51:26 +00001696 if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
cristy3ed852e2009-09-05 21:47:34 +00001697 break;
1698 image=GetImageFromList(images,i % GetImageListLength(images));
cristy051718b2011-08-28 22:49:25 +00001699 (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
cristy3ed852e2009-09-05 21:47:34 +00001700 }
cristy01cdc902011-08-31 01:05:44 +00001701 SetErrorHandler((ErrorHandler) NULL);
1702 SetWarningHandler((WarningHandler) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001703 argv[0]=DestroyString(argv[0]);
1704 (void) XCloseDisplay(display);
1705 XDestroyResourceInfo(&resource_info);
cristy051718b2011-08-28 22:49:25 +00001706 if (exception->severity != UndefinedException)
cristy3ed852e2009-09-05 21:47:34 +00001707 return(MagickFalse);
1708 return(MagickTrue);
1709}
1710
1711/*
1712%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1713% %
1714% %
1715% %
1716% R e m o t e D i s p l a y C o m m a n d %
1717% %
1718% %
1719% %
1720%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1721%
1722% RemoteDisplayCommand() encourages a remote display program to display the
1723% specified image filename.
1724%
1725% The format of the RemoteDisplayCommand method is:
1726%
1727% MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1728% const char *window,const char *filename,ExceptionInfo *exception)
1729%
1730% A description of each parameter follows:
1731%
1732% o image_info: the image info.
1733%
1734% o window: Specifies the name or id of an X window.
1735%
1736% o filename: the name of the image filename to display.
1737%
1738% o exception: return any errors or warnings in this structure.
1739%
1740*/
1741MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1742 const char *window,const char *filename,ExceptionInfo *exception)
1743{
1744 Display
1745 *display;
1746
1747 MagickStatusType
1748 status;
1749
1750 assert(image_info != (const ImageInfo *) NULL);
1751 assert(image_info->signature == MagickSignature);
1752 assert(filename != (char *) NULL);
1753 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1754 display=XOpenDisplay(image_info->server_name);
1755 if (display == (Display *) NULL)
1756 {
1757 (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1758 "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1759 return(MagickFalse);
1760 }
1761 (void) XSetErrorHandler(XError);
1762 status=XRemoteCommand(display,window,filename);
1763 (void) XCloseDisplay(display);
1764 return(status != 0 ? MagickTrue : MagickFalse);
1765}
1766
1767/*
1768%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1769% %
1770% %
1771% %
1772+ X A n n o t a t e E d i t I m a g e %
1773% %
1774% %
1775% %
1776%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1777%
1778% XAnnotateEditImage() annotates the image with text.
1779%
1780% The format of the XAnnotateEditImage method is:
1781%
1782% MagickBooleanType XAnnotateEditImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00001783% XResourceInfo *resource_info,XWindows *windows,Image *image,
1784% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001785%
1786% A description of each parameter follows:
1787%
1788% o display: Specifies a connection to an X server; returned from
1789% XOpenDisplay.
1790%
1791% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1792%
1793% o windows: Specifies a pointer to a XWindows structure.
1794%
1795% o image: the image; returned from ReadImage.
1796%
1797*/
1798
cristybb503372010-05-27 20:51:26 +00001799static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001800{
1801 if (x > y)
1802 return(x);
1803 return(y);
1804}
1805
cristybb503372010-05-27 20:51:26 +00001806static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001807{
1808 if (x < y)
1809 return(x);
1810 return(y);
1811}
1812
1813static MagickBooleanType XAnnotateEditImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00001814 XResourceInfo *resource_info,XWindows *windows,Image *image,
1815 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001816{
1817 static const char
1818 *AnnotateMenu[] =
1819 {
1820 "Font Name",
1821 "Font Color",
1822 "Box Color",
1823 "Rotate Text",
1824 "Help",
1825 "Dismiss",
1826 (char *) NULL
1827 },
1828 *TextMenu[] =
1829 {
1830 "Help",
1831 "Apply",
1832 (char *) NULL
1833 };
1834
1835 static const ModeType
1836 AnnotateCommands[] =
1837 {
1838 AnnotateNameCommand,
1839 AnnotateFontColorCommand,
1840 AnnotateBackgroundColorCommand,
1841 AnnotateRotateCommand,
1842 AnnotateHelpCommand,
1843 AnnotateDismissCommand
1844 },
1845 TextCommands[] =
1846 {
1847 TextHelpCommand,
1848 TextApplyCommand
1849 };
1850
1851 static MagickBooleanType
1852 transparent_box = MagickTrue,
1853 transparent_pen = MagickFalse;
1854
1855 static MagickRealType
1856 degrees = 0.0;
1857
1858 static unsigned int
1859 box_id = MaxNumberPens-2,
1860 font_id = 0,
1861 pen_id = 0;
1862
1863 char
1864 command[MaxTextExtent],
1865 text[MaxTextExtent];
1866
1867 const char
1868 *ColorMenu[MaxNumberPens+1];
1869
1870 Cursor
1871 cursor;
1872
1873 GC
1874 annotate_context;
1875
1876 int
1877 id,
1878 pen_number,
1879 status,
1880 x,
1881 y;
1882
1883 KeySym
1884 key_symbol;
1885
1886 register char
1887 *p;
1888
cristybb503372010-05-27 20:51:26 +00001889 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001890 i;
1891
1892 unsigned int
1893 height,
1894 width;
1895
cristybb503372010-05-27 20:51:26 +00001896 size_t
cristy3ed852e2009-09-05 21:47:34 +00001897 state;
1898
1899 XAnnotateInfo
1900 *annotate_info,
1901 *previous_info;
1902
1903 XColor
1904 color;
1905
1906 XFontStruct
1907 *font_info;
1908
1909 XEvent
1910 event,
1911 text_event;
1912
1913 /*
1914 Map Command widget.
1915 */
1916 (void) CloneString(&windows->command.name,"Annotate");
1917 windows->command.data=4;
1918 (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1919 (void) XMapRaised(display,windows->command.id);
1920 XClientMessage(display,windows->image.id,windows->im_protocols,
1921 windows->im_update_widget,CurrentTime);
1922 /*
1923 Track pointer until button 1 is pressed.
1924 */
1925 XQueryPosition(display,windows->image.id,&x,&y);
1926 (void) XSelectInput(display,windows->image.id,
1927 windows->image.attributes.event_mask | PointerMotionMask);
1928 cursor=XCreateFontCursor(display,XC_left_side);
1929 (void) XCheckDefineCursor(display,windows->image.id,cursor);
1930 state=DefaultState;
1931 do
1932 {
1933 if (windows->info.mapped != MagickFalse)
1934 {
1935 /*
1936 Display pointer position.
1937 */
cristyb51dff52011-05-19 16:55:47 +00001938 (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
cristy3ed852e2009-09-05 21:47:34 +00001939 x+windows->image.x,y+windows->image.y);
1940 XInfoWidget(display,windows,text);
1941 }
1942 /*
1943 Wait for next event.
1944 */
1945 XScreenEvent(display,windows,&event);
1946 if (event.xany.window == windows->command.id)
1947 {
1948 /*
1949 Select a command from the Command widget.
1950 */
1951 id=XCommandWidget(display,windows,AnnotateMenu,&event);
1952 (void) XCheckDefineCursor(display,windows->image.id,cursor);
1953 if (id < 0)
1954 continue;
1955 switch (AnnotateCommands[id])
1956 {
1957 case AnnotateNameCommand:
1958 {
1959 const char
1960 *FontMenu[MaxNumberFonts];
1961
1962 int
1963 font_number;
1964
1965 /*
1966 Initialize menu selections.
1967 */
1968 for (i=0; i < MaxNumberFonts; i++)
1969 FontMenu[i]=resource_info->font_name[i];
1970 FontMenu[MaxNumberFonts-2]="Browser...";
1971 FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1972 /*
1973 Select a font name from the pop-up menu.
1974 */
1975 font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1976 (const char **) FontMenu,command);
1977 if (font_number < 0)
1978 break;
1979 if (font_number == (MaxNumberFonts-2))
1980 {
1981 static char
1982 font_name[MaxTextExtent] = "fixed";
1983
1984 /*
1985 Select a font name from a browser.
1986 */
1987 resource_info->font_name[font_number]=font_name;
1988 XFontBrowserWidget(display,windows,"Select",font_name);
1989 if (*font_name == '\0')
1990 break;
1991 }
1992 /*
1993 Initialize font info.
1994 */
1995 font_info=XLoadQueryFont(display,resource_info->font_name[
1996 font_number]);
1997 if (font_info == (XFontStruct *) NULL)
1998 {
1999 XNoticeWidget(display,windows,"Unable to load font:",
2000 resource_info->font_name[font_number]);
2001 break;
2002 }
2003 font_id=(unsigned int) font_number;
2004 (void) XFreeFont(display,font_info);
2005 break;
2006 }
2007 case AnnotateFontColorCommand:
2008 {
2009 /*
2010 Initialize menu selections.
2011 */
2012 for (i=0; i < (int) (MaxNumberPens-2); i++)
2013 ColorMenu[i]=resource_info->pen_colors[i];
2014 ColorMenu[MaxNumberPens-2]="transparent";
2015 ColorMenu[MaxNumberPens-1]="Browser...";
2016 ColorMenu[MaxNumberPens]=(const char *) NULL;
2017 /*
2018 Select a pen color from the pop-up menu.
2019 */
2020 pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2021 (const char **) ColorMenu,command);
2022 if (pen_number < 0)
2023 break;
2024 transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2025 MagickFalse;
2026 if (transparent_pen != MagickFalse)
2027 break;
2028 if (pen_number == (MaxNumberPens-1))
2029 {
2030 static char
2031 color_name[MaxTextExtent] = "gray";
2032
2033 /*
2034 Select a pen color from a dialog.
2035 */
2036 resource_info->pen_colors[pen_number]=color_name;
2037 XColorBrowserWidget(display,windows,"Select",color_name);
2038 if (*color_name == '\0')
2039 break;
2040 }
2041 /*
2042 Set pen color.
2043 */
2044 (void) XParseColor(display,windows->map_info->colormap,
2045 resource_info->pen_colors[pen_number],&color);
2046 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2047 (unsigned int) MaxColors,&color);
2048 windows->pixel_info->pen_colors[pen_number]=color;
2049 pen_id=(unsigned int) pen_number;
2050 break;
2051 }
2052 case AnnotateBackgroundColorCommand:
2053 {
2054 /*
2055 Initialize menu selections.
2056 */
2057 for (i=0; i < (int) (MaxNumberPens-2); i++)
2058 ColorMenu[i]=resource_info->pen_colors[i];
2059 ColorMenu[MaxNumberPens-2]="transparent";
2060 ColorMenu[MaxNumberPens-1]="Browser...";
2061 ColorMenu[MaxNumberPens]=(const char *) NULL;
2062 /*
2063 Select a pen color from the pop-up menu.
2064 */
2065 pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2066 (const char **) ColorMenu,command);
2067 if (pen_number < 0)
2068 break;
2069 transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2070 MagickFalse;
2071 if (transparent_box != MagickFalse)
2072 break;
2073 if (pen_number == (MaxNumberPens-1))
2074 {
2075 static char
2076 color_name[MaxTextExtent] = "gray";
2077
2078 /*
2079 Select a pen color from a dialog.
2080 */
2081 resource_info->pen_colors[pen_number]=color_name;
2082 XColorBrowserWidget(display,windows,"Select",color_name);
2083 if (*color_name == '\0')
2084 break;
2085 }
2086 /*
2087 Set pen color.
2088 */
2089 (void) XParseColor(display,windows->map_info->colormap,
2090 resource_info->pen_colors[pen_number],&color);
2091 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2092 (unsigned int) MaxColors,&color);
2093 windows->pixel_info->pen_colors[pen_number]=color;
2094 box_id=(unsigned int) pen_number;
2095 break;
2096 }
2097 case AnnotateRotateCommand:
2098 {
2099 int
2100 entry;
2101
2102 static char
2103 angle[MaxTextExtent] = "30.0";
2104
2105 static const char
2106 *RotateMenu[] =
2107 {
2108 "-90",
2109 "-45",
2110 "-30",
2111 "0",
2112 "30",
2113 "45",
2114 "90",
2115 "180",
2116 "Dialog...",
2117 (char *) NULL,
2118 };
2119
2120 /*
2121 Select a command from the pop-up menu.
2122 */
2123 entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2124 command);
2125 if (entry < 0)
2126 break;
2127 if (entry != 8)
2128 {
cristyc1acd842011-05-19 23:05:47 +00002129 degrees=InterpretLocaleValue(RotateMenu[entry],(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002130 break;
2131 }
2132 (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2133 angle);
2134 if (*angle == '\0')
2135 break;
cristyc1acd842011-05-19 23:05:47 +00002136 degrees=InterpretLocaleValue(angle,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002137 break;
2138 }
2139 case AnnotateHelpCommand:
2140 {
2141 XTextViewWidget(display,resource_info,windows,MagickFalse,
2142 "Help Viewer - Image Annotation",ImageAnnotateHelp);
2143 break;
2144 }
2145 case AnnotateDismissCommand:
2146 {
2147 /*
2148 Prematurely exit.
2149 */
2150 state|=EscapeState;
2151 state|=ExitState;
2152 break;
2153 }
2154 default:
2155 break;
2156 }
2157 continue;
2158 }
2159 switch (event.type)
2160 {
2161 case ButtonPress:
2162 {
2163 if (event.xbutton.button != Button1)
2164 break;
2165 if (event.xbutton.window != windows->image.id)
2166 break;
2167 /*
2168 Change to text entering mode.
2169 */
2170 x=event.xbutton.x;
2171 y=event.xbutton.y;
2172 state|=ExitState;
2173 break;
2174 }
2175 case ButtonRelease:
2176 break;
2177 case Expose:
2178 break;
2179 case KeyPress:
2180 {
2181 if (event.xkey.window != windows->image.id)
2182 break;
2183 /*
2184 Respond to a user key press.
2185 */
2186 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2187 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2188 switch ((int) key_symbol)
2189 {
2190 case XK_Escape:
2191 case XK_F20:
2192 {
2193 /*
2194 Prematurely exit.
2195 */
2196 state|=EscapeState;
2197 state|=ExitState;
2198 break;
2199 }
2200 case XK_F1:
2201 case XK_Help:
2202 {
2203 XTextViewWidget(display,resource_info,windows,MagickFalse,
2204 "Help Viewer - Image Annotation",ImageAnnotateHelp);
2205 break;
2206 }
2207 default:
2208 {
2209 (void) XBell(display,0);
2210 break;
2211 }
2212 }
2213 break;
2214 }
2215 case MotionNotify:
2216 {
2217 /*
2218 Map and unmap Info widget as cursor crosses its boundaries.
2219 */
2220 x=event.xmotion.x;
2221 y=event.xmotion.y;
2222 if (windows->info.mapped != MagickFalse)
2223 {
2224 if ((x < (int) (windows->info.x+windows->info.width)) &&
2225 (y < (int) (windows->info.y+windows->info.height)))
2226 (void) XWithdrawWindow(display,windows->info.id,
2227 windows->info.screen);
2228 }
2229 else
2230 if ((x > (int) (windows->info.x+windows->info.width)) ||
2231 (y > (int) (windows->info.y+windows->info.height)))
2232 (void) XMapWindow(display,windows->info.id);
2233 break;
2234 }
2235 default:
2236 break;
2237 }
2238 } while ((state & ExitState) == 0);
2239 (void) XSelectInput(display,windows->image.id,
2240 windows->image.attributes.event_mask);
2241 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2242 if ((state & EscapeState) != 0)
2243 return(MagickTrue);
2244 /*
2245 Set font info and check boundary conditions.
2246 */
2247 font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2248 if (font_info == (XFontStruct *) NULL)
2249 {
2250 XNoticeWidget(display,windows,"Unable to load font:",
2251 resource_info->font_name[font_id]);
2252 font_info=windows->font_info;
2253 }
2254 if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2255 x=(int) windows->image.width-font_info->max_bounds.width;
2256 if (y < (int) (font_info->ascent+font_info->descent))
2257 y=(int) font_info->ascent+font_info->descent;
2258 if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2259 ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2260 return(MagickFalse);
2261 /*
2262 Initialize annotate structure.
2263 */
cristy73bd4a52010-10-05 11:24:23 +00002264 annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
cristy3ed852e2009-09-05 21:47:34 +00002265 if (annotate_info == (XAnnotateInfo *) NULL)
2266 return(MagickFalse);
2267 XGetAnnotateInfo(annotate_info);
2268 annotate_info->x=x;
2269 annotate_info->y=y;
2270 if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2271 annotate_info->stencil=OpaqueStencil;
2272 else
2273 if (transparent_box == MagickFalse)
2274 annotate_info->stencil=BackgroundStencil;
2275 else
2276 annotate_info->stencil=ForegroundStencil;
2277 annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2278 annotate_info->degrees=degrees;
2279 annotate_info->font_info=font_info;
2280 annotate_info->text=(char *) AcquireQuantumMemory((size_t)
cristy49e2d862010-11-12 02:50:30 +00002281 windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
cristy3ed852e2009-09-05 21:47:34 +00002282 sizeof(*annotate_info->text));
2283 if (annotate_info->text == (char *) NULL)
2284 return(MagickFalse);
2285 /*
2286 Create cursor and set graphic context.
2287 */
2288 cursor=XCreateFontCursor(display,XC_pencil);
2289 (void) XCheckDefineCursor(display,windows->image.id,cursor);
2290 annotate_context=windows->image.annotate_context;
2291 (void) XSetFont(display,annotate_context,font_info->fid);
2292 (void) XSetBackground(display,annotate_context,
2293 windows->pixel_info->pen_colors[box_id].pixel);
2294 (void) XSetForeground(display,annotate_context,
2295 windows->pixel_info->pen_colors[pen_id].pixel);
2296 /*
2297 Begin annotating the image with text.
2298 */
2299 (void) CloneString(&windows->command.name,"Text");
2300 windows->command.data=0;
2301 (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2302 state=DefaultState;
2303 (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2304 text_event.xexpose.width=(int) font_info->max_bounds.width;
2305 text_event.xexpose.height=font_info->max_bounds.ascent+
2306 font_info->max_bounds.descent;
2307 p=annotate_info->text;
2308 do
2309 {
2310 /*
2311 Display text cursor.
2312 */
2313 *p='\0';
2314 (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2315 /*
2316 Wait for next event.
2317 */
2318 XScreenEvent(display,windows,&event);
2319 if (event.xany.window == windows->command.id)
2320 {
2321 /*
2322 Select a command from the Command widget.
2323 */
2324 (void) XSetBackground(display,annotate_context,
2325 windows->pixel_info->background_color.pixel);
2326 (void) XSetForeground(display,annotate_context,
2327 windows->pixel_info->foreground_color.pixel);
2328 id=XCommandWidget(display,windows,AnnotateMenu,&event);
2329 (void) XSetBackground(display,annotate_context,
2330 windows->pixel_info->pen_colors[box_id].pixel);
2331 (void) XSetForeground(display,annotate_context,
2332 windows->pixel_info->pen_colors[pen_id].pixel);
2333 if (id < 0)
2334 continue;
2335 switch (TextCommands[id])
2336 {
2337 case TextHelpCommand:
2338 {
2339 XTextViewWidget(display,resource_info,windows,MagickFalse,
2340 "Help Viewer - Image Annotation",ImageAnnotateHelp);
2341 (void) XCheckDefineCursor(display,windows->image.id,cursor);
2342 break;
2343 }
2344 case TextApplyCommand:
2345 {
2346 /*
2347 Finished annotating.
2348 */
2349 annotate_info->width=(unsigned int) XTextWidth(font_info,
2350 annotate_info->text,(int) strlen(annotate_info->text));
2351 XRefreshWindow(display,&windows->image,&text_event);
2352 state|=ExitState;
2353 break;
2354 }
2355 default:
2356 break;
2357 }
2358 continue;
2359 }
2360 /*
2361 Erase text cursor.
2362 */
2363 text_event.xexpose.x=x;
2364 text_event.xexpose.y=y-font_info->max_bounds.ascent;
2365 (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2366 (unsigned int) text_event.xexpose.width,(unsigned int)
2367 text_event.xexpose.height,MagickFalse);
2368 XRefreshWindow(display,&windows->image,&text_event);
2369 switch (event.type)
2370 {
2371 case ButtonPress:
2372 {
2373 if (event.xbutton.window != windows->image.id)
2374 break;
2375 if (event.xbutton.button == Button2)
2376 {
2377 /*
2378 Request primary selection.
2379 */
2380 (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2381 windows->image.id,CurrentTime);
2382 break;
2383 }
2384 break;
2385 }
2386 case Expose:
2387 {
2388 if (event.xexpose.count == 0)
2389 {
2390 XAnnotateInfo
2391 *text_info;
2392
2393 /*
2394 Refresh Image window.
2395 */
2396 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2397 text_info=annotate_info;
2398 while (text_info != (XAnnotateInfo *) NULL)
2399 {
2400 if (annotate_info->stencil == ForegroundStencil)
2401 (void) XDrawString(display,windows->image.id,annotate_context,
2402 text_info->x,text_info->y,text_info->text,
2403 (int) strlen(text_info->text));
2404 else
2405 (void) XDrawImageString(display,windows->image.id,
2406 annotate_context,text_info->x,text_info->y,text_info->text,
2407 (int) strlen(text_info->text));
2408 text_info=text_info->previous;
2409 }
2410 (void) XDrawString(display,windows->image.id,annotate_context,
2411 x,y,"_",1);
2412 }
2413 break;
2414 }
2415 case KeyPress:
2416 {
2417 int
2418 length;
2419
2420 if (event.xkey.window != windows->image.id)
2421 break;
2422 /*
2423 Respond to a user key press.
2424 */
2425 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2426 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2427 *(command+length)='\0';
2428 if (((event.xkey.state & ControlMask) != 0) ||
2429 ((event.xkey.state & Mod1Mask) != 0))
2430 state|=ModifierState;
2431 if ((state & ModifierState) != 0)
2432 switch ((int) key_symbol)
2433 {
2434 case XK_u:
2435 case XK_U:
2436 {
2437 key_symbol=DeleteCommand;
2438 break;
2439 }
2440 default:
2441 break;
2442 }
2443 switch ((int) key_symbol)
2444 {
2445 case XK_BackSpace:
2446 {
2447 /*
2448 Erase one character.
2449 */
2450 if (p == annotate_info->text)
2451 {
2452 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2453 break;
2454 else
2455 {
2456 /*
2457 Go to end of the previous line of text.
2458 */
2459 annotate_info=annotate_info->previous;
2460 p=annotate_info->text;
2461 x=annotate_info->x+annotate_info->width;
2462 y=annotate_info->y;
2463 if (annotate_info->width != 0)
2464 p+=strlen(annotate_info->text);
2465 break;
2466 }
2467 }
2468 p--;
2469 x-=XTextWidth(font_info,p,1);
2470 text_event.xexpose.x=x;
2471 text_event.xexpose.y=y-font_info->max_bounds.ascent;
2472 XRefreshWindow(display,&windows->image,&text_event);
2473 break;
2474 }
2475 case XK_bracketleft:
2476 {
2477 key_symbol=XK_Escape;
2478 break;
2479 }
2480 case DeleteCommand:
2481 {
2482 /*
2483 Erase the entire line of text.
2484 */
2485 while (p != annotate_info->text)
2486 {
2487 p--;
2488 x-=XTextWidth(font_info,p,1);
2489 text_event.xexpose.x=x;
2490 XRefreshWindow(display,&windows->image,&text_event);
2491 }
2492 break;
2493 }
2494 case XK_Escape:
2495 case XK_F20:
2496 {
2497 /*
2498 Finished annotating.
2499 */
2500 annotate_info->width=(unsigned int) XTextWidth(font_info,
2501 annotate_info->text,(int) strlen(annotate_info->text));
2502 XRefreshWindow(display,&windows->image,&text_event);
2503 state|=ExitState;
2504 break;
2505 }
2506 default:
2507 {
2508 /*
2509 Draw a single character on the Image window.
2510 */
2511 if ((state & ModifierState) != 0)
2512 break;
2513 if (*command == '\0')
2514 break;
2515 *p=(*command);
2516 if (annotate_info->stencil == ForegroundStencil)
2517 (void) XDrawString(display,windows->image.id,annotate_context,
2518 x,y,p,1);
2519 else
2520 (void) XDrawImageString(display,windows->image.id,
2521 annotate_context,x,y,p,1);
2522 x+=XTextWidth(font_info,p,1);
2523 p++;
2524 if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2525 break;
2526 }
2527 case XK_Return:
2528 case XK_KP_Enter:
2529 {
2530 /*
2531 Advance to the next line of text.
2532 */
2533 *p='\0';
2534 annotate_info->width=(unsigned int) XTextWidth(font_info,
2535 annotate_info->text,(int) strlen(annotate_info->text));
2536 if (annotate_info->next != (XAnnotateInfo *) NULL)
2537 {
2538 /*
2539 Line of text already exists.
2540 */
2541 annotate_info=annotate_info->next;
2542 x=annotate_info->x;
2543 y=annotate_info->y;
2544 p=annotate_info->text;
2545 break;
2546 }
2547 annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2548 sizeof(*annotate_info->next));
2549 if (annotate_info->next == (XAnnotateInfo *) NULL)
2550 return(MagickFalse);
2551 *annotate_info->next=(*annotate_info);
2552 annotate_info->next->previous=annotate_info;
2553 annotate_info=annotate_info->next;
2554 annotate_info->text=(char *) AcquireQuantumMemory((size_t)
cristy49e2d862010-11-12 02:50:30 +00002555 windows->image.width/MagickMax((ssize_t)
2556 font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
cristy3ed852e2009-09-05 21:47:34 +00002557 if (annotate_info->text == (char *) NULL)
2558 return(MagickFalse);
2559 annotate_info->y+=annotate_info->height;
2560 if (annotate_info->y > (int) windows->image.height)
2561 annotate_info->y=(int) annotate_info->height;
2562 annotate_info->next=(XAnnotateInfo *) NULL;
2563 x=annotate_info->x;
2564 y=annotate_info->y;
2565 p=annotate_info->text;
2566 break;
2567 }
2568 }
2569 break;
2570 }
2571 case KeyRelease:
2572 {
2573 /*
2574 Respond to a user key release.
2575 */
2576 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2577 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2578 state&=(~ModifierState);
2579 break;
2580 }
2581 case SelectionNotify:
2582 {
2583 Atom
2584 type;
2585
2586 int
2587 format;
2588
2589 unsigned char
2590 *data;
2591
cristyf2faecf2010-05-28 19:19:36 +00002592 unsigned long
cristy3ed852e2009-09-05 21:47:34 +00002593 after,
2594 length;
2595
2596 /*
2597 Obtain response from primary selection.
2598 */
2599 if (event.xselection.property == (Atom) None)
2600 break;
2601 status=XGetWindowProperty(display,event.xselection.requestor,
cristyecd0ab52010-05-30 14:59:20 +00002602 event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
cristy3ed852e2009-09-05 21:47:34 +00002603 &type,&format,&length,&after,&data);
2604 if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2605 (length == 0))
2606 break;
2607 /*
2608 Annotate Image window with primary selection.
2609 */
cristybb503372010-05-27 20:51:26 +00002610 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00002611 {
2612 if ((char) data[i] != '\n')
2613 {
2614 /*
2615 Draw a single character on the Image window.
2616 */
2617 *p=(char) data[i];
2618 (void) XDrawString(display,windows->image.id,annotate_context,
2619 x,y,p,1);
2620 x+=XTextWidth(font_info,p,1);
2621 p++;
2622 if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2623 continue;
2624 }
2625 /*
2626 Advance to the next line of text.
2627 */
2628 *p='\0';
2629 annotate_info->width=(unsigned int) XTextWidth(font_info,
2630 annotate_info->text,(int) strlen(annotate_info->text));
2631 if (annotate_info->next != (XAnnotateInfo *) NULL)
2632 {
2633 /*
2634 Line of text already exists.
2635 */
2636 annotate_info=annotate_info->next;
2637 x=annotate_info->x;
2638 y=annotate_info->y;
2639 p=annotate_info->text;
2640 continue;
2641 }
2642 annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2643 sizeof(*annotate_info->next));
2644 if (annotate_info->next == (XAnnotateInfo *) NULL)
2645 return(MagickFalse);
2646 *annotate_info->next=(*annotate_info);
2647 annotate_info->next->previous=annotate_info;
2648 annotate_info=annotate_info->next;
2649 annotate_info->text=(char *) AcquireQuantumMemory((size_t)
cristy49e2d862010-11-12 02:50:30 +00002650 windows->image.width/MagickMax((ssize_t)
2651 font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
cristy3ed852e2009-09-05 21:47:34 +00002652 if (annotate_info->text == (char *) NULL)
2653 return(MagickFalse);
2654 annotate_info->y+=annotate_info->height;
2655 if (annotate_info->y > (int) windows->image.height)
2656 annotate_info->y=(int) annotate_info->height;
2657 annotate_info->next=(XAnnotateInfo *) NULL;
2658 x=annotate_info->x;
2659 y=annotate_info->y;
2660 p=annotate_info->text;
2661 }
2662 (void) XFree((void *) data);
2663 break;
2664 }
2665 default:
2666 break;
2667 }
2668 } while ((state & ExitState) == 0);
2669 (void) XFreeCursor(display,cursor);
2670 /*
2671 Annotation is relative to image configuration.
2672 */
2673 width=(unsigned int) image->columns;
2674 height=(unsigned int) image->rows;
2675 x=0;
2676 y=0;
2677 if (windows->image.crop_geometry != (char *) NULL)
2678 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2679 /*
2680 Initialize annotated image.
2681 */
2682 XSetCursorState(display,windows,MagickTrue);
2683 XCheckRefreshWindows(display,windows);
2684 while (annotate_info != (XAnnotateInfo *) NULL)
2685 {
2686 if (annotate_info->width == 0)
2687 {
2688 /*
2689 No text on this line-- go to the next line of text.
2690 */
2691 previous_info=annotate_info->previous;
2692 annotate_info->text=(char *)
2693 RelinquishMagickMemory(annotate_info->text);
2694 annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2695 annotate_info=previous_info;
2696 continue;
2697 }
2698 /*
2699 Determine pixel index for box and pen color.
2700 */
2701 windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2702 if (windows->pixel_info->colors != 0)
cristybb503372010-05-27 20:51:26 +00002703 for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002704 if (windows->pixel_info->pixels[i] ==
2705 windows->pixel_info->pen_colors[box_id].pixel)
2706 {
2707 windows->pixel_info->box_index=(unsigned short) i;
2708 break;
2709 }
2710 windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2711 if (windows->pixel_info->colors != 0)
cristybb503372010-05-27 20:51:26 +00002712 for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002713 if (windows->pixel_info->pixels[i] ==
2714 windows->pixel_info->pen_colors[pen_id].pixel)
2715 {
2716 windows->pixel_info->pen_index=(unsigned short) i;
2717 break;
2718 }
2719 /*
2720 Define the annotate geometry string.
2721 */
2722 annotate_info->x=(int)
2723 width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2724 annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2725 windows->image.y)/windows->image.ximage->height;
cristyb51dff52011-05-19 16:55:47 +00002726 (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00002727 "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2728 height*annotate_info->height/windows->image.ximage->height,
2729 annotate_info->x+x,annotate_info->y+y);
2730 /*
2731 Annotate image with text.
2732 */
2733 status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
2734 if (status == 0)
2735 return(MagickFalse);
2736 /*
2737 Free up memory.
2738 */
2739 previous_info=annotate_info->previous;
2740 annotate_info->text=DestroyString(annotate_info->text);
2741 annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2742 annotate_info=previous_info;
2743 }
2744 (void) XSetForeground(display,annotate_context,
2745 windows->pixel_info->foreground_color.pixel);
2746 (void) XSetBackground(display,annotate_context,
2747 windows->pixel_info->background_color.pixel);
2748 (void) XSetFont(display,annotate_context,windows->font_info->fid);
2749 XSetCursorState(display,windows,MagickFalse);
2750 (void) XFreeFont(display,font_info);
2751 /*
2752 Update image configuration.
2753 */
2754 XConfigureImageColormap(display,resource_info,windows,image);
cristy051718b2011-08-28 22:49:25 +00002755 (void) XConfigureImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00002756 return(MagickTrue);
2757}
2758
2759/*
2760%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2761% %
2762% %
2763% %
2764+ X B a c k g r o u n d I m a g e %
2765% %
2766% %
2767% %
2768%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2769%
2770% XBackgroundImage() displays the image in the background of a window.
2771%
2772% The format of the XBackgroundImage method is:
2773%
2774% MagickBooleanType XBackgroundImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00002775% XResourceInfo *resource_info,XWindows *windows,Image **image,
2776% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002777%
2778% A description of each parameter follows:
2779%
2780% o display: Specifies a connection to an X server; returned from
2781% XOpenDisplay.
2782%
2783% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2784%
2785% o windows: Specifies a pointer to a XWindows structure.
2786%
2787% o image: the image.
2788%
cristy051718b2011-08-28 22:49:25 +00002789% o exception: return any errors or warnings in this structure.
2790%
cristy3ed852e2009-09-05 21:47:34 +00002791*/
2792static MagickBooleanType XBackgroundImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00002793 XResourceInfo *resource_info,XWindows *windows,Image **image,
2794 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002795{
2796#define BackgroundImageTag "Background/Image"
2797
2798 int
2799 status;
2800
2801 static char
2802 window_id[MaxTextExtent] = "root";
2803
2804 XResourceInfo
2805 background_resources;
2806
2807 /*
2808 Put image in background.
2809 */
2810 status=XDialogWidget(display,windows,"Background",
2811 "Enter window id (id 0x00 selects window with pointer):",window_id);
2812 if (*window_id == '\0')
2813 return(MagickFalse);
cristy051718b2011-08-28 22:49:25 +00002814 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2815 exception);
cristy3ed852e2009-09-05 21:47:34 +00002816 XInfoWidget(display,windows,BackgroundImageTag);
2817 XSetCursorState(display,windows,MagickTrue);
2818 XCheckRefreshWindows(display,windows);
2819 background_resources=(*resource_info);
2820 background_resources.window_id=window_id;
2821 background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
cristy051718b2011-08-28 22:49:25 +00002822 status=XDisplayBackgroundImage(display,&background_resources,*image,
2823 exception);
cristy3ed852e2009-09-05 21:47:34 +00002824 if (status != MagickFalse)
2825 XClientMessage(display,windows->image.id,windows->im_protocols,
2826 windows->im_retain_colors,CurrentTime);
2827 XSetCursorState(display,windows,MagickFalse);
cristy051718b2011-08-28 22:49:25 +00002828 (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2829 exception);
cristy3ed852e2009-09-05 21:47:34 +00002830 return(MagickTrue);
2831}
2832
2833/*
2834%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2835% %
2836% %
2837% %
2838+ X C h o p I m a g e %
2839% %
2840% %
2841% %
2842%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2843%
2844% XChopImage() chops the X image.
2845%
2846% The format of the XChopImage method is:
2847%
2848% MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +00002849% XWindows *windows,Image **image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002850%
2851% A description of each parameter follows:
2852%
2853% o display: Specifies a connection to an X server; returned from
2854% XOpenDisplay.
2855%
2856% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2857%
2858% o windows: Specifies a pointer to a XWindows structure.
2859%
2860% o image: the image.
2861%
cristy051718b2011-08-28 22:49:25 +00002862% o exception: return any errors or warnings in this structure.
2863%
cristy3ed852e2009-09-05 21:47:34 +00002864*/
2865static MagickBooleanType XChopImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00002866 XResourceInfo *resource_info,XWindows *windows,Image **image,
2867 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002868{
2869 static const char
2870 *ChopMenu[] =
2871 {
2872 "Direction",
2873 "Help",
2874 "Dismiss",
2875 (char *) NULL
2876 };
2877
2878 static ModeType
2879 direction = HorizontalChopCommand;
2880
2881 static const ModeType
2882 ChopCommands[] =
2883 {
2884 ChopDirectionCommand,
2885 ChopHelpCommand,
2886 ChopDismissCommand
2887 },
2888 DirectionCommands[] =
2889 {
2890 HorizontalChopCommand,
2891 VerticalChopCommand
2892 };
2893
2894 char
2895 text[MaxTextExtent];
2896
2897 Image
2898 *chop_image;
2899
2900 int
2901 id,
2902 x,
2903 y;
2904
2905 MagickRealType
2906 scale_factor;
2907
2908 RectangleInfo
2909 chop_info;
2910
2911 unsigned int
2912 distance,
2913 height,
2914 width;
2915
cristybb503372010-05-27 20:51:26 +00002916 size_t
cristy3ed852e2009-09-05 21:47:34 +00002917 state;
2918
2919 XEvent
2920 event;
2921
2922 XSegment
2923 segment_info;
2924
2925 /*
2926 Map Command widget.
2927 */
2928 (void) CloneString(&windows->command.name,"Chop");
2929 windows->command.data=1;
2930 (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2931 (void) XMapRaised(display,windows->command.id);
2932 XClientMessage(display,windows->image.id,windows->im_protocols,
2933 windows->im_update_widget,CurrentTime);
2934 /*
2935 Track pointer until button 1 is pressed.
2936 */
2937 XQueryPosition(display,windows->image.id,&x,&y);
2938 (void) XSelectInput(display,windows->image.id,
2939 windows->image.attributes.event_mask | PointerMotionMask);
2940 state=DefaultState;
2941 do
2942 {
2943 if (windows->info.mapped != MagickFalse)
2944 {
2945 /*
2946 Display pointer position.
2947 */
cristyb51dff52011-05-19 16:55:47 +00002948 (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
cristy3ed852e2009-09-05 21:47:34 +00002949 x+windows->image.x,y+windows->image.y);
2950 XInfoWidget(display,windows,text);
2951 }
2952 /*
2953 Wait for next event.
2954 */
2955 XScreenEvent(display,windows,&event);
2956 if (event.xany.window == windows->command.id)
2957 {
2958 /*
2959 Select a command from the Command widget.
2960 */
2961 id=XCommandWidget(display,windows,ChopMenu,&event);
2962 if (id < 0)
2963 continue;
2964 switch (ChopCommands[id])
2965 {
2966 case ChopDirectionCommand:
2967 {
2968 char
2969 command[MaxTextExtent];
2970
2971 static const char
2972 *Directions[] =
2973 {
2974 "horizontal",
2975 "vertical",
2976 (char *) NULL,
2977 };
2978
2979 /*
2980 Select a command from the pop-up menu.
2981 */
2982 id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2983 if (id >= 0)
2984 direction=DirectionCommands[id];
2985 break;
2986 }
2987 case ChopHelpCommand:
2988 {
2989 XTextViewWidget(display,resource_info,windows,MagickFalse,
2990 "Help Viewer - Image Chop",ImageChopHelp);
2991 break;
2992 }
2993 case ChopDismissCommand:
2994 {
2995 /*
2996 Prematurely exit.
2997 */
2998 state|=EscapeState;
2999 state|=ExitState;
3000 break;
3001 }
3002 default:
3003 break;
3004 }
3005 continue;
3006 }
3007 switch (event.type)
3008 {
3009 case ButtonPress:
3010 {
3011 if (event.xbutton.button != Button1)
3012 break;
3013 if (event.xbutton.window != windows->image.id)
3014 break;
3015 /*
3016 User has committed to start point of chopping line.
3017 */
3018 segment_info.x1=(short int) event.xbutton.x;
3019 segment_info.x2=(short int) event.xbutton.x;
3020 segment_info.y1=(short int) event.xbutton.y;
3021 segment_info.y2=(short int) event.xbutton.y;
3022 state|=ExitState;
3023 break;
3024 }
3025 case ButtonRelease:
3026 break;
3027 case Expose:
3028 break;
3029 case KeyPress:
3030 {
3031 char
3032 command[MaxTextExtent];
3033
3034 KeySym
3035 key_symbol;
3036
3037 if (event.xkey.window != windows->image.id)
3038 break;
3039 /*
3040 Respond to a user key press.
3041 */
3042 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3043 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3044 switch ((int) key_symbol)
3045 {
3046 case XK_Escape:
3047 case XK_F20:
3048 {
3049 /*
3050 Prematurely exit.
3051 */
3052 state|=EscapeState;
3053 state|=ExitState;
3054 break;
3055 }
3056 case XK_F1:
3057 case XK_Help:
3058 {
3059 (void) XSetFunction(display,windows->image.highlight_context,
3060 GXcopy);
3061 XTextViewWidget(display,resource_info,windows,MagickFalse,
3062 "Help Viewer - Image Chop",ImageChopHelp);
3063 (void) XSetFunction(display,windows->image.highlight_context,
3064 GXinvert);
3065 break;
3066 }
3067 default:
3068 {
3069 (void) XBell(display,0);
3070 break;
3071 }
3072 }
3073 break;
3074 }
3075 case MotionNotify:
3076 {
3077 /*
3078 Map and unmap Info widget as text cursor crosses its boundaries.
3079 */
3080 x=event.xmotion.x;
3081 y=event.xmotion.y;
3082 if (windows->info.mapped != MagickFalse)
3083 {
3084 if ((x < (int) (windows->info.x+windows->info.width)) &&
3085 (y < (int) (windows->info.y+windows->info.height)))
3086 (void) XWithdrawWindow(display,windows->info.id,
3087 windows->info.screen);
3088 }
3089 else
3090 if ((x > (int) (windows->info.x+windows->info.width)) ||
3091 (y > (int) (windows->info.y+windows->info.height)))
3092 (void) XMapWindow(display,windows->info.id);
3093 }
3094 }
3095 } while ((state & ExitState) == 0);
3096 (void) XSelectInput(display,windows->image.id,
3097 windows->image.attributes.event_mask);
3098 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3099 if ((state & EscapeState) != 0)
3100 return(MagickTrue);
3101 /*
3102 Draw line as pointer moves until the mouse button is released.
3103 */
3104 chop_info.width=0;
3105 chop_info.height=0;
3106 chop_info.x=0;
3107 chop_info.y=0;
3108 distance=0;
3109 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3110 state=DefaultState;
3111 do
3112 {
3113 if (distance > 9)
3114 {
3115 /*
3116 Display info and draw chopping line.
3117 */
3118 if (windows->info.mapped == MagickFalse)
3119 (void) XMapWindow(display,windows->info.id);
cristyb51dff52011-05-19 16:55:47 +00003120 (void) FormatLocaleString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00003121 " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +00003122 chop_info.height,(double) chop_info.x,(double) chop_info.y);
cristy3ed852e2009-09-05 21:47:34 +00003123 XInfoWidget(display,windows,text);
3124 XHighlightLine(display,windows->image.id,
3125 windows->image.highlight_context,&segment_info);
3126 }
3127 else
3128 if (windows->info.mapped != MagickFalse)
3129 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3130 /*
3131 Wait for next event.
3132 */
3133 XScreenEvent(display,windows,&event);
3134 if (distance > 9)
3135 XHighlightLine(display,windows->image.id,
3136 windows->image.highlight_context,&segment_info);
3137 switch (event.type)
3138 {
3139 case ButtonPress:
3140 {
3141 segment_info.x2=(short int) event.xmotion.x;
3142 segment_info.y2=(short int) event.xmotion.y;
3143 break;
3144 }
3145 case ButtonRelease:
3146 {
3147 /*
3148 User has committed to chopping line.
3149 */
3150 segment_info.x2=(short int) event.xbutton.x;
3151 segment_info.y2=(short int) event.xbutton.y;
3152 state|=ExitState;
3153 break;
3154 }
3155 case Expose:
3156 break;
3157 case MotionNotify:
3158 {
3159 segment_info.x2=(short int) event.xmotion.x;
3160 segment_info.y2=(short int) event.xmotion.y;
3161 }
3162 default:
3163 break;
3164 }
3165 /*
3166 Check boundary conditions.
3167 */
3168 if (segment_info.x2 < 0)
3169 segment_info.x2=0;
3170 else
3171 if (segment_info.x2 > windows->image.ximage->width)
3172 segment_info.x2=windows->image.ximage->width;
3173 if (segment_info.y2 < 0)
3174 segment_info.y2=0;
3175 else
3176 if (segment_info.y2 > windows->image.ximage->height)
3177 segment_info.y2=windows->image.ximage->height;
3178 distance=(unsigned int)
3179 (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3180 ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3181 /*
3182 Compute chopping geometry.
3183 */
3184 if (direction == HorizontalChopCommand)
3185 {
cristybb503372010-05-27 20:51:26 +00003186 chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
cristy49e2d862010-11-12 02:50:30 +00003187 chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
cristy3ed852e2009-09-05 21:47:34 +00003188 chop_info.height=0;
3189 chop_info.y=0;
3190 if (segment_info.x1 > (int) segment_info.x2)
3191 {
cristybb503372010-05-27 20:51:26 +00003192 chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
cristy49e2d862010-11-12 02:50:30 +00003193 chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
cristy3ed852e2009-09-05 21:47:34 +00003194 }
3195 }
3196 else
3197 {
3198 chop_info.width=0;
cristybb503372010-05-27 20:51:26 +00003199 chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
cristy3ed852e2009-09-05 21:47:34 +00003200 chop_info.x=0;
cristy49e2d862010-11-12 02:50:30 +00003201 chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
cristy3ed852e2009-09-05 21:47:34 +00003202 if (segment_info.y1 > segment_info.y2)
3203 {
cristy49e2d862010-11-12 02:50:30 +00003204 chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3205 chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
cristy3ed852e2009-09-05 21:47:34 +00003206 }
3207 }
3208 } while ((state & ExitState) == 0);
3209 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3210 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3211 if (distance <= 9)
3212 return(MagickTrue);
3213 /*
3214 Image chopping is relative to image configuration.
3215 */
cristy051718b2011-08-28 22:49:25 +00003216 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3217 exception);
cristy3ed852e2009-09-05 21:47:34 +00003218 XSetCursorState(display,windows,MagickTrue);
3219 XCheckRefreshWindows(display,windows);
3220 windows->image.window_changes.width=windows->image.ximage->width-
3221 (unsigned int) chop_info.width;
3222 windows->image.window_changes.height=windows->image.ximage->height-
3223 (unsigned int) chop_info.height;
3224 width=(unsigned int) (*image)->columns;
3225 height=(unsigned int) (*image)->rows;
3226 x=0;
3227 y=0;
3228 if (windows->image.crop_geometry != (char *) NULL)
3229 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3230 scale_factor=(MagickRealType) width/windows->image.ximage->width;
3231 chop_info.x+=x;
cristy49e2d862010-11-12 02:50:30 +00003232 chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003233 chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3234 scale_factor=(MagickRealType) height/windows->image.ximage->height;
3235 chop_info.y+=y;
cristy49e2d862010-11-12 02:50:30 +00003236 chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003237 chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3238 /*
3239 Chop image.
3240 */
cristy051718b2011-08-28 22:49:25 +00003241 chop_image=ChopImage(*image,&chop_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003242 XSetCursorState(display,windows,MagickFalse);
3243 if (chop_image == (Image *) NULL)
3244 return(MagickFalse);
3245 *image=DestroyImage(*image);
3246 *image=chop_image;
3247 /*
3248 Update image configuration.
3249 */
3250 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00003251 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003252 return(MagickTrue);
3253}
3254
3255/*
3256%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3257% %
3258% %
3259% %
3260+ X C o l o r E d i t I m a g e %
3261% %
3262% %
3263% %
3264%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3265%
3266% XColorEditImage() allows the user to interactively change the color of one
3267% pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3268%
3269% The format of the XColorEditImage method is:
3270%
3271% MagickBooleanType XColorEditImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00003272% XResourceInfo *resource_info,XWindows *windows,Image **image,
3273% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003274%
3275% A description of each parameter follows:
3276%
3277% o display: Specifies a connection to an X server; returned from
3278% XOpenDisplay.
3279%
3280% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3281%
3282% o windows: Specifies a pointer to a XWindows structure.
3283%
3284% o image: the image; returned from ReadImage.
3285%
cristy051718b2011-08-28 22:49:25 +00003286% o exception: return any errors or warnings in this structure.
3287%
cristy3ed852e2009-09-05 21:47:34 +00003288*/
cristy3ed852e2009-09-05 21:47:34 +00003289static MagickBooleanType XColorEditImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00003290 XResourceInfo *resource_info,XWindows *windows,Image **image,
3291 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003292{
3293 static const char
3294 *ColorEditMenu[] =
3295 {
3296 "Method",
3297 "Pixel Color",
3298 "Border Color",
3299 "Fuzz",
3300 "Undo",
3301 "Help",
3302 "Dismiss",
3303 (char *) NULL
3304 };
3305
3306 static const ModeType
3307 ColorEditCommands[] =
3308 {
3309 ColorEditMethodCommand,
3310 ColorEditColorCommand,
3311 ColorEditBorderCommand,
3312 ColorEditFuzzCommand,
3313 ColorEditUndoCommand,
3314 ColorEditHelpCommand,
3315 ColorEditDismissCommand
3316 };
3317
3318 static PaintMethod
3319 method = PointMethod;
3320
3321 static unsigned int
3322 pen_id = 0;
3323
3324 static XColor
3325 border_color = { 0, 0, 0, 0, 0, 0 };
3326
3327 char
3328 command[MaxTextExtent],
3329 text[MaxTextExtent];
3330
3331 Cursor
3332 cursor;
3333
cristy3ed852e2009-09-05 21:47:34 +00003334 int
3335 entry,
3336 id,
3337 x,
3338 x_offset,
3339 y,
3340 y_offset;
3341
cristy4c08aed2011-07-01 19:47:50 +00003342 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00003343 *q;
3344
cristybb503372010-05-27 20:51:26 +00003345 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003346 i;
3347
3348 unsigned int
3349 height,
3350 width;
3351
cristybb503372010-05-27 20:51:26 +00003352 size_t
cristy3ed852e2009-09-05 21:47:34 +00003353 state;
3354
3355 XColor
3356 color;
3357
3358 XEvent
3359 event;
3360
3361 /*
3362 Map Command widget.
3363 */
3364 (void) CloneString(&windows->command.name,"Color Edit");
3365 windows->command.data=4;
3366 (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3367 (void) XMapRaised(display,windows->command.id);
3368 XClientMessage(display,windows->image.id,windows->im_protocols,
3369 windows->im_update_widget,CurrentTime);
3370 /*
3371 Make cursor.
3372 */
3373 cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3374 resource_info->background_color,resource_info->foreground_color);
3375 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3376 /*
3377 Track pointer until button 1 is pressed.
3378 */
3379 XQueryPosition(display,windows->image.id,&x,&y);
3380 (void) XSelectInput(display,windows->image.id,
3381 windows->image.attributes.event_mask | PointerMotionMask);
3382 state=DefaultState;
3383 do
3384 {
3385 if (windows->info.mapped != MagickFalse)
3386 {
3387 /*
3388 Display pointer position.
3389 */
cristyb51dff52011-05-19 16:55:47 +00003390 (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
cristy3ed852e2009-09-05 21:47:34 +00003391 x+windows->image.x,y+windows->image.y);
3392 XInfoWidget(display,windows,text);
3393 }
3394 /*
3395 Wait for next event.
3396 */
3397 XScreenEvent(display,windows,&event);
3398 if (event.xany.window == windows->command.id)
3399 {
3400 /*
3401 Select a command from the Command widget.
3402 */
3403 id=XCommandWidget(display,windows,ColorEditMenu,&event);
3404 if (id < 0)
3405 {
3406 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3407 continue;
3408 }
3409 switch (ColorEditCommands[id])
3410 {
3411 case ColorEditMethodCommand:
3412 {
3413 char
3414 **methods;
3415
3416 /*
3417 Select a method from the pop-up menu.
3418 */
cristy042ee782011-04-22 18:48:30 +00003419 methods=(char **) GetCommandOptions(MagickMethodOptions);
cristy3ed852e2009-09-05 21:47:34 +00003420 if (methods == (char **) NULL)
3421 break;
3422 entry=XMenuWidget(display,windows,ColorEditMenu[id],
3423 (const char **) methods,command);
3424 if (entry >= 0)
cristy042ee782011-04-22 18:48:30 +00003425 method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
cristy3ed852e2009-09-05 21:47:34 +00003426 MagickFalse,methods[entry]);
3427 methods=DestroyStringList(methods);
3428 break;
3429 }
3430 case ColorEditColorCommand:
3431 {
3432 const char
3433 *ColorMenu[MaxNumberPens];
3434
3435 int
3436 pen_number;
3437
3438 /*
3439 Initialize menu selections.
3440 */
3441 for (i=0; i < (int) (MaxNumberPens-2); i++)
3442 ColorMenu[i]=resource_info->pen_colors[i];
3443 ColorMenu[MaxNumberPens-2]="Browser...";
3444 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3445 /*
3446 Select a pen color from the pop-up menu.
3447 */
3448 pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3449 (const char **) ColorMenu,command);
3450 if (pen_number < 0)
3451 break;
3452 if (pen_number == (MaxNumberPens-2))
3453 {
3454 static char
3455 color_name[MaxTextExtent] = "gray";
3456
3457 /*
3458 Select a pen color from a dialog.
3459 */
3460 resource_info->pen_colors[pen_number]=color_name;
3461 XColorBrowserWidget(display,windows,"Select",color_name);
3462 if (*color_name == '\0')
3463 break;
3464 }
3465 /*
3466 Set pen color.
3467 */
3468 (void) XParseColor(display,windows->map_info->colormap,
3469 resource_info->pen_colors[pen_number],&color);
3470 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3471 (unsigned int) MaxColors,&color);
3472 windows->pixel_info->pen_colors[pen_number]=color;
3473 pen_id=(unsigned int) pen_number;
3474 break;
3475 }
3476 case ColorEditBorderCommand:
3477 {
3478 const char
3479 *ColorMenu[MaxNumberPens];
3480
3481 int
3482 pen_number;
3483
3484 /*
3485 Initialize menu selections.
3486 */
3487 for (i=0; i < (int) (MaxNumberPens-2); i++)
3488 ColorMenu[i]=resource_info->pen_colors[i];
3489 ColorMenu[MaxNumberPens-2]="Browser...";
3490 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3491 /*
3492 Select a pen color from the pop-up menu.
3493 */
3494 pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3495 (const char **) ColorMenu,command);
3496 if (pen_number < 0)
3497 break;
3498 if (pen_number == (MaxNumberPens-2))
3499 {
3500 static char
3501 color_name[MaxTextExtent] = "gray";
3502
3503 /*
3504 Select a pen color from a dialog.
3505 */
3506 resource_info->pen_colors[pen_number]=color_name;
3507 XColorBrowserWidget(display,windows,"Select",color_name);
3508 if (*color_name == '\0')
3509 break;
3510 }
3511 /*
3512 Set border color.
3513 */
3514 (void) XParseColor(display,windows->map_info->colormap,
3515 resource_info->pen_colors[pen_number],&border_color);
3516 break;
3517 }
3518 case ColorEditFuzzCommand:
3519 {
3520 static char
3521 fuzz[MaxTextExtent];
3522
3523 static const char
3524 *FuzzMenu[] =
3525 {
3526 "0%",
3527 "2%",
3528 "5%",
3529 "10%",
3530 "15%",
3531 "Dialog...",
3532 (char *) NULL,
3533 };
3534
3535 /*
3536 Select a command from the pop-up menu.
3537 */
3538 entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3539 command);
3540 if (entry < 0)
3541 break;
3542 if (entry != 5)
3543 {
cristy40a08ad2010-02-09 02:27:44 +00003544 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
3545 QuantumRange+1.0);
cristy3ed852e2009-09-05 21:47:34 +00003546 break;
3547 }
3548 (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3549 (void) XDialogWidget(display,windows,"Ok",
3550 "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3551 if (*fuzz == '\0')
3552 break;
3553 (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
cristyf2f27272009-12-17 14:48:46 +00003554 (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
cristy3ed852e2009-09-05 21:47:34 +00003555 break;
3556 }
3557 case ColorEditUndoCommand:
3558 {
3559 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
cristy051718b2011-08-28 22:49:25 +00003560 image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003561 break;
3562 }
3563 case ColorEditHelpCommand:
3564 default:
3565 {
3566 XTextViewWidget(display,resource_info,windows,MagickFalse,
3567 "Help Viewer - Image Annotation",ImageColorEditHelp);
3568 break;
3569 }
3570 case ColorEditDismissCommand:
3571 {
3572 /*
3573 Prematurely exit.
3574 */
3575 state|=EscapeState;
3576 state|=ExitState;
3577 break;
3578 }
3579 }
3580 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3581 continue;
3582 }
3583 switch (event.type)
3584 {
3585 case ButtonPress:
3586 {
3587 if (event.xbutton.button != Button1)
3588 break;
3589 if ((event.xbutton.window != windows->image.id) &&
3590 (event.xbutton.window != windows->magnify.id))
3591 break;
3592 /*
3593 exit loop.
3594 */
3595 x=event.xbutton.x;
3596 y=event.xbutton.y;
3597 (void) XMagickCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +00003598 SaveToUndoBufferCommand,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003599 state|=UpdateConfigurationState;
3600 break;
3601 }
3602 case ButtonRelease:
3603 {
3604 if (event.xbutton.button != Button1)
3605 break;
3606 if ((event.xbutton.window != windows->image.id) &&
3607 (event.xbutton.window != windows->magnify.id))
3608 break;
3609 /*
3610 Update colormap information.
3611 */
3612 x=event.xbutton.x;
3613 y=event.xbutton.y;
3614 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00003615 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003616 XInfoWidget(display,windows,text);
3617 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3618 state&=(~UpdateConfigurationState);
3619 break;
3620 }
3621 case Expose:
3622 break;
3623 case KeyPress:
3624 {
3625 KeySym
3626 key_symbol;
3627
3628 if (event.xkey.window == windows->magnify.id)
3629 {
3630 Window
3631 window;
3632
3633 window=windows->magnify.id;
3634 while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3635 }
3636 if (event.xkey.window != windows->image.id)
3637 break;
3638 /*
3639 Respond to a user key press.
3640 */
3641 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3642 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3643 switch ((int) key_symbol)
3644 {
3645 case XK_Escape:
3646 case XK_F20:
3647 {
3648 /*
3649 Prematurely exit.
3650 */
3651 state|=ExitState;
3652 break;
3653 }
3654 case XK_F1:
3655 case XK_Help:
3656 {
3657 XTextViewWidget(display,resource_info,windows,MagickFalse,
3658 "Help Viewer - Image Annotation",ImageColorEditHelp);
3659 break;
3660 }
3661 default:
3662 {
3663 (void) XBell(display,0);
3664 break;
3665 }
3666 }
3667 break;
3668 }
3669 case MotionNotify:
3670 {
3671 /*
3672 Map and unmap Info widget as cursor crosses its boundaries.
3673 */
3674 x=event.xmotion.x;
3675 y=event.xmotion.y;
3676 if (windows->info.mapped != MagickFalse)
3677 {
3678 if ((x < (int) (windows->info.x+windows->info.width)) &&
3679 (y < (int) (windows->info.y+windows->info.height)))
3680 (void) XWithdrawWindow(display,windows->info.id,
3681 windows->info.screen);
3682 }
3683 else
3684 if ((x > (int) (windows->info.x+windows->info.width)) ||
3685 (y > (int) (windows->info.y+windows->info.height)))
3686 (void) XMapWindow(display,windows->info.id);
3687 break;
3688 }
3689 default:
3690 break;
3691 }
3692 if (event.xany.window == windows->magnify.id)
3693 {
3694 x=windows->magnify.x-windows->image.x;
3695 y=windows->magnify.y-windows->image.y;
3696 }
3697 x_offset=x;
3698 y_offset=y;
3699 if ((state & UpdateConfigurationState) != 0)
3700 {
cristy49e2d862010-11-12 02:50:30 +00003701 CacheView
3702 *image_view;
3703
cristy3ed852e2009-09-05 21:47:34 +00003704 int
3705 x,
3706 y;
3707
3708 /*
3709 Pixel edit is relative to image configuration.
3710 */
3711 (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3712 MagickTrue);
3713 color=windows->pixel_info->pen_colors[pen_id];
3714 XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3715 width=(unsigned int) (*image)->columns;
3716 height=(unsigned int) (*image)->rows;
3717 x=0;
3718 y=0;
3719 if (windows->image.crop_geometry != (char *) NULL)
3720 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3721 &width,&height);
3722 x_offset=(int)
3723 (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3724 y_offset=(int)
3725 (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3726 if ((x_offset < 0) || (y_offset < 0))
3727 continue;
cristy49e2d862010-11-12 02:50:30 +00003728 if ((x_offset >= (int) (*image)->columns) ||
3729 (y_offset >= (int) (*image)->rows))
cristy3ed852e2009-09-05 21:47:34 +00003730 continue;
cristy49e2d862010-11-12 02:50:30 +00003731 image_view=AcquireCacheView(*image);
cristy3ed852e2009-09-05 21:47:34 +00003732 switch (method)
3733 {
3734 case PointMethod:
3735 default:
3736 {
3737 /*
3738 Update color information using point algorithm.
3739 */
cristy574cc262011-08-05 01:23:58 +00003740 if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003741 return(MagickFalse);
cristy49e2d862010-11-12 02:50:30 +00003742 q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
cristy574cc262011-08-05 01:23:58 +00003743 (ssize_t) y_offset,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003744 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003745 break;
cristy4c08aed2011-07-01 19:47:50 +00003746 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3747 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3748 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
cristy051718b2011-08-28 22:49:25 +00003749 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003750 break;
3751 }
3752 case ReplaceMethod:
3753 {
3754 PixelPacket
cristy4c08aed2011-07-01 19:47:50 +00003755 pixel,
cristy3ed852e2009-09-05 21:47:34 +00003756 target;
3757
3758 /*
3759 Update color information using replace algorithm.
3760 */
cristy49e2d862010-11-12 02:50:30 +00003761 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
cristy051718b2011-08-28 22:49:25 +00003762 (ssize_t) y_offset,&target,exception);
cristy3ed852e2009-09-05 21:47:34 +00003763 if ((*image)->storage_class == DirectClass)
3764 {
cristy49e2d862010-11-12 02:50:30 +00003765 for (y=0; y < (int) (*image)->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003766 {
cristy49e2d862010-11-12 02:50:30 +00003767 q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3768 (*image)->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003769 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003770 break;
3771 for (x=0; x < (int) (*image)->columns; x++)
3772 {
cristy4c08aed2011-07-01 19:47:50 +00003773 GetPixelPacket(*image,q,&pixel);
3774 if (IsFuzzyEquivalencePixelPacket(*image,&pixel,&target))
cristy3ed852e2009-09-05 21:47:34 +00003775 {
cristy4c08aed2011-07-01 19:47:50 +00003776 SetPixelRed(*image,ScaleShortToQuantum(
3777 color.red),q);
3778 SetPixelGreen(*image,ScaleShortToQuantum(
3779 color.green),q);
3780 SetPixelBlue(*image,ScaleShortToQuantum(
3781 color.blue),q);
cristy3ed852e2009-09-05 21:47:34 +00003782 }
cristyed231572011-07-14 02:18:59 +00003783 q+=GetPixelChannels(*image);
cristy3ed852e2009-09-05 21:47:34 +00003784 }
cristy49e2d862010-11-12 02:50:30 +00003785 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003786 break;
3787 }
3788 }
3789 else
3790 {
cristy49e2d862010-11-12 02:50:30 +00003791 for (i=0; i < (ssize_t) (*image)->colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00003792 if (IsFuzzyEquivalencePixelPacket(*image,(*image)->colormap+i,&target))
cristy3ed852e2009-09-05 21:47:34 +00003793 {
cristy4c08aed2011-07-01 19:47:50 +00003794 (*image)->colormap[i].red=ScaleShortToQuantum(
3795 color.red);
cristy3ed852e2009-09-05 21:47:34 +00003796 (*image)->colormap[i].green=ScaleShortToQuantum(
3797 color.green);
3798 (*image)->colormap[i].blue=ScaleShortToQuantum(
3799 color.blue);
3800 }
3801 (void) SyncImage(*image);
3802 }
3803 break;
3804 }
3805 case FloodfillMethod:
3806 case FillToBorderMethod:
3807 {
3808 DrawInfo
3809 *draw_info;
3810
cristy4c08aed2011-07-01 19:47:50 +00003811 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003812 target;
3813
3814 /*
3815 Update color information using floodfill algorithm.
3816 */
cristy49e2d862010-11-12 02:50:30 +00003817 (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
3818 (ssize_t) y_offset,&target,exception);
cristy3ed852e2009-09-05 21:47:34 +00003819 if (method == FillToBorderMethod)
3820 {
3821 target.red=(MagickRealType)
3822 ScaleShortToQuantum(border_color.red);
3823 target.green=(MagickRealType)
3824 ScaleShortToQuantum(border_color.green);
3825 target.blue=(MagickRealType)
3826 ScaleShortToQuantum(border_color.blue);
3827 }
3828 draw_info=CloneDrawInfo(resource_info->image_info,
3829 (DrawInfo *) NULL);
3830 (void) QueryColorDatabase(resource_info->pen_colors[pen_id],
3831 &draw_info->fill,exception);
cristyd42d9952011-07-08 14:21:50 +00003832 (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
3833 x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
cristy189e84c2011-08-27 18:08:53 +00003834 MagickFalse : MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00003835 draw_info=DestroyDrawInfo(draw_info);
3836 break;
3837 }
3838 case ResetMethod:
3839 {
3840 /*
3841 Update color information using reset algorithm.
3842 */
cristy574cc262011-08-05 01:23:58 +00003843 if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003844 return(MagickFalse);
cristy49e2d862010-11-12 02:50:30 +00003845 for (y=0; y < (int) (*image)->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003846 {
cristy49e2d862010-11-12 02:50:30 +00003847 q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3848 (*image)->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003849 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003850 break;
3851 for (x=0; x < (int) (*image)->columns; x++)
3852 {
cristy4c08aed2011-07-01 19:47:50 +00003853 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3854 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3855 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
cristyed231572011-07-14 02:18:59 +00003856 q+=GetPixelChannels(*image);
cristy3ed852e2009-09-05 21:47:34 +00003857 }
cristy49e2d862010-11-12 02:50:30 +00003858 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003859 break;
3860 }
3861 break;
3862 }
3863 }
cristy49e2d862010-11-12 02:50:30 +00003864 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003865 state&=(~UpdateConfigurationState);
3866 }
3867 } while ((state & ExitState) == 0);
3868 (void) XSelectInput(display,windows->image.id,
3869 windows->image.attributes.event_mask);
3870 XSetCursorState(display,windows,MagickFalse);
3871 (void) XFreeCursor(display,cursor);
3872 return(MagickTrue);
3873}
3874
3875/*
3876%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3877% %
3878% %
3879% %
3880+ X C o m p o s i t e I m a g e %
3881% %
3882% %
3883% %
3884%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3885%
3886% XCompositeImage() requests an image name from the user, reads the image and
3887% composites it with the X window image at a location the user chooses with
3888% the pointer.
3889%
3890% The format of the XCompositeImage method is:
3891%
3892% MagickBooleanType XCompositeImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00003893% XResourceInfo *resource_info,XWindows *windows,Image *image,
3894% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003895%
3896% A description of each parameter follows:
3897%
3898% o display: Specifies a connection to an X server; returned from
3899% XOpenDisplay.
3900%
3901% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3902%
3903% o windows: Specifies a pointer to a XWindows structure.
3904%
3905% o image: the image; returned from ReadImage.
3906%
cristy051718b2011-08-28 22:49:25 +00003907% o exception: return any errors or warnings in this structure.
3908%
cristy3ed852e2009-09-05 21:47:34 +00003909*/
3910static MagickBooleanType XCompositeImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00003911 XResourceInfo *resource_info,XWindows *windows,Image *image,
3912 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003913{
3914 static char
3915 displacement_geometry[MaxTextExtent] = "30x30",
3916 filename[MaxTextExtent] = "\0";
3917
3918 static const char
3919 *CompositeMenu[] =
3920 {
3921 "Operators",
3922 "Dissolve",
3923 "Displace",
3924 "Help",
3925 "Dismiss",
3926 (char *) NULL
3927 };
3928
3929 static CompositeOperator
3930 compose = CopyCompositeOp;
3931
3932 static const ModeType
3933 CompositeCommands[] =
3934 {
3935 CompositeOperatorsCommand,
3936 CompositeDissolveCommand,
3937 CompositeDisplaceCommand,
3938 CompositeHelpCommand,
3939 CompositeDismissCommand
3940 };
3941
3942 char
3943 text[MaxTextExtent];
3944
3945 Cursor
3946 cursor;
3947
3948 Image
3949 *composite_image;
3950
3951 int
3952 entry,
3953 id,
3954 x,
3955 y;
3956
3957 MagickRealType
3958 blend,
3959 scale_factor;
3960
3961 RectangleInfo
3962 highlight_info,
3963 composite_info;
3964
3965 unsigned int
3966 height,
3967 width;
3968
cristybb503372010-05-27 20:51:26 +00003969 size_t
cristy3ed852e2009-09-05 21:47:34 +00003970 state;
3971
3972 XEvent
3973 event;
3974
3975 /*
3976 Request image file name from user.
3977 */
3978 XFileBrowserWidget(display,windows,"Composite",filename);
3979 if (*filename == '\0')
3980 return(MagickTrue);
3981 /*
3982 Read image.
3983 */
3984 XSetCursorState(display,windows,MagickTrue);
3985 XCheckRefreshWindows(display,windows);
3986 (void) CopyMagickString(resource_info->image_info->filename,filename,
3987 MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +00003988 composite_image=ReadImage(resource_info->image_info,exception);
3989 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00003990 XSetCursorState(display,windows,MagickFalse);
3991 if (composite_image == (Image *) NULL)
3992 return(MagickFalse);
3993 /*
3994 Map Command widget.
3995 */
3996 (void) CloneString(&windows->command.name,"Composite");
3997 windows->command.data=1;
3998 (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3999 (void) XMapRaised(display,windows->command.id);
4000 XClientMessage(display,windows->image.id,windows->im_protocols,
4001 windows->im_update_widget,CurrentTime);
4002 /*
4003 Track pointer until button 1 is pressed.
4004 */
4005 XQueryPosition(display,windows->image.id,&x,&y);
4006 (void) XSelectInput(display,windows->image.id,
4007 windows->image.attributes.event_mask | PointerMotionMask);
cristy49e2d862010-11-12 02:50:30 +00004008 composite_info.x=(ssize_t) windows->image.x+x;
4009 composite_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +00004010 composite_info.width=0;
4011 composite_info.height=0;
4012 cursor=XCreateFontCursor(display,XC_ul_angle);
4013 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4014 blend=0.0;
4015 state=DefaultState;
4016 do
4017 {
4018 if (windows->info.mapped != MagickFalse)
4019 {
4020 /*
4021 Display pointer position.
4022 */
cristyb51dff52011-05-19 16:55:47 +00004023 (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
cristyf2faecf2010-05-28 19:19:36 +00004024 (long) composite_info.x,(long) composite_info.y);
cristy3ed852e2009-09-05 21:47:34 +00004025 XInfoWidget(display,windows,text);
4026 }
4027 highlight_info=composite_info;
4028 highlight_info.x=composite_info.x-windows->image.x;
4029 highlight_info.y=composite_info.y-windows->image.y;
4030 XHighlightRectangle(display,windows->image.id,
4031 windows->image.highlight_context,&highlight_info);
4032 /*
4033 Wait for next event.
4034 */
4035 XScreenEvent(display,windows,&event);
4036 XHighlightRectangle(display,windows->image.id,
4037 windows->image.highlight_context,&highlight_info);
4038 if (event.xany.window == windows->command.id)
4039 {
4040 /*
4041 Select a command from the Command widget.
4042 */
4043 id=XCommandWidget(display,windows,CompositeMenu,&event);
4044 if (id < 0)
4045 continue;
4046 switch (CompositeCommands[id])
4047 {
4048 case CompositeOperatorsCommand:
4049 {
4050 char
4051 command[MaxTextExtent],
4052 **operators;
4053
4054 /*
4055 Select a command from the pop-up menu.
4056 */
cristy042ee782011-04-22 18:48:30 +00004057 operators=GetCommandOptions(MagickComposeOptions);
cristy3ed852e2009-09-05 21:47:34 +00004058 if (operators == (char **) NULL)
4059 break;
4060 entry=XMenuWidget(display,windows,CompositeMenu[id],
4061 (const char **) operators,command);
4062 if (entry >= 0)
cristy042ee782011-04-22 18:48:30 +00004063 compose=(CompositeOperator) ParseCommandOption(
cristy3ed852e2009-09-05 21:47:34 +00004064 MagickComposeOptions,MagickFalse,operators[entry]);
4065 operators=DestroyStringList(operators);
4066 break;
4067 }
4068 case CompositeDissolveCommand:
4069 {
4070 static char
4071 factor[MaxTextExtent] = "20.0";
4072
4073 /*
4074 Dissolve the two images a given percent.
4075 */
4076 (void) XSetFunction(display,windows->image.highlight_context,
4077 GXcopy);
4078 (void) XDialogWidget(display,windows,"Dissolve",
4079 "Enter the blend factor (0.0 - 99.9%):",factor);
4080 (void) XSetFunction(display,windows->image.highlight_context,
4081 GXinvert);
4082 if (*factor == '\0')
4083 break;
cristyc1acd842011-05-19 23:05:47 +00004084 blend=InterpretLocaleValue(factor,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004085 compose=DissolveCompositeOp;
4086 break;
4087 }
4088 case CompositeDisplaceCommand:
4089 {
4090 /*
4091 Get horizontal and vertical scale displacement geometry.
4092 */
4093 (void) XSetFunction(display,windows->image.highlight_context,
4094 GXcopy);
4095 (void) XDialogWidget(display,windows,"Displace",
4096 "Enter the horizontal and vertical scale:",displacement_geometry);
4097 (void) XSetFunction(display,windows->image.highlight_context,
4098 GXinvert);
4099 if (*displacement_geometry == '\0')
4100 break;
4101 compose=DisplaceCompositeOp;
4102 break;
4103 }
4104 case CompositeHelpCommand:
4105 {
4106 (void) XSetFunction(display,windows->image.highlight_context,
4107 GXcopy);
4108 XTextViewWidget(display,resource_info,windows,MagickFalse,
4109 "Help Viewer - Image Composite",ImageCompositeHelp);
4110 (void) XSetFunction(display,windows->image.highlight_context,
4111 GXinvert);
4112 break;
4113 }
4114 case CompositeDismissCommand:
4115 {
4116 /*
4117 Prematurely exit.
4118 */
4119 state|=EscapeState;
4120 state|=ExitState;
4121 break;
4122 }
4123 default:
4124 break;
4125 }
4126 continue;
4127 }
4128 switch (event.type)
4129 {
4130 case ButtonPress:
4131 {
4132 if (image->debug != MagickFalse)
4133 (void) LogMagickEvent(X11Event,GetMagickModule(),
4134 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4135 event.xbutton.button,event.xbutton.x,event.xbutton.y);
4136 if (event.xbutton.button != Button1)
4137 break;
4138 if (event.xbutton.window != windows->image.id)
4139 break;
4140 /*
4141 Change cursor.
4142 */
4143 composite_info.width=composite_image->columns;
4144 composite_info.height=composite_image->rows;
4145 (void) XCheckDefineCursor(display,windows->image.id,cursor);
cristy49e2d862010-11-12 02:50:30 +00004146 composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4147 composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +00004148 break;
4149 }
4150 case ButtonRelease:
4151 {
4152 if (image->debug != MagickFalse)
4153 (void) LogMagickEvent(X11Event,GetMagickModule(),
4154 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4155 event.xbutton.button,event.xbutton.x,event.xbutton.y);
4156 if (event.xbutton.button != Button1)
4157 break;
4158 if (event.xbutton.window != windows->image.id)
4159 break;
4160 if ((composite_info.width != 0) && (composite_info.height != 0))
4161 {
4162 /*
4163 User has selected the location of the composite image.
4164 */
cristy49e2d862010-11-12 02:50:30 +00004165 composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4166 composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +00004167 state|=ExitState;
4168 }
4169 break;
4170 }
4171 case Expose:
4172 break;
4173 case KeyPress:
4174 {
4175 char
4176 command[MaxTextExtent];
4177
4178 KeySym
4179 key_symbol;
4180
4181 int
4182 length;
4183
4184 if (event.xkey.window != windows->image.id)
4185 break;
4186 /*
4187 Respond to a user key press.
4188 */
4189 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4190 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4191 *(command+length)='\0';
4192 if (image->debug != MagickFalse)
4193 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00004194 "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
cristy3ed852e2009-09-05 21:47:34 +00004195 switch ((int) key_symbol)
4196 {
4197 case XK_Escape:
4198 case XK_F20:
4199 {
4200 /*
4201 Prematurely exit.
4202 */
4203 composite_image=DestroyImage(composite_image);
4204 state|=EscapeState;
4205 state|=ExitState;
4206 break;
4207 }
4208 case XK_F1:
4209 case XK_Help:
4210 {
4211 (void) XSetFunction(display,windows->image.highlight_context,
4212 GXcopy);
4213 XTextViewWidget(display,resource_info,windows,MagickFalse,
4214 "Help Viewer - Image Composite",ImageCompositeHelp);
4215 (void) XSetFunction(display,windows->image.highlight_context,
4216 GXinvert);
4217 break;
4218 }
4219 default:
4220 {
4221 (void) XBell(display,0);
4222 break;
4223 }
4224 }
4225 break;
4226 }
4227 case MotionNotify:
4228 {
4229 /*
4230 Map and unmap Info widget as text cursor crosses its boundaries.
4231 */
4232 x=event.xmotion.x;
4233 y=event.xmotion.y;
4234 if (windows->info.mapped != MagickFalse)
4235 {
4236 if ((x < (int) (windows->info.x+windows->info.width)) &&
4237 (y < (int) (windows->info.y+windows->info.height)))
4238 (void) XWithdrawWindow(display,windows->info.id,
4239 windows->info.screen);
4240 }
4241 else
4242 if ((x > (int) (windows->info.x+windows->info.width)) ||
4243 (y > (int) (windows->info.y+windows->info.height)))
4244 (void) XMapWindow(display,windows->info.id);
cristy49e2d862010-11-12 02:50:30 +00004245 composite_info.x=(ssize_t) windows->image.x+x;
4246 composite_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +00004247 break;
4248 }
4249 default:
4250 {
4251 if (image->debug != MagickFalse)
4252 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4253 event.type);
4254 break;
4255 }
4256 }
4257 } while ((state & ExitState) == 0);
4258 (void) XSelectInput(display,windows->image.id,
4259 windows->image.attributes.event_mask);
4260 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4261 XSetCursorState(display,windows,MagickFalse);
4262 (void) XFreeCursor(display,cursor);
4263 if ((state & EscapeState) != 0)
4264 return(MagickTrue);
4265 /*
4266 Image compositing is relative to image configuration.
4267 */
4268 XSetCursorState(display,windows,MagickTrue);
4269 XCheckRefreshWindows(display,windows);
4270 width=(unsigned int) image->columns;
4271 height=(unsigned int) image->rows;
4272 x=0;
4273 y=0;
4274 if (windows->image.crop_geometry != (char *) NULL)
4275 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4276 scale_factor=(MagickRealType) width/windows->image.ximage->width;
4277 composite_info.x+=x;
cristy49e2d862010-11-12 02:50:30 +00004278 composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +00004279 composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4280 scale_factor=(MagickRealType) height/windows->image.ximage->height;
4281 composite_info.y+=y;
cristy49e2d862010-11-12 02:50:30 +00004282 composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00004283 composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4284 if ((composite_info.width != composite_image->columns) ||
4285 (composite_info.height != composite_image->rows))
4286 {
4287 Image
4288 *resize_image;
4289
4290 /*
4291 Scale composite image.
4292 */
cristy15b98cd2010-09-12 19:42:50 +00004293 resize_image=ResizeImage(composite_image,composite_info.width,
4294 composite_info.height,composite_image->filter,composite_image->blur,
cristy051718b2011-08-28 22:49:25 +00004295 exception);
cristy3ed852e2009-09-05 21:47:34 +00004296 composite_image=DestroyImage(composite_image);
4297 if (resize_image == (Image *) NULL)
4298 {
4299 XSetCursorState(display,windows,MagickFalse);
4300 return(MagickFalse);
4301 }
4302 composite_image=resize_image;
4303 }
4304 if (compose == DisplaceCompositeOp)
4305 (void) SetImageArtifact(composite_image,"compose:args",
4306 displacement_geometry);
4307 if (blend != 0.0)
4308 {
cristy49e2d862010-11-12 02:50:30 +00004309 CacheView
4310 *image_view;
4311
cristy3ed852e2009-09-05 21:47:34 +00004312 int
4313 y;
4314
4315 Quantum
4316 opacity;
4317
4318 register int
4319 x;
4320
cristy4c08aed2011-07-01 19:47:50 +00004321 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004322 *q;
4323
4324 /*
4325 Create mattes for blending.
4326 */
cristy63240882011-08-05 19:05:27 +00004327 (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00004328 opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
cristybb503372010-05-27 20:51:26 +00004329 ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
cristy574cc262011-08-05 01:23:58 +00004330 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004331 return(MagickFalse);
4332 image->matte=MagickTrue;
cristy49e2d862010-11-12 02:50:30 +00004333 image_view=AcquireCacheView(image);
4334 for (y=0; y < (int) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004335 {
cristy49e2d862010-11-12 02:50:30 +00004336 q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4337 exception);
cristy4c08aed2011-07-01 19:47:50 +00004338 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004339 break;
4340 for (x=0; x < (int) image->columns; x++)
4341 {
cristy4c08aed2011-07-01 19:47:50 +00004342 SetPixelAlpha(image,opacity,q);
cristyed231572011-07-14 02:18:59 +00004343 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004344 }
cristy49e2d862010-11-12 02:50:30 +00004345 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004346 break;
4347 }
cristy49e2d862010-11-12 02:50:30 +00004348 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00004349 }
4350 /*
4351 Composite image with X Image window.
4352 */
4353 (void) CompositeImage(image,compose,composite_image,composite_info.x,
4354 composite_info.y);
4355 composite_image=DestroyImage(composite_image);
4356 XSetCursorState(display,windows,MagickFalse);
4357 /*
4358 Update image configuration.
4359 */
4360 XConfigureImageColormap(display,resource_info,windows,image);
cristy051718b2011-08-28 22:49:25 +00004361 (void) XConfigureImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00004362 return(MagickTrue);
4363}
4364
4365/*
4366%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4367% %
4368% %
4369% %
4370+ X C o n f i g u r e I m a g e %
4371% %
4372% %
4373% %
4374%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4375%
4376% XConfigureImage() creates a new X image. It also notifies the window
4377% manager of the new image size and configures the transient widows.
4378%
4379% The format of the XConfigureImage method is:
4380%
4381% MagickBooleanType XConfigureImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00004382% XResourceInfo *resource_info,XWindows *windows,Image *image,
4383% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004384%
4385% A description of each parameter follows:
4386%
4387% o display: Specifies a connection to an X server; returned from
4388% XOpenDisplay.
4389%
4390% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4391%
4392% o windows: Specifies a pointer to a XWindows structure.
4393%
4394% o image: the image.
4395%
cristy051718b2011-08-28 22:49:25 +00004396% o exception: return any errors or warnings in this structure.
4397%
4398% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00004399%
4400*/
4401static MagickBooleanType XConfigureImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00004402 XResourceInfo *resource_info,XWindows *windows,Image *image,
4403 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004404{
4405 char
4406 geometry[MaxTextExtent];
4407
cristy3ed852e2009-09-05 21:47:34 +00004408 MagickStatusType
4409 status;
4410
cristybb503372010-05-27 20:51:26 +00004411 size_t
cristy3ed852e2009-09-05 21:47:34 +00004412 mask,
4413 height,
4414 width;
4415
cristy9d314ff2011-03-09 01:30:28 +00004416 ssize_t
4417 x,
4418 y;
4419
cristy3ed852e2009-09-05 21:47:34 +00004420 XSizeHints
4421 *size_hints;
4422
4423 XWindowChanges
4424 window_changes;
4425
4426 /*
4427 Dismiss if window dimensions are zero.
4428 */
4429 width=(unsigned int) windows->image.window_changes.width;
4430 height=(unsigned int) windows->image.window_changes.height;
4431 if (image->debug != MagickFalse)
4432 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004433 "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4434 windows->image.ximage->height,(double) width,(double) height);
cristy3ed852e2009-09-05 21:47:34 +00004435 if ((width*height) == 0)
4436 return(MagickTrue);
4437 x=0;
4438 y=0;
4439 /*
4440 Resize image to fit Image window dimensions.
4441 */
4442 XSetCursorState(display,windows,MagickTrue);
4443 (void) XFlush(display);
4444 if (((int) width != windows->image.ximage->width) ||
4445 ((int) height != windows->image.ximage->height))
4446 image->taint=MagickTrue;
4447 windows->magnify.x=(int)
4448 width*windows->magnify.x/windows->image.ximage->width;
4449 windows->magnify.y=(int)
4450 height*windows->magnify.y/windows->image.ximage->height;
4451 windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4452 windows->image.y=(int)
4453 (height*windows->image.y/windows->image.ximage->height);
4454 status=XMakeImage(display,resource_info,&windows->image,image,
cristy051718b2011-08-28 22:49:25 +00004455 (unsigned int) width,(unsigned int) height,exception);
cristy3ed852e2009-09-05 21:47:34 +00004456 if (status == MagickFalse)
4457 XNoticeWidget(display,windows,"Unable to configure X image:",
4458 windows->image.name);
4459 /*
4460 Notify window manager of the new configuration.
4461 */
4462 if (resource_info->image_geometry != (char *) NULL)
cristyb51dff52011-05-19 16:55:47 +00004463 (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
cristy3ed852e2009-09-05 21:47:34 +00004464 resource_info->image_geometry);
4465 else
cristyb51dff52011-05-19 16:55:47 +00004466 (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
cristy3ed852e2009-09-05 21:47:34 +00004467 XDisplayWidth(display,windows->image.screen),
4468 XDisplayHeight(display,windows->image.screen));
4469 (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4470 window_changes.width=(int) width;
4471 if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4472 window_changes.width=XDisplayWidth(display,windows->image.screen);
4473 window_changes.height=(int) height;
4474 if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4475 window_changes.height=XDisplayHeight(display,windows->image.screen);
cristybb503372010-05-27 20:51:26 +00004476 mask=(size_t) (CWWidth | CWHeight);
cristy3ed852e2009-09-05 21:47:34 +00004477 if (resource_info->backdrop)
4478 {
4479 mask|=CWX | CWY;
4480 window_changes.x=(int)
4481 ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4482 window_changes.y=(int)
4483 ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4484 }
4485 (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4486 (unsigned int) mask,&window_changes);
4487 (void) XClearWindow(display,windows->image.id);
4488 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4489 /*
4490 Update Magnify window configuration.
4491 */
4492 if (windows->magnify.mapped != MagickFalse)
4493 XMakeMagnifyImage(display,windows);
4494 windows->pan.crop_geometry=windows->image.crop_geometry;
4495 XBestIconSize(display,&windows->pan,image);
4496 while (((windows->pan.width << 1) < MaxIconSize) &&
4497 ((windows->pan.height << 1) < MaxIconSize))
4498 {
4499 windows->pan.width<<=1;
4500 windows->pan.height<<=1;
4501 }
4502 if (windows->pan.geometry != (char *) NULL)
4503 (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4504 &windows->pan.width,&windows->pan.height);
4505 window_changes.width=(int) windows->pan.width;
4506 window_changes.height=(int) windows->pan.height;
4507 size_hints=XAllocSizeHints();
4508 if (size_hints != (XSizeHints *) NULL)
4509 {
4510 /*
4511 Set new size hints.
4512 */
4513 size_hints->flags=PSize | PMinSize | PMaxSize;
4514 size_hints->width=window_changes.width;
4515 size_hints->height=window_changes.height;
4516 size_hints->min_width=size_hints->width;
4517 size_hints->min_height=size_hints->height;
4518 size_hints->max_width=size_hints->width;
4519 size_hints->max_height=size_hints->height;
4520 (void) XSetNormalHints(display,windows->pan.id,size_hints);
4521 (void) XFree((void *) size_hints);
4522 }
4523 (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4524 (unsigned int) (CWWidth | CWHeight),&window_changes);
4525 /*
4526 Update icon window configuration.
4527 */
4528 windows->icon.crop_geometry=windows->image.crop_geometry;
4529 XBestIconSize(display,&windows->icon,image);
4530 window_changes.width=(int) windows->icon.width;
4531 window_changes.height=(int) windows->icon.height;
4532 (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4533 (unsigned int) (CWWidth | CWHeight),&window_changes);
4534 XSetCursorState(display,windows,MagickFalse);
4535 return(status != 0 ? MagickTrue : MagickFalse);
4536}
4537
4538/*
4539%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4540% %
4541% %
4542% %
4543+ X C r o p I m a g e %
4544% %
4545% %
4546% %
4547%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4548%
4549% XCropImage() allows the user to select a region of the image and crop, copy,
4550% or cut it. For copy or cut, the image can subsequently be composited onto
4551% the image with XPasteImage.
4552%
4553% The format of the XCropImage method is:
4554%
4555% MagickBooleanType XCropImage(Display *display,
4556% XResourceInfo *resource_info,XWindows *windows,Image *image,
cristy051718b2011-08-28 22:49:25 +00004557% const ClipboardMode mode,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004558%
4559% A description of each parameter follows:
4560%
4561% o display: Specifies a connection to an X server; returned from
4562% XOpenDisplay.
4563%
4564% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4565%
4566% o windows: Specifies a pointer to a XWindows structure.
4567%
4568% o image: the image; returned from ReadImage.
4569%
4570% o mode: This unsigned value specified whether the image should be
4571% cropped, copied, or cut.
4572%
cristy051718b2011-08-28 22:49:25 +00004573% o exception: return any errors or warnings in this structure.
4574%
cristy3ed852e2009-09-05 21:47:34 +00004575*/
4576static MagickBooleanType XCropImage(Display *display,
4577 XResourceInfo *resource_info,XWindows *windows,Image *image,
cristy051718b2011-08-28 22:49:25 +00004578 const ClipboardMode mode,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004579{
4580 static const char
4581 *CropModeMenu[] =
4582 {
4583 "Help",
4584 "Dismiss",
4585 (char *) NULL
4586 },
4587 *RectifyModeMenu[] =
4588 {
4589 "Crop",
4590 "Help",
4591 "Dismiss",
4592 (char *) NULL
4593 };
4594
4595 static const ModeType
4596 CropCommands[] =
4597 {
4598 CropHelpCommand,
4599 CropDismissCommand
4600 },
4601 RectifyCommands[] =
4602 {
4603 RectifyCopyCommand,
4604 RectifyHelpCommand,
4605 RectifyDismissCommand
4606 };
4607
cristy49e2d862010-11-12 02:50:30 +00004608 CacheView
4609 *image_view;
4610
cristy3ed852e2009-09-05 21:47:34 +00004611 char
4612 command[MaxTextExtent],
4613 text[MaxTextExtent];
4614
4615 Cursor
4616 cursor;
4617
cristy3ed852e2009-09-05 21:47:34 +00004618 int
4619 id,
4620 x,
4621 y;
4622
4623 KeySym
4624 key_symbol;
4625
4626 Image
4627 *crop_image;
4628
4629 MagickRealType
4630 scale_factor;
4631
4632 RectangleInfo
4633 crop_info,
4634 highlight_info;
4635
cristy4c08aed2011-07-01 19:47:50 +00004636 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004637 *q;
4638
4639 unsigned int
4640 height,
4641 width;
4642
cristybb503372010-05-27 20:51:26 +00004643 size_t
cristy3ed852e2009-09-05 21:47:34 +00004644 state;
4645
4646 XEvent
4647 event;
4648
4649 /*
4650 Map Command widget.
4651 */
4652 switch (mode)
4653 {
4654 case CopyMode:
4655 {
4656 (void) CloneString(&windows->command.name,"Copy");
4657 break;
4658 }
4659 case CropMode:
4660 {
4661 (void) CloneString(&windows->command.name,"Crop");
4662 break;
4663 }
4664 case CutMode:
4665 {
4666 (void) CloneString(&windows->command.name,"Cut");
4667 break;
4668 }
4669 }
4670 RectifyModeMenu[0]=windows->command.name;
4671 windows->command.data=0;
4672 (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4673 (void) XMapRaised(display,windows->command.id);
4674 XClientMessage(display,windows->image.id,windows->im_protocols,
4675 windows->im_update_widget,CurrentTime);
4676 /*
4677 Track pointer until button 1 is pressed.
4678 */
4679 XQueryPosition(display,windows->image.id,&x,&y);
4680 (void) XSelectInput(display,windows->image.id,
4681 windows->image.attributes.event_mask | PointerMotionMask);
cristy49e2d862010-11-12 02:50:30 +00004682 crop_info.x=(ssize_t) windows->image.x+x;
4683 crop_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +00004684 crop_info.width=0;
4685 crop_info.height=0;
4686 cursor=XCreateFontCursor(display,XC_fleur);
4687 state=DefaultState;
4688 do
4689 {
4690 if (windows->info.mapped != MagickFalse)
4691 {
4692 /*
4693 Display pointer position.
4694 */
cristyb51dff52011-05-19 16:55:47 +00004695 (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
cristyf2faecf2010-05-28 19:19:36 +00004696 (long) crop_info.x,(long) crop_info.y);
cristy3ed852e2009-09-05 21:47:34 +00004697 XInfoWidget(display,windows,text);
4698 }
4699 /*
4700 Wait for next event.
4701 */
4702 XScreenEvent(display,windows,&event);
4703 if (event.xany.window == windows->command.id)
4704 {
4705 /*
4706 Select a command from the Command widget.
4707 */
4708 id=XCommandWidget(display,windows,CropModeMenu,&event);
4709 if (id < 0)
4710 continue;
4711 switch (CropCommands[id])
4712 {
4713 case CropHelpCommand:
4714 {
4715 switch (mode)
4716 {
4717 case CopyMode:
4718 {
4719 XTextViewWidget(display,resource_info,windows,MagickFalse,
4720 "Help Viewer - Image Copy",ImageCopyHelp);
4721 break;
4722 }
4723 case CropMode:
4724 {
4725 XTextViewWidget(display,resource_info,windows,MagickFalse,
4726 "Help Viewer - Image Crop",ImageCropHelp);
4727 break;
4728 }
4729 case CutMode:
4730 {
4731 XTextViewWidget(display,resource_info,windows,MagickFalse,
4732 "Help Viewer - Image Cut",ImageCutHelp);
4733 break;
4734 }
4735 }
4736 break;
4737 }
4738 case CropDismissCommand:
4739 {
4740 /*
4741 Prematurely exit.
4742 */
4743 state|=EscapeState;
4744 state|=ExitState;
4745 break;
4746 }
4747 default:
4748 break;
4749 }
4750 continue;
4751 }
4752 switch (event.type)
4753 {
4754 case ButtonPress:
4755 {
4756 if (event.xbutton.button != Button1)
4757 break;
4758 if (event.xbutton.window != windows->image.id)
4759 break;
4760 /*
4761 Note first corner of cropping rectangle-- exit loop.
4762 */
4763 (void) XCheckDefineCursor(display,windows->image.id,cursor);
cristy49e2d862010-11-12 02:50:30 +00004764 crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4765 crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +00004766 state|=ExitState;
4767 break;
4768 }
4769 case ButtonRelease:
4770 break;
4771 case Expose:
4772 break;
4773 case KeyPress:
4774 {
4775 if (event.xkey.window != windows->image.id)
4776 break;
4777 /*
4778 Respond to a user key press.
4779 */
4780 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4781 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4782 switch ((int) key_symbol)
4783 {
4784 case XK_Escape:
4785 case XK_F20:
4786 {
4787 /*
4788 Prematurely exit.
4789 */
4790 state|=EscapeState;
4791 state|=ExitState;
4792 break;
4793 }
4794 case XK_F1:
4795 case XK_Help:
4796 {
4797 switch (mode)
4798 {
4799 case CopyMode:
4800 {
4801 XTextViewWidget(display,resource_info,windows,MagickFalse,
4802 "Help Viewer - Image Copy",ImageCopyHelp);
4803 break;
4804 }
4805 case CropMode:
4806 {
4807 XTextViewWidget(display,resource_info,windows,MagickFalse,
4808 "Help Viewer - Image Crop",ImageCropHelp);
4809 break;
4810 }
4811 case CutMode:
4812 {
4813 XTextViewWidget(display,resource_info,windows,MagickFalse,
4814 "Help Viewer - Image Cut",ImageCutHelp);
4815 break;
4816 }
4817 }
4818 break;
4819 }
4820 default:
4821 {
4822 (void) XBell(display,0);
4823 break;
4824 }
4825 }
4826 break;
4827 }
4828 case MotionNotify:
4829 {
4830 if (event.xmotion.window != windows->image.id)
4831 break;
4832 /*
4833 Map and unmap Info widget as text cursor crosses its boundaries.
4834 */
4835 x=event.xmotion.x;
4836 y=event.xmotion.y;
4837 if (windows->info.mapped != MagickFalse)
4838 {
4839 if ((x < (int) (windows->info.x+windows->info.width)) &&
4840 (y < (int) (windows->info.y+windows->info.height)))
4841 (void) XWithdrawWindow(display,windows->info.id,
4842 windows->info.screen);
4843 }
4844 else
4845 if ((x > (int) (windows->info.x+windows->info.width)) ||
4846 (y > (int) (windows->info.y+windows->info.height)))
4847 (void) XMapWindow(display,windows->info.id);
cristy49e2d862010-11-12 02:50:30 +00004848 crop_info.x=(ssize_t) windows->image.x+x;
4849 crop_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +00004850 break;
4851 }
4852 default:
4853 break;
4854 }
4855 } while ((state & ExitState) == 0);
4856 (void) XSelectInput(display,windows->image.id,
4857 windows->image.attributes.event_mask);
4858 if ((state & EscapeState) != 0)
4859 {
4860 /*
4861 User want to exit without cropping.
4862 */
4863 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4864 (void) XFreeCursor(display,cursor);
4865 return(MagickTrue);
4866 }
4867 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4868 do
4869 {
4870 /*
4871 Size rectangle as pointer moves until the mouse button is released.
4872 */
4873 x=(int) crop_info.x;
4874 y=(int) crop_info.y;
4875 crop_info.width=0;
4876 crop_info.height=0;
4877 state=DefaultState;
4878 do
4879 {
4880 highlight_info=crop_info;
4881 highlight_info.x=crop_info.x-windows->image.x;
4882 highlight_info.y=crop_info.y-windows->image.y;
4883 if ((highlight_info.width > 3) && (highlight_info.height > 3))
4884 {
4885 /*
4886 Display info and draw cropping rectangle.
4887 */
4888 if (windows->info.mapped == MagickFalse)
4889 (void) XMapWindow(display,windows->info.id);
cristyb51dff52011-05-19 16:55:47 +00004890 (void) FormatLocaleString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00004891 " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +00004892 crop_info.height,(double) crop_info.x,(double) crop_info.y);
cristy3ed852e2009-09-05 21:47:34 +00004893 XInfoWidget(display,windows,text);
4894 XHighlightRectangle(display,windows->image.id,
4895 windows->image.highlight_context,&highlight_info);
4896 }
4897 else
4898 if (windows->info.mapped != MagickFalse)
4899 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4900 /*
4901 Wait for next event.
4902 */
4903 XScreenEvent(display,windows,&event);
4904 if ((highlight_info.width > 3) && (highlight_info.height > 3))
4905 XHighlightRectangle(display,windows->image.id,
4906 windows->image.highlight_context,&highlight_info);
4907 switch (event.type)
4908 {
4909 case ButtonPress:
4910 {
cristy49e2d862010-11-12 02:50:30 +00004911 crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4912 crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +00004913 break;
4914 }
4915 case ButtonRelease:
4916 {
4917 /*
4918 User has committed to cropping rectangle.
4919 */
cristy49e2d862010-11-12 02:50:30 +00004920 crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4921 crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +00004922 XSetCursorState(display,windows,MagickFalse);
4923 state|=ExitState;
4924 windows->command.data=0;
4925 (void) XCommandWidget(display,windows,RectifyModeMenu,
4926 (XEvent *) NULL);
4927 break;
4928 }
4929 case Expose:
4930 break;
4931 case MotionNotify:
4932 {
cristy49e2d862010-11-12 02:50:30 +00004933 crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4934 crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
cristy3ed852e2009-09-05 21:47:34 +00004935 }
4936 default:
4937 break;
4938 }
4939 if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4940 ((state & ExitState) != 0))
4941 {
4942 /*
4943 Check boundary conditions.
4944 */
4945 if (crop_info.x < 0)
4946 crop_info.x=0;
4947 else
cristy49e2d862010-11-12 02:50:30 +00004948 if (crop_info.x > (ssize_t) windows->image.ximage->width)
4949 crop_info.x=(ssize_t) windows->image.ximage->width;
cristy3ed852e2009-09-05 21:47:34 +00004950 if ((int) crop_info.x < x)
4951 crop_info.width=(unsigned int) (x-crop_info.x);
4952 else
4953 {
4954 crop_info.width=(unsigned int) (crop_info.x-x);
cristy49e2d862010-11-12 02:50:30 +00004955 crop_info.x=(ssize_t) x;
cristy3ed852e2009-09-05 21:47:34 +00004956 }
4957 if (crop_info.y < 0)
4958 crop_info.y=0;
4959 else
cristy49e2d862010-11-12 02:50:30 +00004960 if (crop_info.y > (ssize_t) windows->image.ximage->height)
4961 crop_info.y=(ssize_t) windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +00004962 if ((int) crop_info.y < y)
4963 crop_info.height=(unsigned int) (y-crop_info.y);
4964 else
4965 {
4966 crop_info.height=(unsigned int) (crop_info.y-y);
cristy49e2d862010-11-12 02:50:30 +00004967 crop_info.y=(ssize_t) y;
cristy3ed852e2009-09-05 21:47:34 +00004968 }
4969 }
4970 } while ((state & ExitState) == 0);
4971 /*
4972 Wait for user to grab a corner of the rectangle or press return.
4973 */
4974 state=DefaultState;
4975 (void) XMapWindow(display,windows->info.id);
4976 do
4977 {
4978 if (windows->info.mapped != MagickFalse)
4979 {
4980 /*
4981 Display pointer position.
4982 */
cristyb51dff52011-05-19 16:55:47 +00004983 (void) FormatLocaleString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00004984 " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +00004985 crop_info.height,(double) crop_info.x,(double) crop_info.y);
cristy3ed852e2009-09-05 21:47:34 +00004986 XInfoWidget(display,windows,text);
4987 }
4988 highlight_info=crop_info;
4989 highlight_info.x=crop_info.x-windows->image.x;
4990 highlight_info.y=crop_info.y-windows->image.y;
4991 if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4992 {
4993 state|=EscapeState;
4994 state|=ExitState;
4995 break;
4996 }
4997 XHighlightRectangle(display,windows->image.id,
4998 windows->image.highlight_context,&highlight_info);
4999 XScreenEvent(display,windows,&event);
5000 if (event.xany.window == windows->command.id)
5001 {
5002 /*
5003 Select a command from the Command widget.
5004 */
5005 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5006 id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5007 (void) XSetFunction(display,windows->image.highlight_context,
5008 GXinvert);
5009 XHighlightRectangle(display,windows->image.id,
5010 windows->image.highlight_context,&highlight_info);
5011 if (id >= 0)
5012 switch (RectifyCommands[id])
5013 {
5014 case RectifyCopyCommand:
5015 {
5016 state|=ExitState;
5017 break;
5018 }
5019 case RectifyHelpCommand:
5020 {
5021 (void) XSetFunction(display,windows->image.highlight_context,
5022 GXcopy);
5023 switch (mode)
5024 {
5025 case CopyMode:
5026 {
5027 XTextViewWidget(display,resource_info,windows,MagickFalse,
5028 "Help Viewer - Image Copy",ImageCopyHelp);
5029 break;
5030 }
5031 case CropMode:
5032 {
5033 XTextViewWidget(display,resource_info,windows,MagickFalse,
5034 "Help Viewer - Image Crop",ImageCropHelp);
5035 break;
5036 }
5037 case CutMode:
5038 {
5039 XTextViewWidget(display,resource_info,windows,MagickFalse,
5040 "Help Viewer - Image Cut",ImageCutHelp);
5041 break;
5042 }
5043 }
5044 (void) XSetFunction(display,windows->image.highlight_context,
5045 GXinvert);
5046 break;
5047 }
5048 case RectifyDismissCommand:
5049 {
5050 /*
5051 Prematurely exit.
5052 */
5053 state|=EscapeState;
5054 state|=ExitState;
5055 break;
5056 }
5057 default:
5058 break;
5059 }
5060 continue;
5061 }
5062 XHighlightRectangle(display,windows->image.id,
5063 windows->image.highlight_context,&highlight_info);
5064 switch (event.type)
5065 {
5066 case ButtonPress:
5067 {
5068 if (event.xbutton.button != Button1)
5069 break;
5070 if (event.xbutton.window != windows->image.id)
5071 break;
5072 x=windows->image.x+event.xbutton.x;
5073 y=windows->image.y+event.xbutton.y;
5074 if ((x < (int) (crop_info.x+RoiDelta)) &&
5075 (x > (int) (crop_info.x-RoiDelta)) &&
5076 (y < (int) (crop_info.y+RoiDelta)) &&
5077 (y > (int) (crop_info.y-RoiDelta)))
5078 {
cristybb503372010-05-27 20:51:26 +00005079 crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5080 crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
cristy3ed852e2009-09-05 21:47:34 +00005081 state|=UpdateConfigurationState;
5082 break;
5083 }
5084 if ((x < (int) (crop_info.x+RoiDelta)) &&
5085 (x > (int) (crop_info.x-RoiDelta)) &&
5086 (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5087 (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5088 {
cristybb503372010-05-27 20:51:26 +00005089 crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
cristy3ed852e2009-09-05 21:47:34 +00005090 state|=UpdateConfigurationState;
5091 break;
5092 }
5093 if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5094 (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5095 (y < (int) (crop_info.y+RoiDelta)) &&
5096 (y > (int) (crop_info.y-RoiDelta)))
5097 {
cristybb503372010-05-27 20:51:26 +00005098 crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
cristy3ed852e2009-09-05 21:47:34 +00005099 state|=UpdateConfigurationState;
5100 break;
5101 }
5102 if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5103 (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5104 (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5105 (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5106 {
5107 state|=UpdateConfigurationState;
5108 break;
5109 }
5110 }
5111 case ButtonRelease:
5112 {
5113 if (event.xbutton.window == windows->pan.id)
5114 if ((highlight_info.x != crop_info.x-windows->image.x) ||
5115 (highlight_info.y != crop_info.y-windows->image.y))
5116 XHighlightRectangle(display,windows->image.id,
5117 windows->image.highlight_context,&highlight_info);
5118 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5119 event.xbutton.time);
5120 break;
5121 }
5122 case Expose:
5123 {
5124 if (event.xexpose.window == windows->image.id)
5125 if (event.xexpose.count == 0)
5126 {
5127 event.xexpose.x=(int) highlight_info.x;
5128 event.xexpose.y=(int) highlight_info.y;
5129 event.xexpose.width=(int) highlight_info.width;
5130 event.xexpose.height=(int) highlight_info.height;
5131 XRefreshWindow(display,&windows->image,&event);
5132 }
5133 if (event.xexpose.window == windows->info.id)
5134 if (event.xexpose.count == 0)
5135 XInfoWidget(display,windows,text);
5136 break;
5137 }
5138 case KeyPress:
5139 {
5140 if (event.xkey.window != windows->image.id)
5141 break;
5142 /*
5143 Respond to a user key press.
5144 */
5145 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5146 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5147 switch ((int) key_symbol)
5148 {
5149 case XK_Escape:
5150 case XK_F20:
5151 state|=EscapeState;
5152 case XK_Return:
5153 {
5154 state|=ExitState;
5155 break;
5156 }
5157 case XK_Home:
5158 case XK_KP_Home:
5159 {
cristy49e2d862010-11-12 02:50:30 +00005160 crop_info.x=(ssize_t) (windows->image.width/2L-
5161 crop_info.width/2L);
5162 crop_info.y=(ssize_t) (windows->image.height/2L-
5163 crop_info.height/2L);
cristy3ed852e2009-09-05 21:47:34 +00005164 break;
5165 }
5166 case XK_Left:
5167 case XK_KP_Left:
5168 {
5169 crop_info.x--;
5170 break;
5171 }
5172 case XK_Up:
5173 case XK_KP_Up:
5174 case XK_Next:
5175 {
5176 crop_info.y--;
5177 break;
5178 }
5179 case XK_Right:
5180 case XK_KP_Right:
5181 {
5182 crop_info.x++;
5183 break;
5184 }
5185 case XK_Prior:
5186 case XK_Down:
5187 case XK_KP_Down:
5188 {
5189 crop_info.y++;
5190 break;
5191 }
5192 case XK_F1:
5193 case XK_Help:
5194 {
5195 (void) XSetFunction(display,windows->image.highlight_context,
5196 GXcopy);
5197 switch (mode)
5198 {
5199 case CopyMode:
5200 {
5201 XTextViewWidget(display,resource_info,windows,MagickFalse,
5202 "Help Viewer - Image Copy",ImageCopyHelp);
5203 break;
5204 }
5205 case CropMode:
5206 {
5207 XTextViewWidget(display,resource_info,windows,MagickFalse,
5208 "Help Viewer - Image Cropg",ImageCropHelp);
5209 break;
5210 }
5211 case CutMode:
5212 {
5213 XTextViewWidget(display,resource_info,windows,MagickFalse,
5214 "Help Viewer - Image Cutg",ImageCutHelp);
5215 break;
5216 }
5217 }
5218 (void) XSetFunction(display,windows->image.highlight_context,
5219 GXinvert);
5220 break;
5221 }
5222 default:
5223 {
5224 (void) XBell(display,0);
5225 break;
5226 }
5227 }
5228 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5229 event.xkey.time);
5230 break;
5231 }
5232 case KeyRelease:
5233 break;
5234 case MotionNotify:
5235 {
5236 if (event.xmotion.window != windows->image.id)
5237 break;
5238 /*
5239 Map and unmap Info widget as text cursor crosses its boundaries.
5240 */
5241 x=event.xmotion.x;
5242 y=event.xmotion.y;
5243 if (windows->info.mapped != MagickFalse)
5244 {
5245 if ((x < (int) (windows->info.x+windows->info.width)) &&
5246 (y < (int) (windows->info.y+windows->info.height)))
5247 (void) XWithdrawWindow(display,windows->info.id,
5248 windows->info.screen);
5249 }
5250 else
5251 if ((x > (int) (windows->info.x+windows->info.width)) ||
5252 (y > (int) (windows->info.y+windows->info.height)))
5253 (void) XMapWindow(display,windows->info.id);
cristy49e2d862010-11-12 02:50:30 +00005254 crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5255 crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
cristy3ed852e2009-09-05 21:47:34 +00005256 break;
5257 }
5258 case SelectionRequest:
5259 {
5260 XSelectionEvent
5261 notify;
5262
5263 XSelectionRequestEvent
5264 *request;
5265
5266 /*
5267 Set primary selection.
5268 */
cristyb51dff52011-05-19 16:55:47 +00005269 (void) FormatLocaleString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00005270 "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +00005271 crop_info.height,(double) crop_info.x,(double) crop_info.y);
cristy3ed852e2009-09-05 21:47:34 +00005272 request=(&(event.xselectionrequest));
5273 (void) XChangeProperty(request->display,request->requestor,
5274 request->property,request->target,8,PropModeReplace,
5275 (unsigned char *) text,(int) strlen(text));
5276 notify.type=SelectionNotify;
5277 notify.display=request->display;
5278 notify.requestor=request->requestor;
5279 notify.selection=request->selection;
5280 notify.target=request->target;
5281 notify.time=request->time;
5282 if (request->property == None)
5283 notify.property=request->target;
5284 else
5285 notify.property=request->property;
5286 (void) XSendEvent(request->display,request->requestor,False,0,
5287 (XEvent *) &notify);
5288 }
5289 default:
5290 break;
5291 }
5292 if ((state & UpdateConfigurationState) != 0)
5293 {
5294 (void) XPutBackEvent(display,&event);
5295 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5296 break;
5297 }
5298 } while ((state & ExitState) == 0);
5299 } while ((state & ExitState) == 0);
5300 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5301 XSetCursorState(display,windows,MagickFalse);
5302 if ((state & EscapeState) != 0)
5303 return(MagickTrue);
5304 if (mode == CropMode)
5305 if (((int) crop_info.width != windows->image.ximage->width) ||
5306 ((int) crop_info.height != windows->image.ximage->height))
5307 {
5308 /*
5309 Reconfigure Image window as defined by cropping rectangle.
5310 */
5311 XSetCropGeometry(display,windows,&crop_info,image);
5312 windows->image.window_changes.width=(int) crop_info.width;
5313 windows->image.window_changes.height=(int) crop_info.height;
cristy051718b2011-08-28 22:49:25 +00005314 (void) XConfigureImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005315 return(MagickTrue);
5316 }
5317 /*
5318 Copy image before applying image transforms.
5319 */
5320 XSetCursorState(display,windows,MagickTrue);
5321 XCheckRefreshWindows(display,windows);
5322 width=(unsigned int) image->columns;
5323 height=(unsigned int) image->rows;
5324 x=0;
5325 y=0;
5326 if (windows->image.crop_geometry != (char *) NULL)
5327 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5328 scale_factor=(MagickRealType) width/windows->image.ximage->width;
5329 crop_info.x+=x;
cristy49e2d862010-11-12 02:50:30 +00005330 crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +00005331 crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5332 scale_factor=(MagickRealType) height/windows->image.ximage->height;
5333 crop_info.y+=y;
cristy49e2d862010-11-12 02:50:30 +00005334 crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00005335 crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
cristy051718b2011-08-28 22:49:25 +00005336 crop_image=CropImage(image,&crop_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00005337 XSetCursorState(display,windows,MagickFalse);
5338 if (crop_image == (Image *) NULL)
5339 return(MagickFalse);
5340 if (resource_info->copy_image != (Image *) NULL)
5341 resource_info->copy_image=DestroyImage(resource_info->copy_image);
5342 resource_info->copy_image=crop_image;
5343 if (mode == CopyMode)
5344 {
cristy051718b2011-08-28 22:49:25 +00005345 (void) XConfigureImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005346 return(MagickTrue);
5347 }
5348 /*
5349 Cut image.
5350 */
cristy574cc262011-08-05 01:23:58 +00005351 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005352 return(MagickFalse);
5353 image->matte=MagickTrue;
cristy49e2d862010-11-12 02:50:30 +00005354 image_view=AcquireCacheView(image);
5355 for (y=0; y < (int) crop_info.height; y++)
cristy3ed852e2009-09-05 21:47:34 +00005356 {
cristy49e2d862010-11-12 02:50:30 +00005357 q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5358 crop_info.width,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00005359 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005360 break;
5361 for (x=0; x < (int) crop_info.width; x++)
5362 {
cristy4c08aed2011-07-01 19:47:50 +00005363 SetPixelAlpha(image,TransparentAlpha,q);
cristyed231572011-07-14 02:18:59 +00005364 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00005365 }
cristy49e2d862010-11-12 02:50:30 +00005366 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005367 break;
5368 }
cristy49e2d862010-11-12 02:50:30 +00005369 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005370 /*
5371 Update image configuration.
5372 */
5373 XConfigureImageColormap(display,resource_info,windows,image);
cristy051718b2011-08-28 22:49:25 +00005374 (void) XConfigureImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005375 return(MagickTrue);
5376}
5377
5378/*
5379%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5380% %
5381% %
5382% %
5383+ X D r a w I m a g e %
5384% %
5385% %
5386% %
5387%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5388%
5389% XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5390% the image.
5391%
5392% The format of the XDrawEditImage method is:
5393%
5394% MagickBooleanType XDrawEditImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00005395% XResourceInfo *resource_info,XWindows *windows,Image **image,
5396% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005397%
5398% A description of each parameter follows:
5399%
5400% o display: Specifies a connection to an X server; returned from
5401% XOpenDisplay.
5402%
5403% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5404%
5405% o windows: Specifies a pointer to a XWindows structure.
5406%
5407% o image: the image.
5408%
cristy051718b2011-08-28 22:49:25 +00005409% o exception: return any errors or warnings in this structure.
5410%
cristy3ed852e2009-09-05 21:47:34 +00005411*/
5412static MagickBooleanType XDrawEditImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00005413 XResourceInfo *resource_info,XWindows *windows,Image **image,
5414 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005415{
5416 static const char
5417 *DrawMenu[] =
5418 {
5419 "Element",
5420 "Color",
5421 "Stipple",
5422 "Width",
5423 "Undo",
5424 "Help",
5425 "Dismiss",
5426 (char *) NULL
5427 };
5428
5429 static ElementType
5430 element = PointElement;
5431
5432 static const ModeType
5433 DrawCommands[] =
5434 {
5435 DrawElementCommand,
5436 DrawColorCommand,
5437 DrawStippleCommand,
5438 DrawWidthCommand,
5439 DrawUndoCommand,
5440 DrawHelpCommand,
5441 DrawDismissCommand
5442 };
5443
5444 static Pixmap
5445 stipple = (Pixmap) NULL;
5446
5447 static unsigned int
5448 pen_id = 0,
5449 line_width = 1;
5450
5451 char
5452 command[MaxTextExtent],
5453 text[MaxTextExtent];
5454
5455 Cursor
5456 cursor;
5457
5458 int
5459 entry,
5460 id,
5461 number_coordinates,
5462 x,
5463 y;
5464
5465 MagickRealType
5466 degrees;
5467
5468 MagickStatusType
5469 status;
5470
5471 RectangleInfo
5472 rectangle_info;
5473
5474 register int
5475 i;
5476
5477 unsigned int
5478 distance,
5479 height,
5480 max_coordinates,
5481 width;
5482
cristybb503372010-05-27 20:51:26 +00005483 size_t
cristy3ed852e2009-09-05 21:47:34 +00005484 state;
5485
5486 Window
5487 root_window;
5488
5489 XDrawInfo
5490 draw_info;
5491
5492 XEvent
5493 event;
5494
5495 XPoint
5496 *coordinate_info;
5497
5498 XSegment
5499 line_info;
5500
5501 /*
5502 Allocate polygon info.
5503 */
5504 max_coordinates=2048;
5505 coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5506 sizeof(*coordinate_info));
5507 if (coordinate_info == (XPoint *) NULL)
5508 {
cristy051718b2011-08-28 22:49:25 +00005509 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005510 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5511 return(MagickFalse);
5512 }
5513 /*
5514 Map Command widget.
5515 */
5516 (void) CloneString(&windows->command.name,"Draw");
5517 windows->command.data=4;
5518 (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5519 (void) XMapRaised(display,windows->command.id);
5520 XClientMessage(display,windows->image.id,windows->im_protocols,
5521 windows->im_update_widget,CurrentTime);
5522 /*
5523 Wait for first button press.
5524 */
5525 root_window=XRootWindow(display,XDefaultScreen(display));
5526 draw_info.stencil=OpaqueStencil;
5527 status=MagickTrue;
5528 cursor=XCreateFontCursor(display,XC_tcross);
5529 for ( ; ; )
5530 {
5531 XQueryPosition(display,windows->image.id,&x,&y);
5532 (void) XSelectInput(display,windows->image.id,
5533 windows->image.attributes.event_mask | PointerMotionMask);
5534 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5535 state=DefaultState;
5536 do
5537 {
5538 if (windows->info.mapped != MagickFalse)
5539 {
5540 /*
5541 Display pointer position.
5542 */
cristyb51dff52011-05-19 16:55:47 +00005543 (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
cristy3ed852e2009-09-05 21:47:34 +00005544 x+windows->image.x,y+windows->image.y);
5545 XInfoWidget(display,windows,text);
5546 }
5547 /*
5548 Wait for next event.
5549 */
5550 XScreenEvent(display,windows,&event);
5551 if (event.xany.window == windows->command.id)
5552 {
5553 /*
5554 Select a command from the Command widget.
5555 */
5556 id=XCommandWidget(display,windows,DrawMenu,&event);
5557 if (id < 0)
5558 continue;
5559 switch (DrawCommands[id])
5560 {
5561 case DrawElementCommand:
5562 {
5563 static const char
5564 *Elements[] =
5565 {
5566 "point",
5567 "line",
5568 "rectangle",
5569 "fill rectangle",
5570 "circle",
5571 "fill circle",
5572 "ellipse",
5573 "fill ellipse",
5574 "polygon",
5575 "fill polygon",
5576 (char *) NULL,
5577 };
5578
5579 /*
5580 Select a command from the pop-up menu.
5581 */
5582 element=(ElementType) (XMenuWidget(display,windows,
5583 DrawMenu[id],Elements,command)+1);
5584 break;
5585 }
5586 case DrawColorCommand:
5587 {
5588 const char
5589 *ColorMenu[MaxNumberPens+1];
5590
5591 int
5592 pen_number;
5593
5594 MagickBooleanType
5595 transparent;
5596
5597 XColor
5598 color;
5599
5600 /*
5601 Initialize menu selections.
5602 */
5603 for (i=0; i < (int) (MaxNumberPens-2); i++)
5604 ColorMenu[i]=resource_info->pen_colors[i];
5605 ColorMenu[MaxNumberPens-2]="transparent";
5606 ColorMenu[MaxNumberPens-1]="Browser...";
5607 ColorMenu[MaxNumberPens]=(char *) NULL;
5608 /*
5609 Select a pen color from the pop-up menu.
5610 */
5611 pen_number=XMenuWidget(display,windows,DrawMenu[id],
5612 (const char **) ColorMenu,command);
5613 if (pen_number < 0)
5614 break;
5615 transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5616 MagickFalse;
5617 if (transparent != MagickFalse)
5618 {
5619 draw_info.stencil=TransparentStencil;
5620 break;
5621 }
5622 if (pen_number == (MaxNumberPens-1))
5623 {
5624 static char
5625 color_name[MaxTextExtent] = "gray";
5626
5627 /*
5628 Select a pen color from a dialog.
5629 */
5630 resource_info->pen_colors[pen_number]=color_name;
5631 XColorBrowserWidget(display,windows,"Select",color_name);
5632 if (*color_name == '\0')
5633 break;
5634 }
5635 /*
5636 Set pen color.
5637 */
5638 (void) XParseColor(display,windows->map_info->colormap,
5639 resource_info->pen_colors[pen_number],&color);
5640 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5641 (unsigned int) MaxColors,&color);
5642 windows->pixel_info->pen_colors[pen_number]=color;
5643 pen_id=(unsigned int) pen_number;
5644 draw_info.stencil=OpaqueStencil;
5645 break;
5646 }
5647 case DrawStippleCommand:
5648 {
5649 Image
5650 *stipple_image;
5651
5652 ImageInfo
5653 *image_info;
5654
5655 int
5656 status;
5657
5658 static char
5659 filename[MaxTextExtent] = "\0";
5660
5661 static const char
5662 *StipplesMenu[] =
5663 {
5664 "Brick",
5665 "Diagonal",
5666 "Scales",
5667 "Vertical",
5668 "Wavy",
5669 "Translucent",
5670 "Opaque",
5671 (char *) NULL,
5672 (char *) NULL,
5673 };
5674
5675 /*
5676 Select a command from the pop-up menu.
5677 */
5678 StipplesMenu[7]="Open...";
5679 entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5680 command);
5681 if (entry < 0)
5682 break;
5683 if (stipple != (Pixmap) NULL)
5684 (void) XFreePixmap(display,stipple);
5685 stipple=(Pixmap) NULL;
cristy3ed852e2009-09-05 21:47:34 +00005686 if (entry != 7)
5687 {
5688 switch (entry)
5689 {
5690 case 0:
5691 {
5692 stipple=XCreateBitmapFromData(display,root_window,
5693 (char *) BricksBitmap,BricksWidth,BricksHeight);
5694 break;
5695 }
5696 case 1:
5697 {
5698 stipple=XCreateBitmapFromData(display,root_window,
5699 (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5700 break;
5701 }
5702 case 2:
5703 {
5704 stipple=XCreateBitmapFromData(display,root_window,
5705 (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5706 break;
5707 }
5708 case 3:
5709 {
5710 stipple=XCreateBitmapFromData(display,root_window,
5711 (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5712 break;
5713 }
5714 case 4:
5715 {
5716 stipple=XCreateBitmapFromData(display,root_window,
5717 (char *) WavyBitmap,WavyWidth,WavyHeight);
5718 break;
5719 }
5720 case 5:
cristy3ed852e2009-09-05 21:47:34 +00005721 {
5722 stipple=XCreateBitmapFromData(display,root_window,
5723 (char *) HighlightBitmap,HighlightWidth,
5724 HighlightHeight);
5725 break;
5726 }
cristydd05beb2010-11-21 21:23:39 +00005727 case 6:
5728 default:
5729 {
5730 stipple=XCreateBitmapFromData(display,root_window,
5731 (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5732 break;
5733 }
cristy3ed852e2009-09-05 21:47:34 +00005734 }
5735 break;
5736 }
5737 XFileBrowserWidget(display,windows,"Stipple",filename);
5738 if (*filename == '\0')
5739 break;
5740 /*
5741 Read image.
5742 */
5743 XSetCursorState(display,windows,MagickTrue);
5744 XCheckRefreshWindows(display,windows);
5745 image_info=AcquireImageInfo();
5746 (void) CopyMagickString(image_info->filename,filename,
5747 MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +00005748 stipple_image=ReadImage(image_info,exception);
5749 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00005750 XSetCursorState(display,windows,MagickFalse);
5751 if (stipple_image == (Image *) NULL)
5752 break;
5753 (void) AcquireUniqueFileResource(filename);
cristyb51dff52011-05-19 16:55:47 +00005754 (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00005755 "xbm:%s",filename);
cristy051718b2011-08-28 22:49:25 +00005756 (void) WriteImage(image_info,stipple_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005757 stipple_image=DestroyImage(stipple_image);
5758 image_info=DestroyImageInfo(image_info);
5759 status=XReadBitmapFile(display,root_window,filename,&width,
5760 &height,&stipple,&x,&y);
5761 (void) RelinquishUniqueFileResource(filename);
5762 if ((status != BitmapSuccess) != 0)
5763 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5764 filename);
5765 break;
5766 }
5767 case DrawWidthCommand:
5768 {
5769 static char
5770 width[MaxTextExtent] = "0";
5771
5772 static const char
5773 *WidthsMenu[] =
5774 {
5775 "1",
5776 "2",
5777 "4",
5778 "8",
5779 "16",
5780 "Dialog...",
5781 (char *) NULL,
5782 };
5783
5784 /*
5785 Select a command from the pop-up menu.
5786 */
5787 entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5788 command);
5789 if (entry < 0)
5790 break;
5791 if (entry != 5)
5792 {
cristydd05beb2010-11-21 21:23:39 +00005793 line_width=(unsigned int) StringToUnsignedLong(
5794 WidthsMenu[entry]);
cristy3ed852e2009-09-05 21:47:34 +00005795 break;
5796 }
5797 (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5798 width);
5799 if (*width == '\0')
5800 break;
cristye27293e2009-12-18 02:53:20 +00005801 line_width=(unsigned int) StringToUnsignedLong(width);
cristy3ed852e2009-09-05 21:47:34 +00005802 break;
5803 }
5804 case DrawUndoCommand:
5805 {
5806 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
cristy051718b2011-08-28 22:49:25 +00005807 image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005808 break;
5809 }
5810 case DrawHelpCommand:
5811 {
5812 XTextViewWidget(display,resource_info,windows,MagickFalse,
5813 "Help Viewer - Image Rotation",ImageDrawHelp);
5814 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5815 break;
5816 }
5817 case DrawDismissCommand:
5818 {
5819 /*
5820 Prematurely exit.
5821 */
5822 state|=EscapeState;
5823 state|=ExitState;
5824 break;
5825 }
5826 default:
5827 break;
5828 }
5829 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5830 continue;
5831 }
5832 switch (event.type)
5833 {
5834 case ButtonPress:
5835 {
5836 if (event.xbutton.button != Button1)
5837 break;
5838 if (event.xbutton.window != windows->image.id)
5839 break;
5840 /*
5841 exit loop.
5842 */
5843 x=event.xbutton.x;
5844 y=event.xbutton.y;
5845 state|=ExitState;
5846 break;
5847 }
5848 case ButtonRelease:
5849 break;
5850 case Expose:
5851 break;
5852 case KeyPress:
5853 {
5854 KeySym
5855 key_symbol;
5856
5857 if (event.xkey.window != windows->image.id)
5858 break;
5859 /*
5860 Respond to a user key press.
5861 */
5862 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5863 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5864 switch ((int) key_symbol)
5865 {
5866 case XK_Escape:
5867 case XK_F20:
5868 {
5869 /*
5870 Prematurely exit.
5871 */
5872 state|=EscapeState;
5873 state|=ExitState;
5874 break;
5875 }
5876 case XK_F1:
5877 case XK_Help:
5878 {
5879 XTextViewWidget(display,resource_info,windows,MagickFalse,
5880 "Help Viewer - Image Rotation",ImageDrawHelp);
5881 break;
5882 }
5883 default:
5884 {
5885 (void) XBell(display,0);
5886 break;
5887 }
5888 }
5889 break;
5890 }
5891 case MotionNotify:
5892 {
5893 /*
5894 Map and unmap Info widget as text cursor crosses its boundaries.
5895 */
5896 x=event.xmotion.x;
5897 y=event.xmotion.y;
5898 if (windows->info.mapped != MagickFalse)
5899 {
5900 if ((x < (int) (windows->info.x+windows->info.width)) &&
5901 (y < (int) (windows->info.y+windows->info.height)))
5902 (void) XWithdrawWindow(display,windows->info.id,
5903 windows->info.screen);
5904 }
5905 else
5906 if ((x > (int) (windows->info.x+windows->info.width)) ||
5907 (y > (int) (windows->info.y+windows->info.height)))
5908 (void) XMapWindow(display,windows->info.id);
5909 break;
5910 }
5911 }
5912 } while ((state & ExitState) == 0);
5913 (void) XSelectInput(display,windows->image.id,
5914 windows->image.attributes.event_mask);
5915 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5916 if ((state & EscapeState) != 0)
5917 break;
5918 /*
5919 Draw element as pointer moves until the button is released.
5920 */
5921 distance=0;
5922 degrees=0.0;
5923 line_info.x1=x;
5924 line_info.y1=y;
5925 line_info.x2=x;
5926 line_info.y2=y;
cristy49e2d862010-11-12 02:50:30 +00005927 rectangle_info.x=(ssize_t) x;
5928 rectangle_info.y=(ssize_t) y;
cristy3ed852e2009-09-05 21:47:34 +00005929 rectangle_info.width=0;
5930 rectangle_info.height=0;
5931 number_coordinates=1;
5932 coordinate_info->x=x;
5933 coordinate_info->y=y;
5934 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5935 state=DefaultState;
5936 do
5937 {
5938 switch (element)
5939 {
5940 case PointElement:
5941 default:
5942 {
5943 if (number_coordinates > 1)
5944 {
5945 (void) XDrawLines(display,windows->image.id,
5946 windows->image.highlight_context,coordinate_info,
5947 number_coordinates,CoordModeOrigin);
cristyb51dff52011-05-19 16:55:47 +00005948 (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
cristy3ed852e2009-09-05 21:47:34 +00005949 coordinate_info[number_coordinates-1].x,
5950 coordinate_info[number_coordinates-1].y);
5951 XInfoWidget(display,windows,text);
5952 }
5953 break;
5954 }
5955 case LineElement:
5956 {
5957 if (distance > 9)
5958 {
5959 /*
5960 Display angle of the line.
5961 */
5962 degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5963 line_info.y1),(double) (line_info.x2-line_info.x1)));
cristyb51dff52011-05-19 16:55:47 +00005964 (void) FormatLocaleString(text,MaxTextExtent," %g",
cristy3ed852e2009-09-05 21:47:34 +00005965 (double) degrees);
5966 XInfoWidget(display,windows,text);
5967 XHighlightLine(display,windows->image.id,
5968 windows->image.highlight_context,&line_info);
5969 }
5970 else
5971 if (windows->info.mapped != MagickFalse)
5972 (void) XWithdrawWindow(display,windows->info.id,
5973 windows->info.screen);
5974 break;
5975 }
5976 case RectangleElement:
5977 case FillRectangleElement:
5978 {
5979 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5980 {
5981 /*
5982 Display info and draw drawing rectangle.
5983 */
cristyb51dff52011-05-19 16:55:47 +00005984 (void) FormatLocaleString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00005985 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
cristye8c25f92010-06-03 00:53:06 +00005986 (double) rectangle_info.height,(double) rectangle_info.x,
5987 (double) rectangle_info.y);
cristy3ed852e2009-09-05 21:47:34 +00005988 XInfoWidget(display,windows,text);
5989 XHighlightRectangle(display,windows->image.id,
5990 windows->image.highlight_context,&rectangle_info);
5991 }
5992 else
5993 if (windows->info.mapped != MagickFalse)
5994 (void) XWithdrawWindow(display,windows->info.id,
5995 windows->info.screen);
5996 break;
5997 }
5998 case CircleElement:
5999 case FillCircleElement:
6000 case EllipseElement:
6001 case FillEllipseElement:
6002 {
6003 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6004 {
6005 /*
6006 Display info and draw drawing rectangle.
6007 */
cristyb51dff52011-05-19 16:55:47 +00006008 (void) FormatLocaleString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00006009 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
cristye8c25f92010-06-03 00:53:06 +00006010 (double) rectangle_info.height,(double) rectangle_info.x,
6011 (double) rectangle_info.y);
cristy3ed852e2009-09-05 21:47:34 +00006012 XInfoWidget(display,windows,text);
6013 XHighlightEllipse(display,windows->image.id,
6014 windows->image.highlight_context,&rectangle_info);
6015 }
6016 else
6017 if (windows->info.mapped != MagickFalse)
6018 (void) XWithdrawWindow(display,windows->info.id,
6019 windows->info.screen);
6020 break;
6021 }
6022 case PolygonElement:
6023 case FillPolygonElement:
6024 {
6025 if (number_coordinates > 1)
6026 (void) XDrawLines(display,windows->image.id,
6027 windows->image.highlight_context,coordinate_info,
6028 number_coordinates,CoordModeOrigin);
6029 if (distance > 9)
6030 {
6031 /*
6032 Display angle of the line.
6033 */
6034 degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6035 line_info.y1),(double) (line_info.x2-line_info.x1)));
cristyb51dff52011-05-19 16:55:47 +00006036 (void) FormatLocaleString(text,MaxTextExtent," %g",
cristy3ed852e2009-09-05 21:47:34 +00006037 (double) degrees);
6038 XInfoWidget(display,windows,text);
6039 XHighlightLine(display,windows->image.id,
6040 windows->image.highlight_context,&line_info);
6041 }
6042 else
6043 if (windows->info.mapped != MagickFalse)
6044 (void) XWithdrawWindow(display,windows->info.id,
6045 windows->info.screen);
6046 break;
6047 }
6048 }
6049 /*
6050 Wait for next event.
6051 */
6052 XScreenEvent(display,windows,&event);
6053 switch (element)
6054 {
6055 case PointElement:
6056 default:
6057 {
6058 if (number_coordinates > 1)
6059 (void) XDrawLines(display,windows->image.id,
6060 windows->image.highlight_context,coordinate_info,
6061 number_coordinates,CoordModeOrigin);
6062 break;
6063 }
6064 case LineElement:
6065 {
6066 if (distance > 9)
6067 XHighlightLine(display,windows->image.id,
6068 windows->image.highlight_context,&line_info);
6069 break;
6070 }
6071 case RectangleElement:
6072 case FillRectangleElement:
6073 {
6074 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6075 XHighlightRectangle(display,windows->image.id,
6076 windows->image.highlight_context,&rectangle_info);
6077 break;
6078 }
6079 case CircleElement:
6080 case FillCircleElement:
6081 case EllipseElement:
6082 case FillEllipseElement:
6083 {
6084 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6085 XHighlightEllipse(display,windows->image.id,
6086 windows->image.highlight_context,&rectangle_info);
6087 break;
6088 }
6089 case PolygonElement:
6090 case FillPolygonElement:
6091 {
6092 if (number_coordinates > 1)
6093 (void) XDrawLines(display,windows->image.id,
6094 windows->image.highlight_context,coordinate_info,
6095 number_coordinates,CoordModeOrigin);
6096 if (distance > 9)
6097 XHighlightLine(display,windows->image.id,
6098 windows->image.highlight_context,&line_info);
6099 break;
6100 }
6101 }
6102 switch (event.type)
6103 {
6104 case ButtonPress:
6105 break;
6106 case ButtonRelease:
6107 {
6108 /*
6109 User has committed to element.
6110 */
6111 line_info.x2=event.xbutton.x;
6112 line_info.y2=event.xbutton.y;
cristy49e2d862010-11-12 02:50:30 +00006113 rectangle_info.x=(ssize_t) event.xbutton.x;
6114 rectangle_info.y=(ssize_t) event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +00006115 coordinate_info[number_coordinates].x=event.xbutton.x;
6116 coordinate_info[number_coordinates].y=event.xbutton.y;
6117 if (((element != PolygonElement) &&
6118 (element != FillPolygonElement)) || (distance <= 9))
6119 {
6120 state|=ExitState;
6121 break;
6122 }
6123 number_coordinates++;
6124 if (number_coordinates < (int) max_coordinates)
6125 {
6126 line_info.x1=event.xbutton.x;
6127 line_info.y1=event.xbutton.y;
6128 break;
6129 }
6130 max_coordinates<<=1;
6131 coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6132 max_coordinates,sizeof(*coordinate_info));
6133 if (coordinate_info == (XPoint *) NULL)
cristy051718b2011-08-28 22:49:25 +00006134 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006135 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6136 break;
6137 }
6138 case Expose:
6139 break;
6140 case MotionNotify:
6141 {
6142 if (event.xmotion.window != windows->image.id)
6143 break;
6144 if (element != PointElement)
6145 {
6146 line_info.x2=event.xmotion.x;
6147 line_info.y2=event.xmotion.y;
cristy49e2d862010-11-12 02:50:30 +00006148 rectangle_info.x=(ssize_t) event.xmotion.x;
6149 rectangle_info.y=(ssize_t) event.xmotion.y;
cristy3ed852e2009-09-05 21:47:34 +00006150 break;
6151 }
6152 coordinate_info[number_coordinates].x=event.xbutton.x;
6153 coordinate_info[number_coordinates].y=event.xbutton.y;
6154 number_coordinates++;
6155 if (number_coordinates < (int) max_coordinates)
6156 break;
6157 max_coordinates<<=1;
6158 coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6159 max_coordinates,sizeof(*coordinate_info));
6160 if (coordinate_info == (XPoint *) NULL)
cristy051718b2011-08-28 22:49:25 +00006161 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006162 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6163 break;
6164 }
6165 default:
6166 break;
6167 }
6168 /*
6169 Check boundary conditions.
6170 */
6171 if (line_info.x2 < 0)
6172 line_info.x2=0;
6173 else
6174 if (line_info.x2 > (int) windows->image.width)
6175 line_info.x2=(short) windows->image.width;
6176 if (line_info.y2 < 0)
6177 line_info.y2=0;
6178 else
6179 if (line_info.y2 > (int) windows->image.height)
6180 line_info.y2=(short) windows->image.height;
6181 distance=(unsigned int)
6182 (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6183 ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6184 if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6185 ((state & ExitState) != 0))
6186 {
6187 if (rectangle_info.x < 0)
6188 rectangle_info.x=0;
6189 else
cristy49e2d862010-11-12 02:50:30 +00006190 if (rectangle_info.x > (ssize_t) windows->image.width)
cristybb503372010-05-27 20:51:26 +00006191 rectangle_info.x=(ssize_t) windows->image.width;
cristy3ed852e2009-09-05 21:47:34 +00006192 if ((int) rectangle_info.x < x)
6193 rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6194 else
6195 {
6196 rectangle_info.width=(unsigned int) (rectangle_info.x-x);
cristy49e2d862010-11-12 02:50:30 +00006197 rectangle_info.x=(ssize_t) x;
cristy3ed852e2009-09-05 21:47:34 +00006198 }
6199 if (rectangle_info.y < 0)
6200 rectangle_info.y=0;
6201 else
cristy49e2d862010-11-12 02:50:30 +00006202 if (rectangle_info.y > (ssize_t) windows->image.height)
cristybb503372010-05-27 20:51:26 +00006203 rectangle_info.y=(ssize_t) windows->image.height;
cristy3ed852e2009-09-05 21:47:34 +00006204 if ((int) rectangle_info.y < y)
6205 rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6206 else
6207 {
6208 rectangle_info.height=(unsigned int) (rectangle_info.y-y);
cristy49e2d862010-11-12 02:50:30 +00006209 rectangle_info.y=(ssize_t) y;
cristy3ed852e2009-09-05 21:47:34 +00006210 }
6211 }
6212 } while ((state & ExitState) == 0);
6213 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6214 if ((element == PointElement) || (element == PolygonElement) ||
6215 (element == FillPolygonElement))
6216 {
6217 /*
6218 Determine polygon bounding box.
6219 */
cristy49e2d862010-11-12 02:50:30 +00006220 rectangle_info.x=(ssize_t) coordinate_info->x;
6221 rectangle_info.y=(ssize_t) coordinate_info->y;
cristy3ed852e2009-09-05 21:47:34 +00006222 x=coordinate_info->x;
6223 y=coordinate_info->y;
6224 for (i=1; i < number_coordinates; i++)
6225 {
6226 if (coordinate_info[i].x > x)
6227 x=coordinate_info[i].x;
6228 if (coordinate_info[i].y > y)
6229 y=coordinate_info[i].y;
cristy49e2d862010-11-12 02:50:30 +00006230 if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6231 rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6232 if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6233 rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
cristy3ed852e2009-09-05 21:47:34 +00006234 }
cristybb503372010-05-27 20:51:26 +00006235 rectangle_info.width=(size_t) (x-rectangle_info.x);
6236 rectangle_info.height=(size_t) (y-rectangle_info.y);
cristy3ed852e2009-09-05 21:47:34 +00006237 for (i=0; i < number_coordinates; i++)
6238 {
6239 coordinate_info[i].x-=rectangle_info.x;
6240 coordinate_info[i].y-=rectangle_info.y;
6241 }
6242 }
6243 else
6244 if (distance <= 9)
6245 continue;
6246 else
6247 if ((element == RectangleElement) ||
6248 (element == CircleElement) || (element == EllipseElement))
6249 {
6250 rectangle_info.width--;
6251 rectangle_info.height--;
6252 }
6253 /*
6254 Drawing is relative to image configuration.
6255 */
6256 draw_info.x=(int) rectangle_info.x;
6257 draw_info.y=(int) rectangle_info.y;
6258 (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
cristy051718b2011-08-28 22:49:25 +00006259 image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006260 width=(unsigned int) (*image)->columns;
6261 height=(unsigned int) (*image)->rows;
6262 x=0;
6263 y=0;
6264 if (windows->image.crop_geometry != (char *) NULL)
6265 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6266 draw_info.x+=windows->image.x-(line_width/2);
6267 if (draw_info.x < 0)
6268 draw_info.x=0;
6269 draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6270 draw_info.y+=windows->image.y-(line_width/2);
6271 if (draw_info.y < 0)
6272 draw_info.y=0;
6273 draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6274 draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6275 if (draw_info.width > (unsigned int) (*image)->columns)
6276 draw_info.width=(unsigned int) (*image)->columns;
6277 draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6278 if (draw_info.height > (unsigned int) (*image)->rows)
6279 draw_info.height=(unsigned int) (*image)->rows;
cristyb51dff52011-05-19 16:55:47 +00006280 (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
cristy3ed852e2009-09-05 21:47:34 +00006281 width*draw_info.width/windows->image.ximage->width,
6282 height*draw_info.height/windows->image.ximage->height,
6283 draw_info.x+x,draw_info.y+y);
6284 /*
6285 Initialize drawing attributes.
6286 */
6287 draw_info.degrees=0.0;
6288 draw_info.element=element;
6289 draw_info.stipple=stipple;
6290 draw_info.line_width=line_width;
6291 draw_info.line_info=line_info;
6292 if (line_info.x1 > (int) (line_width/2))
6293 draw_info.line_info.x1=(short) line_width/2;
6294 if (line_info.y1 > (int) (line_width/2))
6295 draw_info.line_info.y1=(short) line_width/2;
6296 draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6297 draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6298 if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6299 {
6300 draw_info.line_info.x2=(-draw_info.line_info.x2);
6301 draw_info.line_info.y2=(-draw_info.line_info.y2);
6302 }
6303 if (draw_info.line_info.x2 < 0)
6304 {
6305 draw_info.line_info.x2=(-draw_info.line_info.x2);
6306 Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6307 }
6308 if (draw_info.line_info.y2 < 0)
6309 {
6310 draw_info.line_info.y2=(-draw_info.line_info.y2);
6311 Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6312 }
6313 draw_info.rectangle_info=rectangle_info;
cristy49e2d862010-11-12 02:50:30 +00006314 if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
cristybb503372010-05-27 20:51:26 +00006315 draw_info.rectangle_info.x=(ssize_t) line_width/2;
cristy49e2d862010-11-12 02:50:30 +00006316 if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
cristybb503372010-05-27 20:51:26 +00006317 draw_info.rectangle_info.y=(ssize_t) line_width/2;
cristy3ed852e2009-09-05 21:47:34 +00006318 draw_info.number_coordinates=(unsigned int) number_coordinates;
6319 draw_info.coordinate_info=coordinate_info;
6320 windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6321 /*
6322 Draw element on image.
6323 */
6324 XSetCursorState(display,windows,MagickTrue);
6325 XCheckRefreshWindows(display,windows);
6326 status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6327 XSetCursorState(display,windows,MagickFalse);
6328 /*
6329 Update image colormap and return to image drawing.
6330 */
6331 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00006332 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006333 }
6334 XSetCursorState(display,windows,MagickFalse);
6335 coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6336 return(status != 0 ? MagickTrue : MagickFalse);
6337}
6338
6339/*
6340%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6341% %
6342% %
6343% %
6344+ X D r a w P a n R e c t a n g l e %
6345% %
6346% %
6347% %
6348%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6349%
6350% XDrawPanRectangle() draws a rectangle in the pan window. The pan window
6351% displays a zoom image and the rectangle shows which portion of the image is
6352% displayed in the Image window.
6353%
6354% The format of the XDrawPanRectangle method is:
6355%
6356% XDrawPanRectangle(Display *display,XWindows *windows)
6357%
6358% A description of each parameter follows:
6359%
6360% o display: Specifies a connection to an X server; returned from
6361% XOpenDisplay.
6362%
6363% o windows: Specifies a pointer to a XWindows structure.
6364%
6365*/
6366static void XDrawPanRectangle(Display *display,XWindows *windows)
6367{
6368 MagickRealType
6369 scale_factor;
6370
6371 RectangleInfo
6372 highlight_info;
6373
6374 /*
6375 Determine dimensions of the panning rectangle.
6376 */
6377 scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
cristy49e2d862010-11-12 02:50:30 +00006378 highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +00006379 highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6380 scale_factor=(MagickRealType)
6381 windows->pan.height/windows->image.ximage->height;
cristy49e2d862010-11-12 02:50:30 +00006382 highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00006383 highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6384 /*
6385 Display the panning rectangle.
6386 */
6387 (void) XClearWindow(display,windows->pan.id);
6388 XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6389 &highlight_info);
6390}
6391
6392/*
6393%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6394% %
6395% %
6396% %
6397+ X I m a g e C a c h e %
6398% %
6399% %
6400% %
6401%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6402%
6403% XImageCache() handles the creation, manipulation, and destruction of the
6404% image cache (undo and redo buffers).
6405%
6406% The format of the XImageCache method is:
6407%
6408% void XImageCache(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +00006409% XWindows *windows,const CommandType command,Image **image,
6410% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00006411%
6412% A description of each parameter follows:
6413%
6414% o display: Specifies a connection to an X server; returned from
6415% XOpenDisplay.
6416%
6417% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6418%
6419% o windows: Specifies a pointer to a XWindows structure.
6420%
6421% o command: Specifies a command to perform.
6422%
cristya9a86bb2011-01-13 01:11:00 +00006423% o image: the image; XImageCache may transform the image and return a new
6424% image pointer.
cristy3ed852e2009-09-05 21:47:34 +00006425%
cristy051718b2011-08-28 22:49:25 +00006426% o exception: return any errors or warnings in this structure.
6427%
cristy3ed852e2009-09-05 21:47:34 +00006428*/
6429static void XImageCache(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +00006430 XWindows *windows,const CommandType command,Image **image,
6431 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00006432{
6433 Image
6434 *cache_image;
6435
6436 static Image
6437 *redo_image = (Image *) NULL,
6438 *undo_image = (Image *) NULL;
6439
6440 switch (command)
6441 {
6442 case FreeBuffersCommand:
6443 {
6444 /*
6445 Free memory from the undo and redo cache.
6446 */
6447 while (undo_image != (Image *) NULL)
6448 {
6449 cache_image=undo_image;
6450 undo_image=GetPreviousImageInList(undo_image);
6451 cache_image->list=DestroyImage(cache_image->list);
6452 cache_image=DestroyImage(cache_image);
6453 }
6454 undo_image=NewImageList();
6455 if (redo_image != (Image *) NULL)
6456 redo_image=DestroyImage(redo_image);
6457 redo_image=NewImageList();
6458 return;
6459 }
6460 case UndoCommand:
6461 {
cristya9a86bb2011-01-13 01:11:00 +00006462 char
6463 image_geometry[MaxTextExtent];
6464
cristy3ed852e2009-09-05 21:47:34 +00006465 /*
6466 Undo the last image transformation.
6467 */
6468 if (undo_image == (Image *) NULL)
6469 {
6470 (void) XBell(display,0);
6471 return;
6472 }
6473 cache_image=undo_image;
6474 undo_image=GetPreviousImageInList(undo_image);
6475 windows->image.window_changes.width=(int) cache_image->columns;
6476 windows->image.window_changes.height=(int) cache_image->rows;
cristyb51dff52011-05-19 16:55:47 +00006477 (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
cristya9a86bb2011-01-13 01:11:00 +00006478 windows->image.ximage->width,windows->image.ximage->height);
6479 (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
cristy3ed852e2009-09-05 21:47:34 +00006480 if (windows->image.crop_geometry != (char *) NULL)
6481 windows->image.crop_geometry=(char *)
6482 RelinquishMagickMemory(windows->image.crop_geometry);
6483 windows->image.crop_geometry=cache_image->geometry;
6484 if (redo_image != (Image *) NULL)
6485 redo_image=DestroyImage(redo_image);
6486 redo_image=(*image);
6487 *image=cache_image->list;
6488 cache_image=DestroyImage(cache_image);
6489 if (windows->image.orphan != MagickFalse)
6490 return;
6491 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00006492 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006493 return;
6494 }
6495 case CutCommand:
6496 case PasteCommand:
6497 case ApplyCommand:
6498 case HalfSizeCommand:
6499 case OriginalSizeCommand:
6500 case DoubleSizeCommand:
6501 case ResizeCommand:
6502 case TrimCommand:
6503 case CropCommand:
6504 case ChopCommand:
6505 case FlipCommand:
6506 case FlopCommand:
6507 case RotateRightCommand:
6508 case RotateLeftCommand:
6509 case RotateCommand:
6510 case ShearCommand:
6511 case RollCommand:
6512 case NegateCommand:
6513 case ContrastStretchCommand:
6514 case SigmoidalContrastCommand:
6515 case NormalizeCommand:
6516 case EqualizeCommand:
6517 case HueCommand:
6518 case SaturationCommand:
6519 case BrightnessCommand:
6520 case GammaCommand:
6521 case SpiffCommand:
6522 case DullCommand:
6523 case GrayscaleCommand:
6524 case MapCommand:
6525 case QuantizeCommand:
6526 case DespeckleCommand:
6527 case EmbossCommand:
6528 case ReduceNoiseCommand:
6529 case AddNoiseCommand:
6530 case SharpenCommand:
6531 case BlurCommand:
6532 case ThresholdCommand:
6533 case EdgeDetectCommand:
6534 case SpreadCommand:
6535 case ShadeCommand:
6536 case RaiseCommand:
6537 case SegmentCommand:
6538 case SolarizeCommand:
6539 case SepiaToneCommand:
6540 case SwirlCommand:
6541 case ImplodeCommand:
6542 case VignetteCommand:
6543 case WaveCommand:
6544 case OilPaintCommand:
6545 case CharcoalDrawCommand:
6546 case AnnotateCommand:
6547 case AddBorderCommand:
6548 case AddFrameCommand:
6549 case CompositeCommand:
6550 case CommentCommand:
6551 case LaunchCommand:
6552 case RegionofInterestCommand:
6553 case SaveToUndoBufferCommand:
6554 case RedoCommand:
6555 {
6556 Image
6557 *previous_image;
6558
cristybb503372010-05-27 20:51:26 +00006559 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006560 bytes;
6561
cristybb503372010-05-27 20:51:26 +00006562 bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
cristy3ed852e2009-09-05 21:47:34 +00006563 if (undo_image != (Image *) NULL)
6564 {
6565 /*
cristya9a86bb2011-01-13 01:11:00 +00006566 Ensure the undo cache has enough memory available.
cristy3ed852e2009-09-05 21:47:34 +00006567 */
6568 previous_image=undo_image;
6569 while (previous_image != (Image *) NULL)
6570 {
6571 bytes+=previous_image->list->columns*previous_image->list->rows*
6572 sizeof(PixelPacket);
cristybb503372010-05-27 20:51:26 +00006573 if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
cristy3ed852e2009-09-05 21:47:34 +00006574 {
6575 previous_image=GetPreviousImageInList(previous_image);
6576 continue;
6577 }
6578 bytes-=previous_image->list->columns*previous_image->list->rows*
6579 sizeof(PixelPacket);
6580 if (previous_image == undo_image)
6581 undo_image=NewImageList();
6582 else
6583 previous_image->next->previous=NewImageList();
6584 break;
6585 }
6586 while (previous_image != (Image *) NULL)
6587 {
6588 /*
6589 Delete any excess memory from undo cache.
6590 */
6591 cache_image=previous_image;
6592 previous_image=GetPreviousImageInList(previous_image);
6593 cache_image->list=DestroyImage(cache_image->list);
6594 cache_image=DestroyImage(cache_image);
6595 }
6596 }
cristybb503372010-05-27 20:51:26 +00006597 if (bytes > (ssize_t) (resource_info->undo_cache << 20))
cristy3ed852e2009-09-05 21:47:34 +00006598 break;
6599 /*
6600 Save image before transformations are applied.
6601 */
6602 cache_image=AcquireImage((ImageInfo *) NULL);
6603 if (cache_image == (Image *) NULL)
6604 break;
6605 XSetCursorState(display,windows,MagickTrue);
6606 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00006607 cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00006608 XSetCursorState(display,windows,MagickFalse);
6609 if (cache_image->list == (Image *) NULL)
6610 {
6611 cache_image=DestroyImage(cache_image);
6612 break;
6613 }
cristybb503372010-05-27 20:51:26 +00006614 cache_image->columns=(size_t) windows->image.ximage->width;
6615 cache_image->rows=(size_t) windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +00006616 cache_image->geometry=windows->image.crop_geometry;
6617 if (windows->image.crop_geometry != (char *) NULL)
6618 {
6619 cache_image->geometry=AcquireString((char *) NULL);
6620 (void) CopyMagickString(cache_image->geometry,
6621 windows->image.crop_geometry,MaxTextExtent);
6622 }
6623 if (undo_image == (Image *) NULL)
6624 {
6625 undo_image=cache_image;
6626 break;
6627 }
6628 undo_image->next=cache_image;
6629 undo_image->next->previous=undo_image;
6630 undo_image=undo_image->next;
6631 break;
6632 }
6633 default:
6634 break;
6635 }
6636 if (command == RedoCommand)
6637 {
6638 /*
6639 Redo the last image transformation.
6640 */
6641 if (redo_image == (Image *) NULL)
6642 {
6643 (void) XBell(display,0);
6644 return;
6645 }
6646 windows->image.window_changes.width=(int) redo_image->columns;
6647 windows->image.window_changes.height=(int) redo_image->rows;
6648 if (windows->image.crop_geometry != (char *) NULL)
6649 windows->image.crop_geometry=(char *)
6650 RelinquishMagickMemory(windows->image.crop_geometry);
6651 windows->image.crop_geometry=redo_image->geometry;
6652 *image=DestroyImage(*image);
6653 *image=redo_image;
6654 redo_image=NewImageList();
6655 if (windows->image.orphan != MagickFalse)
6656 return;
6657 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00006658 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006659 return;
6660 }
6661 if (command != InfoCommand)
6662 return;
6663 /*
6664 Display image info.
6665 */
6666 XSetCursorState(display,windows,MagickTrue);
6667 XCheckRefreshWindows(display,windows);
6668 XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6669 XSetCursorState(display,windows,MagickFalse);
6670}
6671
6672/*
6673%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6674% %
6675% %
6676% %
6677+ X I m a g e W i n d o w C o m m a n d %
6678% %
6679% %
6680% %
6681%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6682%
6683% XImageWindowCommand() makes a transform to the image or Image window as
6684% specified by a user menu button or keyboard command.
6685%
cristy051718b2011-08-28 22:49:25 +00006686% The format of the XImageWindowCommand method is:
cristy3ed852e2009-09-05 21:47:34 +00006687%
6688% CommandType XImageWindowCommand(Display *display,
6689% XResourceInfo *resource_info,XWindows *windows,
cristy051718b2011-08-28 22:49:25 +00006690% const MagickStatusType state,KeySym key_symbol,Image **image,
6691% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00006692%
6693% A description of each parameter follows:
6694%
6695% o nexus: Method XImageWindowCommand returns an image when the
6696% user chooses 'Open Image' from the command menu. Otherwise a null
6697% image is returned.
6698%
6699% o display: Specifies a connection to an X server; returned from
6700% XOpenDisplay.
6701%
6702% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6703%
6704% o windows: Specifies a pointer to a XWindows structure.
6705%
6706% o state: key mask.
6707%
6708% o key_symbol: Specifies a command to perform.
6709%
cristy051718b2011-08-28 22:49:25 +00006710% o image: the image; XImageWIndowCommand may transform the image and
6711% return a new image pointer.
6712%
6713% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00006714%
6715*/
6716static CommandType XImageWindowCommand(Display *display,
6717 XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
cristy051718b2011-08-28 22:49:25 +00006718 KeySym key_symbol,Image **image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00006719{
6720 static char
6721 delta[MaxTextExtent] = "";
6722
6723 static const char
6724 Digits[] = "01234567890";
6725
6726 static KeySym
6727 last_symbol = XK_0;
6728
6729 if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6730 {
6731 if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6732 {
6733 *delta='\0';
6734 resource_info->quantum=1;
6735 }
6736 last_symbol=key_symbol;
6737 delta[strlen(delta)+1]='\0';
6738 delta[strlen(delta)]=Digits[key_symbol-XK_0];
cristyf2f27272009-12-17 14:48:46 +00006739 resource_info->quantum=StringToLong(delta);
cristy3ed852e2009-09-05 21:47:34 +00006740 return(NullCommand);
6741 }
6742 last_symbol=key_symbol;
6743 if (resource_info->immutable)
6744 {
6745 /*
6746 Virtual image window has a restricted command set.
6747 */
6748 switch (key_symbol)
6749 {
6750 case XK_question:
6751 return(InfoCommand);
6752 case XK_p:
6753 case XK_Print:
6754 return(PrintCommand);
6755 case XK_space:
6756 return(NextCommand);
6757 case XK_q:
6758 case XK_Escape:
6759 return(QuitCommand);
6760 default:
6761 break;
6762 }
6763 return(NullCommand);
6764 }
6765 switch ((int) key_symbol)
6766 {
6767 case XK_o:
6768 {
6769 if ((state & ControlMask) == 0)
6770 break;
6771 return(OpenCommand);
6772 }
6773 case XK_space:
6774 return(NextCommand);
6775 case XK_BackSpace:
6776 return(FormerCommand);
6777 case XK_s:
6778 {
6779 if ((state & Mod1Mask) != 0)
6780 return(SwirlCommand);
6781 if ((state & ControlMask) == 0)
6782 return(ShearCommand);
6783 return(SaveCommand);
6784 }
6785 case XK_p:
6786 case XK_Print:
6787 {
6788 if ((state & Mod1Mask) != 0)
6789 return(OilPaintCommand);
6790 if ((state & Mod4Mask) != 0)
6791 return(ColorCommand);
6792 if ((state & ControlMask) == 0)
6793 return(NullCommand);
6794 return(PrintCommand);
6795 }
6796 case XK_d:
6797 {
6798 if ((state & Mod4Mask) != 0)
6799 return(DrawCommand);
6800 if ((state & ControlMask) == 0)
6801 return(NullCommand);
6802 return(DeleteCommand);
6803 }
6804 case XK_Select:
6805 {
6806 if ((state & ControlMask) == 0)
6807 return(NullCommand);
6808 return(SelectCommand);
6809 }
6810 case XK_n:
6811 {
6812 if ((state & ControlMask) == 0)
6813 return(NullCommand);
6814 return(NewCommand);
6815 }
6816 case XK_q:
6817 case XK_Escape:
6818 return(QuitCommand);
6819 case XK_z:
6820 case XK_Undo:
6821 {
6822 if ((state & ControlMask) == 0)
6823 return(NullCommand);
6824 return(UndoCommand);
6825 }
6826 case XK_r:
6827 case XK_Redo:
6828 {
6829 if ((state & ControlMask) == 0)
6830 return(RollCommand);
6831 return(RedoCommand);
6832 }
6833 case XK_x:
6834 {
6835 if ((state & ControlMask) == 0)
6836 return(NullCommand);
6837 return(CutCommand);
6838 }
6839 case XK_c:
6840 {
6841 if ((state & Mod1Mask) != 0)
6842 return(CharcoalDrawCommand);
6843 if ((state & ControlMask) == 0)
6844 return(CropCommand);
6845 return(CopyCommand);
6846 }
6847 case XK_v:
6848 case XK_Insert:
6849 {
6850 if ((state & Mod4Mask) != 0)
6851 return(CompositeCommand);
6852 if ((state & ControlMask) == 0)
6853 return(FlipCommand);
6854 return(PasteCommand);
6855 }
6856 case XK_less:
6857 return(HalfSizeCommand);
6858 case XK_minus:
6859 return(OriginalSizeCommand);
6860 case XK_greater:
6861 return(DoubleSizeCommand);
6862 case XK_percent:
6863 return(ResizeCommand);
6864 case XK_at:
6865 return(RefreshCommand);
6866 case XK_bracketleft:
6867 return(ChopCommand);
6868 case XK_h:
6869 return(FlopCommand);
6870 case XK_slash:
6871 return(RotateRightCommand);
6872 case XK_backslash:
6873 return(RotateLeftCommand);
6874 case XK_asterisk:
6875 return(RotateCommand);
6876 case XK_t:
6877 return(TrimCommand);
6878 case XK_H:
6879 return(HueCommand);
6880 case XK_S:
6881 return(SaturationCommand);
6882 case XK_L:
6883 return(BrightnessCommand);
6884 case XK_G:
6885 return(GammaCommand);
6886 case XK_C:
6887 return(SpiffCommand);
6888 case XK_Z:
6889 return(DullCommand);
6890 case XK_N:
6891 return(NormalizeCommand);
6892 case XK_equal:
6893 return(EqualizeCommand);
6894 case XK_asciitilde:
6895 return(NegateCommand);
6896 case XK_period:
6897 return(GrayscaleCommand);
6898 case XK_numbersign:
6899 return(QuantizeCommand);
6900 case XK_F2:
6901 return(DespeckleCommand);
6902 case XK_F3:
6903 return(EmbossCommand);
6904 case XK_F4:
6905 return(ReduceNoiseCommand);
6906 case XK_F5:
6907 return(AddNoiseCommand);
6908 case XK_F6:
6909 return(SharpenCommand);
6910 case XK_F7:
6911 return(BlurCommand);
6912 case XK_F8:
6913 return(ThresholdCommand);
6914 case XK_F9:
6915 return(EdgeDetectCommand);
6916 case XK_F10:
6917 return(SpreadCommand);
6918 case XK_F11:
6919 return(ShadeCommand);
6920 case XK_F12:
6921 return(RaiseCommand);
6922 case XK_F13:
6923 return(SegmentCommand);
6924 case XK_i:
6925 {
6926 if ((state & Mod1Mask) == 0)
6927 return(NullCommand);
6928 return(ImplodeCommand);
6929 }
6930 case XK_w:
6931 {
6932 if ((state & Mod1Mask) == 0)
6933 return(NullCommand);
6934 return(WaveCommand);
6935 }
6936 case XK_m:
6937 {
6938 if ((state & Mod4Mask) == 0)
6939 return(NullCommand);
6940 return(MatteCommand);
6941 }
6942 case XK_b:
6943 {
6944 if ((state & Mod4Mask) == 0)
6945 return(NullCommand);
6946 return(AddBorderCommand);
6947 }
6948 case XK_f:
6949 {
6950 if ((state & Mod4Mask) == 0)
6951 return(NullCommand);
6952 return(AddFrameCommand);
6953 }
6954 case XK_exclam:
6955 {
6956 if ((state & Mod4Mask) == 0)
6957 return(NullCommand);
6958 return(CommentCommand);
6959 }
6960 case XK_a:
6961 {
6962 if ((state & Mod1Mask) != 0)
6963 return(ApplyCommand);
6964 if ((state & Mod4Mask) != 0)
6965 return(AnnotateCommand);
6966 if ((state & ControlMask) == 0)
6967 return(NullCommand);
6968 return(RegionofInterestCommand);
6969 }
6970 case XK_question:
6971 return(InfoCommand);
6972 case XK_plus:
6973 return(ZoomCommand);
6974 case XK_P:
6975 {
6976 if ((state & ShiftMask) == 0)
6977 return(NullCommand);
6978 return(ShowPreviewCommand);
6979 }
6980 case XK_Execute:
6981 return(LaunchCommand);
6982 case XK_F1:
6983 return(HelpCommand);
6984 case XK_Find:
6985 return(BrowseDocumentationCommand);
6986 case XK_Menu:
6987 {
6988 (void) XMapRaised(display,windows->command.id);
6989 return(NullCommand);
6990 }
6991 case XK_Next:
6992 case XK_Prior:
6993 case XK_Home:
6994 case XK_KP_Home:
6995 {
6996 XTranslateImage(display,windows,*image,key_symbol);
6997 return(NullCommand);
6998 }
6999 case XK_Up:
7000 case XK_KP_Up:
7001 case XK_Down:
7002 case XK_KP_Down:
7003 case XK_Left:
7004 case XK_KP_Left:
7005 case XK_Right:
7006 case XK_KP_Right:
7007 {
7008 if ((state & Mod1Mask) != 0)
7009 {
7010 RectangleInfo
7011 crop_info;
7012
7013 /*
7014 Trim one pixel from edge of image.
7015 */
7016 crop_info.x=0;
7017 crop_info.y=0;
cristybb503372010-05-27 20:51:26 +00007018 crop_info.width=(size_t) windows->image.ximage->width;
7019 crop_info.height=(size_t) windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +00007020 if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7021 {
7022 if (resource_info->quantum >= (int) crop_info.height)
7023 resource_info->quantum=(int) crop_info.height-1;
7024 crop_info.height-=resource_info->quantum;
7025 }
7026 if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7027 {
7028 if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7029 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7030 crop_info.y+=resource_info->quantum;
7031 crop_info.height-=resource_info->quantum;
7032 }
7033 if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7034 {
7035 if (resource_info->quantum >= (int) crop_info.width)
7036 resource_info->quantum=(int) crop_info.width-1;
7037 crop_info.width-=resource_info->quantum;
7038 }
7039 if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7040 {
7041 if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7042 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7043 crop_info.x+=resource_info->quantum;
7044 crop_info.width-=resource_info->quantum;
7045 }
7046 if ((int) (windows->image.x+windows->image.width) >
7047 (int) crop_info.width)
7048 windows->image.x=(int) (crop_info.width-windows->image.width);
7049 if ((int) (windows->image.y+windows->image.height) >
7050 (int) crop_info.height)
7051 windows->image.y=(int) (crop_info.height-windows->image.height);
7052 XSetCropGeometry(display,windows,&crop_info,*image);
7053 windows->image.window_changes.width=(int) crop_info.width;
7054 windows->image.window_changes.height=(int) crop_info.height;
7055 (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
cristy051718b2011-08-28 22:49:25 +00007056 (void) XConfigureImage(display,resource_info,windows,*image,
7057 exception);
cristy3ed852e2009-09-05 21:47:34 +00007058 return(NullCommand);
7059 }
7060 XTranslateImage(display,windows,*image,key_symbol);
7061 return(NullCommand);
7062 }
7063 default:
7064 return(NullCommand);
7065 }
7066 return(NullCommand);
7067}
7068
7069/*
7070%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7071% %
7072% %
7073% %
7074+ X M a g i c k C o m m a n d %
7075% %
7076% %
7077% %
7078%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7079%
7080% XMagickCommand() makes a transform to the image or Image window as
7081% specified by a user menu button or keyboard command.
7082%
7083% The format of the XMagickCommand method is:
7084%
7085% Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +00007086% XWindows *windows,const CommandType command,Image **image,
7087% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007088%
7089% A description of each parameter follows:
7090%
cristy3ed852e2009-09-05 21:47:34 +00007091% o display: Specifies a connection to an X server; returned from
7092% XOpenDisplay.
7093%
7094% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7095%
7096% o windows: Specifies a pointer to a XWindows structure.
7097%
7098% o command: Specifies a command to perform.
7099%
cristy051718b2011-08-28 22:49:25 +00007100% o image: the image; XMagickCommand may transform the image and return a
7101% new image pointer.
7102%
7103% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007104%
7105*/
7106static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +00007107 XWindows *windows,const CommandType command,Image **image,
7108 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007109{
7110 char
7111 filename[MaxTextExtent],
7112 geometry[MaxTextExtent],
7113 modulate_factors[MaxTextExtent];
7114
7115 GeometryInfo
7116 geometry_info;
7117
7118 Image
7119 *nexus;
7120
7121 ImageInfo
7122 *image_info;
7123
7124 int
7125 x,
7126 y;
7127
7128 MagickStatusType
7129 flags,
7130 status;
7131
7132 QuantizeInfo
7133 quantize_info;
7134
7135 RectangleInfo
7136 page_geometry;
7137
7138 register int
7139 i;
7140
7141 static char
7142 color[MaxTextExtent] = "gray";
7143
7144 unsigned int
7145 height,
7146 width;
7147
7148 /*
7149 Process user command.
7150 */
7151 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007152 XImageCache(display,resource_info,windows,command,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007153 nexus=NewImageList();
7154 windows->image.window_changes.width=windows->image.ximage->width;
7155 windows->image.window_changes.height=windows->image.ximage->height;
7156 image_info=CloneImageInfo(resource_info->image_info);
7157 SetGeometryInfo(&geometry_info);
7158 GetQuantizeInfo(&quantize_info);
7159 switch (command)
7160 {
7161 case OpenCommand:
7162 {
7163 /*
7164 Load image.
7165 */
7166 nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7167 break;
7168 }
7169 case NextCommand:
7170 {
7171 /*
7172 Display next image.
7173 */
7174 for (i=0; i < resource_info->quantum; i++)
7175 XClientMessage(display,windows->image.id,windows->im_protocols,
7176 windows->im_next_image,CurrentTime);
7177 break;
7178 }
7179 case FormerCommand:
7180 {
7181 /*
7182 Display former image.
7183 */
7184 for (i=0; i < resource_info->quantum; i++)
7185 XClientMessage(display,windows->image.id,windows->im_protocols,
7186 windows->im_former_image,CurrentTime);
7187 break;
7188 }
7189 case SelectCommand:
7190 {
7191 int
7192 status;
7193
7194 /*
7195 Select image.
7196 */
7197 status=chdir(resource_info->home_directory);
7198 if (status == -1)
cristy051718b2011-08-28 22:49:25 +00007199 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7200 "UnableToOpenFile","%s",resource_info->home_directory);
cristy3ed852e2009-09-05 21:47:34 +00007201 nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7202 break;
7203 }
7204 case SaveCommand:
7205 {
7206 /*
7207 Save image.
7208 */
cristy051718b2011-08-28 22:49:25 +00007209 status=XSaveImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007210 if (status == MagickFalse)
7211 {
7212 XNoticeWidget(display,windows,"Unable to write X image:",
7213 (*image)->filename);
7214 break;
7215 }
7216 break;
7217 }
7218 case PrintCommand:
7219 {
7220 /*
7221 Print image.
7222 */
cristy051718b2011-08-28 22:49:25 +00007223 status=XPrintImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007224 if (status == MagickFalse)
7225 {
7226 XNoticeWidget(display,windows,"Unable to print X image:",
7227 (*image)->filename);
7228 break;
7229 }
7230 break;
7231 }
7232 case DeleteCommand:
7233 {
7234 static char
7235 filename[MaxTextExtent] = "\0";
7236
7237 /*
7238 Delete image file.
7239 */
7240 XFileBrowserWidget(display,windows,"Delete",filename);
7241 if (*filename == '\0')
7242 break;
7243 status=remove(filename) != 0 ? MagickTrue : MagickFalse;
7244 if (status != MagickFalse)
7245 XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7246 break;
7247 }
7248 case NewCommand:
7249 {
7250 int
7251 status;
7252
7253 static char
7254 color[MaxTextExtent] = "gray",
7255 geometry[MaxTextExtent] = "640x480";
7256
7257 static const char
7258 *format = "gradient";
7259
7260 /*
7261 Query user for canvas geometry.
7262 */
7263 status=XDialogWidget(display,windows,"New","Enter image geometry:",
7264 geometry);
7265 if (*geometry == '\0')
7266 break;
7267 if (status == 0)
7268 format="xc";
7269 XColorBrowserWidget(display,windows,"Select",color);
7270 if (*color == '\0')
7271 break;
7272 /*
7273 Create canvas.
7274 */
cristyb51dff52011-05-19 16:55:47 +00007275 (void) FormatLocaleString(image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00007276 "%s:%s",format,color);
7277 (void) CloneString(&image_info->size,geometry);
cristy051718b2011-08-28 22:49:25 +00007278 nexus=ReadImage(image_info,exception);
7279 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00007280 XClientMessage(display,windows->image.id,windows->im_protocols,
7281 windows->im_next_image,CurrentTime);
7282 break;
7283 }
7284 case VisualDirectoryCommand:
7285 {
7286 /*
7287 Visual Image directory.
7288 */
cristy051718b2011-08-28 22:49:25 +00007289 nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
cristy3ed852e2009-09-05 21:47:34 +00007290 break;
7291 }
7292 case QuitCommand:
7293 {
7294 /*
7295 exit program.
7296 */
7297 if (resource_info->confirm_exit == MagickFalse)
7298 XClientMessage(display,windows->image.id,windows->im_protocols,
7299 windows->im_exit,CurrentTime);
7300 else
7301 {
7302 int
7303 status;
7304
7305 /*
7306 Confirm program exit.
7307 */
7308 status=XConfirmWidget(display,windows,"Do you really want to exit",
7309 resource_info->client_name);
7310 if (status > 0)
7311 XClientMessage(display,windows->image.id,windows->im_protocols,
7312 windows->im_exit,CurrentTime);
7313 }
7314 break;
7315 }
7316 case CutCommand:
7317 {
7318 /*
7319 Cut image.
7320 */
cristy051718b2011-08-28 22:49:25 +00007321 (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00007322 break;
7323 }
7324 case CopyCommand:
7325 {
7326 /*
7327 Copy image.
7328 */
cristy051718b2011-08-28 22:49:25 +00007329 (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7330 exception);
cristy3ed852e2009-09-05 21:47:34 +00007331 break;
7332 }
7333 case PasteCommand:
7334 {
7335 /*
7336 Paste image.
7337 */
cristy051718b2011-08-28 22:49:25 +00007338 status=XPasteImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007339 if (status == MagickFalse)
7340 {
7341 XNoticeWidget(display,windows,"Unable to paste X image",
7342 (*image)->filename);
7343 break;
7344 }
7345 break;
7346 }
7347 case HalfSizeCommand:
7348 {
7349 /*
7350 Half image size.
7351 */
7352 windows->image.window_changes.width=windows->image.ximage->width/2;
7353 windows->image.window_changes.height=windows->image.ximage->height/2;
cristy051718b2011-08-28 22:49:25 +00007354 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007355 break;
7356 }
7357 case OriginalSizeCommand:
7358 {
7359 /*
7360 Original image size.
7361 */
7362 windows->image.window_changes.width=(int) (*image)->columns;
7363 windows->image.window_changes.height=(int) (*image)->rows;
cristy051718b2011-08-28 22:49:25 +00007364 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007365 break;
7366 }
7367 case DoubleSizeCommand:
7368 {
7369 /*
7370 Double the image size.
7371 */
7372 windows->image.window_changes.width=windows->image.ximage->width << 1;
7373 windows->image.window_changes.height=windows->image.ximage->height << 1;
cristy051718b2011-08-28 22:49:25 +00007374 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007375 break;
7376 }
7377 case ResizeCommand:
7378 {
7379 int
7380 status;
7381
cristybb503372010-05-27 20:51:26 +00007382 size_t
cristy3ed852e2009-09-05 21:47:34 +00007383 height,
7384 width;
7385
cristy9d314ff2011-03-09 01:30:28 +00007386 ssize_t
7387 x,
7388 y;
7389
cristy3ed852e2009-09-05 21:47:34 +00007390 /*
7391 Resize image.
7392 */
cristybb503372010-05-27 20:51:26 +00007393 width=(size_t) windows->image.ximage->width;
7394 height=(size_t) windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +00007395 x=0;
7396 y=0;
cristyb51dff52011-05-19 16:55:47 +00007397 (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
cristye8c25f92010-06-03 00:53:06 +00007398 (double) width,(double) height);
cristy3ed852e2009-09-05 21:47:34 +00007399 status=XDialogWidget(display,windows,"Resize",
7400 "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7401 if (*geometry == '\0')
7402 break;
7403 if (status == 0)
7404 (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7405 (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7406 windows->image.window_changes.width=(int) width;
7407 windows->image.window_changes.height=(int) height;
cristy051718b2011-08-28 22:49:25 +00007408 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007409 break;
7410 }
7411 case ApplyCommand:
7412 {
7413 char
7414 image_geometry[MaxTextExtent];
7415
7416 if ((windows->image.crop_geometry == (char *) NULL) &&
7417 ((int) (*image)->columns == windows->image.ximage->width) &&
7418 ((int) (*image)->rows == windows->image.ximage->height))
7419 break;
7420 /*
7421 Apply size transforms to image.
7422 */
7423 XSetCursorState(display,windows,MagickTrue);
7424 XCheckRefreshWindows(display,windows);
7425 /*
7426 Crop and/or scale displayed image.
7427 */
cristyb51dff52011-05-19 16:55:47 +00007428 (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
cristy3ed852e2009-09-05 21:47:34 +00007429 windows->image.ximage->width,windows->image.ximage->height);
7430 (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7431 if (windows->image.crop_geometry != (char *) NULL)
7432 windows->image.crop_geometry=(char *)
7433 RelinquishMagickMemory(windows->image.crop_geometry);
7434 windows->image.x=0;
7435 windows->image.y=0;
7436 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00007437 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007438 break;
7439 }
7440 case RefreshCommand:
7441 {
cristy051718b2011-08-28 22:49:25 +00007442 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007443 break;
7444 }
7445 case RestoreCommand:
7446 {
7447 /*
7448 Restore Image window to its original size.
7449 */
7450 if ((windows->image.width == (unsigned int) (*image)->columns) &&
7451 (windows->image.height == (unsigned int) (*image)->rows) &&
7452 (windows->image.crop_geometry == (char *) NULL))
7453 {
7454 (void) XBell(display,0);
7455 break;
7456 }
7457 windows->image.window_changes.width=(int) (*image)->columns;
7458 windows->image.window_changes.height=(int) (*image)->rows;
7459 if (windows->image.crop_geometry != (char *) NULL)
7460 {
7461 windows->image.crop_geometry=(char *)
7462 RelinquishMagickMemory(windows->image.crop_geometry);
7463 windows->image.crop_geometry=(char *) NULL;
7464 windows->image.x=0;
7465 windows->image.y=0;
7466 }
7467 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00007468 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007469 break;
7470 }
7471 case CropCommand:
7472 {
7473 /*
7474 Crop image.
7475 */
cristy051718b2011-08-28 22:49:25 +00007476 (void) XCropImage(display,resource_info,windows,*image,CropMode,
7477 exception);
cristy3ed852e2009-09-05 21:47:34 +00007478 break;
7479 }
7480 case ChopCommand:
7481 {
7482 /*
7483 Chop image.
7484 */
cristy051718b2011-08-28 22:49:25 +00007485 status=XChopImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007486 if (status == MagickFalse)
7487 {
7488 XNoticeWidget(display,windows,"Unable to cut X image",
7489 (*image)->filename);
7490 break;
7491 }
7492 break;
7493 }
7494 case FlopCommand:
7495 {
7496 Image
7497 *flop_image;
7498
7499 /*
7500 Flop image scanlines.
7501 */
7502 XSetCursorState(display,windows,MagickTrue);
7503 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007504 flop_image=FlopImage(*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007505 if (flop_image != (Image *) NULL)
7506 {
7507 *image=DestroyImage(*image);
7508 *image=flop_image;
7509 }
cristy051718b2011-08-28 22:49:25 +00007510 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00007511 XSetCursorState(display,windows,MagickFalse);
7512 if (windows->image.crop_geometry != (char *) NULL)
7513 {
7514 /*
7515 Flop crop geometry.
7516 */
7517 width=(unsigned int) (*image)->columns;
7518 height=(unsigned int) (*image)->rows;
7519 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7520 &width,&height);
cristyb51dff52011-05-19 16:55:47 +00007521 (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00007522 "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7523 }
7524 if (windows->image.orphan != MagickFalse)
7525 break;
cristy051718b2011-08-28 22:49:25 +00007526 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007527 break;
7528 }
7529 case FlipCommand:
7530 {
7531 Image
7532 *flip_image;
7533
7534 /*
7535 Flip image scanlines.
7536 */
7537 XSetCursorState(display,windows,MagickTrue);
7538 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007539 flip_image=FlipImage(*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007540 if (flip_image != (Image *) NULL)
7541 {
7542 *image=DestroyImage(*image);
7543 *image=flip_image;
7544 }
cristy051718b2011-08-28 22:49:25 +00007545 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00007546 XSetCursorState(display,windows,MagickFalse);
7547 if (windows->image.crop_geometry != (char *) NULL)
7548 {
7549 /*
7550 Flip crop geometry.
7551 */
7552 width=(unsigned int) (*image)->columns;
7553 height=(unsigned int) (*image)->rows;
7554 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7555 &width,&height);
cristyb51dff52011-05-19 16:55:47 +00007556 (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00007557 "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7558 }
7559 if (windows->image.orphan != MagickFalse)
7560 break;
cristy051718b2011-08-28 22:49:25 +00007561 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007562 break;
7563 }
7564 case RotateRightCommand:
7565 {
7566 /*
7567 Rotate image 90 degrees clockwise.
7568 */
cristy051718b2011-08-28 22:49:25 +00007569 status=XRotateImage(display,resource_info,windows,90.0,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007570 if (status == MagickFalse)
7571 {
7572 XNoticeWidget(display,windows,"Unable to rotate X image",
7573 (*image)->filename);
7574 break;
7575 }
7576 break;
7577 }
7578 case RotateLeftCommand:
7579 {
7580 /*
7581 Rotate image 90 degrees counter-clockwise.
7582 */
cristy051718b2011-08-28 22:49:25 +00007583 status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007584 if (status == MagickFalse)
7585 {
7586 XNoticeWidget(display,windows,"Unable to rotate X image",
7587 (*image)->filename);
7588 break;
7589 }
7590 break;
7591 }
7592 case RotateCommand:
7593 {
7594 /*
7595 Rotate image.
7596 */
cristy051718b2011-08-28 22:49:25 +00007597 status=XRotateImage(display,resource_info,windows,0.0,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007598 if (status == MagickFalse)
7599 {
7600 XNoticeWidget(display,windows,"Unable to rotate X image",
7601 (*image)->filename);
7602 break;
7603 }
7604 break;
7605 }
7606 case ShearCommand:
7607 {
7608 Image
7609 *shear_image;
7610
7611 static char
7612 geometry[MaxTextExtent] = "45.0x45.0";
7613
7614 /*
7615 Query user for shear color and geometry.
7616 */
7617 XColorBrowserWidget(display,windows,"Select",color);
7618 if (*color == '\0')
7619 break;
7620 (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7621 geometry);
7622 if (*geometry == '\0')
7623 break;
7624 /*
7625 Shear image.
7626 */
cristy051718b2011-08-28 22:49:25 +00007627 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7628 exception);
cristy3ed852e2009-09-05 21:47:34 +00007629 XSetCursorState(display,windows,MagickTrue);
7630 XCheckRefreshWindows(display,windows);
7631 (void) QueryColorDatabase(color,&(*image)->background_color,
cristy051718b2011-08-28 22:49:25 +00007632 exception);
cristy3ed852e2009-09-05 21:47:34 +00007633 flags=ParseGeometry(geometry,&geometry_info);
7634 if ((flags & SigmaValue) == 0)
7635 geometry_info.sigma=geometry_info.rho;
7636 shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
cristy051718b2011-08-28 22:49:25 +00007637 exception);
cristy3ed852e2009-09-05 21:47:34 +00007638 if (shear_image != (Image *) NULL)
7639 {
7640 *image=DestroyImage(*image);
7641 *image=shear_image;
7642 }
cristy051718b2011-08-28 22:49:25 +00007643 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00007644 XSetCursorState(display,windows,MagickFalse);
7645 if (windows->image.orphan != MagickFalse)
7646 break;
7647 windows->image.window_changes.width=(int) (*image)->columns;
7648 windows->image.window_changes.height=(int) (*image)->rows;
7649 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00007650 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007651 break;
7652 }
7653 case RollCommand:
7654 {
7655 Image
7656 *roll_image;
7657
7658 static char
7659 geometry[MaxTextExtent] = "+2+2";
7660
7661 /*
7662 Query user for the roll geometry.
7663 */
7664 (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7665 geometry);
7666 if (*geometry == '\0')
7667 break;
7668 /*
7669 Roll image.
7670 */
cristy051718b2011-08-28 22:49:25 +00007671 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7672 exception);
cristy3ed852e2009-09-05 21:47:34 +00007673 XSetCursorState(display,windows,MagickTrue);
7674 XCheckRefreshWindows(display,windows);
7675 (void) ParsePageGeometry(*image,geometry,&page_geometry,
cristy051718b2011-08-28 22:49:25 +00007676 exception);
cristy3ed852e2009-09-05 21:47:34 +00007677 roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
cristy051718b2011-08-28 22:49:25 +00007678 exception);
cristy3ed852e2009-09-05 21:47:34 +00007679 if (roll_image != (Image *) NULL)
7680 {
7681 *image=DestroyImage(*image);
7682 *image=roll_image;
7683 }
cristy051718b2011-08-28 22:49:25 +00007684 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00007685 XSetCursorState(display,windows,MagickFalse);
7686 if (windows->image.orphan != MagickFalse)
7687 break;
7688 windows->image.window_changes.width=(int) (*image)->columns;
7689 windows->image.window_changes.height=(int) (*image)->rows;
7690 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00007691 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007692 break;
7693 }
7694 case TrimCommand:
7695 {
7696 static char
7697 fuzz[MaxTextExtent];
7698
7699 /*
7700 Query user for the fuzz factor.
7701 */
cristyb51dff52011-05-19 16:55:47 +00007702 (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
cristy8cd5b312010-01-07 01:10:24 +00007703 (*image)->fuzz/(QuantumRange+1.0));
cristy3ed852e2009-09-05 21:47:34 +00007704 (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7705 if (*fuzz == '\0')
7706 break;
cristyf2f27272009-12-17 14:48:46 +00007707 (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
cristy3ed852e2009-09-05 21:47:34 +00007708 /*
7709 Trim image.
7710 */
cristy051718b2011-08-28 22:49:25 +00007711 status=XTrimImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007712 if (status == MagickFalse)
7713 {
7714 XNoticeWidget(display,windows,"Unable to trim X image",
7715 (*image)->filename);
7716 break;
7717 }
7718 break;
7719 }
7720 case HueCommand:
7721 {
7722 static char
7723 hue_percent[MaxTextExtent] = "110";
7724
7725 /*
7726 Query user for percent hue change.
7727 */
7728 (void) XDialogWidget(display,windows,"Apply",
7729 "Enter percent change in image hue (0-200):",hue_percent);
7730 if (*hue_percent == '\0')
7731 break;
7732 /*
7733 Vary the image hue.
7734 */
7735 XSetCursorState(display,windows,MagickTrue);
7736 XCheckRefreshWindows(display,windows);
7737 (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7738 (void) ConcatenateMagickString(modulate_factors,hue_percent,
7739 MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +00007740 (void) ModulateImage(*image,modulate_factors,exception);
cristy3ed852e2009-09-05 21:47:34 +00007741 XSetCursorState(display,windows,MagickFalse);
7742 if (windows->image.orphan != MagickFalse)
7743 break;
7744 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00007745 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007746 break;
7747 }
7748 case SaturationCommand:
7749 {
7750 static char
7751 saturation_percent[MaxTextExtent] = "110";
7752
7753 /*
7754 Query user for percent saturation change.
7755 */
7756 (void) XDialogWidget(display,windows,"Apply",
7757 "Enter percent change in color saturation (0-200):",saturation_percent);
7758 if (*saturation_percent == '\0')
7759 break;
7760 /*
7761 Vary color saturation.
7762 */
7763 XSetCursorState(display,windows,MagickTrue);
7764 XCheckRefreshWindows(display,windows);
7765 (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7766 (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7767 MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +00007768 (void) ModulateImage(*image,modulate_factors,exception);
cristy3ed852e2009-09-05 21:47:34 +00007769 XSetCursorState(display,windows,MagickFalse);
7770 if (windows->image.orphan != MagickFalse)
7771 break;
7772 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00007773 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007774 break;
7775 }
7776 case BrightnessCommand:
7777 {
7778 static char
7779 brightness_percent[MaxTextExtent] = "110";
7780
7781 /*
7782 Query user for percent brightness change.
7783 */
7784 (void) XDialogWidget(display,windows,"Apply",
7785 "Enter percent change in color brightness (0-200):",brightness_percent);
7786 if (*brightness_percent == '\0')
7787 break;
7788 /*
7789 Vary the color brightness.
7790 */
7791 XSetCursorState(display,windows,MagickTrue);
7792 XCheckRefreshWindows(display,windows);
7793 (void) CopyMagickString(modulate_factors,brightness_percent,
7794 MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +00007795 (void) ModulateImage(*image,modulate_factors,exception);
cristy3ed852e2009-09-05 21:47:34 +00007796 XSetCursorState(display,windows,MagickFalse);
7797 if (windows->image.orphan != MagickFalse)
7798 break;
7799 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00007800 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007801 break;
7802 }
7803 case GammaCommand:
7804 {
7805 static char
7806 factor[MaxTextExtent] = "1.6";
7807
7808 /*
7809 Query user for gamma value.
7810 */
7811 (void) XDialogWidget(display,windows,"Gamma",
cristy50fbc382011-07-07 02:19:17 +00007812 "Enter gamma value (e.g. 1.2):",factor);
cristy3ed852e2009-09-05 21:47:34 +00007813 if (*factor == '\0')
7814 break;
7815 /*
7816 Gamma correct image.
7817 */
7818 XSetCursorState(display,windows,MagickTrue);
7819 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007820 (void) GammaImage(*image,atof(factor),exception);
cristy3ed852e2009-09-05 21:47:34 +00007821 XSetCursorState(display,windows,MagickFalse);
7822 if (windows->image.orphan != MagickFalse)
7823 break;
7824 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00007825 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007826 break;
7827 }
7828 case SpiffCommand:
7829 {
7830 /*
7831 Sharpen the image contrast.
7832 */
7833 XSetCursorState(display,windows,MagickTrue);
7834 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007835 (void) ContrastImage(*image,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00007836 XSetCursorState(display,windows,MagickFalse);
7837 if (windows->image.orphan != MagickFalse)
7838 break;
7839 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00007840 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007841 break;
7842 }
7843 case DullCommand:
7844 {
7845 /*
7846 Dull the image contrast.
7847 */
7848 XSetCursorState(display,windows,MagickTrue);
7849 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007850 (void) ContrastImage(*image,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00007851 XSetCursorState(display,windows,MagickFalse);
7852 if (windows->image.orphan != MagickFalse)
7853 break;
7854 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00007855 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007856 break;
7857 }
7858 case ContrastStretchCommand:
7859 {
7860 double
7861 black_point,
7862 white_point;
7863
7864 static char
7865 levels[MaxTextExtent] = "1%";
7866
7867 /*
7868 Query user for gamma value.
7869 */
7870 (void) XDialogWidget(display,windows,"Contrast Stretch",
7871 "Enter black and white points:",levels);
7872 if (*levels == '\0')
7873 break;
7874 /*
7875 Contrast stretch image.
7876 */
7877 XSetCursorState(display,windows,MagickTrue);
7878 XCheckRefreshWindows(display,windows);
7879 flags=ParseGeometry(levels,&geometry_info);
7880 black_point=geometry_info.rho;
7881 white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7882 if ((flags & PercentValue) != 0)
7883 {
7884 black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7885 white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7886 }
7887 white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
cristye23ec9d2011-08-16 18:15:40 +00007888 (void) ContrastStretchImage(*image,black_point,white_point,
cristy051718b2011-08-28 22:49:25 +00007889 exception);
cristy3ed852e2009-09-05 21:47:34 +00007890 XSetCursorState(display,windows,MagickFalse);
7891 if (windows->image.orphan != MagickFalse)
7892 break;
7893 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00007894 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007895 break;
7896 }
7897 case SigmoidalContrastCommand:
7898 {
cristy9ee60942011-07-06 14:54:38 +00007899 GeometryInfo
7900 geometry_info;
7901
7902 MagickStatusType
7903 flags;
7904
cristy3ed852e2009-09-05 21:47:34 +00007905 static char
7906 levels[MaxTextExtent] = "3x50%";
7907
7908 /*
7909 Query user for gamma value.
7910 */
7911 (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7912 "Enter contrast and midpoint:",levels);
7913 if (*levels == '\0')
7914 break;
7915 /*
7916 Contrast stretch image.
7917 */
7918 XSetCursorState(display,windows,MagickTrue);
7919 XCheckRefreshWindows(display,windows);
cristy9ee60942011-07-06 14:54:38 +00007920 flags=ParseGeometry(levels,&geometry_info);
7921 if ((flags & SigmaValue) == 0)
7922 geometry_info.sigma=1.0*QuantumRange/2.0;
7923 if ((flags & PercentValue) != 0)
7924 geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7925 (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
cristy051718b2011-08-28 22:49:25 +00007926 geometry_info.sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +00007927 XSetCursorState(display,windows,MagickFalse);
7928 if (windows->image.orphan != MagickFalse)
7929 break;
7930 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00007931 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007932 break;
7933 }
7934 case NormalizeCommand:
7935 {
7936 /*
7937 Perform histogram normalization on the image.
7938 */
7939 XSetCursorState(display,windows,MagickTrue);
7940 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007941 (void) NormalizeImage(*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007942 XSetCursorState(display,windows,MagickFalse);
7943 if (windows->image.orphan != MagickFalse)
7944 break;
7945 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00007946 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007947 break;
7948 }
7949 case EqualizeCommand:
7950 {
7951 /*
7952 Perform histogram equalization on the image.
7953 */
7954 XSetCursorState(display,windows,MagickTrue);
7955 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007956 (void) EqualizeImage(*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007957 XSetCursorState(display,windows,MagickFalse);
7958 if (windows->image.orphan != MagickFalse)
7959 break;
7960 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00007961 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007962 break;
7963 }
7964 case NegateCommand:
7965 {
7966 /*
7967 Negate colors in image.
7968 */
7969 XSetCursorState(display,windows,MagickTrue);
7970 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007971 (void) NegateImage(*image,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00007972 XSetCursorState(display,windows,MagickFalse);
7973 if (windows->image.orphan != MagickFalse)
7974 break;
7975 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00007976 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007977 break;
7978 }
7979 case GrayscaleCommand:
7980 {
7981 /*
7982 Convert image to grayscale.
7983 */
7984 XSetCursorState(display,windows,MagickTrue);
7985 XCheckRefreshWindows(display,windows);
7986 (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7987 GrayscaleType : GrayscaleMatteType);
7988 XSetCursorState(display,windows,MagickFalse);
7989 if (windows->image.orphan != MagickFalse)
7990 break;
7991 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00007992 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007993 break;
7994 }
7995 case MapCommand:
7996 {
7997 Image
7998 *affinity_image;
7999
8000 static char
8001 filename[MaxTextExtent] = "\0";
8002
8003 /*
8004 Request image file name from user.
8005 */
8006 XFileBrowserWidget(display,windows,"Map",filename);
8007 if (*filename == '\0')
8008 break;
8009 /*
8010 Map image.
8011 */
8012 XSetCursorState(display,windows,MagickTrue);
8013 XCheckRefreshWindows(display,windows);
8014 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +00008015 affinity_image=ReadImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00008016 if (affinity_image != (Image *) NULL)
8017 {
8018 (void) RemapImage(&quantize_info,*image,affinity_image);
8019 affinity_image=DestroyImage(affinity_image);
8020 }
cristy051718b2011-08-28 22:49:25 +00008021 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008022 XSetCursorState(display,windows,MagickFalse);
8023 if (windows->image.orphan != MagickFalse)
8024 break;
8025 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00008026 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008027 break;
8028 }
8029 case QuantizeCommand:
8030 {
8031 int
8032 status;
8033
8034 static char
8035 colors[MaxTextExtent] = "256";
8036
8037 /*
8038 Query user for maximum number of colors.
8039 */
8040 status=XDialogWidget(display,windows,"Quantize",
8041 "Maximum number of colors:",colors);
8042 if (*colors == '\0')
8043 break;
8044 /*
8045 Color reduce the image.
8046 */
8047 XSetCursorState(display,windows,MagickTrue);
8048 XCheckRefreshWindows(display,windows);
cristye27293e2009-12-18 02:53:20 +00008049 quantize_info.number_colors=StringToUnsignedLong(colors);
cristy3ed852e2009-09-05 21:47:34 +00008050 quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
8051 (void) QuantizeImage(&quantize_info,*image);
8052 XSetCursorState(display,windows,MagickFalse);
8053 if (windows->image.orphan != MagickFalse)
8054 break;
8055 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00008056 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008057 break;
8058 }
8059 case DespeckleCommand:
8060 {
8061 Image
8062 *despeckle_image;
8063
8064 /*
8065 Despeckle image.
8066 */
8067 XSetCursorState(display,windows,MagickTrue);
8068 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00008069 despeckle_image=DespeckleImage(*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008070 if (despeckle_image != (Image *) NULL)
8071 {
8072 *image=DestroyImage(*image);
8073 *image=despeckle_image;
8074 }
cristy051718b2011-08-28 22:49:25 +00008075 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008076 XSetCursorState(display,windows,MagickFalse);
8077 if (windows->image.orphan != MagickFalse)
8078 break;
8079 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00008080 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008081 break;
8082 }
8083 case EmbossCommand:
8084 {
8085 Image
8086 *emboss_image;
8087
8088 static char
8089 radius[MaxTextExtent] = "0.0x1.0";
8090
8091 /*
8092 Query user for emboss radius.
8093 */
8094 (void) XDialogWidget(display,windows,"Emboss",
8095 "Enter the emboss radius and standard deviation:",radius);
8096 if (*radius == '\0')
8097 break;
8098 /*
8099 Reduce noise in the image.
8100 */
8101 XSetCursorState(display,windows,MagickTrue);
8102 XCheckRefreshWindows(display,windows);
8103 flags=ParseGeometry(radius,&geometry_info);
8104 if ((flags & SigmaValue) == 0)
8105 geometry_info.sigma=1.0;
8106 emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
cristy051718b2011-08-28 22:49:25 +00008107 exception);
cristy3ed852e2009-09-05 21:47:34 +00008108 if (emboss_image != (Image *) NULL)
8109 {
8110 *image=DestroyImage(*image);
8111 *image=emboss_image;
8112 }
cristy051718b2011-08-28 22:49:25 +00008113 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008114 XSetCursorState(display,windows,MagickFalse);
8115 if (windows->image.orphan != MagickFalse)
8116 break;
8117 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00008118 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008119 break;
8120 }
8121 case ReduceNoiseCommand:
8122 {
8123 Image
8124 *noise_image;
8125
8126 static char
8127 radius[MaxTextExtent] = "0";
8128
8129 /*
8130 Query user for noise radius.
8131 */
8132 (void) XDialogWidget(display,windows,"Reduce Noise",
8133 "Enter the noise radius:",radius);
8134 if (*radius == '\0')
8135 break;
8136 /*
8137 Reduce noise in the image.
8138 */
8139 XSetCursorState(display,windows,MagickTrue);
8140 XCheckRefreshWindows(display,windows);
8141 flags=ParseGeometry(radius,&geometry_info);
cristy95c38342011-03-18 22:39:51 +00008142 noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
cristy051718b2011-08-28 22:49:25 +00008143 geometry_info.rho,(size_t) geometry_info.rho,exception);
cristy3ed852e2009-09-05 21:47:34 +00008144 if (noise_image != (Image *) NULL)
8145 {
8146 *image=DestroyImage(*image);
8147 *image=noise_image;
8148 }
cristy051718b2011-08-28 22:49:25 +00008149 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008150 XSetCursorState(display,windows,MagickFalse);
8151 if (windows->image.orphan != MagickFalse)
8152 break;
8153 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00008154 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008155 break;
8156 }
8157 case AddNoiseCommand:
8158 {
8159 char
8160 **noises;
8161
8162 Image
8163 *noise_image;
8164
8165 static char
8166 noise_type[MaxTextExtent] = "Gaussian";
8167
8168 /*
8169 Add noise to the image.
8170 */
cristy042ee782011-04-22 18:48:30 +00008171 noises=GetCommandOptions(MagickNoiseOptions);
cristy3ed852e2009-09-05 21:47:34 +00008172 if (noises == (char **) NULL)
8173 break;
8174 XListBrowserWidget(display,windows,&windows->widget,
8175 (const char **) noises,"Add Noise",
8176 "Select a type of noise to add to your image:",noise_type);
8177 noises=DestroyStringList(noises);
8178 if (*noise_type == '\0')
8179 break;
8180 XSetCursorState(display,windows,MagickTrue);
8181 XCheckRefreshWindows(display,windows);
cristy042ee782011-04-22 18:48:30 +00008182 noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
cristy051718b2011-08-28 22:49:25 +00008183 MagickNoiseOptions,MagickFalse,noise_type),exception);
cristy3ed852e2009-09-05 21:47:34 +00008184 if (noise_image != (Image *) NULL)
8185 {
8186 *image=DestroyImage(*image);
8187 *image=noise_image;
8188 }
cristy051718b2011-08-28 22:49:25 +00008189 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008190 XSetCursorState(display,windows,MagickFalse);
8191 if (windows->image.orphan != MagickFalse)
8192 break;
8193 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00008194 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008195 break;
8196 }
8197 case SharpenCommand:
8198 {
8199 Image
8200 *sharp_image;
8201
8202 static char
8203 radius[MaxTextExtent] = "0.0x1.0";
8204
8205 /*
8206 Query user for sharpen radius.
8207 */
8208 (void) XDialogWidget(display,windows,"Sharpen",
8209 "Enter the sharpen radius and standard deviation:",radius);
8210 if (*radius == '\0')
8211 break;
8212 /*
8213 Sharpen image scanlines.
8214 */
8215 XSetCursorState(display,windows,MagickTrue);
8216 XCheckRefreshWindows(display,windows);
8217 flags=ParseGeometry(radius,&geometry_info);
8218 sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
cristy051718b2011-08-28 22:49:25 +00008219 exception);
cristy3ed852e2009-09-05 21:47:34 +00008220 if (sharp_image != (Image *) NULL)
8221 {
8222 *image=DestroyImage(*image);
8223 *image=sharp_image;
8224 }
cristy051718b2011-08-28 22:49:25 +00008225 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008226 XSetCursorState(display,windows,MagickFalse);
8227 if (windows->image.orphan != MagickFalse)
8228 break;
8229 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00008230 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008231 break;
8232 }
8233 case BlurCommand:
8234 {
8235 Image
8236 *blur_image;
8237
8238 static char
8239 radius[MaxTextExtent] = "0.0x1.0";
8240
8241 /*
8242 Query user for blur radius.
8243 */
8244 (void) XDialogWidget(display,windows,"Blur",
8245 "Enter the blur radius and standard deviation:",radius);
8246 if (*radius == '\0')
8247 break;
8248 /*
8249 Blur an image.
8250 */
8251 XSetCursorState(display,windows,MagickTrue);
8252 XCheckRefreshWindows(display,windows);
8253 flags=ParseGeometry(radius,&geometry_info);
8254 blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
cristy051718b2011-08-28 22:49:25 +00008255 exception);
cristy3ed852e2009-09-05 21:47:34 +00008256 if (blur_image != (Image *) NULL)
8257 {
8258 *image=DestroyImage(*image);
8259 *image=blur_image;
8260 }
cristy051718b2011-08-28 22:49:25 +00008261 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008262 XSetCursorState(display,windows,MagickFalse);
8263 if (windows->image.orphan != MagickFalse)
8264 break;
8265 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00008266 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008267 break;
8268 }
8269 case ThresholdCommand:
8270 {
8271 double
8272 threshold;
8273
8274 static char
8275 factor[MaxTextExtent] = "128";
8276
8277 /*
8278 Query user for threshold value.
8279 */
8280 (void) XDialogWidget(display,windows,"Threshold",
8281 "Enter threshold value:",factor);
8282 if (*factor == '\0')
8283 break;
8284 /*
8285 Gamma correct image.
8286 */
8287 XSetCursorState(display,windows,MagickTrue);
8288 XCheckRefreshWindows(display,windows);
cristyf2f27272009-12-17 14:48:46 +00008289 threshold=SiPrefixToDouble(factor,QuantumRange);
cristy3ed852e2009-09-05 21:47:34 +00008290 (void) BilevelImage(*image,threshold);
8291 XSetCursorState(display,windows,MagickFalse);
8292 if (windows->image.orphan != MagickFalse)
8293 break;
8294 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00008295 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008296 break;
8297 }
8298 case EdgeDetectCommand:
8299 {
8300 Image
8301 *edge_image;
8302
8303 static char
8304 radius[MaxTextExtent] = "0";
8305
8306 /*
8307 Query user for edge factor.
8308 */
8309 (void) XDialogWidget(display,windows,"Detect Edges",
8310 "Enter the edge detect radius:",radius);
8311 if (*radius == '\0')
8312 break;
8313 /*
8314 Detect edge in image.
8315 */
8316 XSetCursorState(display,windows,MagickTrue);
8317 XCheckRefreshWindows(display,windows);
8318 flags=ParseGeometry(radius,&geometry_info);
cristy051718b2011-08-28 22:49:25 +00008319 edge_image=EdgeImage(*image,geometry_info.rho,exception);
cristy3ed852e2009-09-05 21:47:34 +00008320 if (edge_image != (Image *) NULL)
8321 {
8322 *image=DestroyImage(*image);
8323 *image=edge_image;
8324 }
cristy051718b2011-08-28 22:49:25 +00008325 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008326 XSetCursorState(display,windows,MagickFalse);
8327 if (windows->image.orphan != MagickFalse)
8328 break;
8329 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00008330 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008331 break;
8332 }
8333 case SpreadCommand:
8334 {
8335 Image
8336 *spread_image;
8337
8338 static char
8339 amount[MaxTextExtent] = "2";
8340
8341 /*
8342 Query user for spread amount.
8343 */
8344 (void) XDialogWidget(display,windows,"Spread",
8345 "Enter the displacement amount:",amount);
8346 if (*amount == '\0')
8347 break;
8348 /*
8349 Displace image pixels by a random amount.
8350 */
8351 XSetCursorState(display,windows,MagickTrue);
8352 XCheckRefreshWindows(display,windows);
8353 flags=ParseGeometry(amount,&geometry_info);
cristy051718b2011-08-28 22:49:25 +00008354 spread_image=EdgeImage(*image,geometry_info.rho,exception);
cristy3ed852e2009-09-05 21:47:34 +00008355 if (spread_image != (Image *) NULL)
8356 {
8357 *image=DestroyImage(*image);
8358 *image=spread_image;
8359 }
cristy051718b2011-08-28 22:49:25 +00008360 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008361 XSetCursorState(display,windows,MagickFalse);
8362 if (windows->image.orphan != MagickFalse)
8363 break;
8364 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00008365 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008366 break;
8367 }
8368 case ShadeCommand:
8369 {
8370 Image
8371 *shade_image;
8372
8373 int
8374 status;
8375
8376 static char
8377 geometry[MaxTextExtent] = "30x30";
8378
8379 /*
8380 Query user for the shade geometry.
8381 */
8382 status=XDialogWidget(display,windows,"Shade",
8383 "Enter the azimuth and elevation of the light source:",geometry);
8384 if (*geometry == '\0')
8385 break;
8386 /*
8387 Shade image pixels.
8388 */
8389 XSetCursorState(display,windows,MagickTrue);
8390 XCheckRefreshWindows(display,windows);
8391 flags=ParseGeometry(geometry,&geometry_info);
8392 if ((flags & SigmaValue) == 0)
8393 geometry_info.sigma=1.0;
8394 shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
cristy051718b2011-08-28 22:49:25 +00008395 geometry_info.rho,geometry_info.sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +00008396 if (shade_image != (Image *) NULL)
8397 {
8398 *image=DestroyImage(*image);
8399 *image=shade_image;
8400 }
cristy051718b2011-08-28 22:49:25 +00008401 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008402 XSetCursorState(display,windows,MagickFalse);
8403 if (windows->image.orphan != MagickFalse)
8404 break;
8405 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00008406 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008407 break;
8408 }
8409 case RaiseCommand:
8410 {
8411 static char
8412 bevel_width[MaxTextExtent] = "10";
8413
8414 /*
8415 Query user for bevel width.
8416 */
8417 (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8418 if (*bevel_width == '\0')
8419 break;
8420 /*
8421 Raise an image.
8422 */
cristy051718b2011-08-28 22:49:25 +00008423 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8424 exception);
cristy3ed852e2009-09-05 21:47:34 +00008425 XSetCursorState(display,windows,MagickTrue);
8426 XCheckRefreshWindows(display,windows);
8427 (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
cristy051718b2011-08-28 22:49:25 +00008428 exception);
8429 (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00008430 XSetCursorState(display,windows,MagickFalse);
8431 if (windows->image.orphan != MagickFalse)
8432 break;
8433 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00008434 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008435 break;
8436 }
8437 case SegmentCommand:
8438 {
8439 static char
8440 threshold[MaxTextExtent] = "1.0x1.5";
8441
8442 /*
8443 Query user for smoothing threshold.
8444 */
8445 (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8446 threshold);
8447 if (*threshold == '\0')
8448 break;
8449 /*
8450 Segment an image.
8451 */
8452 XSetCursorState(display,windows,MagickTrue);
8453 XCheckRefreshWindows(display,windows);
8454 flags=ParseGeometry(threshold,&geometry_info);
8455 if ((flags & SigmaValue) == 0)
8456 geometry_info.sigma=1.0;
8457 (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8458 geometry_info.sigma);
8459 XSetCursorState(display,windows,MagickFalse);
8460 if (windows->image.orphan != MagickFalse)
8461 break;
8462 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00008463 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008464 break;
8465 }
8466 case SepiaToneCommand:
8467 {
8468 double
8469 threshold;
8470
8471 Image
8472 *sepia_image;
8473
8474 static char
8475 factor[MaxTextExtent] = "80%";
8476
8477 /*
8478 Query user for sepia-tone factor.
8479 */
8480 (void) XDialogWidget(display,windows,"Sepia Tone",
8481 "Enter the sepia tone factor (0 - 99.9%):",factor);
8482 if (*factor == '\0')
8483 break;
8484 /*
8485 Sepia tone image pixels.
8486 */
8487 XSetCursorState(display,windows,MagickTrue);
8488 XCheckRefreshWindows(display,windows);
cristyf2f27272009-12-17 14:48:46 +00008489 threshold=SiPrefixToDouble(factor,QuantumRange);
cristy051718b2011-08-28 22:49:25 +00008490 sepia_image=SepiaToneImage(*image,threshold,exception);
cristy3ed852e2009-09-05 21:47:34 +00008491 if (sepia_image != (Image *) NULL)
8492 {
8493 *image=DestroyImage(*image);
8494 *image=sepia_image;
8495 }
cristy051718b2011-08-28 22:49:25 +00008496 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008497 XSetCursorState(display,windows,MagickFalse);
8498 if (windows->image.orphan != MagickFalse)
8499 break;
8500 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00008501 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008502 break;
8503 }
8504 case SolarizeCommand:
8505 {
8506 double
8507 threshold;
8508
8509 static char
8510 factor[MaxTextExtent] = "60%";
8511
8512 /*
8513 Query user for solarize factor.
8514 */
8515 (void) XDialogWidget(display,windows,"Solarize",
8516 "Enter the solarize factor (0 - 99.9%):",factor);
8517 if (*factor == '\0')
8518 break;
8519 /*
8520 Solarize image pixels.
8521 */
8522 XSetCursorState(display,windows,MagickTrue);
8523 XCheckRefreshWindows(display,windows);
cristyf2f27272009-12-17 14:48:46 +00008524 threshold=SiPrefixToDouble(factor,QuantumRange);
cristy5cbc0162011-08-29 00:36:28 +00008525 (void) SolarizeImage(*image,threshold,exception);
cristy3ed852e2009-09-05 21:47:34 +00008526 XSetCursorState(display,windows,MagickFalse);
8527 if (windows->image.orphan != MagickFalse)
8528 break;
8529 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00008530 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008531 break;
8532 }
8533 case SwirlCommand:
8534 {
8535 Image
8536 *swirl_image;
8537
8538 static char
8539 degrees[MaxTextExtent] = "60";
8540
8541 /*
8542 Query user for swirl angle.
8543 */
8544 (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8545 degrees);
8546 if (*degrees == '\0')
8547 break;
8548 /*
8549 Swirl image pixels about the center.
8550 */
8551 XSetCursorState(display,windows,MagickTrue);
8552 XCheckRefreshWindows(display,windows);
8553 flags=ParseGeometry(degrees,&geometry_info);
cristy051718b2011-08-28 22:49:25 +00008554 swirl_image=SwirlImage(*image,geometry_info.rho,exception);
cristy3ed852e2009-09-05 21:47:34 +00008555 if (swirl_image != (Image *) NULL)
8556 {
8557 *image=DestroyImage(*image);
8558 *image=swirl_image;
8559 }
cristy051718b2011-08-28 22:49:25 +00008560 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008561 XSetCursorState(display,windows,MagickFalse);
8562 if (windows->image.orphan != MagickFalse)
8563 break;
8564 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00008565 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008566 break;
8567 }
8568 case ImplodeCommand:
8569 {
8570 Image
8571 *implode_image;
8572
8573 static char
8574 factor[MaxTextExtent] = "0.3";
8575
8576 /*
8577 Query user for implode factor.
8578 */
8579 (void) XDialogWidget(display,windows,"Implode",
8580 "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8581 if (*factor == '\0')
8582 break;
8583 /*
8584 Implode image pixels about the center.
8585 */
8586 XSetCursorState(display,windows,MagickTrue);
8587 XCheckRefreshWindows(display,windows);
8588 flags=ParseGeometry(factor,&geometry_info);
cristy051718b2011-08-28 22:49:25 +00008589 implode_image=ImplodeImage(*image,geometry_info.rho,exception);
cristy3ed852e2009-09-05 21:47:34 +00008590 if (implode_image != (Image *) NULL)
8591 {
8592 *image=DestroyImage(*image);
8593 *image=implode_image;
8594 }
cristy051718b2011-08-28 22:49:25 +00008595 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008596 XSetCursorState(display,windows,MagickFalse);
8597 if (windows->image.orphan != MagickFalse)
8598 break;
8599 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00008600 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008601 break;
8602 }
8603 case VignetteCommand:
8604 {
8605 Image
8606 *vignette_image;
8607
8608 static char
8609 geometry[MaxTextExtent] = "0x20";
8610
8611 /*
8612 Query user for the vignette geometry.
8613 */
8614 (void) XDialogWidget(display,windows,"Vignette",
8615 "Enter the radius, sigma, and x and y offsets:",geometry);
8616 if (*geometry == '\0')
8617 break;
8618 /*
8619 Soften the edges of the image in vignette style
8620 */
8621 XSetCursorState(display,windows,MagickTrue);
8622 XCheckRefreshWindows(display,windows);
8623 flags=ParseGeometry(geometry,&geometry_info);
8624 if ((flags & SigmaValue) == 0)
8625 geometry_info.sigma=1.0;
8626 if ((flags & XiValue) == 0)
8627 geometry_info.xi=0.1*(*image)->columns;
8628 if ((flags & PsiValue) == 0)
8629 geometry_info.psi=0.1*(*image)->rows;
8630 vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
cristyb70a4862010-06-18 01:18:44 +00008631 (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
cristy051718b2011-08-28 22:49:25 +00008632 0.5),exception);
cristy3ed852e2009-09-05 21:47:34 +00008633 if (vignette_image != (Image *) NULL)
8634 {
8635 *image=DestroyImage(*image);
8636 *image=vignette_image;
8637 }
cristy051718b2011-08-28 22:49:25 +00008638 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008639 XSetCursorState(display,windows,MagickFalse);
8640 if (windows->image.orphan != MagickFalse)
8641 break;
8642 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00008643 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008644 break;
8645 }
8646 case WaveCommand:
8647 {
8648 Image
8649 *wave_image;
8650
8651 static char
8652 geometry[MaxTextExtent] = "25x150";
8653
8654 /*
8655 Query user for the wave geometry.
8656 */
8657 (void) XDialogWidget(display,windows,"Wave",
8658 "Enter the amplitude and length of the wave:",geometry);
8659 if (*geometry == '\0')
8660 break;
8661 /*
cristycee97112010-05-28 00:44:52 +00008662 Alter an image along a sine wave.
cristy3ed852e2009-09-05 21:47:34 +00008663 */
8664 XSetCursorState(display,windows,MagickTrue);
8665 XCheckRefreshWindows(display,windows);
8666 flags=ParseGeometry(geometry,&geometry_info);
8667 if ((flags & SigmaValue) == 0)
8668 geometry_info.sigma=1.0;
8669 wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
cristy051718b2011-08-28 22:49:25 +00008670 exception);
cristy3ed852e2009-09-05 21:47:34 +00008671 if (wave_image != (Image *) NULL)
8672 {
8673 *image=DestroyImage(*image);
8674 *image=wave_image;
8675 }
cristy051718b2011-08-28 22:49:25 +00008676 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008677 XSetCursorState(display,windows,MagickFalse);
8678 if (windows->image.orphan != MagickFalse)
8679 break;
8680 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00008681 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008682 break;
8683 }
8684 case OilPaintCommand:
8685 {
8686 Image
8687 *paint_image;
8688
8689 static char
8690 radius[MaxTextExtent] = "0";
8691
8692 /*
8693 Query user for circular neighborhood radius.
8694 */
8695 (void) XDialogWidget(display,windows,"Oil Paint",
8696 "Enter the mask radius:",radius);
8697 if (*radius == '\0')
8698 break;
8699 /*
8700 OilPaint image scanlines.
8701 */
8702 XSetCursorState(display,windows,MagickTrue);
8703 XCheckRefreshWindows(display,windows);
8704 flags=ParseGeometry(radius,&geometry_info);
cristy14973ba2011-08-27 23:48:07 +00008705 paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
cristy051718b2011-08-28 22:49:25 +00008706 exception);
cristy3ed852e2009-09-05 21:47:34 +00008707 if (paint_image != (Image *) NULL)
8708 {
8709 *image=DestroyImage(*image);
8710 *image=paint_image;
8711 }
cristy051718b2011-08-28 22:49:25 +00008712 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008713 XSetCursorState(display,windows,MagickFalse);
8714 if (windows->image.orphan != MagickFalse)
8715 break;
8716 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00008717 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008718 break;
8719 }
8720 case CharcoalDrawCommand:
8721 {
8722 Image
8723 *charcoal_image;
8724
8725 static char
8726 radius[MaxTextExtent] = "0x1";
8727
8728 /*
8729 Query user for charcoal radius.
8730 */
8731 (void) XDialogWidget(display,windows,"Charcoal Draw",
8732 "Enter the charcoal radius and sigma:",radius);
8733 if (*radius == '\0')
8734 break;
8735 /*
8736 Charcoal the image.
8737 */
cristy051718b2011-08-28 22:49:25 +00008738 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8739 exception);
cristy3ed852e2009-09-05 21:47:34 +00008740 XSetCursorState(display,windows,MagickTrue);
8741 XCheckRefreshWindows(display,windows);
8742 flags=ParseGeometry(radius,&geometry_info);
8743 if ((flags & SigmaValue) == 0)
8744 geometry_info.sigma=geometry_info.rho;
8745 charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
cristy051718b2011-08-28 22:49:25 +00008746 exception);
cristy3ed852e2009-09-05 21:47:34 +00008747 if (charcoal_image != (Image *) NULL)
8748 {
8749 *image=DestroyImage(*image);
8750 *image=charcoal_image;
8751 }
cristy051718b2011-08-28 22:49:25 +00008752 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008753 XSetCursorState(display,windows,MagickFalse);
8754 if (windows->image.orphan != MagickFalse)
8755 break;
8756 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00008757 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008758 break;
8759 }
8760 case AnnotateCommand:
8761 {
8762 /*
8763 Annotate the image with text.
8764 */
cristy051718b2011-08-28 22:49:25 +00008765 status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008766 if (status == MagickFalse)
8767 {
8768 XNoticeWidget(display,windows,"Unable to annotate X image",
8769 (*image)->filename);
8770 break;
8771 }
8772 break;
8773 }
8774 case DrawCommand:
8775 {
8776 /*
8777 Draw image.
8778 */
cristy051718b2011-08-28 22:49:25 +00008779 status=XDrawEditImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008780 if (status == MagickFalse)
8781 {
8782 XNoticeWidget(display,windows,"Unable to draw on the X image",
8783 (*image)->filename);
8784 break;
8785 }
8786 break;
8787 }
8788 case ColorCommand:
8789 {
8790 /*
8791 Color edit.
8792 */
cristy051718b2011-08-28 22:49:25 +00008793 status=XColorEditImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008794 if (status == MagickFalse)
8795 {
8796 XNoticeWidget(display,windows,"Unable to pixel edit X image",
8797 (*image)->filename);
8798 break;
8799 }
8800 break;
8801 }
8802 case MatteCommand:
8803 {
8804 /*
8805 Matte edit.
8806 */
cristy051718b2011-08-28 22:49:25 +00008807 status=XMatteEditImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008808 if (status == MagickFalse)
8809 {
8810 XNoticeWidget(display,windows,"Unable to matte edit X image",
8811 (*image)->filename);
8812 break;
8813 }
8814 break;
8815 }
8816 case CompositeCommand:
8817 {
8818 /*
8819 Composite image.
8820 */
cristy051718b2011-08-28 22:49:25 +00008821 status=XCompositeImage(display,resource_info,windows,*image,
8822 exception);
cristy3ed852e2009-09-05 21:47:34 +00008823 if (status == MagickFalse)
8824 {
8825 XNoticeWidget(display,windows,"Unable to composite X image",
8826 (*image)->filename);
8827 break;
8828 }
8829 break;
8830 }
8831 case AddBorderCommand:
8832 {
8833 Image
8834 *border_image;
8835
8836 static char
8837 geometry[MaxTextExtent] = "6x6";
8838
8839 /*
8840 Query user for border color and geometry.
8841 */
8842 XColorBrowserWidget(display,windows,"Select",color);
8843 if (*color == '\0')
8844 break;
8845 (void) XDialogWidget(display,windows,"Add Border",
8846 "Enter border geometry:",geometry);
8847 if (*geometry == '\0')
8848 break;
8849 /*
8850 Add a border to the image.
8851 */
cristy051718b2011-08-28 22:49:25 +00008852 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8853 exception);
cristy3ed852e2009-09-05 21:47:34 +00008854 XSetCursorState(display,windows,MagickTrue);
8855 XCheckRefreshWindows(display,windows);
8856 (void) QueryColorDatabase(color,&(*image)->border_color,
cristy051718b2011-08-28 22:49:25 +00008857 exception);
cristy3ed852e2009-09-05 21:47:34 +00008858 (void) ParsePageGeometry(*image,geometry,&page_geometry,
cristy051718b2011-08-28 22:49:25 +00008859 exception);
8860 border_image=BorderImage(*image,&page_geometry,exception);
cristy3ed852e2009-09-05 21:47:34 +00008861 if (border_image != (Image *) NULL)
8862 {
8863 *image=DestroyImage(*image);
8864 *image=border_image;
8865 }
cristy051718b2011-08-28 22:49:25 +00008866 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008867 XSetCursorState(display,windows,MagickFalse);
8868 if (windows->image.orphan != MagickFalse)
8869 break;
8870 windows->image.window_changes.width=(int) (*image)->columns;
8871 windows->image.window_changes.height=(int) (*image)->rows;
8872 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00008873 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008874 break;
8875 }
8876 case AddFrameCommand:
8877 {
8878 FrameInfo
8879 frame_info;
8880
8881 Image
8882 *frame_image;
8883
8884 static char
8885 geometry[MaxTextExtent] = "6x6";
8886
8887 /*
8888 Query user for frame color and geometry.
8889 */
8890 XColorBrowserWidget(display,windows,"Select",color);
8891 if (*color == '\0')
8892 break;
8893 (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8894 geometry);
8895 if (*geometry == '\0')
8896 break;
8897 /*
8898 Surround image with an ornamental border.
8899 */
cristy051718b2011-08-28 22:49:25 +00008900 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8901 exception);
cristy3ed852e2009-09-05 21:47:34 +00008902 XSetCursorState(display,windows,MagickTrue);
8903 XCheckRefreshWindows(display,windows);
8904 (void) QueryColorDatabase(color,&(*image)->matte_color,
cristy051718b2011-08-28 22:49:25 +00008905 exception);
cristy3ed852e2009-09-05 21:47:34 +00008906 (void) ParsePageGeometry(*image,geometry,&page_geometry,
cristy051718b2011-08-28 22:49:25 +00008907 exception);
cristy3ed852e2009-09-05 21:47:34 +00008908 frame_info.width=page_geometry.width;
8909 frame_info.height=page_geometry.height;
8910 frame_info.outer_bevel=page_geometry.x;
8911 frame_info.inner_bevel=page_geometry.y;
cristybb503372010-05-27 20:51:26 +00008912 frame_info.x=(ssize_t) frame_info.width;
8913 frame_info.y=(ssize_t) frame_info.height;
cristy3ed852e2009-09-05 21:47:34 +00008914 frame_info.width=(*image)->columns+2*frame_info.width;
8915 frame_info.height=(*image)->rows+2*frame_info.height;
cristy051718b2011-08-28 22:49:25 +00008916 frame_image=FrameImage(*image,&frame_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00008917 if (frame_image != (Image *) NULL)
8918 {
8919 *image=DestroyImage(*image);
8920 *image=frame_image;
8921 }
cristy051718b2011-08-28 22:49:25 +00008922 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008923 XSetCursorState(display,windows,MagickFalse);
8924 if (windows->image.orphan != MagickFalse)
8925 break;
8926 windows->image.window_changes.width=(int) (*image)->columns;
8927 windows->image.window_changes.height=(int) (*image)->rows;
8928 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00008929 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008930 break;
8931 }
8932 case CommentCommand:
8933 {
8934 const char
8935 *value;
8936
8937 FILE
8938 *file;
8939
8940 int
8941 unique_file;
8942
8943 /*
8944 Edit image comment.
8945 */
8946 unique_file=AcquireUniqueFileResource(image_info->filename);
8947 if (unique_file == -1)
8948 XNoticeWidget(display,windows,"Unable to edit image comment",
8949 image_info->filename);
8950 value=GetImageProperty(*image,"comment");
8951 if (value == (char *) NULL)
8952 unique_file=close(unique_file)-1;
8953 else
8954 {
8955 register const char
8956 *p;
8957
8958 file=fdopen(unique_file,"w");
8959 if (file == (FILE *) NULL)
8960 {
8961 XNoticeWidget(display,windows,"Unable to edit image comment",
8962 image_info->filename);
8963 break;
8964 }
8965 for (p=value; *p != '\0'; p++)
8966 (void) fputc((int) *p,file);
8967 (void) fputc('\n',file);
8968 (void) fclose(file);
8969 }
8970 XSetCursorState(display,windows,MagickTrue);
8971 XCheckRefreshWindows(display,windows);
8972 status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
cristy051718b2011-08-28 22:49:25 +00008973 exception);
cristy3ed852e2009-09-05 21:47:34 +00008974 if (status == MagickFalse)
8975 XNoticeWidget(display,windows,"Unable to edit image comment",
8976 (char *) NULL);
8977 else
8978 {
8979 char
8980 *comment;
8981
cristy051718b2011-08-28 22:49:25 +00008982 comment=FileToString(image_info->filename,~0UL,exception);
cristy3ed852e2009-09-05 21:47:34 +00008983 if (comment != (char *) NULL)
8984 {
8985 (void) SetImageProperty(*image,"comment",comment);
8986 (*image)->taint=MagickTrue;
8987 }
8988 }
8989 (void) RelinquishUniqueFileResource(image_info->filename);
8990 XSetCursorState(display,windows,MagickFalse);
8991 break;
8992 }
8993 case LaunchCommand:
8994 {
8995 /*
8996 Launch program.
8997 */
8998 XSetCursorState(display,windows,MagickTrue);
8999 XCheckRefreshWindows(display,windows);
9000 (void) AcquireUniqueFilename(filename);
cristyb51dff52011-05-19 16:55:47 +00009001 (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
cristy3ed852e2009-09-05 21:47:34 +00009002 filename);
cristy051718b2011-08-28 22:49:25 +00009003 status=WriteImage(image_info,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009004 if (status == MagickFalse)
9005 XNoticeWidget(display,windows,"Unable to launch image editor",
9006 (char *) NULL);
9007 else
9008 {
cristy051718b2011-08-28 22:49:25 +00009009 nexus=ReadImage(resource_info->image_info,exception);
9010 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00009011 XClientMessage(display,windows->image.id,windows->im_protocols,
9012 windows->im_next_image,CurrentTime);
9013 }
9014 (void) RelinquishUniqueFileResource(filename);
9015 XSetCursorState(display,windows,MagickFalse);
9016 break;
9017 }
9018 case RegionofInterestCommand:
9019 {
9020 /*
9021 Apply an image processing technique to a region of interest.
9022 */
cristy051718b2011-08-28 22:49:25 +00009023 (void) XROIImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009024 break;
9025 }
9026 case InfoCommand:
9027 break;
9028 case ZoomCommand:
9029 {
9030 /*
9031 Zoom image.
9032 */
9033 if (windows->magnify.mapped != MagickFalse)
9034 (void) XRaiseWindow(display,windows->magnify.id);
9035 else
9036 {
9037 /*
9038 Make magnify image.
9039 */
9040 XSetCursorState(display,windows,MagickTrue);
9041 (void) XMapRaised(display,windows->magnify.id);
9042 XSetCursorState(display,windows,MagickFalse);
9043 }
9044 break;
9045 }
9046 case ShowPreviewCommand:
9047 {
9048 char
9049 **previews;
9050
9051 Image
9052 *preview_image;
9053
9054 static char
9055 preview_type[MaxTextExtent] = "Gamma";
9056
9057 /*
9058 Select preview type from menu.
9059 */
cristy042ee782011-04-22 18:48:30 +00009060 previews=GetCommandOptions(MagickPreviewOptions);
cristy3ed852e2009-09-05 21:47:34 +00009061 if (previews == (char **) NULL)
9062 break;
9063 XListBrowserWidget(display,windows,&windows->widget,
9064 (const char **) previews,"Preview",
9065 "Select an enhancement, effect, or F/X:",preview_type);
9066 previews=DestroyStringList(previews);
9067 if (*preview_type == '\0')
9068 break;
9069 /*
9070 Show image preview.
9071 */
9072 XSetCursorState(display,windows,MagickTrue);
9073 XCheckRefreshWindows(display,windows);
9074 image_info->preview_type=(PreviewType)
cristy042ee782011-04-22 18:48:30 +00009075 ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
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","Preview");
9079 (void) AcquireUniqueFilename(filename);
cristyb51dff52011-05-19 16:55:47 +00009080 (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
cristy3ed852e2009-09-05 21:47:34 +00009081 filename);
cristy051718b2011-08-28 22:49:25 +00009082 status=WriteImage(image_info,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009083 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +00009084 preview_image=ReadImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00009085 (void) RelinquishUniqueFileResource(filename);
9086 if (preview_image == (Image *) NULL)
9087 break;
cristyb51dff52011-05-19 16:55:47 +00009088 (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
cristy3ed852e2009-09-05 21:47:34 +00009089 filename);
cristy051718b2011-08-28 22:49:25 +00009090 status=WriteImage(image_info,preview_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009091 preview_image=DestroyImage(preview_image);
9092 if (status == MagickFalse)
9093 XNoticeWidget(display,windows,"Unable to show image preview",
9094 (*image)->filename);
9095 XDelay(display,1500);
9096 XSetCursorState(display,windows,MagickFalse);
9097 break;
9098 }
9099 case ShowHistogramCommand:
9100 {
9101 Image
9102 *histogram_image;
9103
9104 /*
9105 Show image histogram.
9106 */
9107 XSetCursorState(display,windows,MagickTrue);
9108 XCheckRefreshWindows(display,windows);
cristybb503372010-05-27 20:51:26 +00009109 image_info->group=(ssize_t) windows->image.id;
cristy3ed852e2009-09-05 21:47:34 +00009110 (void) DeleteImageProperty(*image,"label");
9111 (void) SetImageProperty(*image,"label","Histogram");
9112 (void) AcquireUniqueFilename(filename);
cristyb51dff52011-05-19 16:55:47 +00009113 (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
cristy3ed852e2009-09-05 21:47:34 +00009114 filename);
cristy051718b2011-08-28 22:49:25 +00009115 status=WriteImage(image_info,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009116 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +00009117 histogram_image=ReadImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00009118 (void) RelinquishUniqueFileResource(filename);
9119 if (histogram_image == (Image *) NULL)
9120 break;
cristyb51dff52011-05-19 16:55:47 +00009121 (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00009122 "show:%s",filename);
cristy051718b2011-08-28 22:49:25 +00009123 status=WriteImage(image_info,histogram_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009124 histogram_image=DestroyImage(histogram_image);
9125 if (status == MagickFalse)
9126 XNoticeWidget(display,windows,"Unable to show histogram",
9127 (*image)->filename);
9128 XDelay(display,1500);
9129 XSetCursorState(display,windows,MagickFalse);
9130 break;
9131 }
9132 case ShowMatteCommand:
9133 {
9134 Image
9135 *matte_image;
9136
9137 if ((*image)->matte == MagickFalse)
9138 {
9139 XNoticeWidget(display,windows,
9140 "Image does not have any matte information",(*image)->filename);
9141 break;
9142 }
9143 /*
9144 Show image matte.
9145 */
9146 XSetCursorState(display,windows,MagickTrue);
9147 XCheckRefreshWindows(display,windows);
cristybb503372010-05-27 20:51:26 +00009148 image_info->group=(ssize_t) windows->image.id;
cristy3ed852e2009-09-05 21:47:34 +00009149 (void) DeleteImageProperty(*image,"label");
9150 (void) SetImageProperty(*image,"label","Matte");
9151 (void) AcquireUniqueFilename(filename);
cristyb51dff52011-05-19 16:55:47 +00009152 (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
cristy3ed852e2009-09-05 21:47:34 +00009153 filename);
cristy051718b2011-08-28 22:49:25 +00009154 status=WriteImage(image_info,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009155 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +00009156 matte_image=ReadImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00009157 (void) RelinquishUniqueFileResource(filename);
9158 if (matte_image == (Image *) NULL)
9159 break;
cristyb51dff52011-05-19 16:55:47 +00009160 (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
cristy3ed852e2009-09-05 21:47:34 +00009161 filename);
cristy051718b2011-08-28 22:49:25 +00009162 status=WriteImage(image_info,matte_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009163 matte_image=DestroyImage(matte_image);
9164 if (status == MagickFalse)
9165 XNoticeWidget(display,windows,"Unable to show matte",
9166 (*image)->filename);
9167 XDelay(display,1500);
9168 XSetCursorState(display,windows,MagickFalse);
9169 break;
9170 }
9171 case BackgroundCommand:
9172 {
9173 /*
9174 Background image.
9175 */
cristy051718b2011-08-28 22:49:25 +00009176 status=XBackgroundImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009177 if (status == MagickFalse)
9178 break;
cristy051718b2011-08-28 22:49:25 +00009179 nexus=CloneImage(*image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00009180 if (nexus != (Image *) NULL)
9181 XClientMessage(display,windows->image.id,windows->im_protocols,
9182 windows->im_next_image,CurrentTime);
9183 break;
9184 }
9185 case SlideShowCommand:
9186 {
9187 static char
9188 delay[MaxTextExtent] = "5";
9189
9190 /*
9191 Display next image after pausing.
9192 */
9193 (void) XDialogWidget(display,windows,"Slide Show",
9194 "Pause how many 1/100ths of a second between images:",delay);
9195 if (*delay == '\0')
9196 break;
cristye27293e2009-12-18 02:53:20 +00009197 resource_info->delay=StringToUnsignedLong(delay);
cristy3ed852e2009-09-05 21:47:34 +00009198 XClientMessage(display,windows->image.id,windows->im_protocols,
9199 windows->im_next_image,CurrentTime);
9200 break;
9201 }
9202 case PreferencesCommand:
9203 {
9204 /*
9205 Set user preferences.
9206 */
9207 status=XPreferencesWidget(display,resource_info,windows);
9208 if (status == MagickFalse)
9209 break;
cristy051718b2011-08-28 22:49:25 +00009210 nexus=CloneImage(*image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00009211 if (nexus != (Image *) NULL)
9212 XClientMessage(display,windows->image.id,windows->im_protocols,
9213 windows->im_next_image,CurrentTime);
9214 break;
9215 }
9216 case HelpCommand:
9217 {
9218 /*
9219 User requested help.
9220 */
9221 XTextViewWidget(display,resource_info,windows,MagickFalse,
9222 "Help Viewer - Display",DisplayHelp);
9223 break;
9224 }
9225 case BrowseDocumentationCommand:
9226 {
9227 Atom
9228 mozilla_atom;
9229
9230 Window
9231 mozilla_window,
9232 root_window;
9233
9234 /*
9235 Browse the ImageMagick documentation.
9236 */
9237 root_window=XRootWindow(display,XDefaultScreen(display));
9238 mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9239 mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9240 if (mozilla_window != (Window) NULL)
9241 {
9242 char
9243 command[MaxTextExtent],
9244 *url;
9245
9246 /*
9247 Display documentation using Netscape remote control.
9248 */
9249 url=GetMagickHomeURL();
cristyb51dff52011-05-19 16:55:47 +00009250 (void) FormatLocaleString(command,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00009251 "openurl(%s,new-tab)",url);
9252 url=DestroyString(url);
9253 mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9254 (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9255 8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9256 XSetCursorState(display,windows,MagickFalse);
9257 break;
9258 }
9259 XSetCursorState(display,windows,MagickTrue);
9260 XCheckRefreshWindows(display,windows);
9261 status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
cristy051718b2011-08-28 22:49:25 +00009262 exception);
cristy3ed852e2009-09-05 21:47:34 +00009263 if (status == MagickFalse)
9264 XNoticeWidget(display,windows,"Unable to browse documentation",
9265 (char *) NULL);
9266 XDelay(display,1500);
9267 XSetCursorState(display,windows,MagickFalse);
9268 break;
9269 }
9270 case VersionCommand:
9271 {
cristybb503372010-05-27 20:51:26 +00009272 XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
cristy3ed852e2009-09-05 21:47:34 +00009273 GetMagickCopyright());
9274 break;
9275 }
9276 case SaveToUndoBufferCommand:
9277 break;
9278 default:
9279 {
9280 (void) XBell(display,0);
9281 break;
9282 }
9283 }
9284 image_info=DestroyImageInfo(image_info);
9285 return(nexus);
9286}
9287
9288/*
9289%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9290% %
9291% %
9292% %
9293+ X M a g n i f y I m a g e %
9294% %
9295% %
9296% %
9297%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9298%
9299% XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9300% The magnified portion is displayed in a separate window.
9301%
9302% The format of the XMagnifyImage method is:
9303%
9304% void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9305%
9306% A description of each parameter follows:
9307%
9308% o display: Specifies a connection to an X server; returned from
9309% XOpenDisplay.
9310%
9311% o windows: Specifies a pointer to a XWindows structure.
9312%
9313% o event: Specifies a pointer to a XEvent structure. If it is NULL,
9314% the entire image is refreshed.
9315%
9316*/
9317static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9318{
9319 char
9320 text[MaxTextExtent];
9321
9322 register int
9323 x,
9324 y;
9325
cristybb503372010-05-27 20:51:26 +00009326 size_t
cristy3ed852e2009-09-05 21:47:34 +00009327 state;
9328
9329 /*
9330 Update magnified image until the mouse button is released.
9331 */
9332 (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9333 state=DefaultState;
9334 x=event->xbutton.x;
9335 y=event->xbutton.y;
cristy49e2d862010-11-12 02:50:30 +00009336 windows->magnify.x=(int) windows->image.x+x;
9337 windows->magnify.y=(int) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +00009338 do
9339 {
9340 /*
9341 Map and unmap Info widget as text cursor crosses its boundaries.
9342 */
9343 if (windows->info.mapped != MagickFalse)
9344 {
9345 if ((x < (int) (windows->info.x+windows->info.width)) &&
9346 (y < (int) (windows->info.y+windows->info.height)))
9347 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9348 }
9349 else
9350 if ((x > (int) (windows->info.x+windows->info.width)) ||
9351 (y > (int) (windows->info.y+windows->info.height)))
9352 (void) XMapWindow(display,windows->info.id);
9353 if (windows->info.mapped != MagickFalse)
9354 {
9355 /*
9356 Display pointer position.
9357 */
cristyb51dff52011-05-19 16:55:47 +00009358 (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
cristy3ed852e2009-09-05 21:47:34 +00009359 windows->magnify.x,windows->magnify.y);
9360 XInfoWidget(display,windows,text);
9361 }
9362 /*
9363 Wait for next event.
9364 */
9365 XScreenEvent(display,windows,event);
9366 switch (event->type)
9367 {
9368 case ButtonPress:
9369 break;
9370 case ButtonRelease:
9371 {
9372 /*
9373 User has finished magnifying image.
9374 */
9375 x=event->xbutton.x;
9376 y=event->xbutton.y;
9377 state|=ExitState;
9378 break;
9379 }
9380 case Expose:
9381 break;
9382 case MotionNotify:
9383 {
9384 x=event->xmotion.x;
9385 y=event->xmotion.y;
9386 break;
9387 }
9388 default:
9389 break;
9390 }
9391 /*
9392 Check boundary conditions.
9393 */
9394 if (x < 0)
9395 x=0;
9396 else
9397 if (x >= (int) windows->image.width)
9398 x=(int) windows->image.width-1;
9399 if (y < 0)
9400 y=0;
9401 else
9402 if (y >= (int) windows->image.height)
9403 y=(int) windows->image.height-1;
9404 } while ((state & ExitState) == 0);
9405 /*
9406 Display magnified image.
9407 */
9408 XSetCursorState(display,windows,MagickFalse);
9409}
9410
9411/*
9412%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9413% %
9414% %
9415% %
9416+ X M a g n i f y W i n d o w C o m m a n d %
9417% %
9418% %
9419% %
9420%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9421%
9422% XMagnifyWindowCommand() moves the image within an Magnify window by one
9423% pixel as specified by the key symbol.
9424%
9425% The format of the XMagnifyWindowCommand method is:
9426%
9427% void XMagnifyWindowCommand(Display *display,XWindows *windows,
9428% const MagickStatusType state,const KeySym key_symbol)
9429%
9430% A description of each parameter follows:
9431%
9432% o display: Specifies a connection to an X server; returned from
9433% XOpenDisplay.
9434%
9435% o windows: Specifies a pointer to a XWindows structure.
9436%
9437% o state: key mask.
9438%
9439% o key_symbol: Specifies a KeySym which indicates which side of the image
9440% to trim.
9441%
9442*/
9443static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9444 const MagickStatusType state,const KeySym key_symbol)
9445{
9446 unsigned int
9447 quantum;
9448
9449 /*
9450 User specified a magnify factor or position.
9451 */
9452 quantum=1;
9453 if ((state & Mod1Mask) != 0)
9454 quantum=10;
9455 switch ((int) key_symbol)
9456 {
9457 case QuitCommand:
9458 {
9459 (void) XWithdrawWindow(display,windows->magnify.id,
9460 windows->magnify.screen);
9461 break;
9462 }
9463 case XK_Home:
9464 case XK_KP_Home:
9465 {
9466 windows->magnify.x=(int) windows->image.width/2;
9467 windows->magnify.y=(int) windows->image.height/2;
9468 break;
9469 }
9470 case XK_Left:
9471 case XK_KP_Left:
9472 {
9473 if (windows->magnify.x > 0)
9474 windows->magnify.x-=quantum;
9475 break;
9476 }
9477 case XK_Up:
9478 case XK_KP_Up:
9479 {
9480 if (windows->magnify.y > 0)
9481 windows->magnify.y-=quantum;
9482 break;
9483 }
9484 case XK_Right:
9485 case XK_KP_Right:
9486 {
9487 if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9488 windows->magnify.x+=quantum;
9489 break;
9490 }
9491 case XK_Down:
9492 case XK_KP_Down:
9493 {
9494 if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9495 windows->magnify.y+=quantum;
9496 break;
9497 }
9498 case XK_0:
9499 case XK_1:
9500 case XK_2:
9501 case XK_3:
9502 case XK_4:
9503 case XK_5:
9504 case XK_6:
9505 case XK_7:
9506 case XK_8:
9507 case XK_9:
9508 {
9509 windows->magnify.data=(key_symbol-XK_0);
9510 break;
9511 }
9512 case XK_KP_0:
9513 case XK_KP_1:
9514 case XK_KP_2:
9515 case XK_KP_3:
9516 case XK_KP_4:
9517 case XK_KP_5:
9518 case XK_KP_6:
9519 case XK_KP_7:
9520 case XK_KP_8:
9521 case XK_KP_9:
9522 {
9523 windows->magnify.data=(key_symbol-XK_KP_0);
9524 break;
9525 }
9526 default:
9527 break;
9528 }
9529 XMakeMagnifyImage(display,windows);
9530}
9531
9532/*
9533%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9534% %
9535% %
9536% %
9537+ X M a k e P a n I m a g e %
9538% %
9539% %
9540% %
9541%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9542%
9543% XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9544% icon window.
9545%
9546% The format of the XMakePanImage method is:
9547%
9548% void XMakePanImage(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +00009549% XWindows *windows,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00009550%
9551% A description of each parameter follows:
9552%
9553% o display: Specifies a connection to an X server; returned from
9554% XOpenDisplay.
9555%
9556% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9557%
9558% o windows: Specifies a pointer to a XWindows structure.
9559%
9560% o image: the image.
9561%
cristy051718b2011-08-28 22:49:25 +00009562% o exception: return any errors or warnings in this structure.
9563%
cristy3ed852e2009-09-05 21:47:34 +00009564*/
9565static void XMakePanImage(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +00009566 XWindows *windows,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00009567{
9568 MagickStatusType
9569 status;
9570
9571 /*
9572 Create and display image for panning icon.
9573 */
9574 XSetCursorState(display,windows,MagickTrue);
9575 XCheckRefreshWindows(display,windows);
cristy49e2d862010-11-12 02:50:30 +00009576 windows->pan.x=(int) windows->image.x;
9577 windows->pan.y=(int) windows->image.y;
cristy3ed852e2009-09-05 21:47:34 +00009578 status=XMakeImage(display,resource_info,&windows->pan,image,
cristy051718b2011-08-28 22:49:25 +00009579 windows->pan.width,windows->pan.height,exception);
cristy3ed852e2009-09-05 21:47:34 +00009580 if (status == MagickFalse)
cristy051718b2011-08-28 22:49:25 +00009581 ThrowXWindowFatalException(ResourceLimitError,
9582 "MemoryAllocationFailed",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00009583 (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9584 windows->pan.pixmap);
9585 (void) XClearWindow(display,windows->pan.id);
9586 XDrawPanRectangle(display,windows);
9587 XSetCursorState(display,windows,MagickFalse);
9588}
9589
9590/*
9591%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9592% %
9593% %
9594% %
9595+ X M a t t a E d i t I m a g e %
9596% %
9597% %
9598% %
9599%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9600%
9601% XMatteEditImage() allows the user to interactively change the Matte channel
9602% of an image. If the image is PseudoClass it is promoted to DirectClass
9603% before the matte information is stored.
9604%
9605% The format of the XMatteEditImage method is:
9606%
9607% MagickBooleanType XMatteEditImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00009608% XResourceInfo *resource_info,XWindows *windows,Image **image,
9609% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00009610%
9611% A description of each parameter follows:
9612%
9613% o display: Specifies a connection to an X server; returned from
9614% XOpenDisplay.
9615%
9616% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9617%
9618% o windows: Specifies a pointer to a XWindows structure.
9619%
9620% o image: the image; returned from ReadImage.
9621%
cristy051718b2011-08-28 22:49:25 +00009622% o exception: return any errors or warnings in this structure.
9623%
cristy3ed852e2009-09-05 21:47:34 +00009624*/
9625static MagickBooleanType XMatteEditImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00009626 XResourceInfo *resource_info,XWindows *windows,Image **image,
9627 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00009628{
9629 static char
9630 matte[MaxTextExtent] = "0";
9631
9632 static const char
9633 *MatteEditMenu[] =
9634 {
9635 "Method",
9636 "Border Color",
9637 "Fuzz",
9638 "Matte Value",
9639 "Undo",
9640 "Help",
9641 "Dismiss",
9642 (char *) NULL
9643 };
9644
9645 static const ModeType
9646 MatteEditCommands[] =
9647 {
9648 MatteEditMethod,
9649 MatteEditBorderCommand,
9650 MatteEditFuzzCommand,
9651 MatteEditValueCommand,
9652 MatteEditUndoCommand,
9653 MatteEditHelpCommand,
9654 MatteEditDismissCommand
9655 };
9656
9657 static PaintMethod
9658 method = PointMethod;
9659
9660 static XColor
9661 border_color = { 0, 0, 0, 0, 0, 0 };
9662
9663 char
9664 command[MaxTextExtent],
9665 text[MaxTextExtent];
9666
9667 Cursor
9668 cursor;
9669
9670 int
9671 entry,
9672 id,
9673 x,
9674 x_offset,
9675 y,
9676 y_offset;
9677
9678 register int
9679 i;
9680
cristy4c08aed2011-07-01 19:47:50 +00009681 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00009682 *q;
9683
9684 unsigned int
9685 height,
9686 width;
9687
cristybb503372010-05-27 20:51:26 +00009688 size_t
cristy3ed852e2009-09-05 21:47:34 +00009689 state;
9690
9691 XEvent
9692 event;
9693
9694 /*
9695 Map Command widget.
9696 */
9697 (void) CloneString(&windows->command.name,"Matte Edit");
9698 windows->command.data=4;
9699 (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9700 (void) XMapRaised(display,windows->command.id);
9701 XClientMessage(display,windows->image.id,windows->im_protocols,
9702 windows->im_update_widget,CurrentTime);
9703 /*
9704 Make cursor.
9705 */
9706 cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9707 resource_info->background_color,resource_info->foreground_color);
9708 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9709 /*
9710 Track pointer until button 1 is pressed.
9711 */
9712 XQueryPosition(display,windows->image.id,&x,&y);
9713 (void) XSelectInput(display,windows->image.id,
9714 windows->image.attributes.event_mask | PointerMotionMask);
9715 state=DefaultState;
9716 do
9717 {
9718 if (windows->info.mapped != MagickFalse)
9719 {
9720 /*
9721 Display pointer position.
9722 */
cristyb51dff52011-05-19 16:55:47 +00009723 (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
cristy3ed852e2009-09-05 21:47:34 +00009724 x+windows->image.x,y+windows->image.y);
9725 XInfoWidget(display,windows,text);
9726 }
9727 /*
9728 Wait for next event.
9729 */
9730 XScreenEvent(display,windows,&event);
9731 if (event.xany.window == windows->command.id)
9732 {
9733 /*
9734 Select a command from the Command widget.
9735 */
9736 id=XCommandWidget(display,windows,MatteEditMenu,&event);
9737 if (id < 0)
9738 {
9739 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9740 continue;
9741 }
9742 switch (MatteEditCommands[id])
9743 {
9744 case MatteEditMethod:
9745 {
9746 char
9747 **methods;
9748
9749 /*
9750 Select a method from the pop-up menu.
9751 */
cristy042ee782011-04-22 18:48:30 +00009752 methods=GetCommandOptions(MagickMethodOptions);
cristy3ed852e2009-09-05 21:47:34 +00009753 if (methods == (char **) NULL)
9754 break;
9755 entry=XMenuWidget(display,windows,MatteEditMenu[id],
9756 (const char **) methods,command);
9757 if (entry >= 0)
cristy042ee782011-04-22 18:48:30 +00009758 method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
cristy3ed852e2009-09-05 21:47:34 +00009759 MagickFalse,methods[entry]);
9760 methods=DestroyStringList(methods);
9761 break;
9762 }
9763 case MatteEditBorderCommand:
9764 {
9765 const char
9766 *ColorMenu[MaxNumberPens];
9767
9768 int
9769 pen_number;
9770
9771 /*
9772 Initialize menu selections.
9773 */
9774 for (i=0; i < (int) (MaxNumberPens-2); i++)
9775 ColorMenu[i]=resource_info->pen_colors[i];
9776 ColorMenu[MaxNumberPens-2]="Browser...";
9777 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9778 /*
9779 Select a pen color from the pop-up menu.
9780 */
9781 pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9782 (const char **) ColorMenu,command);
9783 if (pen_number < 0)
9784 break;
9785 if (pen_number == (MaxNumberPens-2))
9786 {
9787 static char
9788 color_name[MaxTextExtent] = "gray";
9789
9790 /*
9791 Select a pen color from a dialog.
9792 */
9793 resource_info->pen_colors[pen_number]=color_name;
9794 XColorBrowserWidget(display,windows,"Select",color_name);
9795 if (*color_name == '\0')
9796 break;
9797 }
9798 /*
9799 Set border color.
9800 */
9801 (void) XParseColor(display,windows->map_info->colormap,
9802 resource_info->pen_colors[pen_number],&border_color);
9803 break;
9804 }
9805 case MatteEditFuzzCommand:
9806 {
9807 static char
9808 fuzz[MaxTextExtent];
9809
9810 static const char
9811 *FuzzMenu[] =
9812 {
9813 "0%",
9814 "2%",
9815 "5%",
9816 "10%",
9817 "15%",
9818 "Dialog...",
9819 (char *) NULL,
9820 };
9821
9822 /*
9823 Select a command from the pop-up menu.
9824 */
9825 entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9826 command);
9827 if (entry < 0)
9828 break;
9829 if (entry != 5)
9830 {
cristy4c08aed2011-07-01 19:47:50 +00009831 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
9832 QuantumRange+1.0);
cristy3ed852e2009-09-05 21:47:34 +00009833 break;
9834 }
9835 (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9836 (void) XDialogWidget(display,windows,"Ok",
9837 "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9838 if (*fuzz == '\0')
9839 break;
9840 (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
cristyf2f27272009-12-17 14:48:46 +00009841 (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
cristy3ed852e2009-09-05 21:47:34 +00009842 break;
9843 }
9844 case MatteEditValueCommand:
9845 {
9846 static char
9847 message[MaxTextExtent];
9848
9849 static const char
9850 *MatteMenu[] =
9851 {
9852 "Opaque",
9853 "Transparent",
9854 "Dialog...",
9855 (char *) NULL,
9856 };
9857
9858 /*
9859 Select a command from the pop-up menu.
9860 */
9861 entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9862 command);
9863 if (entry < 0)
9864 break;
9865 if (entry != 2)
9866 {
cristyb51dff52011-05-19 16:55:47 +00009867 (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
cristy4c08aed2011-07-01 19:47:50 +00009868 OpaqueAlpha);
cristy3ed852e2009-09-05 21:47:34 +00009869 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
cristyb51dff52011-05-19 16:55:47 +00009870 (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
cristy4c08aed2011-07-01 19:47:50 +00009871 (Quantum) TransparentAlpha);
cristy3ed852e2009-09-05 21:47:34 +00009872 break;
9873 }
cristyb51dff52011-05-19 16:55:47 +00009874 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00009875 "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9876 QuantumRange);
9877 (void) XDialogWidget(display,windows,"Matte",message,matte);
9878 if (*matte == '\0')
9879 break;
9880 break;
9881 }
9882 case MatteEditUndoCommand:
9883 {
9884 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
cristy051718b2011-08-28 22:49:25 +00009885 image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009886 break;
9887 }
9888 case MatteEditHelpCommand:
9889 {
9890 XTextViewWidget(display,resource_info,windows,MagickFalse,
9891 "Help Viewer - Matte Edit",ImageMatteEditHelp);
9892 break;
9893 }
9894 case MatteEditDismissCommand:
9895 {
9896 /*
9897 Prematurely exit.
9898 */
9899 state|=EscapeState;
9900 state|=ExitState;
9901 break;
9902 }
9903 default:
9904 break;
9905 }
9906 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9907 continue;
9908 }
9909 switch (event.type)
9910 {
9911 case ButtonPress:
9912 {
9913 if (event.xbutton.button != Button1)
9914 break;
9915 if ((event.xbutton.window != windows->image.id) &&
9916 (event.xbutton.window != windows->magnify.id))
9917 break;
9918 /*
9919 Update matte data.
9920 */
9921 x=event.xbutton.x;
9922 y=event.xbutton.y;
9923 (void) XMagickCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +00009924 SaveToUndoBufferCommand,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009925 state|=UpdateConfigurationState;
9926 break;
9927 }
9928 case ButtonRelease:
9929 {
9930 if (event.xbutton.button != Button1)
9931 break;
9932 if ((event.xbutton.window != windows->image.id) &&
9933 (event.xbutton.window != windows->magnify.id))
9934 break;
9935 /*
9936 Update colormap information.
9937 */
9938 x=event.xbutton.x;
9939 y=event.xbutton.y;
9940 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +00009941 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009942 XInfoWidget(display,windows,text);
9943 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9944 state&=(~UpdateConfigurationState);
9945 break;
9946 }
9947 case Expose:
9948 break;
9949 case KeyPress:
9950 {
9951 char
9952 command[MaxTextExtent];
9953
9954 KeySym
9955 key_symbol;
9956
9957 if (event.xkey.window == windows->magnify.id)
9958 {
9959 Window
9960 window;
9961
9962 window=windows->magnify.id;
9963 while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9964 }
9965 if (event.xkey.window != windows->image.id)
9966 break;
9967 /*
9968 Respond to a user key press.
9969 */
9970 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9971 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9972 switch ((int) key_symbol)
9973 {
9974 case XK_Escape:
9975 case XK_F20:
9976 {
9977 /*
9978 Prematurely exit.
9979 */
9980 state|=ExitState;
9981 break;
9982 }
9983 case XK_F1:
9984 case XK_Help:
9985 {
9986 XTextViewWidget(display,resource_info,windows,MagickFalse,
9987 "Help Viewer - Matte Edit",ImageMatteEditHelp);
9988 break;
9989 }
9990 default:
9991 {
9992 (void) XBell(display,0);
9993 break;
9994 }
9995 }
9996 break;
9997 }
9998 case MotionNotify:
9999 {
10000 /*
10001 Map and unmap Info widget as cursor crosses its boundaries.
10002 */
10003 x=event.xmotion.x;
10004 y=event.xmotion.y;
10005 if (windows->info.mapped != MagickFalse)
10006 {
10007 if ((x < (int) (windows->info.x+windows->info.width)) &&
10008 (y < (int) (windows->info.y+windows->info.height)))
10009 (void) XWithdrawWindow(display,windows->info.id,
10010 windows->info.screen);
10011 }
10012 else
10013 if ((x > (int) (windows->info.x+windows->info.width)) ||
10014 (y > (int) (windows->info.y+windows->info.height)))
10015 (void) XMapWindow(display,windows->info.id);
10016 break;
10017 }
10018 default:
10019 break;
10020 }
10021 if (event.xany.window == windows->magnify.id)
10022 {
10023 x=windows->magnify.x-windows->image.x;
10024 y=windows->magnify.y-windows->image.y;
10025 }
10026 x_offset=x;
10027 y_offset=y;
10028 if ((state & UpdateConfigurationState) != 0)
10029 {
cristy49e2d862010-11-12 02:50:30 +000010030 CacheView
10031 *image_view;
10032
cristy3ed852e2009-09-05 21:47:34 +000010033 int
10034 x,
10035 y;
10036
10037 /*
10038 Matte edit is relative to image configuration.
10039 */
10040 (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10041 MagickTrue);
10042 XPutPixel(windows->image.ximage,x_offset,y_offset,
10043 windows->pixel_info->background_color.pixel);
10044 width=(unsigned int) (*image)->columns;
10045 height=(unsigned int) (*image)->rows;
10046 x=0;
10047 y=0;
10048 if (windows->image.crop_geometry != (char *) NULL)
cristy4c08aed2011-07-01 19:47:50 +000010049 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10050 &height);
10051 x_offset=(int) (width*(windows->image.x+x_offset)/
10052 windows->image.ximage->width+x);
10053 y_offset=(int) (height*(windows->image.y+y_offset)/
10054 windows->image.ximage->height+y);
cristy3ed852e2009-09-05 21:47:34 +000010055 if ((x_offset < 0) || (y_offset < 0))
10056 continue;
10057 if ((x_offset >= (int) (*image)->columns) ||
10058 (y_offset >= (int) (*image)->rows))
10059 continue;
cristy574cc262011-08-05 01:23:58 +000010060 if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010061 return(MagickFalse);
10062 (*image)->matte=MagickTrue;
cristy49e2d862010-11-12 02:50:30 +000010063 image_view=AcquireCacheView(*image);
cristy3ed852e2009-09-05 21:47:34 +000010064 switch (method)
10065 {
10066 case PointMethod:
10067 default:
10068 {
10069 /*
10070 Update matte information using point algorithm.
10071 */
cristy49e2d862010-11-12 02:50:30 +000010072 q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10073 (ssize_t) y_offset,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +000010074 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010075 break;
cristy4c08aed2011-07-01 19:47:50 +000010076 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
cristy49e2d862010-11-12 02:50:30 +000010077 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +000010078 break;
10079 }
10080 case ReplaceMethod:
10081 {
10082 PixelPacket
cristy4c08aed2011-07-01 19:47:50 +000010083 pixel,
cristy3ed852e2009-09-05 21:47:34 +000010084 target;
10085
10086 /*
10087 Update matte information using replace algorithm.
10088 */
cristy49e2d862010-11-12 02:50:30 +000010089 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10090 (ssize_t) y_offset,&target,exception);
10091 for (y=0; y < (int) (*image)->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010092 {
cristy49e2d862010-11-12 02:50:30 +000010093 q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
cristy051718b2011-08-28 22:49:25 +000010094 (*image)->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +000010095 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010096 break;
10097 for (x=0; x < (int) (*image)->columns; x++)
10098 {
cristy4c08aed2011-07-01 19:47:50 +000010099 GetPixelPacket(*image,q,&pixel);
10100 if (IsFuzzyEquivalencePixelPacket(*image,&pixel,&target))
10101 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
cristyed231572011-07-14 02:18:59 +000010102 q+=GetPixelChannels(*image);
cristy3ed852e2009-09-05 21:47:34 +000010103 }
cristy49e2d862010-11-12 02:50:30 +000010104 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010105 break;
10106 }
10107 break;
10108 }
10109 case FloodfillMethod:
10110 case FillToBorderMethod:
10111 {
cristybd5a96c2011-08-21 00:04:26 +000010112 ChannelType
10113 channel_mask;
10114
cristy3ed852e2009-09-05 21:47:34 +000010115 DrawInfo
10116 *draw_info;
10117
cristy4c08aed2011-07-01 19:47:50 +000010118 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +000010119 target;
10120
10121 /*
10122 Update matte information using floodfill algorithm.
10123 */
cristy49e2d862010-11-12 02:50:30 +000010124 (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10125 (ssize_t) y_offset,&target,exception);
cristy3ed852e2009-09-05 21:47:34 +000010126 if (method == FillToBorderMethod)
10127 {
cristy4c08aed2011-07-01 19:47:50 +000010128 target.red=(MagickRealType) ScaleShortToQuantum(
10129 border_color.red);
10130 target.green=(MagickRealType) ScaleShortToQuantum(
10131 border_color.green);
10132 target.blue=(MagickRealType) ScaleShortToQuantum(
10133 border_color.blue);
cristy3ed852e2009-09-05 21:47:34 +000010134 }
10135 draw_info=CloneDrawInfo(resource_info->image_info,
10136 (DrawInfo *) NULL);
cristy4c08aed2011-07-01 19:47:50 +000010137 draw_info->fill.alpha=ClampToQuantum(InterpretLocaleValue(matte,
cristy0df696d2011-05-18 19:55:22 +000010138 (char **) NULL));
cristybd5a96c2011-08-21 00:04:26 +000010139 channel_mask=SetPixelChannelMask(*image,AlphaChannel);
cristyd42d9952011-07-08 14:21:50 +000010140 (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10141 x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
cristy189e84c2011-08-27 18:08:53 +000010142 MagickFalse : MagickTrue,exception);
cristybd5a96c2011-08-21 00:04:26 +000010143 (void) SetPixelChannelMap(*image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +000010144 draw_info=DestroyDrawInfo(draw_info);
10145 break;
10146 }
10147 case ResetMethod:
10148 {
10149 /*
10150 Update matte information using reset algorithm.
10151 */
cristy574cc262011-08-05 01:23:58 +000010152 if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010153 return(MagickFalse);
cristy49e2d862010-11-12 02:50:30 +000010154 for (y=0; y < (int) (*image)->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010155 {
cristy49e2d862010-11-12 02:50:30 +000010156 q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10157 (*image)->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +000010158 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010159 break;
10160 for (x=0; x < (int) (*image)->columns; x++)
10161 {
cristy4c08aed2011-07-01 19:47:50 +000010162 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
cristyed231572011-07-14 02:18:59 +000010163 q+=GetPixelChannels(*image);
cristy3ed852e2009-09-05 21:47:34 +000010164 }
cristy49e2d862010-11-12 02:50:30 +000010165 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010166 break;
10167 }
cristy4c08aed2011-07-01 19:47:50 +000010168 if (StringToLong(matte) == (long) OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +000010169 (*image)->matte=MagickFalse;
10170 break;
10171 }
10172 }
cristy49e2d862010-11-12 02:50:30 +000010173 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +000010174 state&=(~UpdateConfigurationState);
10175 }
10176 } while ((state & ExitState) == 0);
10177 (void) XSelectInput(display,windows->image.id,
10178 windows->image.attributes.event_mask);
10179 XSetCursorState(display,windows,MagickFalse);
10180 (void) XFreeCursor(display,cursor);
10181 return(MagickTrue);
10182}
10183
10184/*
10185%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10186% %
10187% %
10188% %
10189+ X O p e n I m a g e %
10190% %
10191% %
10192% %
10193%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10194%
10195% XOpenImage() loads an image from a file.
10196%
10197% The format of the XOpenImage method is:
10198%
10199% Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10200% XWindows *windows,const unsigned int command)
10201%
10202% A description of each parameter follows:
10203%
10204% o display: Specifies a connection to an X server; returned from
10205% XOpenDisplay.
10206%
10207% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10208%
10209% o windows: Specifies a pointer to a XWindows structure.
10210%
10211% o command: A value other than zero indicates that the file is selected
10212% from the command line argument list.
10213%
10214*/
10215static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10216 XWindows *windows,const MagickBooleanType command)
10217{
10218 const MagickInfo
10219 *magick_info;
10220
10221 ExceptionInfo
10222 *exception;
10223
10224 Image
10225 *nexus;
10226
10227 ImageInfo
10228 *image_info;
10229
10230 static char
10231 filename[MaxTextExtent] = "\0";
10232
10233 /*
10234 Request file name from user.
10235 */
10236 if (command == MagickFalse)
10237 XFileBrowserWidget(display,windows,"Open",filename);
10238 else
10239 {
10240 char
10241 **filelist,
10242 **files;
10243
10244 int
10245 count,
10246 status;
10247
10248 register int
10249 i,
10250 j;
10251
10252 /*
10253 Select next image from the command line.
10254 */
10255 status=XGetCommand(display,windows->image.id,&files,&count);
10256 if (status == 0)
10257 {
10258 ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10259 return((Image *) NULL);
10260 }
10261 filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10262 if (filelist == (char **) NULL)
10263 {
10264 ThrowXWindowFatalException(ResourceLimitError,
10265 "MemoryAllocationFailed","...");
10266 (void) XFreeStringList(files);
10267 return((Image *) NULL);
10268 }
10269 j=0;
10270 for (i=1; i < count; i++)
10271 if (*files[i] != '-')
10272 filelist[j++]=files[i];
10273 filelist[j]=(char *) NULL;
10274 XListBrowserWidget(display,windows,&windows->widget,
10275 (const char **) filelist,"Load","Select Image to Load:",filename);
10276 filelist=(char **) RelinquishMagickMemory(filelist);
10277 (void) XFreeStringList(files);
10278 }
10279 if (*filename == '\0')
10280 return((Image *) NULL);
10281 image_info=CloneImageInfo(resource_info->image_info);
10282 (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10283 (void *) NULL);
10284 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10285 exception=AcquireExceptionInfo();
cristyd965a422010-03-03 17:47:35 +000010286 (void) SetImageInfo(image_info,0,exception);
cristy3ed852e2009-09-05 21:47:34 +000010287 if (LocaleCompare(image_info->magick,"X") == 0)
10288 {
10289 char
10290 seconds[MaxTextExtent];
10291
10292 /*
10293 User may want to delay the X server screen grab.
10294 */
10295 (void) CopyMagickString(seconds,"0",MaxTextExtent);
10296 (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10297 seconds);
10298 if (*seconds == '\0')
10299 return((Image *) NULL);
cristybb503372010-05-27 20:51:26 +000010300 XDelay(display,(size_t) (1000*StringToLong(seconds)));
cristy3ed852e2009-09-05 21:47:34 +000010301 }
10302 magick_info=GetMagickInfo(image_info->magick,exception);
10303 if ((magick_info != (const MagickInfo *) NULL) &&
10304 (magick_info->raw != MagickFalse))
10305 {
10306 char
10307 geometry[MaxTextExtent];
10308
10309 /*
10310 Request image size from the user.
10311 */
10312 (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10313 if (image_info->size != (char *) NULL)
10314 (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10315 (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10316 geometry);
10317 (void) CloneString(&image_info->size,geometry);
10318 }
10319 /*
10320 Load the image.
10321 */
10322 XSetCursorState(display,windows,MagickTrue);
10323 XCheckRefreshWindows(display,windows);
10324 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10325 nexus=ReadImage(image_info,exception);
10326 CatchException(exception);
10327 XSetCursorState(display,windows,MagickFalse);
10328 if (nexus != (Image *) NULL)
10329 XClientMessage(display,windows->image.id,windows->im_protocols,
10330 windows->im_next_image,CurrentTime);
10331 else
10332 {
10333 char
10334 *text,
10335 **textlist;
10336
10337 /*
10338 Unknown image format.
10339 */
10340 text=FileToString(filename,~0,exception);
10341 if (text == (char *) NULL)
10342 return((Image *) NULL);
10343 textlist=StringToList(text);
10344 if (textlist != (char **) NULL)
10345 {
10346 char
10347 title[MaxTextExtent];
10348
10349 register int
10350 i;
10351
cristyb51dff52011-05-19 16:55:47 +000010352 (void) FormatLocaleString(title,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000010353 "Unknown format: %s",filename);
10354 XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10355 (const char **) textlist);
10356 for (i=0; textlist[i] != (char *) NULL; i++)
10357 textlist[i]=DestroyString(textlist[i]);
10358 textlist=(char **) RelinquishMagickMemory(textlist);
10359 }
10360 text=DestroyString(text);
10361 }
10362 exception=DestroyExceptionInfo(exception);
10363 image_info=DestroyImageInfo(image_info);
10364 return(nexus);
10365}
10366
10367/*
10368%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10369% %
10370% %
10371% %
10372+ X P a n I m a g e %
10373% %
10374% %
10375% %
10376%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10377%
10378% XPanImage() pans the image until the mouse button is released.
10379%
10380% The format of the XPanImage method is:
10381%
10382% void XPanImage(Display *display,XWindows *windows,XEvent *event)
10383%
10384% A description of each parameter follows:
10385%
10386% o display: Specifies a connection to an X server; returned from
10387% XOpenDisplay.
10388%
10389% o windows: Specifies a pointer to a XWindows structure.
10390%
10391% o event: Specifies a pointer to a XEvent structure. If it is NULL,
10392% the entire image is refreshed.
10393%
10394*/
10395static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10396{
10397 char
10398 text[MaxTextExtent];
10399
10400 Cursor
10401 cursor;
10402
10403 MagickRealType
10404 x_factor,
10405 y_factor;
10406
10407 RectangleInfo
10408 pan_info;
10409
cristybb503372010-05-27 20:51:26 +000010410 size_t
cristy3ed852e2009-09-05 21:47:34 +000010411 state;
10412
10413 /*
10414 Define cursor.
10415 */
10416 if ((windows->image.ximage->width > (int) windows->image.width) &&
10417 (windows->image.ximage->height > (int) windows->image.height))
10418 cursor=XCreateFontCursor(display,XC_fleur);
10419 else
10420 if (windows->image.ximage->width > (int) windows->image.width)
10421 cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10422 else
10423 if (windows->image.ximage->height > (int) windows->image.height)
10424 cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10425 else
10426 cursor=XCreateFontCursor(display,XC_arrow);
10427 (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10428 /*
10429 Pan image as pointer moves until the mouse button is released.
10430 */
10431 x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10432 y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10433 pan_info.width=windows->pan.width*windows->image.width/
10434 windows->image.ximage->width;
10435 pan_info.height=windows->pan.height*windows->image.height/
10436 windows->image.ximage->height;
10437 pan_info.x=0;
10438 pan_info.y=0;
10439 state=UpdateConfigurationState;
10440 do
10441 {
10442 switch (event->type)
10443 {
10444 case ButtonPress:
10445 {
10446 /*
10447 User choose an initial pan location.
10448 */
cristy49e2d862010-11-12 02:50:30 +000010449 pan_info.x=(ssize_t) event->xbutton.x;
10450 pan_info.y=(ssize_t) event->xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000010451 state|=UpdateConfigurationState;
10452 break;
10453 }
10454 case ButtonRelease:
10455 {
10456 /*
10457 User has finished panning the image.
10458 */
cristy49e2d862010-11-12 02:50:30 +000010459 pan_info.x=(ssize_t) event->xbutton.x;
10460 pan_info.y=(ssize_t) event->xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000010461 state|=UpdateConfigurationState | ExitState;
10462 break;
10463 }
10464 case MotionNotify:
10465 {
cristy49e2d862010-11-12 02:50:30 +000010466 pan_info.x=(ssize_t) event->xmotion.x;
10467 pan_info.y=(ssize_t) event->xmotion.y;
cristy3ed852e2009-09-05 21:47:34 +000010468 state|=UpdateConfigurationState;
10469 }
10470 default:
10471 break;
10472 }
10473 if ((state & UpdateConfigurationState) != 0)
10474 {
10475 /*
10476 Check boundary conditions.
10477 */
cristy49e2d862010-11-12 02:50:30 +000010478 if (pan_info.x < (ssize_t) (pan_info.width/2))
cristy3ed852e2009-09-05 21:47:34 +000010479 pan_info.x=0;
10480 else
cristy49e2d862010-11-12 02:50:30 +000010481 pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
cristy3ed852e2009-09-05 21:47:34 +000010482 if (pan_info.x < 0)
10483 pan_info.x=0;
10484 else
10485 if ((int) (pan_info.x+windows->image.width) >
10486 windows->image.ximage->width)
cristybb503372010-05-27 20:51:26 +000010487 pan_info.x=(ssize_t)
cristy3ed852e2009-09-05 21:47:34 +000010488 (windows->image.ximage->width-windows->image.width);
cristybb503372010-05-27 20:51:26 +000010489 if (pan_info.y < (ssize_t) (pan_info.height/2))
cristy3ed852e2009-09-05 21:47:34 +000010490 pan_info.y=0;
10491 else
cristybb503372010-05-27 20:51:26 +000010492 pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
cristy3ed852e2009-09-05 21:47:34 +000010493 if (pan_info.y < 0)
10494 pan_info.y=0;
10495 else
10496 if ((int) (pan_info.y+windows->image.height) >
10497 windows->image.ximage->height)
cristybb503372010-05-27 20:51:26 +000010498 pan_info.y=(ssize_t)
cristy3ed852e2009-09-05 21:47:34 +000010499 (windows->image.ximage->height-windows->image.height);
10500 if ((windows->image.x != (int) pan_info.x) ||
10501 (windows->image.y != (int) pan_info.y))
10502 {
10503 /*
10504 Display image pan offset.
10505 */
10506 windows->image.x=(int) pan_info.x;
10507 windows->image.y=(int) pan_info.y;
cristyb51dff52011-05-19 16:55:47 +000010508 (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
cristy3ed852e2009-09-05 21:47:34 +000010509 windows->image.width,windows->image.height,windows->image.x,
10510 windows->image.y);
10511 XInfoWidget(display,windows,text);
10512 /*
10513 Refresh Image window.
10514 */
10515 XDrawPanRectangle(display,windows);
10516 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10517 }
10518 state&=(~UpdateConfigurationState);
10519 }
10520 /*
10521 Wait for next event.
10522 */
10523 if ((state & ExitState) == 0)
10524 XScreenEvent(display,windows,event);
10525 } while ((state & ExitState) == 0);
10526 /*
10527 Restore cursor.
10528 */
10529 (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10530 (void) XFreeCursor(display,cursor);
10531 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10532}
10533
10534/*
10535%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10536% %
10537% %
10538% %
10539+ X P a s t e I m a g e %
10540% %
10541% %
10542% %
10543%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10544%
10545% XPasteImage() pastes an image previously saved with XCropImage in the X
10546% window image at a location the user chooses with the pointer.
10547%
10548% The format of the XPasteImage method is:
10549%
10550% MagickBooleanType XPasteImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000010551% XResourceInfo *resource_info,XWindows *windows,Image *image,
10552% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010553%
10554% A description of each parameter follows:
10555%
10556% o display: Specifies a connection to an X server; returned from
10557% XOpenDisplay.
10558%
10559% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10560%
10561% o windows: Specifies a pointer to a XWindows structure.
10562%
10563% o image: the image; returned from ReadImage.
10564%
cristy051718b2011-08-28 22:49:25 +000010565% o exception: return any errors or warnings in this structure.
10566%
cristy3ed852e2009-09-05 21:47:34 +000010567*/
10568static MagickBooleanType XPasteImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000010569 XResourceInfo *resource_info,XWindows *windows,Image *image,
10570 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010571{
10572 static const char
10573 *PasteMenu[] =
10574 {
10575 "Operator",
10576 "Help",
10577 "Dismiss",
10578 (char *) NULL
10579 };
10580
10581 static const ModeType
10582 PasteCommands[] =
10583 {
10584 PasteOperatorsCommand,
10585 PasteHelpCommand,
10586 PasteDismissCommand
10587 };
10588
10589 static CompositeOperator
10590 compose = CopyCompositeOp;
10591
10592 char
10593 text[MaxTextExtent];
10594
10595 Cursor
10596 cursor;
10597
10598 Image
10599 *paste_image;
10600
10601 int
10602 entry,
10603 id,
10604 x,
10605 y;
10606
10607 MagickRealType
10608 scale_factor;
10609
10610 RectangleInfo
10611 highlight_info,
10612 paste_info;
10613
10614 unsigned int
10615 height,
10616 width;
10617
cristybb503372010-05-27 20:51:26 +000010618 size_t
cristy3ed852e2009-09-05 21:47:34 +000010619 state;
10620
10621 XEvent
10622 event;
10623
10624 /*
10625 Copy image.
10626 */
10627 if (resource_info->copy_image == (Image *) NULL)
10628 return(MagickFalse);
cristy051718b2011-08-28 22:49:25 +000010629 paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000010630 /*
10631 Map Command widget.
10632 */
10633 (void) CloneString(&windows->command.name,"Paste");
10634 windows->command.data=1;
10635 (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10636 (void) XMapRaised(display,windows->command.id);
10637 XClientMessage(display,windows->image.id,windows->im_protocols,
10638 windows->im_update_widget,CurrentTime);
10639 /*
10640 Track pointer until button 1 is pressed.
10641 */
10642 XSetCursorState(display,windows,MagickFalse);
10643 XQueryPosition(display,windows->image.id,&x,&y);
10644 (void) XSelectInput(display,windows->image.id,
10645 windows->image.attributes.event_mask | PointerMotionMask);
cristy49e2d862010-11-12 02:50:30 +000010646 paste_info.x=(ssize_t) windows->image.x+x;
10647 paste_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +000010648 paste_info.width=0;
10649 paste_info.height=0;
10650 cursor=XCreateFontCursor(display,XC_ul_angle);
10651 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10652 state=DefaultState;
10653 do
10654 {
10655 if (windows->info.mapped != MagickFalse)
10656 {
10657 /*
10658 Display pointer position.
10659 */
cristyb51dff52011-05-19 16:55:47 +000010660 (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
cristyf2faecf2010-05-28 19:19:36 +000010661 (long) paste_info.x,(long) paste_info.y);
cristy3ed852e2009-09-05 21:47:34 +000010662 XInfoWidget(display,windows,text);
10663 }
10664 highlight_info=paste_info;
10665 highlight_info.x=paste_info.x-windows->image.x;
10666 highlight_info.y=paste_info.y-windows->image.y;
10667 XHighlightRectangle(display,windows->image.id,
10668 windows->image.highlight_context,&highlight_info);
10669 /*
10670 Wait for next event.
10671 */
10672 XScreenEvent(display,windows,&event);
10673 XHighlightRectangle(display,windows->image.id,
10674 windows->image.highlight_context,&highlight_info);
10675 if (event.xany.window == windows->command.id)
10676 {
10677 /*
10678 Select a command from the Command widget.
10679 */
10680 id=XCommandWidget(display,windows,PasteMenu,&event);
10681 if (id < 0)
10682 continue;
10683 switch (PasteCommands[id])
10684 {
10685 case PasteOperatorsCommand:
10686 {
10687 char
10688 command[MaxTextExtent],
10689 **operators;
10690
10691 /*
10692 Select a command from the pop-up menu.
10693 */
cristy042ee782011-04-22 18:48:30 +000010694 operators=GetCommandOptions(MagickComposeOptions);
cristy3ed852e2009-09-05 21:47:34 +000010695 if (operators == (char **) NULL)
10696 break;
10697 entry=XMenuWidget(display,windows,PasteMenu[id],
10698 (const char **) operators,command);
10699 if (entry >= 0)
cristy042ee782011-04-22 18:48:30 +000010700 compose=(CompositeOperator) ParseCommandOption(
cristy3ed852e2009-09-05 21:47:34 +000010701 MagickComposeOptions,MagickFalse,operators[entry]);
10702 operators=DestroyStringList(operators);
10703 break;
10704 }
10705 case PasteHelpCommand:
10706 {
10707 XTextViewWidget(display,resource_info,windows,MagickFalse,
10708 "Help Viewer - Image Composite",ImagePasteHelp);
10709 break;
10710 }
10711 case PasteDismissCommand:
10712 {
10713 /*
10714 Prematurely exit.
10715 */
10716 state|=EscapeState;
10717 state|=ExitState;
10718 break;
10719 }
10720 default:
10721 break;
10722 }
10723 continue;
10724 }
10725 switch (event.type)
10726 {
10727 case ButtonPress:
10728 {
10729 if (image->debug != MagickFalse)
10730 (void) LogMagickEvent(X11Event,GetMagickModule(),
10731 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10732 event.xbutton.button,event.xbutton.x,event.xbutton.y);
10733 if (event.xbutton.button != Button1)
10734 break;
10735 if (event.xbutton.window != windows->image.id)
10736 break;
10737 /*
10738 Paste rectangle is relative to image configuration.
10739 */
10740 width=(unsigned int) image->columns;
10741 height=(unsigned int) image->rows;
10742 x=0;
10743 y=0;
10744 if (windows->image.crop_geometry != (char *) NULL)
10745 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10746 &width,&height);
10747 scale_factor=(MagickRealType) windows->image.ximage->width/width;
10748 paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10749 scale_factor=(MagickRealType) windows->image.ximage->height/height;
10750 paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10751 (void) XCheckDefineCursor(display,windows->image.id,cursor);
cristy49e2d862010-11-12 02:50:30 +000010752 paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10753 paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000010754 break;
10755 }
10756 case ButtonRelease:
10757 {
10758 if (image->debug != MagickFalse)
10759 (void) LogMagickEvent(X11Event,GetMagickModule(),
10760 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10761 event.xbutton.button,event.xbutton.x,event.xbutton.y);
10762 if (event.xbutton.button != Button1)
10763 break;
10764 if (event.xbutton.window != windows->image.id)
10765 break;
10766 if ((paste_info.width != 0) && (paste_info.height != 0))
10767 {
10768 /*
10769 User has selected the location of the paste image.
10770 */
cristy49e2d862010-11-12 02:50:30 +000010771 paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10772 paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000010773 state|=ExitState;
10774 }
10775 break;
10776 }
10777 case Expose:
10778 break;
10779 case KeyPress:
10780 {
10781 char
10782 command[MaxTextExtent];
10783
10784 KeySym
10785 key_symbol;
10786
10787 int
10788 length;
10789
10790 if (event.xkey.window != windows->image.id)
10791 break;
10792 /*
10793 Respond to a user key press.
10794 */
10795 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10796 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10797 *(command+length)='\0';
10798 if (image->debug != MagickFalse)
10799 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010800 "Key press: 0x%lx (%s)",(long) key_symbol,command);
cristy3ed852e2009-09-05 21:47:34 +000010801 switch ((int) key_symbol)
10802 {
10803 case XK_Escape:
10804 case XK_F20:
10805 {
10806 /*
10807 Prematurely exit.
10808 */
10809 paste_image=DestroyImage(paste_image);
10810 state|=EscapeState;
10811 state|=ExitState;
10812 break;
10813 }
10814 case XK_F1:
10815 case XK_Help:
10816 {
10817 (void) XSetFunction(display,windows->image.highlight_context,
10818 GXcopy);
10819 XTextViewWidget(display,resource_info,windows,MagickFalse,
10820 "Help Viewer - Image Composite",ImagePasteHelp);
10821 (void) XSetFunction(display,windows->image.highlight_context,
10822 GXinvert);
10823 break;
10824 }
10825 default:
10826 {
10827 (void) XBell(display,0);
10828 break;
10829 }
10830 }
10831 break;
10832 }
10833 case MotionNotify:
10834 {
10835 /*
10836 Map and unmap Info widget as text cursor crosses its boundaries.
10837 */
10838 x=event.xmotion.x;
10839 y=event.xmotion.y;
10840 if (windows->info.mapped != MagickFalse)
10841 {
10842 if ((x < (int) (windows->info.x+windows->info.width)) &&
10843 (y < (int) (windows->info.y+windows->info.height)))
10844 (void) XWithdrawWindow(display,windows->info.id,
10845 windows->info.screen);
10846 }
10847 else
10848 if ((x > (int) (windows->info.x+windows->info.width)) ||
10849 (y > (int) (windows->info.y+windows->info.height)))
10850 (void) XMapWindow(display,windows->info.id);
cristy49e2d862010-11-12 02:50:30 +000010851 paste_info.x=(ssize_t) windows->image.x+x;
10852 paste_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +000010853 break;
10854 }
10855 default:
10856 {
10857 if (image->debug != MagickFalse)
10858 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10859 event.type);
10860 break;
10861 }
10862 }
10863 } while ((state & ExitState) == 0);
10864 (void) XSelectInput(display,windows->image.id,
10865 windows->image.attributes.event_mask);
10866 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10867 XSetCursorState(display,windows,MagickFalse);
10868 (void) XFreeCursor(display,cursor);
10869 if ((state & EscapeState) != 0)
10870 return(MagickTrue);
10871 /*
10872 Image pasting is relative to image configuration.
10873 */
10874 XSetCursorState(display,windows,MagickTrue);
10875 XCheckRefreshWindows(display,windows);
10876 width=(unsigned int) image->columns;
10877 height=(unsigned int) image->rows;
10878 x=0;
10879 y=0;
10880 if (windows->image.crop_geometry != (char *) NULL)
10881 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10882 scale_factor=(MagickRealType) width/windows->image.ximage->width;
10883 paste_info.x+=x;
cristy49e2d862010-11-12 02:50:30 +000010884 paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +000010885 paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10886 scale_factor=(MagickRealType) height/windows->image.ximage->height;
10887 paste_info.y+=y;
cristy49e2d862010-11-12 02:50:30 +000010888 paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
cristy3ed852e2009-09-05 21:47:34 +000010889 paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10890 /*
10891 Paste image with X Image window.
10892 */
10893 (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10894 paste_image=DestroyImage(paste_image);
10895 XSetCursorState(display,windows,MagickFalse);
10896 /*
10897 Update image colormap.
10898 */
10899 XConfigureImageColormap(display,resource_info,windows,image);
cristy051718b2011-08-28 22:49:25 +000010900 (void) XConfigureImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000010901 return(MagickTrue);
10902}
10903
10904/*
10905%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10906% %
10907% %
10908% %
10909+ X P r i n t I m a g e %
10910% %
10911% %
10912% %
10913%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10914%
10915% XPrintImage() prints an image to a Postscript printer.
10916%
10917% The format of the XPrintImage method is:
10918%
10919% MagickBooleanType XPrintImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000010920% XResourceInfo *resource_info,XWindows *windows,Image *image,
10921% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010922%
10923% A description of each parameter follows:
10924%
10925% o display: Specifies a connection to an X server; returned from
10926% XOpenDisplay.
10927%
10928% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10929%
10930% o windows: Specifies a pointer to a XWindows structure.
10931%
10932% o image: the image.
10933%
cristy051718b2011-08-28 22:49:25 +000010934% o exception: return any errors or warnings in this structure.
10935%
cristy3ed852e2009-09-05 21:47:34 +000010936*/
10937static MagickBooleanType XPrintImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000010938 XResourceInfo *resource_info,XWindows *windows,Image *image,
10939 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010940{
10941 char
10942 filename[MaxTextExtent],
10943 geometry[MaxTextExtent];
10944
10945 Image
10946 *print_image;
10947
10948 ImageInfo
10949 *image_info;
10950
10951 MagickStatusType
10952 status;
10953
10954 /*
10955 Request Postscript page geometry from user.
10956 */
10957 image_info=CloneImageInfo(resource_info->image_info);
cristyb51dff52011-05-19 16:55:47 +000010958 (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
cristy3ed852e2009-09-05 21:47:34 +000010959 if (image_info->page != (char *) NULL)
10960 (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10961 XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10962 "Select Postscript Page Geometry:",geometry);
10963 if (*geometry == '\0')
10964 return(MagickTrue);
10965 image_info->page=GetPageGeometry(geometry);
10966 /*
10967 Apply image transforms.
10968 */
10969 XSetCursorState(display,windows,MagickTrue);
10970 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +000010971 print_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000010972 if (print_image == (Image *) NULL)
10973 return(MagickFalse);
cristyb51dff52011-05-19 16:55:47 +000010974 (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
cristy3ed852e2009-09-05 21:47:34 +000010975 windows->image.ximage->width,windows->image.ximage->height);
10976 (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10977 /*
10978 Print image.
10979 */
10980 (void) AcquireUniqueFilename(filename);
cristyb51dff52011-05-19 16:55:47 +000010981 (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
cristy3ed852e2009-09-05 21:47:34 +000010982 filename);
cristy051718b2011-08-28 22:49:25 +000010983 status=WriteImage(image_info,print_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000010984 (void) RelinquishUniqueFileResource(filename);
10985 print_image=DestroyImage(print_image);
10986 image_info=DestroyImageInfo(image_info);
10987 XSetCursorState(display,windows,MagickFalse);
10988 return(status != 0 ? MagickTrue : MagickFalse);
10989}
10990
10991/*
10992%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10993% %
10994% %
10995% %
10996+ X R O I I m a g e %
10997% %
10998% %
10999% %
11000%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11001%
11002% XROIImage() applies an image processing technique to a region of interest.
11003%
11004% The format of the XROIImage method is:
11005%
11006% MagickBooleanType XROIImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000011007% XResourceInfo *resource_info,XWindows *windows,Image **image,
11008% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011009%
11010% A description of each parameter follows:
11011%
11012% o display: Specifies a connection to an X server; returned from
11013% XOpenDisplay.
11014%
11015% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11016%
11017% o windows: Specifies a pointer to a XWindows structure.
11018%
11019% o image: the image; returned from ReadImage.
11020%
cristy051718b2011-08-28 22:49:25 +000011021% o exception: return any errors or warnings in this structure.
11022%
cristy3ed852e2009-09-05 21:47:34 +000011023*/
11024static MagickBooleanType XROIImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000011025 XResourceInfo *resource_info,XWindows *windows,Image **image,
11026 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011027{
11028#define ApplyMenus 7
11029
11030 static const char
11031 *ROIMenu[] =
11032 {
11033 "Help",
11034 "Dismiss",
11035 (char *) NULL
11036 },
11037 *ApplyMenu[] =
11038 {
11039 "File",
11040 "Edit",
11041 "Transform",
11042 "Enhance",
11043 "Effects",
11044 "F/X",
11045 "Miscellany",
11046 "Help",
11047 "Dismiss",
11048 (char *) NULL
11049 },
11050 *FileMenu[] =
11051 {
11052 "Save...",
11053 "Print...",
11054 (char *) NULL
11055 },
11056 *EditMenu[] =
11057 {
11058 "Undo",
11059 "Redo",
11060 (char *) NULL
11061 },
11062 *TransformMenu[] =
11063 {
11064 "Flop",
11065 "Flip",
11066 "Rotate Right",
11067 "Rotate Left",
11068 (char *) NULL
11069 },
11070 *EnhanceMenu[] =
11071 {
11072 "Hue...",
11073 "Saturation...",
11074 "Brightness...",
11075 "Gamma...",
11076 "Spiff",
11077 "Dull",
11078 "Contrast Stretch...",
11079 "Sigmoidal Contrast...",
11080 "Normalize",
11081 "Equalize",
11082 "Negate",
11083 "Grayscale",
11084 "Map...",
11085 "Quantize...",
11086 (char *) NULL
11087 },
11088 *EffectsMenu[] =
11089 {
11090 "Despeckle",
11091 "Emboss",
11092 "Reduce Noise",
11093 "Add Noise",
11094 "Sharpen...",
11095 "Blur...",
11096 "Threshold...",
11097 "Edge Detect...",
11098 "Spread...",
11099 "Shade...",
11100 "Raise...",
11101 "Segment...",
11102 (char *) NULL
11103 },
11104 *FXMenu[] =
11105 {
11106 "Solarize...",
11107 "Sepia Tone...",
11108 "Swirl...",
11109 "Implode...",
11110 "Vignette...",
11111 "Wave...",
11112 "Oil Paint...",
11113 "Charcoal Draw...",
11114 (char *) NULL
11115 },
11116 *MiscellanyMenu[] =
11117 {
11118 "Image Info",
11119 "Zoom Image",
11120 "Show Preview...",
11121 "Show Histogram",
11122 "Show Matte",
11123 (char *) NULL
11124 };
11125
11126 static const char
11127 **Menus[ApplyMenus] =
11128 {
11129 FileMenu,
11130 EditMenu,
11131 TransformMenu,
11132 EnhanceMenu,
11133 EffectsMenu,
11134 FXMenu,
11135 MiscellanyMenu
11136 };
11137
11138 static const CommandType
11139 ApplyCommands[] =
11140 {
11141 NullCommand,
11142 NullCommand,
11143 NullCommand,
11144 NullCommand,
11145 NullCommand,
11146 NullCommand,
11147 NullCommand,
11148 HelpCommand,
11149 QuitCommand
11150 },
11151 FileCommands[] =
11152 {
11153 SaveCommand,
11154 PrintCommand
11155 },
11156 EditCommands[] =
11157 {
11158 UndoCommand,
11159 RedoCommand
11160 },
11161 TransformCommands[] =
11162 {
11163 FlopCommand,
11164 FlipCommand,
11165 RotateRightCommand,
11166 RotateLeftCommand
11167 },
11168 EnhanceCommands[] =
11169 {
11170 HueCommand,
11171 SaturationCommand,
11172 BrightnessCommand,
11173 GammaCommand,
11174 SpiffCommand,
11175 DullCommand,
11176 ContrastStretchCommand,
11177 SigmoidalContrastCommand,
11178 NormalizeCommand,
11179 EqualizeCommand,
11180 NegateCommand,
11181 GrayscaleCommand,
11182 MapCommand,
11183 QuantizeCommand
11184 },
11185 EffectsCommands[] =
11186 {
11187 DespeckleCommand,
11188 EmbossCommand,
11189 ReduceNoiseCommand,
11190 AddNoiseCommand,
11191 SharpenCommand,
11192 BlurCommand,
11193 EdgeDetectCommand,
11194 SpreadCommand,
11195 ShadeCommand,
11196 RaiseCommand,
11197 SegmentCommand
11198 },
11199 FXCommands[] =
11200 {
11201 SolarizeCommand,
11202 SepiaToneCommand,
11203 SwirlCommand,
11204 ImplodeCommand,
11205 VignetteCommand,
11206 WaveCommand,
11207 OilPaintCommand,
11208 CharcoalDrawCommand
11209 },
11210 MiscellanyCommands[] =
11211 {
11212 InfoCommand,
11213 ZoomCommand,
11214 ShowPreviewCommand,
11215 ShowHistogramCommand,
11216 ShowMatteCommand
11217 },
11218 ROICommands[] =
11219 {
11220 ROIHelpCommand,
11221 ROIDismissCommand
11222 };
11223
11224 static const CommandType
11225 *Commands[ApplyMenus] =
11226 {
11227 FileCommands,
11228 EditCommands,
11229 TransformCommands,
11230 EnhanceCommands,
11231 EffectsCommands,
11232 FXCommands,
11233 MiscellanyCommands
11234 };
11235
11236 char
11237 command[MaxTextExtent],
11238 text[MaxTextExtent];
11239
11240 CommandType
11241 command_type;
11242
11243 Cursor
11244 cursor;
11245
11246 Image
11247 *roi_image;
11248
11249 int
11250 entry,
11251 id,
11252 x,
11253 y;
11254
11255 MagickRealType
11256 scale_factor;
11257
11258 MagickProgressMonitor
11259 progress_monitor;
11260
11261 RectangleInfo
11262 crop_info,
11263 highlight_info,
11264 roi_info;
11265
11266 unsigned int
11267 height,
11268 width;
11269
cristybb503372010-05-27 20:51:26 +000011270 size_t
cristy3ed852e2009-09-05 21:47:34 +000011271 state;
11272
11273 XEvent
11274 event;
11275
11276 /*
11277 Map Command widget.
11278 */
11279 (void) CloneString(&windows->command.name,"ROI");
11280 windows->command.data=0;
11281 (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11282 (void) XMapRaised(display,windows->command.id);
11283 XClientMessage(display,windows->image.id,windows->im_protocols,
11284 windows->im_update_widget,CurrentTime);
11285 /*
11286 Track pointer until button 1 is pressed.
11287 */
11288 XQueryPosition(display,windows->image.id,&x,&y);
11289 (void) XSelectInput(display,windows->image.id,
11290 windows->image.attributes.event_mask | PointerMotionMask);
cristy49e2d862010-11-12 02:50:30 +000011291 roi_info.x=(ssize_t) windows->image.x+x;
11292 roi_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +000011293 roi_info.width=0;
11294 roi_info.height=0;
11295 cursor=XCreateFontCursor(display,XC_fleur);
11296 state=DefaultState;
11297 do
11298 {
11299 if (windows->info.mapped != MagickFalse)
11300 {
11301 /*
11302 Display pointer position.
11303 */
cristyb51dff52011-05-19 16:55:47 +000011304 (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
cristyf2faecf2010-05-28 19:19:36 +000011305 (long) roi_info.x,(long) roi_info.y);
cristy3ed852e2009-09-05 21:47:34 +000011306 XInfoWidget(display,windows,text);
11307 }
11308 /*
11309 Wait for next event.
11310 */
11311 XScreenEvent(display,windows,&event);
11312 if (event.xany.window == windows->command.id)
11313 {
11314 /*
11315 Select a command from the Command widget.
11316 */
11317 id=XCommandWidget(display,windows,ROIMenu,&event);
11318 if (id < 0)
11319 continue;
11320 switch (ROICommands[id])
11321 {
11322 case ROIHelpCommand:
11323 {
11324 XTextViewWidget(display,resource_info,windows,MagickFalse,
11325 "Help Viewer - Region of Interest",ImageROIHelp);
11326 break;
11327 }
11328 case ROIDismissCommand:
11329 {
11330 /*
11331 Prematurely exit.
11332 */
11333 state|=EscapeState;
11334 state|=ExitState;
11335 break;
11336 }
11337 default:
11338 break;
11339 }
11340 continue;
11341 }
11342 switch (event.type)
11343 {
11344 case ButtonPress:
11345 {
11346 if (event.xbutton.button != Button1)
11347 break;
11348 if (event.xbutton.window != windows->image.id)
11349 break;
11350 /*
11351 Note first corner of region of interest rectangle-- exit loop.
11352 */
11353 (void) XCheckDefineCursor(display,windows->image.id,cursor);
cristy49e2d862010-11-12 02:50:30 +000011354 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11355 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000011356 state|=ExitState;
11357 break;
11358 }
11359 case ButtonRelease:
11360 break;
11361 case Expose:
11362 break;
11363 case KeyPress:
11364 {
11365 KeySym
11366 key_symbol;
11367
11368 if (event.xkey.window != windows->image.id)
11369 break;
11370 /*
11371 Respond to a user key press.
11372 */
11373 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11374 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11375 switch ((int) key_symbol)
11376 {
11377 case XK_Escape:
11378 case XK_F20:
11379 {
11380 /*
11381 Prematurely exit.
11382 */
11383 state|=EscapeState;
11384 state|=ExitState;
11385 break;
11386 }
11387 case XK_F1:
11388 case XK_Help:
11389 {
11390 XTextViewWidget(display,resource_info,windows,MagickFalse,
11391 "Help Viewer - Region of Interest",ImageROIHelp);
11392 break;
11393 }
11394 default:
11395 {
11396 (void) XBell(display,0);
11397 break;
11398 }
11399 }
11400 break;
11401 }
11402 case MotionNotify:
11403 {
11404 /*
11405 Map and unmap Info widget as text cursor crosses its boundaries.
11406 */
11407 x=event.xmotion.x;
11408 y=event.xmotion.y;
11409 if (windows->info.mapped != MagickFalse)
11410 {
11411 if ((x < (int) (windows->info.x+windows->info.width)) &&
11412 (y < (int) (windows->info.y+windows->info.height)))
11413 (void) XWithdrawWindow(display,windows->info.id,
11414 windows->info.screen);
11415 }
11416 else
11417 if ((x > (int) (windows->info.x+windows->info.width)) ||
11418 (y > (int) (windows->info.y+windows->info.height)))
11419 (void) XMapWindow(display,windows->info.id);
cristy49e2d862010-11-12 02:50:30 +000011420 roi_info.x=(ssize_t) windows->image.x+x;
11421 roi_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +000011422 break;
11423 }
11424 default:
11425 break;
11426 }
11427 } while ((state & ExitState) == 0);
11428 (void) XSelectInput(display,windows->image.id,
11429 windows->image.attributes.event_mask);
11430 if ((state & EscapeState) != 0)
11431 {
11432 /*
11433 User want to exit without region of interest.
11434 */
11435 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11436 (void) XFreeCursor(display,cursor);
11437 return(MagickTrue);
11438 }
11439 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11440 do
11441 {
11442 /*
11443 Size rectangle as pointer moves until the mouse button is released.
11444 */
11445 x=(int) roi_info.x;
11446 y=(int) roi_info.y;
11447 roi_info.width=0;
11448 roi_info.height=0;
11449 state=DefaultState;
11450 do
11451 {
11452 highlight_info=roi_info;
11453 highlight_info.x=roi_info.x-windows->image.x;
11454 highlight_info.y=roi_info.y-windows->image.y;
11455 if ((highlight_info.width > 3) && (highlight_info.height > 3))
11456 {
11457 /*
11458 Display info and draw region of interest rectangle.
11459 */
11460 if (windows->info.mapped == MagickFalse)
11461 (void) XMapWindow(display,windows->info.id);
cristyb51dff52011-05-19 16:55:47 +000011462 (void) FormatLocaleString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +000011463 " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +000011464 roi_info.height,(double) roi_info.x,(double) roi_info.y);
cristy3ed852e2009-09-05 21:47:34 +000011465 XInfoWidget(display,windows,text);
11466 XHighlightRectangle(display,windows->image.id,
11467 windows->image.highlight_context,&highlight_info);
11468 }
11469 else
11470 if (windows->info.mapped != MagickFalse)
11471 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11472 /*
11473 Wait for next event.
11474 */
11475 XScreenEvent(display,windows,&event);
11476 if ((highlight_info.width > 3) && (highlight_info.height > 3))
11477 XHighlightRectangle(display,windows->image.id,
11478 windows->image.highlight_context,&highlight_info);
11479 switch (event.type)
11480 {
11481 case ButtonPress:
11482 {
cristy49e2d862010-11-12 02:50:30 +000011483 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11484 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000011485 break;
11486 }
11487 case ButtonRelease:
11488 {
11489 /*
11490 User has committed to region of interest rectangle.
11491 */
cristy49e2d862010-11-12 02:50:30 +000011492 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11493 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000011494 XSetCursorState(display,windows,MagickFalse);
11495 state|=ExitState;
11496 if (LocaleCompare(windows->command.name,"Apply") == 0)
11497 break;
11498 (void) CloneString(&windows->command.name,"Apply");
11499 windows->command.data=ApplyMenus;
11500 (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11501 break;
11502 }
11503 case Expose:
11504 break;
11505 case MotionNotify:
11506 {
cristy49e2d862010-11-12 02:50:30 +000011507 roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11508 roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
cristy3ed852e2009-09-05 21:47:34 +000011509 }
11510 default:
11511 break;
11512 }
11513 if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11514 ((state & ExitState) != 0))
11515 {
11516 /*
11517 Check boundary conditions.
11518 */
11519 if (roi_info.x < 0)
11520 roi_info.x=0;
11521 else
cristy49e2d862010-11-12 02:50:30 +000011522 if (roi_info.x > (ssize_t) windows->image.ximage->width)
11523 roi_info.x=(ssize_t) windows->image.ximage->width;
cristy3ed852e2009-09-05 21:47:34 +000011524 if ((int) roi_info.x < x)
11525 roi_info.width=(unsigned int) (x-roi_info.x);
11526 else
11527 {
11528 roi_info.width=(unsigned int) (roi_info.x-x);
cristy49e2d862010-11-12 02:50:30 +000011529 roi_info.x=(ssize_t) x;
cristy3ed852e2009-09-05 21:47:34 +000011530 }
11531 if (roi_info.y < 0)
11532 roi_info.y=0;
11533 else
cristy49e2d862010-11-12 02:50:30 +000011534 if (roi_info.y > (ssize_t) windows->image.ximage->height)
11535 roi_info.y=(ssize_t) windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +000011536 if ((int) roi_info.y < y)
11537 roi_info.height=(unsigned int) (y-roi_info.y);
11538 else
11539 {
11540 roi_info.height=(unsigned int) (roi_info.y-y);
cristy49e2d862010-11-12 02:50:30 +000011541 roi_info.y=(ssize_t) y;
cristy3ed852e2009-09-05 21:47:34 +000011542 }
11543 }
11544 } while ((state & ExitState) == 0);
11545 /*
11546 Wait for user to grab a corner of the rectangle or press return.
11547 */
11548 state=DefaultState;
11549 command_type=NullCommand;
11550 (void) XMapWindow(display,windows->info.id);
11551 do
11552 {
11553 if (windows->info.mapped != MagickFalse)
11554 {
11555 /*
11556 Display pointer position.
11557 */
cristyb51dff52011-05-19 16:55:47 +000011558 (void) FormatLocaleString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +000011559 " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +000011560 roi_info.height,(double) roi_info.x,(double) roi_info.y);
cristy3ed852e2009-09-05 21:47:34 +000011561 XInfoWidget(display,windows,text);
11562 }
11563 highlight_info=roi_info;
11564 highlight_info.x=roi_info.x-windows->image.x;
11565 highlight_info.y=roi_info.y-windows->image.y;
11566 if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11567 {
11568 state|=EscapeState;
11569 state|=ExitState;
11570 break;
11571 }
11572 if ((state & UpdateRegionState) != 0)
11573 {
11574 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11575 switch (command_type)
11576 {
11577 case UndoCommand:
11578 case RedoCommand:
11579 {
11580 (void) XMagickCommand(display,resource_info,windows,command_type,
cristy051718b2011-08-28 22:49:25 +000011581 image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011582 break;
11583 }
11584 default:
11585 {
11586 /*
11587 Region of interest is relative to image configuration.
11588 */
11589 progress_monitor=SetImageProgressMonitor(*image,
11590 (MagickProgressMonitor) NULL,(*image)->client_data);
11591 crop_info=roi_info;
11592 width=(unsigned int) (*image)->columns;
11593 height=(unsigned int) (*image)->rows;
11594 x=0;
11595 y=0;
11596 if (windows->image.crop_geometry != (char *) NULL)
11597 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11598 &width,&height);
11599 scale_factor=(MagickRealType) width/windows->image.ximage->width;
11600 crop_info.x+=x;
cristy49e2d862010-11-12 02:50:30 +000011601 crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +000011602 crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11603 scale_factor=(MagickRealType)
11604 height/windows->image.ximage->height;
11605 crop_info.y+=y;
cristy49e2d862010-11-12 02:50:30 +000011606 crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +000011607 crop_info.height=(unsigned int)
11608 (scale_factor*crop_info.height+0.5);
cristy051718b2011-08-28 22:49:25 +000011609 roi_image=CropImage(*image,&crop_info,exception);
cristy3ed852e2009-09-05 21:47:34 +000011610 (void) SetImageProgressMonitor(*image,progress_monitor,
11611 (*image)->client_data);
11612 if (roi_image == (Image *) NULL)
11613 continue;
11614 /*
11615 Apply image processing technique to the region of interest.
11616 */
11617 windows->image.orphan=MagickTrue;
11618 (void) XMagickCommand(display,resource_info,windows,command_type,
cristy051718b2011-08-28 22:49:25 +000011619 &roi_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011620 progress_monitor=SetImageProgressMonitor(*image,
11621 (MagickProgressMonitor) NULL,(*image)->client_data);
11622 (void) XMagickCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000011623 SaveToUndoBufferCommand,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011624 windows->image.orphan=MagickFalse;
11625 (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11626 crop_info.x,crop_info.y);
11627 roi_image=DestroyImage(roi_image);
11628 (void) SetImageProgressMonitor(*image,progress_monitor,
11629 (*image)->client_data);
11630 break;
11631 }
11632 }
11633 if (command_type != InfoCommand)
11634 {
11635 XConfigureImageColormap(display,resource_info,windows,*image);
cristy051718b2011-08-28 22:49:25 +000011636 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011637 }
11638 XCheckRefreshWindows(display,windows);
11639 XInfoWidget(display,windows,text);
11640 (void) XSetFunction(display,windows->image.highlight_context,
11641 GXinvert);
11642 state&=(~UpdateRegionState);
11643 }
11644 XHighlightRectangle(display,windows->image.id,
11645 windows->image.highlight_context,&highlight_info);
11646 XScreenEvent(display,windows,&event);
11647 if (event.xany.window == windows->command.id)
11648 {
11649 /*
11650 Select a command from the Command widget.
11651 */
11652 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11653 command_type=NullCommand;
11654 id=XCommandWidget(display,windows,ApplyMenu,&event);
11655 if (id >= 0)
11656 {
11657 (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11658 command_type=ApplyCommands[id];
11659 if (id < ApplyMenus)
11660 {
11661 /*
11662 Select a command from a pop-up menu.
11663 */
11664 entry=XMenuWidget(display,windows,ApplyMenu[id],
11665 (const char **) Menus[id],command);
11666 if (entry >= 0)
11667 {
11668 (void) CopyMagickString(command,Menus[id][entry],
11669 MaxTextExtent);
11670 command_type=Commands[id][entry];
11671 }
11672 }
11673 }
11674 (void) XSetFunction(display,windows->image.highlight_context,
11675 GXinvert);
11676 XHighlightRectangle(display,windows->image.id,
11677 windows->image.highlight_context,&highlight_info);
11678 if (command_type == HelpCommand)
11679 {
11680 (void) XSetFunction(display,windows->image.highlight_context,
11681 GXcopy);
11682 XTextViewWidget(display,resource_info,windows,MagickFalse,
11683 "Help Viewer - Region of Interest",ImageROIHelp);
11684 (void) XSetFunction(display,windows->image.highlight_context,
11685 GXinvert);
11686 continue;
11687 }
11688 if (command_type == QuitCommand)
11689 {
11690 /*
11691 exit.
11692 */
11693 state|=EscapeState;
11694 state|=ExitState;
11695 continue;
11696 }
11697 if (command_type != NullCommand)
11698 state|=UpdateRegionState;
11699 continue;
11700 }
11701 XHighlightRectangle(display,windows->image.id,
11702 windows->image.highlight_context,&highlight_info);
11703 switch (event.type)
11704 {
11705 case ButtonPress:
11706 {
11707 x=windows->image.x;
11708 y=windows->image.y;
11709 if (event.xbutton.button != Button1)
11710 break;
11711 if (event.xbutton.window != windows->image.id)
11712 break;
11713 x=windows->image.x+event.xbutton.x;
11714 y=windows->image.y+event.xbutton.y;
11715 if ((x < (int) (roi_info.x+RoiDelta)) &&
11716 (x > (int) (roi_info.x-RoiDelta)) &&
11717 (y < (int) (roi_info.y+RoiDelta)) &&
11718 (y > (int) (roi_info.y-RoiDelta)))
11719 {
cristybb503372010-05-27 20:51:26 +000011720 roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11721 roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
cristy3ed852e2009-09-05 21:47:34 +000011722 state|=UpdateConfigurationState;
11723 break;
11724 }
11725 if ((x < (int) (roi_info.x+RoiDelta)) &&
11726 (x > (int) (roi_info.x-RoiDelta)) &&
11727 (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11728 (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11729 {
cristybb503372010-05-27 20:51:26 +000011730 roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
cristy3ed852e2009-09-05 21:47:34 +000011731 state|=UpdateConfigurationState;
11732 break;
11733 }
11734 if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11735 (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11736 (y < (int) (roi_info.y+RoiDelta)) &&
11737 (y > (int) (roi_info.y-RoiDelta)))
11738 {
cristybb503372010-05-27 20:51:26 +000011739 roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
cristy3ed852e2009-09-05 21:47:34 +000011740 state|=UpdateConfigurationState;
11741 break;
11742 }
11743 if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11744 (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11745 (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11746 (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11747 {
11748 state|=UpdateConfigurationState;
11749 break;
11750 }
11751 }
11752 case ButtonRelease:
11753 {
11754 if (event.xbutton.window == windows->pan.id)
11755 if ((highlight_info.x != crop_info.x-windows->image.x) ||
11756 (highlight_info.y != crop_info.y-windows->image.y))
11757 XHighlightRectangle(display,windows->image.id,
11758 windows->image.highlight_context,&highlight_info);
11759 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11760 event.xbutton.time);
11761 break;
11762 }
11763 case Expose:
11764 {
11765 if (event.xexpose.window == windows->image.id)
11766 if (event.xexpose.count == 0)
11767 {
11768 event.xexpose.x=(int) highlight_info.x;
11769 event.xexpose.y=(int) highlight_info.y;
11770 event.xexpose.width=(int) highlight_info.width;
11771 event.xexpose.height=(int) highlight_info.height;
11772 XRefreshWindow(display,&windows->image,&event);
11773 }
11774 if (event.xexpose.window == windows->info.id)
11775 if (event.xexpose.count == 0)
11776 XInfoWidget(display,windows,text);
11777 break;
11778 }
11779 case KeyPress:
11780 {
11781 KeySym
11782 key_symbol;
11783
11784 if (event.xkey.window != windows->image.id)
11785 break;
11786 /*
11787 Respond to a user key press.
11788 */
11789 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11790 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11791 switch ((int) key_symbol)
11792 {
11793 case XK_Shift_L:
11794 case XK_Shift_R:
11795 break;
11796 case XK_Escape:
11797 case XK_F20:
11798 state|=EscapeState;
11799 case XK_Return:
11800 {
11801 state|=ExitState;
11802 break;
11803 }
11804 case XK_Home:
11805 case XK_KP_Home:
11806 {
cristybb503372010-05-27 20:51:26 +000011807 roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
cristy49e2d862010-11-12 02:50:30 +000011808 roi_info.y=(ssize_t) (windows->image.height/2L-
11809 roi_info.height/2L);
cristy3ed852e2009-09-05 21:47:34 +000011810 break;
11811 }
11812 case XK_Left:
11813 case XK_KP_Left:
11814 {
11815 roi_info.x--;
11816 break;
11817 }
11818 case XK_Up:
11819 case XK_KP_Up:
11820 case XK_Next:
11821 {
11822 roi_info.y--;
11823 break;
11824 }
11825 case XK_Right:
11826 case XK_KP_Right:
11827 {
11828 roi_info.x++;
11829 break;
11830 }
11831 case XK_Prior:
11832 case XK_Down:
11833 case XK_KP_Down:
11834 {
11835 roi_info.y++;
11836 break;
11837 }
11838 case XK_F1:
11839 case XK_Help:
11840 {
11841 (void) XSetFunction(display,windows->image.highlight_context,
11842 GXcopy);
11843 XTextViewWidget(display,resource_info,windows,MagickFalse,
11844 "Help Viewer - Region of Interest",ImageROIHelp);
11845 (void) XSetFunction(display,windows->image.highlight_context,
11846 GXinvert);
11847 break;
11848 }
11849 default:
11850 {
11851 command_type=XImageWindowCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000011852 event.xkey.state,key_symbol,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011853 if (command_type != NullCommand)
11854 state|=UpdateRegionState;
11855 break;
11856 }
11857 }
11858 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11859 event.xkey.time);
11860 break;
11861 }
11862 case KeyRelease:
11863 break;
11864 case MotionNotify:
11865 {
11866 if (event.xbutton.window != windows->image.id)
11867 break;
11868 /*
11869 Map and unmap Info widget as text cursor crosses its boundaries.
11870 */
11871 x=event.xmotion.x;
11872 y=event.xmotion.y;
11873 if (windows->info.mapped != MagickFalse)
11874 {
11875 if ((x < (int) (windows->info.x+windows->info.width)) &&
11876 (y < (int) (windows->info.y+windows->info.height)))
11877 (void) XWithdrawWindow(display,windows->info.id,
11878 windows->info.screen);
11879 }
11880 else
11881 if ((x > (int) (windows->info.x+windows->info.width)) ||
11882 (y > (int) (windows->info.y+windows->info.height)))
11883 (void) XMapWindow(display,windows->info.id);
cristy49e2d862010-11-12 02:50:30 +000011884 roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11885 roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
cristy3ed852e2009-09-05 21:47:34 +000011886 break;
11887 }
11888 case SelectionRequest:
11889 {
11890 XSelectionEvent
11891 notify;
11892
11893 XSelectionRequestEvent
11894 *request;
11895
11896 /*
11897 Set primary selection.
11898 */
cristyb51dff52011-05-19 16:55:47 +000011899 (void) FormatLocaleString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +000011900 "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +000011901 roi_info.height,(double) roi_info.x,(double) roi_info.y);
cristy3ed852e2009-09-05 21:47:34 +000011902 request=(&(event.xselectionrequest));
11903 (void) XChangeProperty(request->display,request->requestor,
11904 request->property,request->target,8,PropModeReplace,
11905 (unsigned char *) text,(int) strlen(text));
11906 notify.type=SelectionNotify;
11907 notify.display=request->display;
11908 notify.requestor=request->requestor;
11909 notify.selection=request->selection;
11910 notify.target=request->target;
11911 notify.time=request->time;
11912 if (request->property == None)
11913 notify.property=request->target;
11914 else
11915 notify.property=request->property;
11916 (void) XSendEvent(request->display,request->requestor,False,0,
11917 (XEvent *) &notify);
11918 }
11919 default:
11920 break;
11921 }
11922 if ((state & UpdateConfigurationState) != 0)
11923 {
11924 (void) XPutBackEvent(display,&event);
11925 (void) XCheckDefineCursor(display,windows->image.id,cursor);
11926 break;
11927 }
11928 } while ((state & ExitState) == 0);
11929 } while ((state & ExitState) == 0);
11930 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11931 XSetCursorState(display,windows,MagickFalse);
11932 if ((state & EscapeState) != 0)
11933 return(MagickTrue);
11934 return(MagickTrue);
11935}
11936
11937/*
11938%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11939% %
11940% %
11941% %
11942+ X R o t a t e I m a g e %
11943% %
11944% %
11945% %
11946%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11947%
11948% XRotateImage() rotates the X image. If the degrees parameter if zero, the
11949% rotation angle is computed from the slope of a line drawn by the user.
11950%
11951% The format of the XRotateImage method is:
11952%
11953% MagickBooleanType XRotateImage(Display *display,
11954% XResourceInfo *resource_info,XWindows *windows,double degrees,
cristy051718b2011-08-28 22:49:25 +000011955% Image **image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011956%
11957% A description of each parameter follows:
11958%
11959% o display: Specifies a connection to an X server; returned from
11960% XOpenDisplay.
11961%
11962% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11963%
11964% o windows: Specifies a pointer to a XWindows structure.
11965%
11966% o degrees: Specifies the number of degrees to rotate the image.
11967%
11968% o image: the image.
11969%
cristy051718b2011-08-28 22:49:25 +000011970% o exception: return any errors or warnings in this structure.
11971%
cristy3ed852e2009-09-05 21:47:34 +000011972*/
11973static MagickBooleanType XRotateImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000011974 XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
11975 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011976{
11977 static const char
11978 *RotateMenu[] =
11979 {
11980 "Pixel Color",
11981 "Direction",
11982 "Help",
11983 "Dismiss",
11984 (char *) NULL
11985 };
11986
11987 static ModeType
11988 direction = HorizontalRotateCommand;
11989
11990 static const ModeType
11991 DirectionCommands[] =
11992 {
11993 HorizontalRotateCommand,
11994 VerticalRotateCommand
11995 },
11996 RotateCommands[] =
11997 {
11998 RotateColorCommand,
11999 RotateDirectionCommand,
12000 RotateHelpCommand,
12001 RotateDismissCommand
12002 };
12003
12004 static unsigned int
12005 pen_id = 0;
12006
12007 char
12008 command[MaxTextExtent],
12009 text[MaxTextExtent];
12010
12011 Image
12012 *rotate_image;
12013
12014 int
12015 id,
12016 x,
12017 y;
12018
12019 MagickRealType
12020 normalized_degrees;
12021
12022 register int
12023 i;
12024
12025 unsigned int
12026 height,
12027 rotations,
12028 width;
12029
12030 if (degrees == 0.0)
12031 {
12032 unsigned int
12033 distance;
12034
cristybb503372010-05-27 20:51:26 +000012035 size_t
cristy3ed852e2009-09-05 21:47:34 +000012036 state;
12037
12038 XEvent
12039 event;
12040
12041 XSegment
12042 rotate_info;
12043
12044 /*
12045 Map Command widget.
12046 */
12047 (void) CloneString(&windows->command.name,"Rotate");
12048 windows->command.data=2;
12049 (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12050 (void) XMapRaised(display,windows->command.id);
12051 XClientMessage(display,windows->image.id,windows->im_protocols,
12052 windows->im_update_widget,CurrentTime);
12053 /*
12054 Wait for first button press.
12055 */
12056 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12057 XQueryPosition(display,windows->image.id,&x,&y);
12058 rotate_info.x1=x;
12059 rotate_info.y1=y;
12060 rotate_info.x2=x;
12061 rotate_info.y2=y;
12062 state=DefaultState;
12063 do
12064 {
12065 XHighlightLine(display,windows->image.id,
12066 windows->image.highlight_context,&rotate_info);
12067 /*
12068 Wait for next event.
12069 */
12070 XScreenEvent(display,windows,&event);
12071 XHighlightLine(display,windows->image.id,
12072 windows->image.highlight_context,&rotate_info);
12073 if (event.xany.window == windows->command.id)
12074 {
12075 /*
12076 Select a command from the Command widget.
12077 */
12078 id=XCommandWidget(display,windows,RotateMenu,&event);
12079 if (id < 0)
12080 continue;
12081 (void) XSetFunction(display,windows->image.highlight_context,
12082 GXcopy);
12083 switch (RotateCommands[id])
12084 {
12085 case RotateColorCommand:
12086 {
12087 const char
12088 *ColorMenu[MaxNumberPens];
12089
12090 int
12091 pen_number;
12092
12093 XColor
12094 color;
12095
12096 /*
12097 Initialize menu selections.
12098 */
12099 for (i=0; i < (int) (MaxNumberPens-2); i++)
12100 ColorMenu[i]=resource_info->pen_colors[i];
12101 ColorMenu[MaxNumberPens-2]="Browser...";
12102 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12103 /*
12104 Select a pen color from the pop-up menu.
12105 */
12106 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12107 (const char **) ColorMenu,command);
12108 if (pen_number < 0)
12109 break;
12110 if (pen_number == (MaxNumberPens-2))
12111 {
12112 static char
12113 color_name[MaxTextExtent] = "gray";
12114
12115 /*
12116 Select a pen color from a dialog.
12117 */
12118 resource_info->pen_colors[pen_number]=color_name;
12119 XColorBrowserWidget(display,windows,"Select",color_name);
12120 if (*color_name == '\0')
12121 break;
12122 }
12123 /*
12124 Set pen color.
12125 */
12126 (void) XParseColor(display,windows->map_info->colormap,
12127 resource_info->pen_colors[pen_number],&color);
12128 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12129 (unsigned int) MaxColors,&color);
12130 windows->pixel_info->pen_colors[pen_number]=color;
12131 pen_id=(unsigned int) pen_number;
12132 break;
12133 }
12134 case RotateDirectionCommand:
12135 {
12136 static const char
12137 *Directions[] =
12138 {
12139 "horizontal",
12140 "vertical",
12141 (char *) NULL,
12142 };
12143
12144 /*
12145 Select a command from the pop-up menu.
12146 */
12147 id=XMenuWidget(display,windows,RotateMenu[id],
12148 Directions,command);
12149 if (id >= 0)
12150 direction=DirectionCommands[id];
12151 break;
12152 }
12153 case RotateHelpCommand:
12154 {
12155 XTextViewWidget(display,resource_info,windows,MagickFalse,
12156 "Help Viewer - Image Rotation",ImageRotateHelp);
12157 break;
12158 }
12159 case RotateDismissCommand:
12160 {
12161 /*
12162 Prematurely exit.
12163 */
12164 state|=EscapeState;
12165 state|=ExitState;
12166 break;
12167 }
12168 default:
12169 break;
12170 }
12171 (void) XSetFunction(display,windows->image.highlight_context,
12172 GXinvert);
12173 continue;
12174 }
12175 switch (event.type)
12176 {
12177 case ButtonPress:
12178 {
12179 if (event.xbutton.button != Button1)
12180 break;
12181 if (event.xbutton.window != windows->image.id)
12182 break;
12183 /*
12184 exit loop.
12185 */
12186 (void) XSetFunction(display,windows->image.highlight_context,
12187 GXcopy);
12188 rotate_info.x1=event.xbutton.x;
12189 rotate_info.y1=event.xbutton.y;
12190 state|=ExitState;
12191 break;
12192 }
12193 case ButtonRelease:
12194 break;
12195 case Expose:
12196 break;
12197 case KeyPress:
12198 {
12199 char
12200 command[MaxTextExtent];
12201
12202 KeySym
12203 key_symbol;
12204
12205 if (event.xkey.window != windows->image.id)
12206 break;
12207 /*
12208 Respond to a user key press.
12209 */
12210 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12211 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12212 switch ((int) key_symbol)
12213 {
12214 case XK_Escape:
12215 case XK_F20:
12216 {
12217 /*
12218 Prematurely exit.
12219 */
12220 state|=EscapeState;
12221 state|=ExitState;
12222 break;
12223 }
12224 case XK_F1:
12225 case XK_Help:
12226 {
12227 (void) XSetFunction(display,windows->image.highlight_context,
12228 GXcopy);
12229 XTextViewWidget(display,resource_info,windows,MagickFalse,
12230 "Help Viewer - Image Rotation",ImageRotateHelp);
12231 (void) XSetFunction(display,windows->image.highlight_context,
12232 GXinvert);
12233 break;
12234 }
12235 default:
12236 {
12237 (void) XBell(display,0);
12238 break;
12239 }
12240 }
12241 break;
12242 }
12243 case MotionNotify:
12244 {
12245 rotate_info.x1=event.xmotion.x;
12246 rotate_info.y1=event.xmotion.y;
12247 }
12248 }
12249 rotate_info.x2=rotate_info.x1;
12250 rotate_info.y2=rotate_info.y1;
12251 if (direction == HorizontalRotateCommand)
12252 rotate_info.x2+=32;
12253 else
12254 rotate_info.y2-=32;
12255 } while ((state & ExitState) == 0);
12256 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12257 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12258 if ((state & EscapeState) != 0)
12259 return(MagickTrue);
12260 /*
12261 Draw line as pointer moves until the mouse button is released.
12262 */
12263 distance=0;
12264 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12265 state=DefaultState;
12266 do
12267 {
12268 if (distance > 9)
12269 {
12270 /*
12271 Display info and draw rotation line.
12272 */
12273 if (windows->info.mapped == MagickFalse)
12274 (void) XMapWindow(display,windows->info.id);
cristyb51dff52011-05-19 16:55:47 +000012275 (void) FormatLocaleString(text,MaxTextExtent," %g",
cristy3ed852e2009-09-05 21:47:34 +000012276 direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12277 XInfoWidget(display,windows,text);
12278 XHighlightLine(display,windows->image.id,
12279 windows->image.highlight_context,&rotate_info);
12280 }
12281 else
12282 if (windows->info.mapped != MagickFalse)
12283 (void) XWithdrawWindow(display,windows->info.id,
12284 windows->info.screen);
12285 /*
12286 Wait for next event.
12287 */
12288 XScreenEvent(display,windows,&event);
12289 if (distance > 9)
12290 XHighlightLine(display,windows->image.id,
12291 windows->image.highlight_context,&rotate_info);
12292 switch (event.type)
12293 {
12294 case ButtonPress:
12295 break;
12296 case ButtonRelease:
12297 {
12298 /*
12299 User has committed to rotation line.
12300 */
12301 rotate_info.x2=event.xbutton.x;
12302 rotate_info.y2=event.xbutton.y;
12303 state|=ExitState;
12304 break;
12305 }
12306 case Expose:
12307 break;
12308 case MotionNotify:
12309 {
12310 rotate_info.x2=event.xmotion.x;
12311 rotate_info.y2=event.xmotion.y;
12312 }
12313 default:
12314 break;
12315 }
12316 /*
12317 Check boundary conditions.
12318 */
12319 if (rotate_info.x2 < 0)
12320 rotate_info.x2=0;
12321 else
12322 if (rotate_info.x2 > (int) windows->image.width)
12323 rotate_info.x2=(short) windows->image.width;
12324 if (rotate_info.y2 < 0)
12325 rotate_info.y2=0;
12326 else
12327 if (rotate_info.y2 > (int) windows->image.height)
12328 rotate_info.y2=(short) windows->image.height;
12329 /*
12330 Compute rotation angle from the slope of the line.
12331 */
12332 degrees=0.0;
12333 distance=(unsigned int)
12334 ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12335 ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12336 if (distance > 9)
12337 degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12338 rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12339 } while ((state & ExitState) == 0);
12340 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12341 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12342 if (distance <= 9)
12343 return(MagickTrue);
12344 }
12345 if (direction == VerticalRotateCommand)
12346 degrees-=90.0;
12347 if (degrees == 0.0)
12348 return(MagickTrue);
12349 /*
12350 Rotate image.
12351 */
12352 normalized_degrees=degrees;
12353 while (normalized_degrees < -45.0)
12354 normalized_degrees+=360.0;
12355 for (rotations=0; normalized_degrees > 45.0; rotations++)
12356 normalized_degrees-=90.0;
12357 if (normalized_degrees != 0.0)
cristy051718b2011-08-28 22:49:25 +000012358 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12359 exception);
cristy3ed852e2009-09-05 21:47:34 +000012360 XSetCursorState(display,windows,MagickTrue);
12361 XCheckRefreshWindows(display,windows);
12362 (*image)->background_color.red=ScaleShortToQuantum(
12363 windows->pixel_info->pen_colors[pen_id].red);
12364 (*image)->background_color.green=ScaleShortToQuantum(
12365 windows->pixel_info->pen_colors[pen_id].green);
12366 (*image)->background_color.blue=ScaleShortToQuantum(
12367 windows->pixel_info->pen_colors[pen_id].blue);
cristy051718b2011-08-28 22:49:25 +000012368 rotate_image=RotateImage(*image,degrees,exception);
cristy3ed852e2009-09-05 21:47:34 +000012369 XSetCursorState(display,windows,MagickFalse);
12370 if (rotate_image == (Image *) NULL)
12371 return(MagickFalse);
12372 *image=DestroyImage(*image);
12373 *image=rotate_image;
12374 if (windows->image.crop_geometry != (char *) NULL)
12375 {
12376 /*
12377 Rotate crop geometry.
12378 */
12379 width=(unsigned int) (*image)->columns;
12380 height=(unsigned int) (*image)->rows;
12381 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12382 switch (rotations % 4)
12383 {
12384 default:
12385 case 0:
12386 break;
12387 case 1:
12388 {
12389 /*
12390 Rotate 90 degrees.
12391 */
cristyb51dff52011-05-19 16:55:47 +000012392 (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000012393 "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12394 (int) height-y,x);
12395 break;
12396 }
12397 case 2:
12398 {
12399 /*
12400 Rotate 180 degrees.
12401 */
cristyb51dff52011-05-19 16:55:47 +000012402 (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000012403 "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12404 break;
12405 }
12406 case 3:
12407 {
12408 /*
12409 Rotate 270 degrees.
12410 */
cristyb51dff52011-05-19 16:55:47 +000012411 (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000012412 "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12413 break;
12414 }
12415 }
12416 }
12417 if (windows->image.orphan != MagickFalse)
12418 return(MagickTrue);
12419 if (normalized_degrees != 0.0)
12420 {
12421 /*
12422 Update image colormap.
12423 */
12424 windows->image.window_changes.width=(int) (*image)->columns;
12425 windows->image.window_changes.height=(int) (*image)->rows;
12426 if (windows->image.crop_geometry != (char *) NULL)
12427 {
12428 /*
12429 Obtain dimensions of image from crop geometry.
12430 */
12431 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12432 &width,&height);
12433 windows->image.window_changes.width=(int) width;
12434 windows->image.window_changes.height=(int) height;
12435 }
12436 XConfigureImageColormap(display,resource_info,windows,*image);
12437 }
12438 else
12439 if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12440 {
12441 windows->image.window_changes.width=windows->image.ximage->height;
12442 windows->image.window_changes.height=windows->image.ximage->width;
12443 }
12444 /*
12445 Update image configuration.
12446 */
cristy051718b2011-08-28 22:49:25 +000012447 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012448 return(MagickTrue);
12449}
12450
12451/*
12452%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12453% %
12454% %
12455% %
12456+ X S a v e I m a g e %
12457% %
12458% %
12459% %
12460%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12461%
12462% XSaveImage() saves an image to a file.
12463%
12464% The format of the XSaveImage method is:
12465%
12466% MagickBooleanType XSaveImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000012467% XResourceInfo *resource_info,XWindows *windows,Image *image,
12468% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012469%
12470% A description of each parameter follows:
12471%
12472% o display: Specifies a connection to an X server; returned from
12473% XOpenDisplay.
12474%
12475% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12476%
12477% o windows: Specifies a pointer to a XWindows structure.
12478%
12479% o image: the image.
12480%
cristy051718b2011-08-28 22:49:25 +000012481% o exception: return any errors or warnings in this structure.
12482%
cristy3ed852e2009-09-05 21:47:34 +000012483*/
12484static MagickBooleanType XSaveImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000012485 XResourceInfo *resource_info,XWindows *windows,Image *image,
12486 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012487{
12488 char
12489 filename[MaxTextExtent],
12490 geometry[MaxTextExtent];
12491
12492 Image
12493 *save_image;
12494
12495 ImageInfo
12496 *image_info;
12497
12498 MagickStatusType
12499 status;
12500
12501 /*
12502 Request file name from user.
12503 */
12504 if (resource_info->write_filename != (char *) NULL)
12505 (void) CopyMagickString(filename,resource_info->write_filename,
12506 MaxTextExtent);
12507 else
12508 {
12509 char
12510 path[MaxTextExtent];
12511
12512 int
12513 status;
12514
12515 GetPathComponent(image->filename,HeadPath,path);
12516 GetPathComponent(image->filename,TailPath,filename);
cristy0da1d642011-08-29 16:53:16 +000012517 if (*path != '\0')
12518 {
12519 status=chdir(path);
12520 if (status == -1)
12521 (void) ThrowMagickException(exception,GetMagickModule(),
12522 FileOpenError,"UnableToOpenFile","%s",path);
12523 }
cristy3ed852e2009-09-05 21:47:34 +000012524 }
12525 XFileBrowserWidget(display,windows,"Save",filename);
12526 if (*filename == '\0')
12527 return(MagickTrue);
12528 if (IsPathAccessible(filename) != MagickFalse)
12529 {
12530 int
12531 status;
12532
12533 /*
12534 File exists-- seek user's permission before overwriting.
12535 */
12536 status=XConfirmWidget(display,windows,"Overwrite",filename);
12537 if (status <= 0)
12538 return(MagickTrue);
12539 }
12540 image_info=CloneImageInfo(resource_info->image_info);
12541 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +000012542 (void) SetImageInfo(image_info,1,exception);
cristy3ed852e2009-09-05 21:47:34 +000012543 if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12544 (LocaleCompare(image_info->magick,"JPG") == 0))
12545 {
12546 char
12547 quality[MaxTextExtent];
12548
12549 int
12550 status;
12551
12552 /*
12553 Request JPEG quality from user.
12554 */
cristyb51dff52011-05-19 16:55:47 +000012555 (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +000012556 image->quality);
cristy3ed852e2009-09-05 21:47:34 +000012557 status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12558 quality);
12559 if (*quality == '\0')
12560 return(MagickTrue);
cristye27293e2009-12-18 02:53:20 +000012561 image->quality=StringToUnsignedLong(quality);
cristy3ed852e2009-09-05 21:47:34 +000012562 image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12563 }
12564 if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12565 (LocaleCompare(image_info->magick,"PDF") == 0) ||
12566 (LocaleCompare(image_info->magick,"PS") == 0) ||
12567 (LocaleCompare(image_info->magick,"PS2") == 0))
12568 {
12569 char
12570 geometry[MaxTextExtent];
12571
12572 /*
12573 Request page geometry from user.
12574 */
cristyb93d9e72009-09-12 20:02:21 +000012575 (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +000012576 if (LocaleCompare(image_info->magick,"PDF") == 0)
cristyb93d9e72009-09-12 20:02:21 +000012577 (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +000012578 if (image_info->page != (char *) NULL)
12579 (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12580 XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12581 "Select page geometry:",geometry);
12582 if (*geometry != '\0')
12583 image_info->page=GetPageGeometry(geometry);
12584 }
12585 /*
12586 Apply image transforms.
12587 */
12588 XSetCursorState(display,windows,MagickTrue);
12589 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +000012590 save_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000012591 if (save_image == (Image *) NULL)
12592 return(MagickFalse);
cristyb51dff52011-05-19 16:55:47 +000012593 (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
cristy3ed852e2009-09-05 21:47:34 +000012594 windows->image.ximage->width,windows->image.ximage->height);
12595 (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12596 /*
12597 Write image.
12598 */
12599 (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +000012600 status=WriteImage(image_info,save_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012601 if (status != MagickFalse)
12602 image->taint=MagickFalse;
12603 save_image=DestroyImage(save_image);
12604 image_info=DestroyImageInfo(image_info);
12605 XSetCursorState(display,windows,MagickFalse);
12606 return(status != 0 ? MagickTrue : MagickFalse);
12607}
12608
12609/*
12610%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12611% %
12612% %
12613% %
12614+ X S c r e e n E v e n t %
12615% %
12616% %
12617% %
12618%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12619%
12620% XScreenEvent() handles global events associated with the Pan and Magnify
12621% windows.
12622%
12623% The format of the XScreenEvent function is:
12624%
12625% void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12626%
12627% A description of each parameter follows:
12628%
12629% o display: Specifies a pointer to the Display structure; returned from
12630% XOpenDisplay.
12631%
12632% o windows: Specifies a pointer to a XWindows structure.
12633%
12634% o event: Specifies a pointer to a X11 XEvent structure.
12635%
12636%
12637*/
12638
12639#if defined(__cplusplus) || defined(c_plusplus)
12640extern "C" {
12641#endif
12642
12643static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12644{
12645 register XWindows
12646 *windows;
12647
12648 windows=(XWindows *) data;
12649 if ((event->type == ClientMessage) &&
12650 (event->xclient.window == windows->image.id))
12651 return(MagickFalse);
12652 return(MagickTrue);
12653}
12654
12655#if defined(__cplusplus) || defined(c_plusplus)
12656}
12657#endif
12658
12659static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12660{
12661 register int
12662 x,
12663 y;
12664
12665 (void) XIfEvent(display,event,XPredicate,(char *) windows);
12666 if (event->xany.window == windows->command.id)
12667 return;
12668 switch (event->type)
12669 {
12670 case ButtonPress:
12671 case ButtonRelease:
12672 {
12673 if ((event->xbutton.button == Button3) &&
12674 (event->xbutton.state & Mod1Mask))
12675 {
12676 /*
12677 Convert Alt-Button3 to Button2.
12678 */
12679 event->xbutton.button=Button2;
12680 event->xbutton.state&=(~Mod1Mask);
12681 }
12682 if (event->xbutton.window == windows->backdrop.id)
12683 {
12684 (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12685 event->xbutton.time);
12686 break;
12687 }
12688 if (event->xbutton.window == windows->pan.id)
12689 {
12690 XPanImage(display,windows,event);
12691 break;
12692 }
12693 if (event->xbutton.window == windows->image.id)
12694 if (event->xbutton.button == Button2)
12695 {
12696 /*
12697 Update magnified image.
12698 */
12699 x=event->xbutton.x;
12700 y=event->xbutton.y;
12701 if (x < 0)
12702 x=0;
12703 else
12704 if (x >= (int) windows->image.width)
12705 x=(int) (windows->image.width-1);
cristy49e2d862010-11-12 02:50:30 +000012706 windows->magnify.x=(int) windows->image.x+x;
cristy3ed852e2009-09-05 21:47:34 +000012707 if (y < 0)
12708 y=0;
12709 else
12710 if (y >= (int) windows->image.height)
12711 y=(int) (windows->image.height-1);
12712 windows->magnify.y=windows->image.y+y;
12713 if (windows->magnify.mapped == MagickFalse)
12714 (void) XMapRaised(display,windows->magnify.id);
12715 XMakeMagnifyImage(display,windows);
12716 if (event->type == ButtonRelease)
12717 (void) XWithdrawWindow(display,windows->info.id,
12718 windows->info.screen);
12719 break;
12720 }
12721 break;
12722 }
12723 case ClientMessage:
12724 {
12725 /*
12726 If client window delete message, exit.
12727 */
12728 if (event->xclient.message_type != windows->wm_protocols)
12729 break;
cristyecd0ab52010-05-30 14:59:20 +000012730 if (*event->xclient.data.l != (long) windows->wm_delete_window)
cristy3ed852e2009-09-05 21:47:34 +000012731 break;
12732 if (event->xclient.window == windows->magnify.id)
12733 {
12734 (void) XWithdrawWindow(display,windows->magnify.id,
12735 windows->magnify.screen);
12736 break;
12737 }
12738 break;
12739 }
12740 case ConfigureNotify:
12741 {
12742 if (event->xconfigure.window == windows->magnify.id)
12743 {
12744 unsigned int
12745 magnify;
12746
12747 /*
12748 Magnify window has a new configuration.
12749 */
12750 windows->magnify.width=(unsigned int) event->xconfigure.width;
12751 windows->magnify.height=(unsigned int) event->xconfigure.height;
12752 if (windows->magnify.mapped == MagickFalse)
12753 break;
12754 magnify=1;
12755 while ((int) magnify <= event->xconfigure.width)
12756 magnify<<=1;
12757 while ((int) magnify <= event->xconfigure.height)
12758 magnify<<=1;
12759 magnify>>=1;
12760 if (((int) magnify != event->xconfigure.width) ||
12761 ((int) magnify != event->xconfigure.height))
12762 {
12763 XWindowChanges
12764 window_changes;
12765
12766 window_changes.width=(int) magnify;
12767 window_changes.height=(int) magnify;
12768 (void) XReconfigureWMWindow(display,windows->magnify.id,
12769 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12770 &window_changes);
12771 break;
12772 }
12773 XMakeMagnifyImage(display,windows);
12774 break;
12775 }
12776 break;
12777 }
12778 case Expose:
12779 {
12780 if (event->xexpose.window == windows->image.id)
12781 {
12782 XRefreshWindow(display,&windows->image,event);
12783 break;
12784 }
12785 if (event->xexpose.window == windows->pan.id)
12786 if (event->xexpose.count == 0)
12787 {
12788 XDrawPanRectangle(display,windows);
12789 break;
12790 }
12791 if (event->xexpose.window == windows->magnify.id)
12792 if (event->xexpose.count == 0)
12793 {
12794 XMakeMagnifyImage(display,windows);
12795 break;
12796 }
12797 break;
12798 }
12799 case KeyPress:
12800 {
12801 char
12802 command[MaxTextExtent];
12803
12804 KeySym
12805 key_symbol;
12806
12807 if (event->xkey.window != windows->magnify.id)
12808 break;
12809 /*
12810 Respond to a user key press.
12811 */
12812 (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12813 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12814 XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12815 break;
12816 }
12817 case MapNotify:
12818 {
12819 if (event->xmap.window == windows->magnify.id)
12820 {
12821 windows->magnify.mapped=MagickTrue;
12822 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12823 break;
12824 }
12825 if (event->xmap.window == windows->info.id)
12826 {
12827 windows->info.mapped=MagickTrue;
12828 break;
12829 }
12830 break;
12831 }
12832 case MotionNotify:
12833 {
12834 while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12835 if (event->xmotion.window == windows->image.id)
12836 if (windows->magnify.mapped != MagickFalse)
12837 {
12838 /*
12839 Update magnified image.
12840 */
12841 x=event->xmotion.x;
12842 y=event->xmotion.y;
12843 if (x < 0)
12844 x=0;
12845 else
12846 if (x >= (int) windows->image.width)
12847 x=(int) (windows->image.width-1);
cristy49e2d862010-11-12 02:50:30 +000012848 windows->magnify.x=(int) windows->image.x+x;
cristy3ed852e2009-09-05 21:47:34 +000012849 if (y < 0)
12850 y=0;
12851 else
12852 if (y >= (int) windows->image.height)
12853 y=(int) (windows->image.height-1);
12854 windows->magnify.y=windows->image.y+y;
12855 XMakeMagnifyImage(display,windows);
12856 }
12857 break;
12858 }
12859 case UnmapNotify:
12860 {
12861 if (event->xunmap.window == windows->magnify.id)
12862 {
12863 windows->magnify.mapped=MagickFalse;
12864 break;
12865 }
12866 if (event->xunmap.window == windows->info.id)
12867 {
12868 windows->info.mapped=MagickFalse;
12869 break;
12870 }
12871 break;
12872 }
12873 default:
12874 break;
12875 }
12876}
12877
12878/*
12879%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12880% %
12881% %
12882% %
12883+ X S e t C r o p G e o m e t r y %
12884% %
12885% %
12886% %
12887%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12888%
12889% XSetCropGeometry() accepts a cropping geometry relative to the Image window
12890% and translates it to a cropping geometry relative to the image.
12891%
12892% The format of the XSetCropGeometry method is:
12893%
12894% void XSetCropGeometry(Display *display,XWindows *windows,
12895% RectangleInfo *crop_info,Image *image)
12896%
12897% A description of each parameter follows:
12898%
12899% o display: Specifies a connection to an X server; returned from
12900% XOpenDisplay.
12901%
12902% o windows: Specifies a pointer to a XWindows structure.
12903%
12904% o crop_info: A pointer to a RectangleInfo that defines a region of the
12905% Image window to crop.
12906%
12907% o image: the image.
12908%
12909*/
12910static void XSetCropGeometry(Display *display,XWindows *windows,
12911 RectangleInfo *crop_info,Image *image)
12912{
12913 char
12914 text[MaxTextExtent];
12915
12916 int
12917 x,
12918 y;
12919
12920 MagickRealType
12921 scale_factor;
12922
12923 unsigned int
12924 height,
12925 width;
12926
12927 if (windows->info.mapped != MagickFalse)
12928 {
12929 /*
12930 Display info on cropping rectangle.
12931 */
cristyb51dff52011-05-19 16:55:47 +000012932 (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
cristye8c25f92010-06-03 00:53:06 +000012933 (double) crop_info->width,(double) crop_info->height,(double)
12934 crop_info->x,(double) crop_info->y);
cristy3ed852e2009-09-05 21:47:34 +000012935 XInfoWidget(display,windows,text);
12936 }
12937 /*
12938 Cropping geometry is relative to any previous crop geometry.
12939 */
12940 x=0;
12941 y=0;
12942 width=(unsigned int) image->columns;
12943 height=(unsigned int) image->rows;
12944 if (windows->image.crop_geometry != (char *) NULL)
12945 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12946 else
12947 windows->image.crop_geometry=AcquireString((char *) NULL);
12948 /*
12949 Define the crop geometry string from the cropping rectangle.
12950 */
12951 scale_factor=(MagickRealType) width/windows->image.ximage->width;
12952 if (crop_info->x > 0)
12953 x+=(int) (scale_factor*crop_info->x+0.5);
12954 width=(unsigned int) (scale_factor*crop_info->width+0.5);
12955 if (width == 0)
12956 width=1;
12957 scale_factor=(MagickRealType) height/windows->image.ximage->height;
12958 if (crop_info->y > 0)
12959 y+=(int) (scale_factor*crop_info->y+0.5);
12960 height=(unsigned int) (scale_factor*crop_info->height+0.5);
12961 if (height == 0)
12962 height=1;
cristyb51dff52011-05-19 16:55:47 +000012963 (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000012964 "%ux%u%+d%+d",width,height,x,y);
12965}
12966
12967/*
12968%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12969% %
12970% %
12971% %
12972+ X T i l e I m a g e %
12973% %
12974% %
12975% %
12976%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12977%
12978% XTileImage() loads or deletes a selected tile from a visual image directory.
12979% The load or delete command is chosen from a menu.
12980%
12981% The format of the XTileImage method is:
12982%
12983% Image *XTileImage(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +000012984% XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012985%
12986% A description of each parameter follows:
12987%
12988% o tile_image: XTileImage reads or deletes the tile image
12989% and returns it. A null image is returned if an error occurs.
12990%
12991% o display: Specifies a connection to an X server; returned from
12992% XOpenDisplay.
12993%
12994% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12995%
12996% o windows: Specifies a pointer to a XWindows structure.
12997%
12998% o image: the image; returned from ReadImage.
12999%
13000% o event: Specifies a pointer to a XEvent structure. If it is NULL,
13001% the entire image is refreshed.
13002%
cristy051718b2011-08-28 22:49:25 +000013003% o exception: return any errors or warnings in this structure.
13004%
cristy3ed852e2009-09-05 21:47:34 +000013005*/
13006static Image *XTileImage(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +000013007 XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000013008{
13009 static const char
13010 *VerbMenu[] =
13011 {
13012 "Load",
13013 "Next",
13014 "Former",
13015 "Delete",
13016 "Update",
13017 (char *) NULL,
13018 };
13019
13020 static const ModeType
13021 TileCommands[] =
13022 {
13023 TileLoadCommand,
13024 TileNextCommand,
13025 TileFormerCommand,
13026 TileDeleteCommand,
13027 TileUpdateCommand
13028 };
13029
13030 char
13031 command[MaxTextExtent],
13032 filename[MaxTextExtent];
13033
13034 Image
13035 *tile_image;
13036
13037 int
13038 id,
13039 status,
13040 tile,
13041 x,
13042 y;
13043
13044 MagickRealType
13045 scale_factor;
13046
13047 register char
13048 *p,
13049 *q;
13050
13051 register int
13052 i;
13053
13054 unsigned int
13055 height,
13056 width;
13057
13058 /*
13059 Tile image is relative to montage image configuration.
13060 */
13061 x=0;
13062 y=0;
13063 width=(unsigned int) image->columns;
13064 height=(unsigned int) image->rows;
13065 if (windows->image.crop_geometry != (char *) NULL)
13066 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13067 scale_factor=(MagickRealType) width/windows->image.ximage->width;
13068 event->xbutton.x+=windows->image.x;
13069 event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13070 scale_factor=(MagickRealType) height/windows->image.ximage->height;
13071 event->xbutton.y+=windows->image.y;
13072 event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13073 /*
13074 Determine size and location of each tile in the visual image directory.
13075 */
13076 width=(unsigned int) image->columns;
13077 height=(unsigned int) image->rows;
13078 x=0;
13079 y=0;
13080 (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13081 tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13082 (event->xbutton.x-x)/width;
13083 if (tile < 0)
13084 {
13085 /*
13086 Button press is outside any tile.
13087 */
13088 (void) XBell(display,0);
13089 return((Image *) NULL);
13090 }
13091 /*
13092 Determine file name from the tile directory.
13093 */
13094 p=image->directory;
13095 for (i=tile; (i != 0) && (*p != '\0'); )
13096 {
13097 if (*p == '\n')
13098 i--;
13099 p++;
13100 }
13101 if (*p == '\0')
13102 {
13103 /*
13104 Button press is outside any tile.
13105 */
13106 (void) XBell(display,0);
13107 return((Image *) NULL);
13108 }
13109 /*
13110 Select a command from the pop-up menu.
13111 */
13112 id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13113 if (id < 0)
13114 return((Image *) NULL);
13115 q=p;
13116 while ((*q != '\n') && (*q != '\0'))
13117 q++;
13118 (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13119 /*
13120 Perform command for the selected tile.
13121 */
13122 XSetCursorState(display,windows,MagickTrue);
13123 XCheckRefreshWindows(display,windows);
13124 tile_image=NewImageList();
13125 switch (TileCommands[id])
13126 {
13127 case TileLoadCommand:
13128 {
13129 /*
13130 Load tile image.
13131 */
13132 XCheckRefreshWindows(display,windows);
13133 (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13134 MaxTextExtent);
13135 (void) CopyMagickString(resource_info->image_info->filename,filename,
13136 MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +000013137 tile_image=ReadImage(resource_info->image_info,exception);
13138 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +000013139 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13140 break;
13141 }
13142 case TileNextCommand:
13143 {
13144 /*
13145 Display next image.
13146 */
13147 XClientMessage(display,windows->image.id,windows->im_protocols,
13148 windows->im_next_image,CurrentTime);
13149 break;
13150 }
13151 case TileFormerCommand:
13152 {
13153 /*
13154 Display former image.
13155 */
13156 XClientMessage(display,windows->image.id,windows->im_protocols,
13157 windows->im_former_image,CurrentTime);
13158 break;
13159 }
13160 case TileDeleteCommand:
13161 {
13162 /*
13163 Delete tile image.
13164 */
13165 if (IsPathAccessible(filename) == MagickFalse)
13166 {
13167 XNoticeWidget(display,windows,"Image file does not exist:",filename);
13168 break;
13169 }
13170 status=XConfirmWidget(display,windows,"Really delete tile",filename);
13171 if (status <= 0)
13172 break;
13173 status=remove(filename) != 0 ? MagickTrue : MagickFalse;
13174 if (status != MagickFalse)
13175 {
13176 XNoticeWidget(display,windows,"Unable to delete image file:",
13177 filename);
13178 break;
13179 }
13180 }
13181 case TileUpdateCommand:
13182 {
cristy3ed852e2009-09-05 21:47:34 +000013183 int
13184 x_offset,
13185 y_offset;
13186
13187 PixelPacket
13188 pixel;
13189
13190 register int
13191 j;
13192
cristy4c08aed2011-07-01 19:47:50 +000013193 register Quantum
cristy3ed852e2009-09-05 21:47:34 +000013194 *s;
13195
13196 /*
13197 Ensure all the images exist.
13198 */
13199 tile=0;
13200 for (p=image->directory; *p != '\0'; p++)
13201 {
cristyf7c25522010-11-15 01:25:14 +000013202 CacheView
13203 *image_view;
13204
cristy3ed852e2009-09-05 21:47:34 +000013205 q=p;
13206 while ((*q != '\n') && (*q != '\0'))
13207 q++;
13208 (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13209 p=q;
13210 if (IsPathAccessible(filename) != MagickFalse)
13211 {
13212 tile++;
13213 continue;
13214 }
13215 /*
13216 Overwrite tile with background color.
13217 */
13218 x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13219 y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
cristy49e2d862010-11-12 02:50:30 +000013220 image_view=AcquireCacheView(image);
13221 (void) GetOneCacheViewVirtualPixel(image_view,0,0,&pixel,exception);
cristy3ed852e2009-09-05 21:47:34 +000013222 for (i=0; i < (int) height; i++)
13223 {
cristy49e2d862010-11-12 02:50:30 +000013224 s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13225 y_offset+i,width,1,exception);
cristy4c08aed2011-07-01 19:47:50 +000013226 if (s == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000013227 break;
13228 for (j=0; j < (int) width; j++)
cristy4c08aed2011-07-01 19:47:50 +000013229 {
13230 SetPixelPacket(image,&pixel,s);
cristyed231572011-07-14 02:18:59 +000013231 s+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +000013232 }
cristy49e2d862010-11-12 02:50:30 +000013233 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000013234 break;
13235 }
cristyca1628f2010-11-15 01:17:49 +000013236 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +000013237 tile++;
13238 }
13239 windows->image.window_changes.width=(int) image->columns;
13240 windows->image.window_changes.height=(int) image->rows;
13241 XConfigureImageColormap(display,resource_info,windows,image);
cristy051718b2011-08-28 22:49:25 +000013242 (void) XConfigureImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013243 break;
13244 }
13245 default:
13246 break;
13247 }
13248 XSetCursorState(display,windows,MagickFalse);
13249 return(tile_image);
13250}
13251
13252/*
13253%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13254% %
13255% %
13256% %
13257+ X T r a n s l a t e I m a g e %
13258% %
13259% %
13260% %
13261%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13262%
13263% XTranslateImage() translates the image within an Image window by one pixel
13264% as specified by the key symbol. If the image has a `montage string the
13265% translation is respect to the width and height contained within the string.
13266%
13267% The format of the XTranslateImage method is:
13268%
13269% void XTranslateImage(Display *display,XWindows *windows,
13270% Image *image,const KeySym key_symbol)
13271%
13272% A description of each parameter follows:
13273%
13274% o display: Specifies a connection to an X server; returned from
13275% XOpenDisplay.
13276%
13277% o windows: Specifies a pointer to a XWindows structure.
13278%
13279% o image: the image.
13280%
13281% o key_symbol: Specifies a KeySym which indicates which side of the image
13282% to trim.
13283%
13284*/
13285static void XTranslateImage(Display *display,XWindows *windows,
13286 Image *image,const KeySym key_symbol)
13287{
13288 char
13289 text[MaxTextExtent];
13290
13291 int
13292 x,
13293 y;
13294
13295 unsigned int
13296 x_offset,
13297 y_offset;
13298
13299 /*
13300 User specified a pan position offset.
13301 */
13302 x_offset=windows->image.width;
13303 y_offset=windows->image.height;
13304 if (image->montage != (char *) NULL)
13305 (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13306 switch ((int) key_symbol)
13307 {
13308 case XK_Home:
13309 case XK_KP_Home:
13310 {
13311 windows->image.x=(int) windows->image.width/2;
13312 windows->image.y=(int) windows->image.height/2;
13313 break;
13314 }
13315 case XK_Left:
13316 case XK_KP_Left:
13317 {
13318 windows->image.x-=x_offset;
13319 break;
13320 }
13321 case XK_Next:
13322 case XK_Up:
13323 case XK_KP_Up:
13324 {
13325 windows->image.y-=y_offset;
13326 break;
13327 }
13328 case XK_Right:
13329 case XK_KP_Right:
13330 {
13331 windows->image.x+=x_offset;
13332 break;
13333 }
13334 case XK_Prior:
13335 case XK_Down:
13336 case XK_KP_Down:
13337 {
13338 windows->image.y+=y_offset;
13339 break;
13340 }
13341 default:
13342 return;
13343 }
13344 /*
13345 Check boundary conditions.
13346 */
13347 if (windows->image.x < 0)
13348 windows->image.x=0;
13349 else
13350 if ((int) (windows->image.x+windows->image.width) >
13351 windows->image.ximage->width)
cristy49e2d862010-11-12 02:50:30 +000013352 windows->image.x=(int) windows->image.ximage->width-windows->image.width;
cristy3ed852e2009-09-05 21:47:34 +000013353 if (windows->image.y < 0)
13354 windows->image.y=0;
13355 else
13356 if ((int) (windows->image.y+windows->image.height) >
13357 windows->image.ximage->height)
cristy49e2d862010-11-12 02:50:30 +000013358 windows->image.y=(int) windows->image.ximage->height-windows->image.height;
cristy3ed852e2009-09-05 21:47:34 +000013359 /*
13360 Refresh Image window.
13361 */
cristyb51dff52011-05-19 16:55:47 +000013362 (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
cristy3ed852e2009-09-05 21:47:34 +000013363 windows->image.width,windows->image.height,windows->image.x,
13364 windows->image.y);
13365 XInfoWidget(display,windows,text);
13366 XCheckRefreshWindows(display,windows);
13367 XDrawPanRectangle(display,windows);
13368 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13369 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13370}
13371
13372/*
13373%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13374% %
13375% %
13376% %
13377+ X T r i m I m a g e %
13378% %
13379% %
13380% %
13381%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13382%
13383% XTrimImage() trims the edges from the Image window.
13384%
13385% The format of the XTrimImage method is:
13386%
13387% MagickBooleanType XTrimImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000013388% XResourceInfo *resource_info,XWindows *windows,Image *image,
13389% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000013390%
13391% A description of each parameter follows:
13392%
13393% o display: Specifies a connection to an X server; returned from
13394% XOpenDisplay.
13395%
13396% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13397%
13398% o windows: Specifies a pointer to a XWindows structure.
13399%
13400% o image: the image.
13401%
cristy051718b2011-08-28 22:49:25 +000013402% o exception: return any errors or warnings in this structure.
13403%
cristy3ed852e2009-09-05 21:47:34 +000013404*/
13405static MagickBooleanType XTrimImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000013406 XResourceInfo *resource_info,XWindows *windows,Image *image,
13407 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000013408{
13409 RectangleInfo
13410 trim_info;
13411
13412 register int
13413 x,
13414 y;
13415
cristybb503372010-05-27 20:51:26 +000013416 size_t
cristy3ed852e2009-09-05 21:47:34 +000013417 background,
13418 pixel;
13419
13420 /*
13421 Trim edges from image.
13422 */
13423 XSetCursorState(display,windows,MagickTrue);
13424 XCheckRefreshWindows(display,windows);
13425 /*
13426 Crop the left edge.
13427 */
13428 background=XGetPixel(windows->image.ximage,0,0);
cristybb503372010-05-27 20:51:26 +000013429 trim_info.width=(size_t) windows->image.ximage->width;
cristy3ed852e2009-09-05 21:47:34 +000013430 for (x=0; x < windows->image.ximage->width; x++)
13431 {
13432 for (y=0; y < windows->image.ximage->height; y++)
13433 {
13434 pixel=XGetPixel(windows->image.ximage,x,y);
13435 if (pixel != background)
13436 break;
13437 }
13438 if (y < windows->image.ximage->height)
13439 break;
13440 }
cristy49e2d862010-11-12 02:50:30 +000013441 trim_info.x=(ssize_t) x;
13442 if (trim_info.x == (ssize_t) windows->image.ximage->width)
cristy3ed852e2009-09-05 21:47:34 +000013443 {
13444 XSetCursorState(display,windows,MagickFalse);
13445 return(MagickFalse);
13446 }
13447 /*
13448 Crop the right edge.
13449 */
13450 background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13451 for (x=windows->image.ximage->width-1; x != 0; x--)
13452 {
13453 for (y=0; y < windows->image.ximage->height; y++)
13454 {
13455 pixel=XGetPixel(windows->image.ximage,x,y);
13456 if (pixel != background)
13457 break;
13458 }
13459 if (y < windows->image.ximage->height)
13460 break;
13461 }
cristybb503372010-05-27 20:51:26 +000013462 trim_info.width=(size_t) (x-trim_info.x+1);
cristy3ed852e2009-09-05 21:47:34 +000013463 /*
13464 Crop the top edge.
13465 */
13466 background=XGetPixel(windows->image.ximage,0,0);
cristybb503372010-05-27 20:51:26 +000013467 trim_info.height=(size_t) windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +000013468 for (y=0; y < windows->image.ximage->height; y++)
13469 {
13470 for (x=0; x < windows->image.ximage->width; x++)
13471 {
13472 pixel=XGetPixel(windows->image.ximage,x,y);
13473 if (pixel != background)
13474 break;
13475 }
13476 if (x < windows->image.ximage->width)
13477 break;
13478 }
cristy49e2d862010-11-12 02:50:30 +000013479 trim_info.y=(ssize_t) y;
cristy3ed852e2009-09-05 21:47:34 +000013480 /*
13481 Crop the bottom edge.
13482 */
13483 background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13484 for (y=windows->image.ximage->height-1; y != 0; y--)
13485 {
13486 for (x=0; x < windows->image.ximage->width; x++)
13487 {
13488 pixel=XGetPixel(windows->image.ximage,x,y);
13489 if (pixel != background)
13490 break;
13491 }
13492 if (x < windows->image.ximage->width)
13493 break;
13494 }
cristybb503372010-05-27 20:51:26 +000013495 trim_info.height=(size_t) y-trim_info.y+1;
cristy3ed852e2009-09-05 21:47:34 +000013496 if (((unsigned int) trim_info.width != windows->image.width) ||
13497 ((unsigned int) trim_info.height != windows->image.height))
13498 {
13499 /*
13500 Reconfigure Image window as defined by the trimming rectangle.
13501 */
13502 XSetCropGeometry(display,windows,&trim_info,image);
13503 windows->image.window_changes.width=(int) trim_info.width;
13504 windows->image.window_changes.height=(int) trim_info.height;
cristy051718b2011-08-28 22:49:25 +000013505 (void) XConfigureImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013506 }
13507 XSetCursorState(display,windows,MagickFalse);
13508 return(MagickTrue);
13509}
13510
13511/*
13512%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13513% %
13514% %
13515% %
13516+ X V i s u a l D i r e c t o r y I m a g e %
13517% %
13518% %
13519% %
13520%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13521%
13522% XVisualDirectoryImage() creates a Visual Image Directory.
13523%
13524% The format of the XVisualDirectoryImage method is:
13525%
13526% Image *XVisualDirectoryImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000013527% XResourceInfo *resource_info,XWindows *windows,
13528% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000013529%
13530% A description of each parameter follows:
13531%
cristy3ed852e2009-09-05 21:47:34 +000013532% o display: Specifies a connection to an X server; returned from
13533% XOpenDisplay.
13534%
13535% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13536%
13537% o windows: Specifies a pointer to a XWindows structure.
13538%
cristy051718b2011-08-28 22:49:25 +000013539% o exception: return any errors or warnings in this structure.
13540%
cristy3ed852e2009-09-05 21:47:34 +000013541*/
13542static Image *XVisualDirectoryImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000013543 XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000013544{
13545#define TileImageTag "Scale/Image"
13546#define XClientName "montage"
13547
13548 char
13549 **filelist;
13550
cristy3ed852e2009-09-05 21:47:34 +000013551 Image
13552 *images,
13553 *montage_image,
13554 *next_image,
13555 *thumbnail_image;
13556
13557 ImageInfo
13558 *read_info;
13559
13560 int
13561 number_files;
13562
13563 MagickBooleanType
13564 backdrop;
13565
13566 MagickStatusType
13567 status;
13568
13569 MontageInfo
13570 *montage_info;
13571
13572 RectangleInfo
13573 geometry;
13574
13575 register int
13576 i;
13577
13578 static char
13579 filename[MaxTextExtent] = "\0",
13580 filenames[MaxTextExtent] = "*";
13581
13582 XResourceInfo
13583 background_resources;
13584
13585 /*
13586 Request file name from user.
13587 */
13588 XFileBrowserWidget(display,windows,"Directory",filenames);
13589 if (*filenames == '\0')
13590 return((Image *) NULL);
13591 /*
13592 Expand the filenames.
13593 */
cristy73bd4a52010-10-05 11:24:23 +000013594 filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
cristy3ed852e2009-09-05 21:47:34 +000013595 if (filelist == (char **) NULL)
13596 {
13597 ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13598 filenames);
13599 return((Image *) NULL);
13600 }
13601 number_files=1;
13602 filelist[0]=filenames;
13603 status=ExpandFilenames(&number_files,&filelist);
13604 if ((status == MagickFalse) || (number_files == 0))
13605 {
13606 if (number_files == 0)
13607 ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13608 else
13609 ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13610 filenames);
13611 return((Image *) NULL);
13612 }
13613 /*
13614 Set image background resources.
13615 */
13616 background_resources=(*resource_info);
13617 background_resources.window_id=AcquireString("");
cristyb51dff52011-05-19 16:55:47 +000013618 (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000013619 "0x%lx",windows->image.id);
13620 background_resources.backdrop=MagickTrue;
13621 /*
13622 Read each image and convert them to a tile.
13623 */
13624 backdrop=(windows->visual_info->klass == TrueColor) ||
13625 (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13626 read_info=CloneImageInfo(resource_info->image_info);
cristy9ce61202010-11-24 00:38:37 +000013627 (void) SetImageOption(read_info,"jpeg:size","120x120");
13628 (void) CloneString(&read_info->size,DefaultTileGeometry);
cristy3ed852e2009-09-05 21:47:34 +000013629 (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13630 (void *) NULL);
13631 images=NewImageList();
cristy3ed852e2009-09-05 21:47:34 +000013632 XSetCursorState(display,windows,MagickTrue);
13633 XCheckRefreshWindows(display,windows);
cristy49e2d862010-11-12 02:50:30 +000013634 for (i=0; i < (int) number_files; i++)
cristy3ed852e2009-09-05 21:47:34 +000013635 {
13636 (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13637 filelist[i]=DestroyString(filelist[i]);
13638 *read_info->magick='\0';
cristy3ed852e2009-09-05 21:47:34 +000013639 next_image=ReadImage(read_info,exception);
13640 CatchException(exception);
13641 if (next_image != (Image *) NULL)
13642 {
13643 (void) DeleteImageProperty(next_image,"label");
cristy77619442010-11-24 00:27:23 +000013644 (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13645 read_info,next_image,DefaultTileLabel));
cristy3ed852e2009-09-05 21:47:34 +000013646 (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13647 exception);
13648 thumbnail_image=ThumbnailImage(next_image,geometry.width,
13649 geometry.height,exception);
13650 if (thumbnail_image != (Image *) NULL)
13651 {
13652 next_image=DestroyImage(next_image);
13653 next_image=thumbnail_image;
13654 }
13655 if (backdrop)
13656 {
13657 (void) XDisplayBackgroundImage(display,&background_resources,
cristy051718b2011-08-28 22:49:25 +000013658 next_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013659 XSetCursorState(display,windows,MagickTrue);
13660 }
13661 AppendImageToList(&images,next_image);
13662 if (images->progress_monitor != (MagickProgressMonitor) NULL)
13663 {
13664 MagickBooleanType
13665 proceed;
13666
13667 proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13668 (MagickSizeType) number_files);
13669 if (proceed == MagickFalse)
13670 break;
13671 }
13672 }
13673 }
cristy3ed852e2009-09-05 21:47:34 +000013674 filelist=(char **) RelinquishMagickMemory(filelist);
cristy3ed852e2009-09-05 21:47:34 +000013675 if (images == (Image *) NULL)
13676 {
cristy8d52fca2010-11-24 00:45:05 +000013677 read_info=DestroyImageInfo(read_info);
cristy3ed852e2009-09-05 21:47:34 +000013678 XSetCursorState(display,windows,MagickFalse);
13679 ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13680 return((Image *) NULL);
13681 }
13682 /*
13683 Create the Visual Image Directory.
13684 */
cristy8d52fca2010-11-24 00:45:05 +000013685 montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13686 montage_info->pointsize=10;
cristy3ed852e2009-09-05 21:47:34 +000013687 if (resource_info->font != (char *) NULL)
13688 (void) CloneString(&montage_info->font,resource_info->font);
13689 (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
cristy8d52fca2010-11-24 00:45:05 +000013690 montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
cristy051718b2011-08-28 22:49:25 +000013691 images),exception);
cristy3ed852e2009-09-05 21:47:34 +000013692 images=DestroyImageList(images);
cristy8d52fca2010-11-24 00:45:05 +000013693 montage_info=DestroyMontageInfo(montage_info);
13694 read_info=DestroyImageInfo(read_info);
cristy3ed852e2009-09-05 21:47:34 +000013695 XSetCursorState(display,windows,MagickFalse);
13696 if (montage_image == (Image *) NULL)
13697 return(montage_image);
13698 XClientMessage(display,windows->image.id,windows->im_protocols,
13699 windows->im_next_image,CurrentTime);
13700 return(montage_image);
13701}
13702
13703/*
13704%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13705% %
13706% %
13707% %
13708% X D i s p l a y B a c k g r o u n d I m a g e %
13709% %
13710% %
13711% %
13712%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13713%
13714% XDisplayBackgroundImage() displays an image in the background of a window.
13715%
13716% The format of the XDisplayBackgroundImage method is:
13717%
13718% MagickBooleanType XDisplayBackgroundImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000013719% XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000013720%
13721% A description of each parameter follows:
13722%
13723% o display: Specifies a connection to an X server; returned from
13724% XOpenDisplay.
13725%
13726% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13727%
13728% o image: the image.
13729%
cristy051718b2011-08-28 22:49:25 +000013730% o exception: return any errors or warnings in this structure.
13731%
cristy3ed852e2009-09-05 21:47:34 +000013732*/
13733MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000013734 XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000013735{
13736 char
13737 geometry[MaxTextExtent],
13738 visual_type[MaxTextExtent];
13739
13740 int
13741 height,
13742 status,
13743 width;
13744
13745 RectangleInfo
13746 geometry_info;
13747
13748 static XPixelInfo
13749 pixel;
13750
13751 static XStandardColormap
13752 *map_info;
13753
13754 static XVisualInfo
13755 *visual_info = (XVisualInfo *) NULL;
13756
13757 static XWindowInfo
13758 window_info;
13759
cristybb503372010-05-27 20:51:26 +000013760 size_t
cristy3ed852e2009-09-05 21:47:34 +000013761 delay;
13762
13763 Window
13764 root_window;
13765
13766 XGCValues
13767 context_values;
13768
13769 XResourceInfo
13770 resources;
13771
13772 XWindowAttributes
13773 window_attributes;
13774
13775 /*
13776 Determine target window.
13777 */
13778 assert(image != (Image *) NULL);
13779 assert(image->signature == MagickSignature);
13780 if (image->debug != MagickFalse)
13781 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13782 resources=(*resource_info);
13783 window_info.id=(Window) NULL;
13784 root_window=XRootWindow(display,XDefaultScreen(display));
13785 if (LocaleCompare(resources.window_id,"root") == 0)
13786 window_info.id=root_window;
13787 else
13788 {
13789 if (isdigit((unsigned char) *resources.window_id) != 0)
13790 window_info.id=XWindowByID(display,root_window,
13791 (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13792 if (window_info.id == (Window) NULL)
13793 window_info.id=XWindowByName(display,root_window,resources.window_id);
13794 }
13795 if (window_info.id == (Window) NULL)
13796 {
13797 ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13798 resources.window_id);
13799 return(MagickFalse);
13800 }
13801 /*
13802 Determine window visual id.
13803 */
13804 window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13805 window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13806 (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13807 status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13808 if (status != 0)
cristyb51dff52011-05-19 16:55:47 +000013809 (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
cristy3ed852e2009-09-05 21:47:34 +000013810 XVisualIDFromVisual(window_attributes.visual));
13811 if (visual_info == (XVisualInfo *) NULL)
13812 {
13813 /*
13814 Allocate standard colormap.
13815 */
13816 map_info=XAllocStandardColormap();
13817 if (map_info == (XStandardColormap *) NULL)
13818 ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13819 image->filename);
13820 map_info->colormap=(Colormap) NULL;
cristyf2faecf2010-05-28 19:19:36 +000013821 pixel.pixels=(unsigned long *) NULL;
cristy3ed852e2009-09-05 21:47:34 +000013822 /*
13823 Initialize visual info.
13824 */
13825 resources.map_type=(char *) NULL;
13826 resources.visual_type=visual_type;
13827 visual_info=XBestVisualInfo(display,map_info,&resources);
13828 if (visual_info == (XVisualInfo *) NULL)
13829 ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13830 resources.visual_type);
13831 /*
13832 Initialize window info.
13833 */
13834 window_info.ximage=(XImage *) NULL;
13835 window_info.matte_image=(XImage *) NULL;
13836 window_info.pixmap=(Pixmap) NULL;
13837 window_info.matte_pixmap=(Pixmap) NULL;
13838 }
13839 /*
13840 Free previous root colors.
13841 */
13842 if (window_info.id == root_window)
13843 (void) XDestroyWindowColors(display,root_window);
13844 /*
13845 Initialize Standard Colormap.
13846 */
13847 resources.colormap=SharedColormap;
13848 XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13849 /*
13850 Graphic context superclass.
13851 */
13852 context_values.background=pixel.background_color.pixel;
13853 context_values.foreground=pixel.foreground_color.pixel;
13854 pixel.annotate_context=XCreateGC(display,window_info.id,
cristybb503372010-05-27 20:51:26 +000013855 (size_t) (GCBackground | GCForeground),&context_values);
cristy3ed852e2009-09-05 21:47:34 +000013856 if (pixel.annotate_context == (GC) NULL)
13857 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13858 image->filename);
13859 /*
13860 Initialize Image window attributes.
13861 */
13862 window_info.name=AcquireString("\0");
13863 window_info.icon_name=AcquireString("\0");
13864 XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13865 &resources,&window_info);
13866 /*
13867 Create the X image.
13868 */
13869 window_info.width=(unsigned int) image->columns;
13870 window_info.height=(unsigned int) image->rows;
13871 if ((image->columns != window_info.width) ||
13872 (image->rows != window_info.height))
13873 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13874 image->filename);
cristyb51dff52011-05-19 16:55:47 +000013875 (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
cristy3ed852e2009-09-05 21:47:34 +000013876 window_attributes.width,window_attributes.height);
13877 geometry_info.width=window_info.width;
13878 geometry_info.height=window_info.height;
cristyecd0ab52010-05-30 14:59:20 +000013879 geometry_info.x=(ssize_t) window_info.x;
13880 geometry_info.y=(ssize_t) window_info.y;
cristy3ed852e2009-09-05 21:47:34 +000013881 (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13882 &geometry_info.width,&geometry_info.height);
13883 window_info.width=(unsigned int) geometry_info.width;
13884 window_info.height=(unsigned int) geometry_info.height;
13885 window_info.x=(int) geometry_info.x;
13886 window_info.y=(int) geometry_info.y;
13887 status=XMakeImage(display,&resources,&window_info,image,window_info.width,
cristy051718b2011-08-28 22:49:25 +000013888 window_info.height,exception);
cristy3ed852e2009-09-05 21:47:34 +000013889 if (status == MagickFalse)
13890 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13891 image->filename);
13892 window_info.x=0;
13893 window_info.y=0;
13894 if (image->debug != MagickFalse)
13895 {
13896 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000013897 "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13898 (double) image->columns,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000013899 if (image->colors != 0)
cristye8c25f92010-06-03 00:53:06 +000013900 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13901 image->colors);
cristy3ed852e2009-09-05 21:47:34 +000013902 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13903 }
13904 /*
13905 Adjust image dimensions as specified by backdrop or geometry options.
13906 */
13907 width=(int) window_info.width;
13908 height=(int) window_info.height;
13909 if (resources.backdrop != MagickFalse)
13910 {
13911 /*
13912 Center image on window.
13913 */
13914 window_info.x=(window_attributes.width/2)-
13915 (window_info.ximage->width/2);
13916 window_info.y=(window_attributes.height/2)-
13917 (window_info.ximage->height/2);
13918 width=window_attributes.width;
13919 height=window_attributes.height;
13920 }
13921 if ((resources.image_geometry != (char *) NULL) &&
13922 (*resources.image_geometry != '\0'))
13923 {
13924 char
13925 default_geometry[MaxTextExtent];
13926
13927 int
13928 flags,
13929 gravity;
13930
13931 XSizeHints
13932 *size_hints;
13933
13934 /*
13935 User specified geometry.
13936 */
13937 size_hints=XAllocSizeHints();
13938 if (size_hints == (XSizeHints *) NULL)
13939 ThrowXWindowFatalException(ResourceLimitFatalError,
13940 "MemoryAllocationFailed",image->filename);
13941 size_hints->flags=0L;
cristyb51dff52011-05-19 16:55:47 +000013942 (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
cristy3ed852e2009-09-05 21:47:34 +000013943 width,height);
13944 flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13945 default_geometry,window_info.border_width,size_hints,&window_info.x,
13946 &window_info.y,&width,&height,&gravity);
13947 if (flags & (XValue | YValue))
13948 {
13949 width=window_attributes.width;
13950 height=window_attributes.height;
13951 }
13952 (void) XFree((void *) size_hints);
13953 }
13954 /*
13955 Create the X pixmap.
13956 */
13957 window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13958 (unsigned int) height,window_info.depth);
13959 if (window_info.pixmap == (Pixmap) NULL)
13960 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13961 image->filename);
13962 /*
13963 Display pixmap on the window.
13964 */
13965 if (((unsigned int) width > window_info.width) ||
13966 ((unsigned int) height > window_info.height))
13967 (void) XFillRectangle(display,window_info.pixmap,
13968 window_info.annotate_context,0,0,(unsigned int) width,
13969 (unsigned int) height);
13970 (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13971 window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13972 window_info.width,(unsigned int) window_info.height);
13973 (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13974 (void) XClearWindow(display,window_info.id);
13975 delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13976 XDelay(display,delay == 0UL ? 10UL : delay);
13977 (void) XSync(display,MagickFalse);
13978 return(window_info.id == root_window ? MagickTrue : MagickFalse);
13979}
13980
13981/*
13982%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13983% %
13984% %
13985% %
13986+ X D i s p l a y I m a g e %
13987% %
13988% %
13989% %
13990%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13991%
13992% XDisplayImage() displays an image via X11. A new image is created and
13993% returned if the user interactively transforms the displayed image.
13994%
13995% The format of the XDisplayImage method is:
13996%
13997% Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +000013998% char **argv,int argc,Image **image,size_t *state,
13999% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000014000%
14001% A description of each parameter follows:
14002%
14003% o nexus: Method XDisplayImage returns an image when the
14004% user chooses 'Open Image' from the command menu or picks a tile
14005% from the image directory. Otherwise a null image is returned.
14006%
14007% o display: Specifies a connection to an X server; returned from
14008% XOpenDisplay.
14009%
14010% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14011%
14012% o argv: Specifies the application's argument list.
14013%
14014% o argc: Specifies the number of arguments.
14015%
14016% o image: Specifies an address to an address of an Image structure;
14017%
cristy051718b2011-08-28 22:49:25 +000014018% o exception: return any errors or warnings in this structure.
14019%
cristy3ed852e2009-09-05 21:47:34 +000014020*/
14021MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +000014022 char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000014023{
14024#define MagnifySize 256 /* must be a power of 2 */
14025#define MagickMenus 10
14026#define MagickTitle "Commands"
14027
14028 static const char
14029 *CommandMenu[] =
14030 {
14031 "File",
14032 "Edit",
14033 "View",
14034 "Transform",
14035 "Enhance",
14036 "Effects",
14037 "F/X",
14038 "Image Edit",
14039 "Miscellany",
14040 "Help",
14041 (char *) NULL
14042 },
14043 *FileMenu[] =
14044 {
14045 "Open...",
14046 "Next",
14047 "Former",
14048 "Select...",
14049 "Save...",
14050 "Print...",
14051 "Delete...",
14052 "New...",
14053 "Visual Directory...",
14054 "Quit",
14055 (char *) NULL
14056 },
14057 *EditMenu[] =
14058 {
14059 "Undo",
14060 "Redo",
14061 "Cut",
14062 "Copy",
14063 "Paste",
14064 (char *) NULL
14065 },
14066 *ViewMenu[] =
14067 {
14068 "Half Size",
14069 "Original Size",
14070 "Double Size",
14071 "Resize...",
14072 "Apply",
14073 "Refresh",
14074 "Restore",
14075 (char *) NULL
14076 },
14077 *TransformMenu[] =
14078 {
14079 "Crop",
14080 "Chop",
14081 "Flop",
14082 "Flip",
14083 "Rotate Right",
14084 "Rotate Left",
14085 "Rotate...",
14086 "Shear...",
14087 "Roll...",
14088 "Trim Edges",
14089 (char *) NULL
14090 },
14091 *EnhanceMenu[] =
14092 {
14093 "Hue...",
14094 "Saturation...",
14095 "Brightness...",
14096 "Gamma...",
14097 "Spiff",
14098 "Dull",
14099 "Contrast Stretch...",
14100 "Sigmoidal Contrast...",
14101 "Normalize",
14102 "Equalize",
14103 "Negate",
14104 "Grayscale",
14105 "Map...",
14106 "Quantize...",
14107 (char *) NULL
14108 },
14109 *EffectsMenu[] =
14110 {
14111 "Despeckle",
14112 "Emboss",
14113 "Reduce Noise",
14114 "Add Noise...",
14115 "Sharpen...",
14116 "Blur...",
14117 "Threshold...",
14118 "Edge Detect...",
14119 "Spread...",
14120 "Shade...",
14121 "Raise...",
14122 "Segment...",
14123 (char *) NULL
14124 },
14125 *FXMenu[] =
14126 {
14127 "Solarize...",
14128 "Sepia Tone...",
14129 "Swirl...",
14130 "Implode...",
14131 "Vignette...",
14132 "Wave...",
14133 "Oil Paint...",
14134 "Charcoal Draw...",
14135 (char *) NULL
14136 },
14137 *ImageEditMenu[] =
14138 {
14139 "Annotate...",
14140 "Draw...",
14141 "Color...",
14142 "Matte...",
14143 "Composite...",
14144 "Add Border...",
14145 "Add Frame...",
14146 "Comment...",
14147 "Launch...",
14148 "Region of Interest...",
14149 (char *) NULL
14150 },
14151 *MiscellanyMenu[] =
14152 {
14153 "Image Info",
14154 "Zoom Image",
14155 "Show Preview...",
14156 "Show Histogram",
14157 "Show Matte",
14158 "Background...",
14159 "Slide Show...",
14160 "Preferences...",
14161 (char *) NULL
14162 },
14163 *HelpMenu[] =
14164 {
14165 "Overview",
14166 "Browse Documentation",
14167 "About Display",
14168 (char *) NULL
14169 },
14170 *ShortCutsMenu[] =
14171 {
14172 "Next",
14173 "Former",
14174 "Open...",
14175 "Save...",
14176 "Print...",
14177 "Undo",
14178 "Restore",
14179 "Image Info",
14180 "Quit",
14181 (char *) NULL
14182 },
14183 *VirtualMenu[] =
14184 {
14185 "Image Info",
14186 "Print",
14187 "Next",
14188 "Quit",
14189 (char *) NULL
14190 };
14191
14192 static const char
14193 **Menus[MagickMenus] =
14194 {
14195 FileMenu,
14196 EditMenu,
14197 ViewMenu,
14198 TransformMenu,
14199 EnhanceMenu,
14200 EffectsMenu,
14201 FXMenu,
14202 ImageEditMenu,
14203 MiscellanyMenu,
14204 HelpMenu
14205 };
14206
14207 static CommandType
14208 CommandMenus[] =
14209 {
14210 NullCommand,
14211 NullCommand,
14212 NullCommand,
14213 NullCommand,
14214 NullCommand,
14215 NullCommand,
14216 NullCommand,
14217 NullCommand,
14218 NullCommand,
14219 NullCommand,
14220 },
14221 FileCommands[] =
14222 {
14223 OpenCommand,
14224 NextCommand,
14225 FormerCommand,
14226 SelectCommand,
14227 SaveCommand,
14228 PrintCommand,
14229 DeleteCommand,
14230 NewCommand,
14231 VisualDirectoryCommand,
14232 QuitCommand
14233 },
14234 EditCommands[] =
14235 {
14236 UndoCommand,
14237 RedoCommand,
14238 CutCommand,
14239 CopyCommand,
14240 PasteCommand
14241 },
14242 ViewCommands[] =
14243 {
14244 HalfSizeCommand,
14245 OriginalSizeCommand,
14246 DoubleSizeCommand,
14247 ResizeCommand,
14248 ApplyCommand,
14249 RefreshCommand,
14250 RestoreCommand
14251 },
14252 TransformCommands[] =
14253 {
14254 CropCommand,
14255 ChopCommand,
14256 FlopCommand,
14257 FlipCommand,
14258 RotateRightCommand,
14259 RotateLeftCommand,
14260 RotateCommand,
14261 ShearCommand,
14262 RollCommand,
14263 TrimCommand
14264 },
14265 EnhanceCommands[] =
14266 {
14267 HueCommand,
14268 SaturationCommand,
14269 BrightnessCommand,
14270 GammaCommand,
14271 SpiffCommand,
14272 DullCommand,
14273 ContrastStretchCommand,
14274 SigmoidalContrastCommand,
14275 NormalizeCommand,
14276 EqualizeCommand,
14277 NegateCommand,
14278 GrayscaleCommand,
14279 MapCommand,
14280 QuantizeCommand
14281 },
14282 EffectsCommands[] =
14283 {
14284 DespeckleCommand,
14285 EmbossCommand,
14286 ReduceNoiseCommand,
14287 AddNoiseCommand,
14288 SharpenCommand,
14289 BlurCommand,
14290 ThresholdCommand,
14291 EdgeDetectCommand,
14292 SpreadCommand,
14293 ShadeCommand,
14294 RaiseCommand,
14295 SegmentCommand
14296 },
14297 FXCommands[] =
14298 {
14299 SolarizeCommand,
14300 SepiaToneCommand,
14301 SwirlCommand,
14302 ImplodeCommand,
14303 VignetteCommand,
14304 WaveCommand,
14305 OilPaintCommand,
14306 CharcoalDrawCommand
14307 },
14308 ImageEditCommands[] =
14309 {
14310 AnnotateCommand,
14311 DrawCommand,
14312 ColorCommand,
14313 MatteCommand,
14314 CompositeCommand,
14315 AddBorderCommand,
14316 AddFrameCommand,
14317 CommentCommand,
14318 LaunchCommand,
14319 RegionofInterestCommand
14320 },
14321 MiscellanyCommands[] =
14322 {
14323 InfoCommand,
14324 ZoomCommand,
14325 ShowPreviewCommand,
14326 ShowHistogramCommand,
14327 ShowMatteCommand,
14328 BackgroundCommand,
14329 SlideShowCommand,
14330 PreferencesCommand
14331 },
14332 HelpCommands[] =
14333 {
14334 HelpCommand,
14335 BrowseDocumentationCommand,
14336 VersionCommand
14337 },
14338 ShortCutsCommands[] =
14339 {
14340 NextCommand,
14341 FormerCommand,
14342 OpenCommand,
14343 SaveCommand,
14344 PrintCommand,
14345 UndoCommand,
14346 RestoreCommand,
14347 InfoCommand,
14348 QuitCommand
14349 },
14350 VirtualCommands[] =
14351 {
14352 InfoCommand,
14353 PrintCommand,
14354 NextCommand,
14355 QuitCommand
14356 };
14357
14358 static CommandType
14359 *Commands[MagickMenus] =
14360 {
14361 FileCommands,
14362 EditCommands,
14363 ViewCommands,
14364 TransformCommands,
14365 EnhanceCommands,
14366 EffectsCommands,
14367 FXCommands,
14368 ImageEditCommands,
14369 MiscellanyCommands,
14370 HelpCommands
14371 };
14372
14373 char
14374 command[MaxTextExtent],
cristy00976d82011-02-20 20:31:28 +000014375 *directory,
cristy3ed852e2009-09-05 21:47:34 +000014376 geometry[MaxTextExtent],
14377 resource_name[MaxTextExtent];
14378
14379 CommandType
14380 command_type;
14381
14382 Image
14383 *display_image,
14384 *nexus;
14385
14386 int
14387 entry,
14388 id;
14389
14390 KeySym
14391 key_symbol;
14392
14393 MagickStatusType
14394 context_mask,
14395 status;
14396
14397 RectangleInfo
14398 geometry_info;
14399
14400 register int
14401 i;
14402
14403 static char
14404 working_directory[MaxTextExtent];
14405
14406 static XPoint
14407 vid_info;
14408
14409 static XWindowInfo
14410 *magick_windows[MaxXWindows];
14411
14412 static unsigned int
14413 number_windows;
14414
14415 struct stat
14416 attributes;
14417
14418 time_t
14419 timer,
14420 timestamp,
14421 update_time;
14422
14423 unsigned int
14424 height,
14425 width;
14426
cristybb503372010-05-27 20:51:26 +000014427 size_t
cristy3ed852e2009-09-05 21:47:34 +000014428 delay;
14429
14430 WarningHandler
14431 warning_handler;
14432
14433 Window
14434 root_window;
14435
14436 XClassHint
14437 *class_hints;
14438
14439 XEvent
14440 event;
14441
14442 XFontStruct
14443 *font_info;
14444
14445 XGCValues
14446 context_values;
14447
14448 XPixelInfo
14449 *icon_pixel,
14450 *pixel;
14451
14452 XResourceInfo
14453 *icon_resources;
14454
14455 XStandardColormap
14456 *icon_map,
14457 *map_info;
14458
14459 XVisualInfo
14460 *icon_visual,
14461 *visual_info;
14462
14463 XWindowChanges
14464 window_changes;
14465
14466 XWindows
14467 *windows;
14468
14469 XWMHints
14470 *manager_hints;
14471
14472 assert(image != (Image **) NULL);
14473 assert((*image)->signature == MagickSignature);
14474 if ((*image)->debug != MagickFalse)
14475 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14476 display_image=(*image);
14477 warning_handler=(WarningHandler) NULL;
14478 windows=XSetWindows((XWindows *) ~0);
14479 if (windows != (XWindows *) NULL)
14480 {
14481 int
14482 status;
14483
14484 status=chdir(working_directory);
14485 if (status == -1)
cristy051718b2011-08-28 22:49:25 +000014486 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14487 "UnableToOpenFile","%s",working_directory);
cristy3ed852e2009-09-05 21:47:34 +000014488 warning_handler=resource_info->display_warnings ?
14489 SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14490 warning_handler=resource_info->display_warnings ?
14491 SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14492 }
14493 else
14494 {
14495 /*
14496 Allocate windows structure.
14497 */
14498 resource_info->colors=display_image->colors;
14499 windows=XSetWindows(XInitializeWindows(display,resource_info));
14500 if (windows == (XWindows *) NULL)
14501 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14502 (*image)->filename);
14503 /*
14504 Initialize window id's.
14505 */
14506 number_windows=0;
14507 magick_windows[number_windows++]=(&windows->icon);
14508 magick_windows[number_windows++]=(&windows->backdrop);
14509 magick_windows[number_windows++]=(&windows->image);
14510 magick_windows[number_windows++]=(&windows->info);
14511 magick_windows[number_windows++]=(&windows->command);
14512 magick_windows[number_windows++]=(&windows->widget);
14513 magick_windows[number_windows++]=(&windows->popup);
14514 magick_windows[number_windows++]=(&windows->magnify);
14515 magick_windows[number_windows++]=(&windows->pan);
14516 for (i=0; i < (int) number_windows; i++)
14517 magick_windows[i]->id=(Window) NULL;
14518 vid_info.x=0;
14519 vid_info.y=0;
14520 }
14521 /*
14522 Initialize font info.
14523 */
14524 if (windows->font_info != (XFontStruct *) NULL)
14525 (void) XFreeFont(display,windows->font_info);
14526 windows->font_info=XBestFont(display,resource_info,MagickFalse);
14527 if (windows->font_info == (XFontStruct *) NULL)
14528 ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14529 resource_info->font);
14530 /*
14531 Initialize Standard Colormap.
14532 */
14533 map_info=windows->map_info;
14534 icon_map=windows->icon_map;
14535 visual_info=windows->visual_info;
14536 icon_visual=windows->icon_visual;
14537 pixel=windows->pixel_info;
14538 icon_pixel=windows->icon_pixel;
14539 font_info=windows->font_info;
14540 icon_resources=windows->icon_resources;
14541 class_hints=windows->class_hints;
14542 manager_hints=windows->manager_hints;
14543 root_window=XRootWindow(display,visual_info->screen);
14544 nexus=NewImageList();
14545 if (display_image->debug != MagickFalse)
14546 {
14547 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000014548 "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14549 (double) display_image->scene,(double) display_image->columns,
14550 (double) display_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000014551 if (display_image->colors != 0)
cristye8c25f92010-06-03 00:53:06 +000014552 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14553 display_image->colors);
cristy3ed852e2009-09-05 21:47:34 +000014554 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14555 display_image->magick);
14556 }
14557 XMakeStandardColormap(display,visual_info,resource_info,display_image,
14558 map_info,pixel);
14559 display_image->taint=MagickFalse;
14560 /*
14561 Initialize graphic context.
14562 */
14563 windows->context.id=(Window) NULL;
14564 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14565 resource_info,&windows->context);
14566 (void) CloneString(&class_hints->res_name,resource_info->client_name);
14567 (void) CloneString(&class_hints->res_class,resource_info->client_name);
14568 class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14569 manager_hints->flags=InputHint | StateHint;
14570 manager_hints->input=MagickFalse;
14571 manager_hints->initial_state=WithdrawnState;
14572 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14573 &windows->context);
14574 if (display_image->debug != MagickFalse)
14575 (void) LogMagickEvent(X11Event,GetMagickModule(),
14576 "Window id: 0x%lx (context)",windows->context.id);
14577 context_values.background=pixel->background_color.pixel;
14578 context_values.font=font_info->fid;
14579 context_values.foreground=pixel->foreground_color.pixel;
14580 context_values.graphics_exposures=MagickFalse;
14581 context_mask=(MagickStatusType)
14582 (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14583 if (pixel->annotate_context != (GC) NULL)
14584 (void) XFreeGC(display,pixel->annotate_context);
14585 pixel->annotate_context=XCreateGC(display,windows->context.id,
14586 context_mask,&context_values);
14587 if (pixel->annotate_context == (GC) NULL)
14588 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14589 display_image->filename);
14590 context_values.background=pixel->depth_color.pixel;
14591 if (pixel->widget_context != (GC) NULL)
14592 (void) XFreeGC(display,pixel->widget_context);
14593 pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14594 &context_values);
14595 if (pixel->widget_context == (GC) NULL)
14596 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14597 display_image->filename);
14598 context_values.background=pixel->foreground_color.pixel;
14599 context_values.foreground=pixel->background_color.pixel;
14600 context_values.plane_mask=context_values.background ^
14601 context_values.foreground;
14602 if (pixel->highlight_context != (GC) NULL)
14603 (void) XFreeGC(display,pixel->highlight_context);
14604 pixel->highlight_context=XCreateGC(display,windows->context.id,
cristybb503372010-05-27 20:51:26 +000014605 (size_t) (context_mask | GCPlaneMask),&context_values);
cristy3ed852e2009-09-05 21:47:34 +000014606 if (pixel->highlight_context == (GC) NULL)
14607 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14608 display_image->filename);
14609 (void) XDestroyWindow(display,windows->context.id);
14610 /*
14611 Initialize icon window.
14612 */
14613 XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14614 icon_resources,&windows->icon);
14615 windows->icon.geometry=resource_info->icon_geometry;
14616 XBestIconSize(display,&windows->icon,display_image);
14617 windows->icon.attributes.colormap=XDefaultColormap(display,
14618 icon_visual->screen);
14619 windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14620 manager_hints->flags=InputHint | StateHint;
14621 manager_hints->input=MagickFalse;
14622 manager_hints->initial_state=IconicState;
14623 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14624 &windows->icon);
14625 if (display_image->debug != MagickFalse)
14626 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14627 windows->icon.id);
14628 /*
14629 Initialize graphic context for icon window.
14630 */
14631 if (icon_pixel->annotate_context != (GC) NULL)
14632 (void) XFreeGC(display,icon_pixel->annotate_context);
14633 context_values.background=icon_pixel->background_color.pixel;
14634 context_values.foreground=icon_pixel->foreground_color.pixel;
14635 icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
cristybb503372010-05-27 20:51:26 +000014636 (size_t) (GCBackground | GCForeground),&context_values);
cristy3ed852e2009-09-05 21:47:34 +000014637 if (icon_pixel->annotate_context == (GC) NULL)
14638 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14639 display_image->filename);
14640 windows->icon.annotate_context=icon_pixel->annotate_context;
14641 /*
14642 Initialize Image window.
14643 */
14644 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14645 &windows->image);
14646 windows->image.shape=MagickTrue; /* non-rectangular shape hint */
14647 if (resource_info->use_shared_memory == MagickFalse)
14648 windows->image.shared_memory=MagickFalse;
14649 if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14650 {
14651 char
14652 *title;
14653
14654 title=InterpretImageProperties(resource_info->image_info,display_image,
14655 resource_info->title);
14656 (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14657 (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14658 title=DestroyString(title);
14659 }
14660 else
14661 {
14662 char
14663 filename[MaxTextExtent];
14664
14665 /*
14666 Window name is the base of the filename.
14667 */
14668 GetPathComponent(display_image->magick_filename,TailPath,filename);
14669 if (GetImageListLength(display_image) == 1)
cristyb51dff52011-05-19 16:55:47 +000014670 (void) FormatLocaleString(windows->image.name,MaxTextExtent,
cristy40a08ad2010-02-09 02:27:44 +000014671 "%s: %s",MagickPackageName,filename);
cristy3ed852e2009-09-05 21:47:34 +000014672 else
cristyb51dff52011-05-19 16:55:47 +000014673 (void) FormatLocaleString(windows->image.name,MaxTextExtent,
cristy04d55062011-03-15 18:58:50 +000014674 "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14675 (double) display_image->scene,(double) GetImageListLength(
14676 display_image));
cristy3ed852e2009-09-05 21:47:34 +000014677 (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14678 }
14679 if (resource_info->immutable)
14680 windows->image.immutable=MagickTrue;
14681 windows->image.use_pixmap=resource_info->use_pixmap;
14682 windows->image.geometry=resource_info->image_geometry;
cristyb51dff52011-05-19 16:55:47 +000014683 (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
cristy3ed852e2009-09-05 21:47:34 +000014684 XDisplayWidth(display,visual_info->screen),
14685 XDisplayHeight(display,visual_info->screen));
14686 geometry_info.width=display_image->columns;
14687 geometry_info.height=display_image->rows;
14688 geometry_info.x=0;
14689 geometry_info.y=0;
14690 (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14691 &geometry_info.width,&geometry_info.height);
14692 windows->image.width=(unsigned int) geometry_info.width;
14693 windows->image.height=(unsigned int) geometry_info.height;
14694 windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14695 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14696 KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14697 PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14698 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14699 resource_info,&windows->backdrop);
14700 if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14701 {
14702 /*
14703 Initialize backdrop window.
14704 */
14705 windows->backdrop.x=0;
14706 windows->backdrop.y=0;
cristy40a08ad2010-02-09 02:27:44 +000014707 (void) CloneString(&windows->backdrop.name,"Backdrop");
cristybb503372010-05-27 20:51:26 +000014708 windows->backdrop.flags=(size_t) (USSize | USPosition);
cristy3ed852e2009-09-05 21:47:34 +000014709 windows->backdrop.width=(unsigned int)
14710 XDisplayWidth(display,visual_info->screen);
14711 windows->backdrop.height=(unsigned int)
14712 XDisplayHeight(display,visual_info->screen);
14713 windows->backdrop.border_width=0;
14714 windows->backdrop.immutable=MagickTrue;
14715 windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14716 ButtonReleaseMask;
14717 windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14718 StructureNotifyMask;
14719 manager_hints->flags=IconWindowHint | InputHint | StateHint;
14720 manager_hints->icon_window=windows->icon.id;
14721 manager_hints->input=MagickTrue;
14722 manager_hints->initial_state=resource_info->iconic ? IconicState :
14723 NormalState;
14724 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14725 &windows->backdrop);
14726 if (display_image->debug != MagickFalse)
14727 (void) LogMagickEvent(X11Event,GetMagickModule(),
14728 "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14729 (void) XMapWindow(display,windows->backdrop.id);
14730 (void) XClearWindow(display,windows->backdrop.id);
14731 if (windows->image.id != (Window) NULL)
14732 {
14733 (void) XDestroyWindow(display,windows->image.id);
14734 windows->image.id=(Window) NULL;
14735 }
14736 /*
14737 Position image in the center the backdrop.
14738 */
14739 windows->image.flags|=USPosition;
14740 windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14741 (windows->image.width/2);
14742 windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14743 (windows->image.height/2);
14744 }
14745 manager_hints->flags=IconWindowHint | InputHint | StateHint;
14746 manager_hints->icon_window=windows->icon.id;
14747 manager_hints->input=MagickTrue;
14748 manager_hints->initial_state=resource_info->iconic ? IconicState :
14749 NormalState;
14750 if (windows->group_leader.id != (Window) NULL)
14751 {
14752 /*
14753 Follow the leader.
14754 */
14755 manager_hints->flags|=WindowGroupHint;
14756 manager_hints->window_group=windows->group_leader.id;
14757 (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14758 if (display_image->debug != MagickFalse)
14759 (void) LogMagickEvent(X11Event,GetMagickModule(),
14760 "Window id: 0x%lx (group leader)",windows->group_leader.id);
14761 }
14762 XMakeWindow(display,
14763 (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14764 argv,argc,class_hints,manager_hints,&windows->image);
14765 (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14766 XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14767 if (windows->group_leader.id != (Window) NULL)
14768 (void) XSetTransientForHint(display,windows->image.id,
14769 windows->group_leader.id);
14770 if (display_image->debug != MagickFalse)
14771 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14772 windows->image.id);
14773 /*
14774 Initialize Info widget.
14775 */
14776 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14777 &windows->info);
14778 (void) CloneString(&windows->info.name,"Info");
14779 (void) CloneString(&windows->info.icon_name,"Info");
14780 windows->info.border_width=1;
14781 windows->info.x=2;
14782 windows->info.y=2;
14783 windows->info.flags|=PPosition;
14784 windows->info.attributes.win_gravity=UnmapGravity;
14785 windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14786 StructureNotifyMask;
14787 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14788 manager_hints->input=MagickFalse;
14789 manager_hints->initial_state=NormalState;
14790 manager_hints->window_group=windows->image.id;
14791 XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14792 &windows->info);
14793 windows->info.highlight_stipple=XCreateBitmapFromData(display,
14794 windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14795 windows->info.shadow_stipple=XCreateBitmapFromData(display,
14796 windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14797 (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14798 if (windows->image.mapped != MagickFalse)
14799 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14800 if (display_image->debug != MagickFalse)
14801 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14802 windows->info.id);
14803 /*
14804 Initialize Command widget.
14805 */
14806 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14807 resource_info,&windows->command);
14808 windows->command.data=MagickMenus;
14809 (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
cristyb51dff52011-05-19 16:55:47 +000014810 (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
cristy3ed852e2009-09-05 21:47:34 +000014811 resource_info->client_name);
14812 windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14813 resource_name,"geometry",(char *) NULL);
14814 (void) CloneString(&windows->command.name,MagickTitle);
14815 windows->command.border_width=0;
14816 windows->command.flags|=PPosition;
14817 windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14818 ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14819 OwnerGrabButtonMask | StructureNotifyMask;
14820 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14821 manager_hints->input=MagickTrue;
14822 manager_hints->initial_state=NormalState;
14823 manager_hints->window_group=windows->image.id;
14824 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14825 &windows->command);
14826 windows->command.highlight_stipple=XCreateBitmapFromData(display,
14827 windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14828 HighlightHeight);
14829 windows->command.shadow_stipple=XCreateBitmapFromData(display,
14830 windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14831 (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14832 if (windows->command.mapped != MagickFalse)
14833 (void) XMapRaised(display,windows->command.id);
14834 if (display_image->debug != MagickFalse)
14835 (void) LogMagickEvent(X11Event,GetMagickModule(),
14836 "Window id: 0x%lx (command)",windows->command.id);
14837 /*
14838 Initialize Widget window.
14839 */
14840 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14841 resource_info,&windows->widget);
cristyb51dff52011-05-19 16:55:47 +000014842 (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
cristy3ed852e2009-09-05 21:47:34 +000014843 resource_info->client_name);
14844 windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14845 resource_name,"geometry",(char *) NULL);
14846 windows->widget.border_width=0;
14847 windows->widget.flags|=PPosition;
14848 windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14849 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14850 KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14851 StructureNotifyMask;
14852 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14853 manager_hints->input=MagickTrue;
14854 manager_hints->initial_state=NormalState;
14855 manager_hints->window_group=windows->image.id;
14856 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14857 &windows->widget);
14858 windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14859 windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14860 windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14861 windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14862 (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14863 if (display_image->debug != MagickFalse)
14864 (void) LogMagickEvent(X11Event,GetMagickModule(),
14865 "Window id: 0x%lx (widget)",windows->widget.id);
14866 /*
14867 Initialize popup window.
14868 */
14869 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14870 resource_info,&windows->popup);
14871 windows->popup.border_width=0;
14872 windows->popup.flags|=PPosition;
14873 windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14874 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14875 KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14876 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14877 manager_hints->input=MagickTrue;
14878 manager_hints->initial_state=NormalState;
14879 manager_hints->window_group=windows->image.id;
14880 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14881 &windows->popup);
14882 windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14883 windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14884 windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14885 windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14886 (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14887 if (display_image->debug != MagickFalse)
14888 (void) LogMagickEvent(X11Event,GetMagickModule(),
14889 "Window id: 0x%lx (pop up)",windows->popup.id);
14890 /*
14891 Initialize Magnify window and cursor.
14892 */
14893 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14894 resource_info,&windows->magnify);
14895 if (resource_info->use_shared_memory == MagickFalse)
14896 windows->magnify.shared_memory=MagickFalse;
cristyb51dff52011-05-19 16:55:47 +000014897 (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
cristy3ed852e2009-09-05 21:47:34 +000014898 resource_info->client_name);
14899 windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14900 resource_name,"geometry",(char *) NULL);
cristyb51dff52011-05-19 16:55:47 +000014901 (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
cristy3ed852e2009-09-05 21:47:34 +000014902 resource_info->magnify);
14903 if (windows->magnify.cursor != (Cursor) NULL)
14904 (void) XFreeCursor(display,windows->magnify.cursor);
14905 windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14906 map_info->colormap,resource_info->background_color,
14907 resource_info->foreground_color);
14908 if (windows->magnify.cursor == (Cursor) NULL)
14909 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14910 display_image->filename);
14911 windows->magnify.width=MagnifySize;
14912 windows->magnify.height=MagnifySize;
14913 windows->magnify.flags|=PPosition;
14914 windows->magnify.min_width=MagnifySize;
14915 windows->magnify.min_height=MagnifySize;
14916 windows->magnify.width_inc=MagnifySize;
14917 windows->magnify.height_inc=MagnifySize;
14918 windows->magnify.data=resource_info->magnify;
14919 windows->magnify.attributes.cursor=windows->magnify.cursor;
14920 windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14921 ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14922 StructureNotifyMask;
14923 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14924 manager_hints->input=MagickTrue;
14925 manager_hints->initial_state=NormalState;
14926 manager_hints->window_group=windows->image.id;
14927 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14928 &windows->magnify);
14929 if (display_image->debug != MagickFalse)
14930 (void) LogMagickEvent(X11Event,GetMagickModule(),
14931 "Window id: 0x%lx (magnify)",windows->magnify.id);
14932 (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14933 /*
14934 Initialize panning window.
14935 */
14936 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14937 resource_info,&windows->pan);
14938 (void) CloneString(&windows->pan.name,"Pan Icon");
14939 windows->pan.width=windows->icon.width;
14940 windows->pan.height=windows->icon.height;
cristyb51dff52011-05-19 16:55:47 +000014941 (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
cristy3ed852e2009-09-05 21:47:34 +000014942 resource_info->client_name);
14943 windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14944 resource_name,"geometry",(char *) NULL);
14945 (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14946 &windows->pan.width,&windows->pan.height);
14947 windows->pan.flags|=PPosition;
14948 windows->pan.immutable=MagickTrue;
14949 windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14950 ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14951 StructureNotifyMask;
14952 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14953 manager_hints->input=MagickFalse;
14954 manager_hints->initial_state=NormalState;
14955 manager_hints->window_group=windows->image.id;
14956 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14957 &windows->pan);
14958 if (display_image->debug != MagickFalse)
14959 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14960 windows->pan.id);
14961 (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14962 if (windows->info.mapped != MagickFalse)
14963 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14964 if ((windows->image.mapped == MagickFalse) ||
14965 (windows->backdrop.id != (Window) NULL))
14966 (void) XMapWindow(display,windows->image.id);
14967 /*
14968 Set our progress monitor and warning handlers.
14969 */
14970 if (warning_handler == (WarningHandler) NULL)
14971 {
14972 warning_handler=resource_info->display_warnings ?
14973 SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14974 warning_handler=resource_info->display_warnings ?
14975 SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14976 }
14977 /*
14978 Initialize Image and Magnify X images.
14979 */
14980 windows->image.x=0;
14981 windows->image.y=0;
14982 windows->magnify.shape=MagickFalse;
14983 width=(unsigned int) display_image->columns;
14984 height=(unsigned int) display_image->rows;
14985 if ((display_image->columns != width) || (display_image->rows != height))
14986 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14987 display_image->filename);
14988 status=XMakeImage(display,resource_info,&windows->image,display_image,
cristy051718b2011-08-28 22:49:25 +000014989 width,height,exception);
cristy3ed852e2009-09-05 21:47:34 +000014990 if (status == MagickFalse)
14991 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14992 display_image->filename);
14993 status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
cristy051718b2011-08-28 22:49:25 +000014994 windows->magnify.width,windows->magnify.height,exception);
cristy3ed852e2009-09-05 21:47:34 +000014995 if (status == MagickFalse)
14996 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14997 display_image->filename);
14998 if (windows->magnify.mapped != MagickFalse)
14999 (void) XMapRaised(display,windows->magnify.id);
15000 if (windows->pan.mapped != MagickFalse)
15001 (void) XMapRaised(display,windows->pan.id);
15002 windows->image.window_changes.width=(int) display_image->columns;
15003 windows->image.window_changes.height=(int) display_image->rows;
cristy051718b2011-08-28 22:49:25 +000015004 (void) XConfigureImage(display,resource_info,windows,display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000015005 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15006 (void) XSync(display,MagickFalse);
15007 /*
15008 Respond to events.
15009 */
15010 delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15011 timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15012 update_time=0;
15013 if (resource_info->update != MagickFalse)
15014 {
15015 MagickBooleanType
15016 status;
15017
15018 /*
15019 Determine when file data was last modified.
15020 */
15021 status=GetPathAttributes(display_image->filename,&attributes);
15022 if (status != MagickFalse)
15023 update_time=attributes.st_mtime;
15024 }
15025 *state&=(~FormerImageState);
15026 *state&=(~MontageImageState);
15027 *state&=(~NextImageState);
15028 do
15029 {
15030 /*
15031 Handle a window event.
15032 */
15033 if (windows->image.mapped != MagickFalse)
15034 if ((display_image->delay != 0) || (resource_info->update != 0))
15035 {
15036 if (timer < time((time_t *) NULL))
15037 {
15038 if (resource_info->update == MagickFalse)
15039 *state|=NextImageState | ExitState;
15040 else
15041 {
15042 MagickBooleanType
15043 status;
15044
15045 /*
15046 Determine if image file was modified.
15047 */
15048 status=GetPathAttributes(display_image->filename,&attributes);
15049 if (status != MagickFalse)
15050 if (update_time != attributes.st_mtime)
15051 {
15052 /*
15053 Redisplay image.
15054 */
cristyb51dff52011-05-19 16:55:47 +000015055 (void) FormatLocaleString(
cristy3ed852e2009-09-05 21:47:34 +000015056 resource_info->image_info->filename,MaxTextExtent,
15057 "%s:%s",display_image->magick,
15058 display_image->filename);
15059 nexus=ReadImage(resource_info->image_info,
15060 &display_image->exception);
15061 if (nexus != (Image *) NULL)
15062 {
15063 nexus=DestroyImage(nexus);
15064 *state|=NextImageState | ExitState;
15065 }
15066 }
15067 delay=display_image->delay/MagickMax(
15068 display_image->ticks_per_second,1L);
15069 timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15070 }
15071 }
15072 if (XEventsQueued(display,QueuedAfterFlush) == 0)
15073 {
15074 /*
15075 Do not block if delay > 0.
15076 */
15077 XDelay(display,SuspendTime << 2);
15078 continue;
15079 }
15080 }
15081 timestamp=time((time_t *) NULL);
15082 (void) XNextEvent(display,&event);
15083 if (windows->image.stasis == MagickFalse)
15084 windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15085 MagickTrue : MagickFalse;
15086 if (windows->magnify.stasis == MagickFalse)
15087 windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15088 MagickTrue : MagickFalse;
15089 if (event.xany.window == windows->command.id)
15090 {
15091 /*
15092 Select a command from the Command widget.
15093 */
15094 id=XCommandWidget(display,windows,CommandMenu,&event);
15095 if (id < 0)
15096 continue;
15097 (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15098 command_type=CommandMenus[id];
15099 if (id < MagickMenus)
15100 {
15101 /*
15102 Select a command from a pop-up menu.
15103 */
15104 entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15105 command);
15106 if (entry < 0)
15107 continue;
15108 (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15109 command_type=Commands[id][entry];
15110 }
15111 if (command_type != NullCommand)
15112 nexus=XMagickCommand(display,resource_info,windows,command_type,
cristy051718b2011-08-28 22:49:25 +000015113 &display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000015114 continue;
15115 }
15116 switch (event.type)
15117 {
15118 case ButtonPress:
15119 {
15120 if (display_image->debug != MagickFalse)
15121 (void) LogMagickEvent(X11Event,GetMagickModule(),
15122 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15123 event.xbutton.button,event.xbutton.x,event.xbutton.y);
15124 if ((event.xbutton.button == Button3) &&
15125 (event.xbutton.state & Mod1Mask))
15126 {
15127 /*
15128 Convert Alt-Button3 to Button2.
15129 */
15130 event.xbutton.button=Button2;
15131 event.xbutton.state&=(~Mod1Mask);
15132 }
15133 if (event.xbutton.window == windows->backdrop.id)
15134 {
15135 (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15136 event.xbutton.time);
15137 break;
15138 }
15139 if (event.xbutton.window == windows->image.id)
15140 {
15141 switch (event.xbutton.button)
15142 {
15143 case Button1:
15144 {
15145 if (resource_info->immutable)
15146 {
15147 /*
15148 Select a command from the Virtual menu.
15149 */
15150 entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15151 command);
15152 if (entry >= 0)
15153 nexus=XMagickCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000015154 VirtualCommands[entry],&display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000015155 break;
15156 }
15157 /*
15158 Map/unmap Command widget.
15159 */
15160 if (windows->command.mapped != MagickFalse)
15161 (void) XWithdrawWindow(display,windows->command.id,
15162 windows->command.screen);
15163 else
15164 {
15165 (void) XCommandWidget(display,windows,CommandMenu,
15166 (XEvent *) NULL);
15167 (void) XMapRaised(display,windows->command.id);
15168 }
15169 break;
15170 }
15171 case Button2:
15172 {
15173 /*
15174 User pressed the image magnify button.
15175 */
15176 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
cristy051718b2011-08-28 22:49:25 +000015177 &display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000015178 XMagnifyImage(display,windows,&event);
15179 break;
15180 }
15181 case Button3:
15182 {
15183 if (resource_info->immutable)
15184 {
15185 /*
15186 Select a command from the Virtual menu.
15187 */
15188 entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15189 command);
15190 if (entry >= 0)
15191 nexus=XMagickCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000015192 VirtualCommands[entry],&display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000015193 break;
15194 }
15195 if (display_image->montage != (char *) NULL)
15196 {
15197 /*
15198 Open or delete a tile from a visual image directory.
15199 */
15200 nexus=XTileImage(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000015201 display_image,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +000015202 if (nexus != (Image *) NULL)
15203 *state|=MontageImageState | NextImageState | ExitState;
cristy49e2d862010-11-12 02:50:30 +000015204 vid_info.x=(short int) windows->image.x;
15205 vid_info.y=(short int) windows->image.y;
cristy3ed852e2009-09-05 21:47:34 +000015206 break;
15207 }
15208 /*
15209 Select a command from the Short Cuts menu.
15210 */
15211 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15212 command);
15213 if (entry >= 0)
15214 nexus=XMagickCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000015215 ShortCutsCommands[entry],&display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000015216 break;
15217 }
15218 case Button4:
15219 {
15220 /*
15221 Wheel up.
15222 */
15223 XTranslateImage(display,windows,*image,XK_Up);
15224 break;
15225 }
15226 case Button5:
15227 {
15228 /*
15229 Wheel down.
15230 */
15231 XTranslateImage(display,windows,*image,XK_Down);
15232 break;
15233 }
15234 default:
15235 break;
15236 }
15237 break;
15238 }
15239 if (event.xbutton.window == windows->magnify.id)
15240 {
15241 int
15242 factor;
15243
15244 static const char
15245 *MagnifyMenu[] =
15246 {
15247 "2",
15248 "4",
15249 "5",
15250 "6",
15251 "7",
15252 "8",
15253 "9",
15254 "3",
15255 (char *) NULL,
15256 };
15257
15258 static KeySym
15259 MagnifyCommands[] =
15260 {
15261 XK_2,
15262 XK_4,
15263 XK_5,
15264 XK_6,
15265 XK_7,
15266 XK_8,
15267 XK_9,
15268 XK_3
15269 };
15270
15271 /*
15272 Select a magnify factor from the pop-up menu.
15273 */
15274 factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15275 if (factor >= 0)
15276 XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15277 break;
15278 }
15279 if (event.xbutton.window == windows->pan.id)
15280 {
15281 switch (event.xbutton.button)
15282 {
15283 case Button4:
15284 {
15285 /*
15286 Wheel up.
15287 */
15288 XTranslateImage(display,windows,*image,XK_Up);
15289 break;
15290 }
15291 case Button5:
15292 {
15293 /*
15294 Wheel down.
15295 */
15296 XTranslateImage(display,windows,*image,XK_Down);
15297 break;
15298 }
15299 default:
15300 {
15301 XPanImage(display,windows,&event);
15302 break;
15303 }
15304 }
15305 break;
15306 }
15307 delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15308 1L);
15309 timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15310 break;
15311 }
15312 case ButtonRelease:
15313 {
15314 if (display_image->debug != MagickFalse)
15315 (void) LogMagickEvent(X11Event,GetMagickModule(),
15316 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15317 event.xbutton.button,event.xbutton.x,event.xbutton.y);
15318 break;
15319 }
15320 case ClientMessage:
15321 {
15322 if (display_image->debug != MagickFalse)
15323 (void) LogMagickEvent(X11Event,GetMagickModule(),
15324 "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
cristyf2faecf2010-05-28 19:19:36 +000015325 event.xclient.message_type,event.xclient.format,(unsigned long)
cristy3ed852e2009-09-05 21:47:34 +000015326 event.xclient.data.l[0]);
15327 if (event.xclient.message_type == windows->im_protocols)
15328 {
cristyecd0ab52010-05-30 14:59:20 +000015329 if (*event.xclient.data.l == (long) windows->im_update_widget)
cristy3ed852e2009-09-05 21:47:34 +000015330 {
15331 (void) CloneString(&windows->command.name,MagickTitle);
15332 windows->command.data=MagickMenus;
15333 (void) XCommandWidget(display,windows,CommandMenu,
15334 (XEvent *) NULL);
15335 break;
15336 }
cristyecd0ab52010-05-30 14:59:20 +000015337 if (*event.xclient.data.l == (long) windows->im_update_colormap)
cristy3ed852e2009-09-05 21:47:34 +000015338 {
15339 /*
15340 Update graphic context and window colormap.
15341 */
15342 for (i=0; i < (int) number_windows; i++)
15343 {
15344 if (magick_windows[i]->id == windows->icon.id)
15345 continue;
15346 context_values.background=pixel->background_color.pixel;
15347 context_values.foreground=pixel->foreground_color.pixel;
15348 (void) XChangeGC(display,magick_windows[i]->annotate_context,
15349 context_mask,&context_values);
15350 (void) XChangeGC(display,magick_windows[i]->widget_context,
15351 context_mask,&context_values);
15352 context_values.background=pixel->foreground_color.pixel;
15353 context_values.foreground=pixel->background_color.pixel;
15354 context_values.plane_mask=context_values.background ^
15355 context_values.foreground;
15356 (void) XChangeGC(display,magick_windows[i]->highlight_context,
cristybb503372010-05-27 20:51:26 +000015357 (size_t) (context_mask | GCPlaneMask),
cristy3ed852e2009-09-05 21:47:34 +000015358 &context_values);
15359 magick_windows[i]->attributes.background_pixel=
15360 pixel->background_color.pixel;
15361 magick_windows[i]->attributes.border_pixel=
15362 pixel->border_color.pixel;
15363 magick_windows[i]->attributes.colormap=map_info->colormap;
15364 (void) XChangeWindowAttributes(display,magick_windows[i]->id,
cristy49e2d862010-11-12 02:50:30 +000015365 (unsigned long) magick_windows[i]->mask,
15366 &magick_windows[i]->attributes);
cristy3ed852e2009-09-05 21:47:34 +000015367 }
15368 if (windows->pan.mapped != MagickFalse)
15369 {
15370 (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15371 windows->pan.pixmap);
15372 (void) XClearWindow(display,windows->pan.id);
15373 XDrawPanRectangle(display,windows);
15374 }
15375 if (windows->backdrop.id != (Window) NULL)
15376 (void) XInstallColormap(display,map_info->colormap);
15377 break;
15378 }
cristyecd0ab52010-05-30 14:59:20 +000015379 if (*event.xclient.data.l == (long) windows->im_former_image)
cristy3ed852e2009-09-05 21:47:34 +000015380 {
15381 *state|=FormerImageState | ExitState;
15382 break;
15383 }
cristyecd0ab52010-05-30 14:59:20 +000015384 if (*event.xclient.data.l == (long) windows->im_next_image)
cristy3ed852e2009-09-05 21:47:34 +000015385 {
15386 *state|=NextImageState | ExitState;
15387 break;
15388 }
cristyecd0ab52010-05-30 14:59:20 +000015389 if (*event.xclient.data.l == (long) windows->im_retain_colors)
cristy3ed852e2009-09-05 21:47:34 +000015390 {
15391 *state|=RetainColorsState;
15392 break;
15393 }
cristyecd0ab52010-05-30 14:59:20 +000015394 if (*event.xclient.data.l == (long) windows->im_exit)
cristy3ed852e2009-09-05 21:47:34 +000015395 {
15396 *state|=ExitState;
15397 break;
15398 }
15399 break;
15400 }
15401 if (event.xclient.message_type == windows->dnd_protocols)
15402 {
15403 Atom
15404 selection,
15405 type;
15406
15407 int
15408 format,
15409 status;
15410
15411 unsigned char
15412 *data;
15413
cristyf2faecf2010-05-28 19:19:36 +000015414 unsigned long
cristy3ed852e2009-09-05 21:47:34 +000015415 after,
15416 length;
15417
15418 /*
15419 Display image named by the Drag-and-Drop selection.
15420 */
15421 if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15422 break;
15423 selection=XInternAtom(display,"DndSelection",MagickFalse);
cristyecd0ab52010-05-30 14:59:20 +000015424 status=XGetWindowProperty(display,root_window,selection,0L,(long)
cristy3ed852e2009-09-05 21:47:34 +000015425 MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15426 &length,&after,&data);
15427 if ((status != Success) || (length == 0))
15428 break;
15429 if (*event.xclient.data.l == 2)
15430 {
15431 /*
15432 Offix DND.
15433 */
15434 (void) CopyMagickString(resource_info->image_info->filename,
15435 (char *) data,MaxTextExtent);
15436 }
15437 else
15438 {
15439 /*
15440 XDND.
15441 */
15442 if (strncmp((char *) data, "file:", 5) != 0)
15443 {
15444 (void) XFree((void *) data);
15445 break;
15446 }
15447 (void) CopyMagickString(resource_info->image_info->filename,
15448 ((char *) data)+5,MaxTextExtent);
15449 }
15450 nexus=ReadImage(resource_info->image_info,
15451 &display_image->exception);
15452 CatchException(&display_image->exception);
15453 if (nexus != (Image *) NULL)
15454 *state|=NextImageState | ExitState;
15455 (void) XFree((void *) data);
15456 break;
15457 }
15458 /*
15459 If client window delete message, exit.
15460 */
15461 if (event.xclient.message_type != windows->wm_protocols)
15462 break;
cristyecd0ab52010-05-30 14:59:20 +000015463 if (*event.xclient.data.l != (long) windows->wm_delete_window)
cristy3ed852e2009-09-05 21:47:34 +000015464 break;
15465 (void) XWithdrawWindow(display,event.xclient.window,
15466 visual_info->screen);
15467 if (event.xclient.window == windows->image.id)
15468 {
15469 *state|=ExitState;
15470 break;
15471 }
15472 if (event.xclient.window == windows->pan.id)
15473 {
15474 /*
15475 Restore original image size when pan window is deleted.
15476 */
15477 windows->image.window_changes.width=windows->image.ximage->width;
15478 windows->image.window_changes.height=windows->image.ximage->height;
15479 (void) XConfigureImage(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000015480 display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000015481 }
15482 break;
15483 }
15484 case ConfigureNotify:
15485 {
15486 if (display_image->debug != MagickFalse)
15487 (void) LogMagickEvent(X11Event,GetMagickModule(),
15488 "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15489 event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15490 event.xconfigure.y,event.xconfigure.send_event);
15491 if (event.xconfigure.window == windows->image.id)
15492 {
15493 /*
15494 Image window has a new configuration.
15495 */
15496 if (event.xconfigure.send_event != 0)
15497 {
15498 XWindowChanges
15499 window_changes;
15500
15501 /*
15502 Position the transient windows relative of the Image window.
15503 */
15504 if (windows->command.geometry == (char *) NULL)
15505 if (windows->command.mapped == MagickFalse)
15506 {
15507 windows->command.x=event.xconfigure.x-
15508 windows->command.width-25;
15509 windows->command.y=event.xconfigure.y;
15510 XConstrainWindowPosition(display,&windows->command);
15511 window_changes.x=windows->command.x;
15512 window_changes.y=windows->command.y;
15513 (void) XReconfigureWMWindow(display,windows->command.id,
15514 windows->command.screen,(unsigned int) (CWX | CWY),
15515 &window_changes);
15516 }
15517 if (windows->widget.geometry == (char *) NULL)
15518 if (windows->widget.mapped == MagickFalse)
15519 {
15520 windows->widget.x=event.xconfigure.x+
15521 event.xconfigure.width/10;
15522 windows->widget.y=event.xconfigure.y+
15523 event.xconfigure.height/10;
15524 XConstrainWindowPosition(display,&windows->widget);
15525 window_changes.x=windows->widget.x;
15526 window_changes.y=windows->widget.y;
15527 (void) XReconfigureWMWindow(display,windows->widget.id,
15528 windows->widget.screen,(unsigned int) (CWX | CWY),
15529 &window_changes);
15530 }
15531 if (windows->magnify.geometry == (char *) NULL)
15532 if (windows->magnify.mapped == MagickFalse)
15533 {
15534 windows->magnify.x=event.xconfigure.x+
15535 event.xconfigure.width+25;
15536 windows->magnify.y=event.xconfigure.y;
15537 XConstrainWindowPosition(display,&windows->magnify);
15538 window_changes.x=windows->magnify.x;
15539 window_changes.y=windows->magnify.y;
15540 (void) XReconfigureWMWindow(display,windows->magnify.id,
15541 windows->magnify.screen,(unsigned int) (CWX | CWY),
15542 &window_changes);
15543 }
15544 if (windows->pan.geometry == (char *) NULL)
15545 if (windows->pan.mapped == MagickFalse)
15546 {
15547 windows->pan.x=event.xconfigure.x+
15548 event.xconfigure.width+25;
15549 windows->pan.y=event.xconfigure.y+
15550 windows->magnify.height+50;
15551 XConstrainWindowPosition(display,&windows->pan);
15552 window_changes.x=windows->pan.x;
15553 window_changes.y=windows->pan.y;
15554 (void) XReconfigureWMWindow(display,windows->pan.id,
15555 windows->pan.screen,(unsigned int) (CWX | CWY),
15556 &window_changes);
15557 }
15558 }
cristyecd0ab52010-05-30 14:59:20 +000015559 if ((event.xconfigure.width == (int) windows->image.width) &&
15560 (event.xconfigure.height == (int) windows->image.height))
cristy3ed852e2009-09-05 21:47:34 +000015561 break;
15562 windows->image.width=(unsigned int) event.xconfigure.width;
15563 windows->image.height=(unsigned int) event.xconfigure.height;
15564 windows->image.x=0;
15565 windows->image.y=0;
15566 if (display_image->montage != (char *) NULL)
15567 {
15568 windows->image.x=vid_info.x;
15569 windows->image.y=vid_info.y;
15570 }
cristy34b9f452010-01-06 20:04:29 +000015571 if ((windows->image.mapped != MagickFalse) &&
15572 (windows->image.stasis != MagickFalse))
15573 {
15574 /*
15575 Update image window configuration.
15576 */
15577 windows->image.window_changes.width=event.xconfigure.width;
15578 windows->image.window_changes.height=event.xconfigure.height;
15579 (void) XConfigureImage(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000015580 display_image,exception);
cristy34b9f452010-01-06 20:04:29 +000015581 }
cristy3ed852e2009-09-05 21:47:34 +000015582 /*
15583 Update pan window configuration.
15584 */
15585 if ((event.xconfigure.width < windows->image.ximage->width) ||
15586 (event.xconfigure.height < windows->image.ximage->height))
15587 {
15588 (void) XMapRaised(display,windows->pan.id);
15589 XDrawPanRectangle(display,windows);
15590 }
15591 else
15592 if (windows->pan.mapped != MagickFalse)
15593 (void) XWithdrawWindow(display,windows->pan.id,
15594 windows->pan.screen);
15595 break;
15596 }
15597 if (event.xconfigure.window == windows->magnify.id)
15598 {
15599 unsigned int
15600 magnify;
15601
15602 /*
15603 Magnify window has a new configuration.
15604 */
15605 windows->magnify.width=(unsigned int) event.xconfigure.width;
15606 windows->magnify.height=(unsigned int) event.xconfigure.height;
15607 if (windows->magnify.mapped == MagickFalse)
15608 break;
15609 magnify=1;
15610 while ((int) magnify <= event.xconfigure.width)
15611 magnify<<=1;
15612 while ((int) magnify <= event.xconfigure.height)
15613 magnify<<=1;
15614 magnify>>=1;
15615 if (((int) magnify != event.xconfigure.width) ||
15616 ((int) magnify != event.xconfigure.height))
15617 {
15618 window_changes.width=(int) magnify;
15619 window_changes.height=(int) magnify;
15620 (void) XReconfigureWMWindow(display,windows->magnify.id,
15621 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15622 &window_changes);
15623 break;
15624 }
15625 if ((windows->magnify.mapped != MagickFalse) &&
15626 (windows->magnify.stasis != MagickFalse))
15627 {
15628 status=XMakeImage(display,resource_info,&windows->magnify,
cristy051718b2011-08-28 22:49:25 +000015629 display_image,windows->magnify.width,windows->magnify.height,
15630 exception);
cristy3ed852e2009-09-05 21:47:34 +000015631 XMakeMagnifyImage(display,windows);
15632 }
15633 break;
15634 }
15635 if ((windows->magnify.mapped != MagickFalse) &&
15636 (event.xconfigure.window == windows->pan.id))
15637 {
15638 /*
15639 Pan icon window has a new configuration.
15640 */
15641 if (event.xconfigure.send_event != 0)
15642 {
15643 windows->pan.x=event.xconfigure.x;
15644 windows->pan.y=event.xconfigure.y;
15645 }
15646 windows->pan.width=(unsigned int) event.xconfigure.width;
15647 windows->pan.height=(unsigned int) event.xconfigure.height;
15648 break;
15649 }
15650 if (event.xconfigure.window == windows->icon.id)
15651 {
15652 /*
15653 Icon window has a new configuration.
15654 */
15655 windows->icon.width=(unsigned int) event.xconfigure.width;
15656 windows->icon.height=(unsigned int) event.xconfigure.height;
15657 break;
15658 }
15659 break;
15660 }
15661 case DestroyNotify:
15662 {
15663 /*
15664 Group leader has exited.
15665 */
15666 if (display_image->debug != MagickFalse)
15667 (void) LogMagickEvent(X11Event,GetMagickModule(),
15668 "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15669 if (event.xdestroywindow.window == windows->group_leader.id)
15670 {
15671 *state|=ExitState;
15672 break;
15673 }
15674 break;
15675 }
15676 case EnterNotify:
15677 {
15678 /*
15679 Selectively install colormap.
15680 */
15681 if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15682 if (event.xcrossing.mode != NotifyUngrab)
15683 XInstallColormap(display,map_info->colormap);
15684 break;
15685 }
15686 case Expose:
15687 {
15688 if (display_image->debug != MagickFalse)
15689 (void) LogMagickEvent(X11Event,GetMagickModule(),
15690 "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15691 event.xexpose.width,event.xexpose.height,event.xexpose.x,
15692 event.xexpose.y);
15693 /*
15694 Refresh windows that are now exposed.
15695 */
cristy6bee4042010-01-30 15:27:14 +000015696 if ((event.xexpose.window == windows->image.id) &&
15697 (windows->image.mapped != MagickFalse))
15698 {
15699 XRefreshWindow(display,&windows->image,&event);
15700 delay=display_image->delay/MagickMax(
15701 display_image->ticks_per_second,1L);
15702 timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15703 break;
15704 }
15705 if ((event.xexpose.window == windows->magnify.id) &&
15706 (windows->magnify.mapped != MagickFalse))
15707 {
15708 XMakeMagnifyImage(display,windows);
15709 break;
15710 }
cristy3ed852e2009-09-05 21:47:34 +000015711 if (event.xexpose.window == windows->pan.id)
cristy6bee4042010-01-30 15:27:14 +000015712 {
15713 XDrawPanRectangle(display,windows);
15714 break;
15715 }
cristy3ed852e2009-09-05 21:47:34 +000015716 if (event.xexpose.window == windows->icon.id)
cristy6bee4042010-01-30 15:27:14 +000015717 {
15718 XRefreshWindow(display,&windows->icon,&event);
15719 break;
15720 }
cristy3ed852e2009-09-05 21:47:34 +000015721 break;
15722 }
15723 case KeyPress:
15724 {
15725 int
15726 length;
15727
15728 /*
15729 Respond to a user key press.
15730 */
15731 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15732 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15733 *(command+length)='\0';
15734 if (display_image->debug != MagickFalse)
15735 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000015736 "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
cristy3ed852e2009-09-05 21:47:34 +000015737 key_symbol,command);
15738 if (event.xkey.window == windows->image.id)
15739 {
15740 command_type=XImageWindowCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000015741 event.xkey.state,key_symbol,&display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000015742 if (command_type != NullCommand)
15743 nexus=XMagickCommand(display,resource_info,windows,command_type,
cristy051718b2011-08-28 22:49:25 +000015744 &display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000015745 }
15746 if (event.xkey.window == windows->magnify.id)
15747 XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15748 if (event.xkey.window == windows->pan.id)
15749 {
15750 if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15751 (void) XWithdrawWindow(display,windows->pan.id,
15752 windows->pan.screen);
15753 else
15754 if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15755 XTextViewWidget(display,resource_info,windows,MagickFalse,
15756 "Help Viewer - Image Pan",ImagePanHelp);
15757 else
15758 XTranslateImage(display,windows,*image,key_symbol);
15759 }
15760 delay=display_image->delay/MagickMax(
15761 display_image->ticks_per_second,1L);
15762 timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15763 break;
15764 }
15765 case KeyRelease:
15766 {
15767 /*
15768 Respond to a user key release.
15769 */
15770 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15771 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15772 if (display_image->debug != MagickFalse)
15773 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000015774 "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
cristy3ed852e2009-09-05 21:47:34 +000015775 break;
15776 }
15777 case LeaveNotify:
15778 {
15779 /*
15780 Selectively uninstall colormap.
15781 */
15782 if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15783 if (event.xcrossing.mode != NotifyUngrab)
15784 XUninstallColormap(display,map_info->colormap);
15785 break;
15786 }
15787 case MapNotify:
15788 {
15789 if (display_image->debug != MagickFalse)
15790 (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15791 event.xmap.window);
15792 if (event.xmap.window == windows->backdrop.id)
15793 {
15794 (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15795 CurrentTime);
15796 windows->backdrop.mapped=MagickTrue;
15797 break;
15798 }
15799 if (event.xmap.window == windows->image.id)
15800 {
15801 if (windows->backdrop.id != (Window) NULL)
15802 (void) XInstallColormap(display,map_info->colormap);
15803 if (LocaleCompare(display_image->magick,"LOGO") == 0)
15804 {
15805 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15806 nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15807 }
15808 if (((int) windows->image.width < windows->image.ximage->width) ||
15809 ((int) windows->image.height < windows->image.ximage->height))
15810 (void) XMapRaised(display,windows->pan.id);
15811 windows->image.mapped=MagickTrue;
15812 break;
15813 }
15814 if (event.xmap.window == windows->magnify.id)
15815 {
15816 XMakeMagnifyImage(display,windows);
15817 windows->magnify.mapped=MagickTrue;
15818 (void) XWithdrawWindow(display,windows->info.id,
15819 windows->info.screen);
15820 break;
15821 }
15822 if (event.xmap.window == windows->pan.id)
15823 {
cristy051718b2011-08-28 22:49:25 +000015824 XMakePanImage(display,resource_info,windows,display_image,
15825 exception);
cristy3ed852e2009-09-05 21:47:34 +000015826 windows->pan.mapped=MagickTrue;
15827 break;
15828 }
15829 if (event.xmap.window == windows->info.id)
15830 {
15831 windows->info.mapped=MagickTrue;
15832 break;
15833 }
15834 if (event.xmap.window == windows->icon.id)
15835 {
15836 MagickBooleanType
15837 taint;
15838
15839 /*
15840 Create an icon image.
15841 */
15842 taint=display_image->taint;
15843 XMakeStandardColormap(display,icon_visual,icon_resources,
15844 display_image,icon_map,icon_pixel);
15845 (void) XMakeImage(display,icon_resources,&windows->icon,
cristy051718b2011-08-28 22:49:25 +000015846 display_image,windows->icon.width,windows->icon.height,
15847 exception);
cristy3ed852e2009-09-05 21:47:34 +000015848 display_image->taint=taint;
15849 (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15850 windows->icon.pixmap);
15851 (void) XClearWindow(display,windows->icon.id);
15852 (void) XWithdrawWindow(display,windows->info.id,
15853 windows->info.screen);
15854 windows->icon.mapped=MagickTrue;
15855 break;
15856 }
15857 if (event.xmap.window == windows->command.id)
15858 {
15859 windows->command.mapped=MagickTrue;
15860 break;
15861 }
15862 if (event.xmap.window == windows->popup.id)
15863 {
15864 windows->popup.mapped=MagickTrue;
15865 break;
15866 }
15867 if (event.xmap.window == windows->widget.id)
15868 {
15869 windows->widget.mapped=MagickTrue;
15870 break;
15871 }
15872 break;
15873 }
15874 case MappingNotify:
15875 {
15876 (void) XRefreshKeyboardMapping(&event.xmapping);
15877 break;
15878 }
15879 case NoExpose:
15880 break;
15881 case PropertyNotify:
15882 {
15883 Atom
15884 type;
15885
15886 int
15887 format,
15888 status;
15889
15890 unsigned char
15891 *data;
15892
cristyf2faecf2010-05-28 19:19:36 +000015893 unsigned long
cristy3ed852e2009-09-05 21:47:34 +000015894 after,
15895 length;
15896
15897 if (display_image->debug != MagickFalse)
15898 (void) LogMagickEvent(X11Event,GetMagickModule(),
15899 "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15900 event.xproperty.atom,event.xproperty.state);
15901 if (event.xproperty.atom != windows->im_remote_command)
15902 break;
15903 /*
15904 Display image named by the remote command protocol.
15905 */
15906 status=XGetWindowProperty(display,event.xproperty.window,
cristyecd0ab52010-05-30 14:59:20 +000015907 event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
cristy3ed852e2009-09-05 21:47:34 +000015908 AnyPropertyType,&type,&format,&length,&after,&data);
15909 if ((status != Success) || (length == 0))
15910 break;
15911 if (LocaleCompare((char *) data,"-quit") == 0)
15912 {
15913 XClientMessage(display,windows->image.id,windows->im_protocols,
15914 windows->im_exit,CurrentTime);
15915 (void) XFree((void *) data);
15916 break;
15917 }
15918 (void) CopyMagickString(resource_info->image_info->filename,
15919 (char *) data,MaxTextExtent);
15920 (void) XFree((void *) data);
15921 nexus=ReadImage(resource_info->image_info,&display_image->exception);
15922 CatchException(&display_image->exception);
15923 if (nexus != (Image *) NULL)
15924 *state|=NextImageState | ExitState;
15925 break;
15926 }
15927 case ReparentNotify:
15928 {
15929 if (display_image->debug != MagickFalse)
15930 (void) LogMagickEvent(X11Event,GetMagickModule(),
15931 "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15932 event.xreparent.window);
15933 break;
15934 }
15935 case UnmapNotify:
15936 {
15937 if (display_image->debug != MagickFalse)
15938 (void) LogMagickEvent(X11Event,GetMagickModule(),
15939 "Unmap Notify: 0x%lx",event.xunmap.window);
15940 if (event.xunmap.window == windows->backdrop.id)
15941 {
15942 windows->backdrop.mapped=MagickFalse;
15943 break;
15944 }
15945 if (event.xunmap.window == windows->image.id)
15946 {
15947 windows->image.mapped=MagickFalse;
15948 break;
15949 }
15950 if (event.xunmap.window == windows->magnify.id)
15951 {
15952 windows->magnify.mapped=MagickFalse;
15953 break;
15954 }
15955 if (event.xunmap.window == windows->pan.id)
15956 {
15957 windows->pan.mapped=MagickFalse;
15958 break;
15959 }
15960 if (event.xunmap.window == windows->info.id)
15961 {
15962 windows->info.mapped=MagickFalse;
15963 break;
15964 }
15965 if (event.xunmap.window == windows->icon.id)
15966 {
15967 if (map_info->colormap == icon_map->colormap)
15968 XConfigureImageColormap(display,resource_info,windows,
15969 display_image);
15970 (void) XFreeStandardColormap(display,icon_visual,icon_map,
15971 icon_pixel);
15972 windows->icon.mapped=MagickFalse;
15973 break;
15974 }
15975 if (event.xunmap.window == windows->command.id)
15976 {
15977 windows->command.mapped=MagickFalse;
15978 break;
15979 }
15980 if (event.xunmap.window == windows->popup.id)
15981 {
15982 if (windows->backdrop.id != (Window) NULL)
15983 (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15984 CurrentTime);
15985 windows->popup.mapped=MagickFalse;
15986 break;
15987 }
15988 if (event.xunmap.window == windows->widget.id)
15989 {
15990 if (windows->backdrop.id != (Window) NULL)
15991 (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15992 CurrentTime);
15993 windows->widget.mapped=MagickFalse;
15994 break;
15995 }
15996 break;
15997 }
15998 default:
15999 {
16000 if (display_image->debug != MagickFalse)
16001 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16002 event.type);
16003 break;
16004 }
16005 }
16006 } while (!(*state & ExitState));
16007 if ((*state & ExitState) == 0)
16008 (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
cristy051718b2011-08-28 22:49:25 +000016009 &display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000016010 else
16011 if (resource_info->confirm_edit != MagickFalse)
16012 {
16013 /*
16014 Query user if image has changed.
16015 */
16016 if ((resource_info->immutable == MagickFalse) &&
16017 (display_image->taint != MagickFalse))
16018 {
16019 int
16020 status;
16021
16022 status=XConfirmWidget(display,windows,"Your image changed.",
16023 "Do you want to save it");
16024 if (status == 0)
16025 *state&=(~ExitState);
16026 else
16027 if (status > 0)
16028 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
cristy051718b2011-08-28 22:49:25 +000016029 &display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000016030 }
16031 }
16032 if ((windows->visual_info->klass == GrayScale) ||
16033 (windows->visual_info->klass == PseudoColor) ||
16034 (windows->visual_info->klass == DirectColor))
16035 {
16036 /*
16037 Withdraw pan and Magnify window.
16038 */
16039 if (windows->info.mapped != MagickFalse)
16040 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16041 if (windows->magnify.mapped != MagickFalse)
16042 (void) XWithdrawWindow(display,windows->magnify.id,
16043 windows->magnify.screen);
16044 if (windows->command.mapped != MagickFalse)
16045 (void) XWithdrawWindow(display,windows->command.id,
16046 windows->command.screen);
16047 }
16048 if (windows->pan.mapped != MagickFalse)
16049 (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16050 if (resource_info->backdrop == MagickFalse)
16051 if (windows->backdrop.mapped)
16052 {
16053 (void) XWithdrawWindow(display,windows->backdrop.id,
16054 windows->backdrop.screen);
16055 (void) XDestroyWindow(display,windows->backdrop.id);
16056 windows->backdrop.id=(Window) NULL;
16057 (void) XWithdrawWindow(display,windows->image.id,
16058 windows->image.screen);
16059 (void) XDestroyWindow(display,windows->image.id);
16060 windows->image.id=(Window) NULL;
16061 }
16062 XSetCursorState(display,windows,MagickTrue);
16063 XCheckRefreshWindows(display,windows);
16064 if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16065 *state&=(~ExitState);
16066 if (*state & ExitState)
16067 {
16068 /*
16069 Free Standard Colormap.
16070 */
16071 (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16072 if (resource_info->map_type == (char *) NULL)
16073 (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16074 /*
16075 Free X resources.
16076 */
16077 if (resource_info->copy_image != (Image *) NULL)
16078 {
16079 resource_info->copy_image=DestroyImage(resource_info->copy_image);
16080 resource_info->copy_image=NewImageList();
16081 }
16082 DestroyXResources();
16083 }
16084 (void) XSync(display,MagickFalse);
16085 /*
16086 Restore our progress monitor and warning handlers.
16087 */
16088 (void) SetErrorHandler(warning_handler);
16089 (void) SetWarningHandler(warning_handler);
16090 /*
16091 Change to home directory.
16092 */
cristy00976d82011-02-20 20:31:28 +000016093 directory=getcwd(working_directory,MaxTextExtent);
16094 (void) directory;
cristy3ed852e2009-09-05 21:47:34 +000016095 {
16096 int
16097 status;
16098
16099 status=chdir(resource_info->home_directory);
16100 if (status == -1)
16101 (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
16102 FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
16103 }
16104 *image=display_image;
16105 return(nexus);
16106}
16107#else
16108
16109/*
16110%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16111% %
16112% %
16113% %
16114+ D i s p l a y I m a g e s %
16115% %
16116% %
16117% %
16118%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16119%
16120% DisplayImages() displays an image sequence to any X window screen. It
16121% returns a value other than 0 if successful. Check the exception member
16122% of image to determine the reason for any failure.
16123%
16124% The format of the DisplayImages method is:
16125%
16126% MagickBooleanType DisplayImages(const ImageInfo *image_info,
cristy051718b2011-08-28 22:49:25 +000016127% Image *images,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000016128%
16129% A description of each parameter follows:
16130%
16131% o image_info: the image info.
16132%
16133% o image: the image.
16134%
cristy051718b2011-08-28 22:49:25 +000016135% o exception: return any errors or warnings in this structure.
16136%
cristy3ed852e2009-09-05 21:47:34 +000016137*/
16138MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
cristy051718b2011-08-28 22:49:25 +000016139 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000016140{
16141 assert(image_info != (const ImageInfo *) NULL);
16142 assert(image_info->signature == MagickSignature);
16143 assert(image != (Image *) NULL);
16144 assert(image->signature == MagickSignature);
16145 if (image->debug != MagickFalse)
16146 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy051718b2011-08-28 22:49:25 +000016147 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16148 "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image->filename);
cristy3ed852e2009-09-05 21:47:34 +000016149 return(MagickFalse);
16150}
16151
16152/*
16153%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16154% %
16155% %
16156% %
16157+ R e m o t e D i s p l a y C o m m a n d %
16158% %
16159% %
16160% %
16161%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16162%
16163% RemoteDisplayCommand() encourages a remote display program to display the
16164% specified image filename.
16165%
16166% The format of the RemoteDisplayCommand method is:
16167%
16168% MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16169% const char *window,const char *filename,ExceptionInfo *exception)
16170%
16171% A description of each parameter follows:
16172%
16173% o image_info: the image info.
16174%
16175% o window: Specifies the name or id of an X window.
16176%
16177% o filename: the name of the image filename to display.
16178%
16179% o exception: return any errors or warnings in this structure.
16180%
16181*/
16182MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16183 const char *window,const char *filename,ExceptionInfo *exception)
16184{
16185 assert(image_info != (const ImageInfo *) NULL);
16186 assert(image_info->signature == MagickSignature);
16187 assert(filename != (char *) NULL);
16188 (void) window;
16189 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16190 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16191 "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16192 return(MagickFalse);
16193}
16194#endif