blob: 519019030c2642f39f817722213a26f5576dba30 [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"
cristy52010022011-10-21 18:07:37 +000046#include "MagickCore/cache-private.h"
cristy4c08aed2011-07-01 19:47:50 +000047#include "MagickCore/client.h"
48#include "MagickCore/color.h"
49#include "MagickCore/colorspace.h"
50#include "MagickCore/composite.h"
51#include "MagickCore/constitute.h"
52#include "MagickCore/decorate.h"
53#include "MagickCore/delegate.h"
54#include "MagickCore/display.h"
55#include "MagickCore/display-private.h"
cristyc53413d2011-11-17 13:04:26 +000056#include "MagickCore/distort.h"
cristy4c08aed2011-07-01 19:47:50 +000057#include "MagickCore/draw.h"
58#include "MagickCore/effect.h"
59#include "MagickCore/enhance.h"
60#include "MagickCore/exception.h"
61#include "MagickCore/exception-private.h"
62#include "MagickCore/fx.h"
63#include "MagickCore/geometry.h"
64#include "MagickCore/image.h"
65#include "MagickCore/image-private.h"
66#include "MagickCore/list.h"
67#include "MagickCore/log.h"
68#include "MagickCore/magick.h"
69#include "MagickCore/memory_.h"
70#include "MagickCore/monitor.h"
71#include "MagickCore/monitor-private.h"
72#include "MagickCore/montage.h"
73#include "MagickCore/option.h"
74#include "MagickCore/paint.h"
75#include "MagickCore/pixel.h"
76#include "MagickCore/pixel-accessor.h"
77#include "MagickCore/PreRvIcccm.h"
78#include "MagickCore/property.h"
79#include "MagickCore/quantum.h"
80#include "MagickCore/quantum-private.h"
81#include "MagickCore/resize.h"
82#include "MagickCore/resource_.h"
83#include "MagickCore/shear.h"
84#include "MagickCore/segment.h"
cristy7497f482011-12-08 01:57:31 +000085#include "MagickCore/statistic.h"
cristy4c08aed2011-07-01 19:47:50 +000086#include "MagickCore/string_.h"
87#include "MagickCore/string-private.h"
88#include "MagickCore/transform.h"
89#include "MagickCore/threshold.h"
90#include "MagickCore/utility.h"
cristyd1dd6e42011-09-04 01:46:08 +000091#include "MagickCore/utility-private.h"
cristy4c08aed2011-07-01 19:47:50 +000092#include "MagickCore/version.h"
93#include "MagickCore/widget.h"
cristybcbda3f2011-09-03 13:01:22 +000094#include "MagickCore/widget-private.h"
95#include "MagickCore/xwindow.h"
cristy4c08aed2011-07-01 19:47:50 +000096#include "MagickCore/xwindow-private.h"
cristy3ed852e2009-09-05 21:47:34 +000097
98#if defined(MAGICKCORE_X11_DELEGATE)
99/*
100 Define declarations.
101*/
cristy49e2d862010-11-12 02:50:30 +0000102#define MaxColors MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
cristy3ed852e2009-09-05 21:47:34 +0000103
104/*
105 Constant declarations.
106*/
107static const unsigned char
108 HighlightBitmap[8] =
109 {
110 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
111 },
cristydd05beb2010-11-21 21:23:39 +0000112 OpaqueBitmap[8] =
113 {
114 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
115 },
cristy3ed852e2009-09-05 21:47:34 +0000116 ShadowBitmap[8] =
117 {
118 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
119 };
120
121static const char
122 *PageSizes[] =
123 {
124 "Letter",
125 "Tabloid",
126 "Ledger",
127 "Legal",
128 "Statement",
129 "Executive",
130 "A3",
131 "A4",
132 "A5",
133 "B4",
134 "B5",
135 "Folio",
136 "Quarto",
137 "10x14",
138 (char *) NULL
139 };
140
141/*
142 Help widget declarations.
143*/
144static const char
145 *ImageAnnotateHelp[] =
146 {
147 "In annotate mode, the Command widget has these options:",
148 "",
149 " Font Name",
150 " fixed",
151 " variable",
152 " 5x8",
153 " 6x10",
154 " 7x13bold",
155 " 8x13bold",
156 " 9x15bold",
157 " 10x20",
158 " 12x24",
159 " Browser...",
160 " Font Color",
161 " black",
162 " blue",
163 " cyan",
164 " green",
165 " gray",
166 " red",
167 " magenta",
168 " yellow",
169 " white",
170 " transparent",
171 " Browser...",
172 " Font Color",
173 " black",
174 " blue",
175 " cyan",
176 " green",
177 " gray",
178 " red",
179 " magenta",
180 " yellow",
181 " white",
182 " transparent",
183 " Browser...",
184 " Rotate Text",
185 " -90",
186 " -45",
187 " -30",
188 " 0",
189 " 30",
190 " 45",
191 " 90",
192 " 180",
193 " Dialog...",
194 " Help",
195 " Dismiss",
196 "",
197 "Choose a font name from the Font Name sub-menu. Additional",
198 "font names can be specified with the font browser. You can",
199 "change the menu names by setting the X resources font1",
200 "through font9.",
201 "",
202 "Choose a font color from the Font Color sub-menu.",
203 "Additional font colors can be specified with the color",
204 "browser. You can change the menu colors by setting the X",
205 "resources pen1 through pen9.",
206 "",
207 "If you select the color browser and press Grab, you can",
208 "choose the font color by moving the pointer to the desired",
209 "color on the screen and press any button.",
210 "",
211 "If you choose to rotate the text, choose Rotate Text from the",
212 "menu and select an angle. Typically you will only want to",
213 "rotate one line of text at a time. Depending on the angle you",
214 "choose, subsequent lines may end up overwriting each other.",
215 "",
216 "Choosing a font and its color is optional. The default font",
217 "is fixed and the default color is black. However, you must",
218 "choose a location to begin entering text and press button 1.",
219 "An underscore character will appear at the location of the",
220 "pointer. The cursor changes to a pencil to indicate you are",
221 "in text mode. To exit immediately, press Dismiss.",
222 "",
223 "In text mode, any key presses will display the character at",
224 "the location of the underscore and advance the underscore",
225 "cursor. Enter your text and once completed press Apply to",
226 "finish your image annotation. To correct errors press BACK",
227 "SPACE. To delete an entire line of text, press DELETE. Any",
228 "text that exceeds the boundaries of the image window is",
229 "automagically continued onto the next line.",
230 "",
231 "The actual color you request for the font is saved in the",
232 "image. However, the color that appears in your image window",
233 "may be different. For example, on a monochrome screen the",
234 "text will appear black or white even if you choose the color",
235 "red as the font color. However, the image saved to a file",
236 "with -write is written with red lettering. To assure the",
237 "correct color text in the final image, any PseudoClass image",
238 "is promoted to DirectClass (see miff(5)). To force a",
239 "PseudoClass image to remain PseudoClass, use -colors.",
240 (char *) NULL,
241 },
242 *ImageChopHelp[] =
243 {
244 "In chop mode, the Command widget has these options:",
245 "",
246 " Direction",
247 " horizontal",
248 " vertical",
249 " Help",
250 " Dismiss",
251 "",
252 "If the you choose the horizontal direction (this the",
253 "default), the area of the image between the two horizontal",
254 "endpoints of the chop line is removed. Otherwise, the area",
255 "of the image between the two vertical endpoints of the chop",
256 "line is removed.",
257 "",
258 "Select a location within the image window to begin your chop,",
259 "press and hold any button. Next, move the pointer to",
260 "another location in the image. As you move a line will",
261 "connect the initial location and the pointer. When you",
262 "release the button, the area within the image to chop is",
263 "determined by which direction you choose from the Command",
264 "widget.",
265 "",
266 "To cancel the image chopping, move the pointer back to the",
267 "starting point of the line and release the button.",
268 (char *) NULL,
269 },
270 *ImageColorEditHelp[] =
271 {
272 "In color edit mode, the Command widget has these options:",
273 "",
274 " Method",
275 " point",
276 " replace",
277 " floodfill",
278 " filltoborder",
279 " reset",
280 " Pixel Color",
281 " black",
282 " blue",
283 " cyan",
284 " green",
285 " gray",
286 " red",
287 " magenta",
288 " yellow",
289 " white",
290 " Browser...",
291 " Border Color",
292 " black",
293 " blue",
294 " cyan",
295 " green",
296 " gray",
297 " red",
298 " magenta",
299 " yellow",
300 " white",
301 " Browser...",
302 " Fuzz",
303 " 0%",
304 " 2%",
305 " 5%",
306 " 10%",
307 " 15%",
308 " Dialog...",
309 " Undo",
310 " Help",
311 " Dismiss",
312 "",
313 "Choose a color editing method from the Method sub-menu",
314 "of the Command widget. The point method recolors any pixel",
315 "selected with the pointer until the button is released. The",
316 "replace method recolors any pixel that matches the color of",
317 "the pixel you select with a button press. Floodfill recolors",
318 "any pixel that matches the color of the pixel you select with",
319 "a button press and is a neighbor. Whereas filltoborder recolors",
320 "any neighbor pixel that is not the border color. Finally reset",
321 "changes the entire image to the designated color.",
322 "",
323 "Next, choose a pixel color from the Pixel Color sub-menu.",
324 "Additional pixel colors can be specified with the color",
325 "browser. You can change the menu colors by setting the X",
326 "resources pen1 through pen9.",
327 "",
328 "Now press button 1 to select a pixel within the image window",
329 "to change its color. Additional pixels may be recolored as",
330 "prescribed by the method you choose.",
331 "",
332 "If the Magnify widget is mapped, it can be helpful in positioning",
333 "your pointer within the image (refer to button 2).",
334 "",
335 "The actual color you request for the pixels is saved in the",
336 "image. However, the color that appears in your image window",
337 "may be different. For example, on a monochrome screen the",
338 "pixel will appear black or white even if you choose the",
339 "color red as the pixel color. However, the image saved to a",
340 "file with -write is written with red pixels. To assure the",
341 "correct color text in the final image, any PseudoClass image",
342 "is promoted to DirectClass (see miff(5)). To force a",
343 "PseudoClass image to remain PseudoClass, use -colors.",
344 (char *) NULL,
345 },
346 *ImageCompositeHelp[] =
347 {
348 "First a widget window is displayed requesting you to enter an",
349 "image name. Press Composite, Grab or type a file name.",
350 "Press Cancel if you choose not to create a composite image.",
351 "When you choose Grab, move the pointer to the desired window",
352 "and press any button.",
353 "",
354 "If the Composite image does not have any matte information,",
355 "you are informed and the file browser is displayed again.",
356 "Enter the name of a mask image. The image is typically",
357 "grayscale and the same size as the composite image. If the",
358 "image is not grayscale, it is converted to grayscale and the",
359 "resulting intensities are used as matte information.",
360 "",
361 "A small window appears showing the location of the cursor in",
362 "the image window. You are now in composite mode. To exit",
363 "immediately, press Dismiss. In composite mode, the Command",
364 "widget has these options:",
365 "",
366 " Operators",
367 " Over",
368 " In",
369 " Out",
370 " Atop",
371 " Xor",
372 " Plus",
373 " Minus",
374 " Add",
375 " Subtract",
376 " Difference",
377 " Multiply",
378 " Bumpmap",
379 " Copy",
380 " CopyRed",
381 " CopyGreen",
382 " CopyBlue",
383 " CopyOpacity",
384 " Clear",
385 " Dissolve",
386 " Displace",
387 " Help",
388 " Dismiss",
389 "",
390 "Choose a composite operation from the Operators sub-menu of",
391 "the Command widget. How each operator behaves is described",
392 "below. Image window is the image currently displayed on",
393 "your X server and image is the image obtained with the File",
394 "Browser widget.",
395 "",
396 "Over The result is the union of the two image shapes,",
397 " with image obscuring image window in the region of",
398 " overlap.",
399 "",
400 "In The result is simply image cut by the shape of",
401 " image window. None of the image data of image",
402 " window is in the result.",
403 "",
404 "Out The resulting image is image with the shape of",
405 " image window cut out.",
406 "",
407 "Atop The result is the same shape as image image window,",
408 " with image obscuring image window where the image",
409 " shapes overlap. Note this differs from over",
410 " because the portion of image outside image window's",
411 " shape does not appear in the result.",
412 "",
413 "Xor The result is the image data from both image and",
414 " image window that is outside the overlap region.",
415 " The overlap region is blank.",
416 "",
417 "Plus The result is just the sum of the image data.",
418 " Output values are cropped to QuantumRange (no overflow).",
419 "",
420 "Minus The result of image - image window, with underflow",
421 " cropped to zero.",
422 "",
423 "Add The result of image + image window, with overflow",
424 " wrapping around (mod 256).",
425 "",
426 "Subtract The result of image - image window, with underflow",
427 " wrapping around (mod 256). The add and subtract",
428 " operators can be used to perform reversible",
429 " transformations.",
430 "",
431 "Difference",
432 " The result of abs(image - image window). This",
433 " useful for comparing two very similar images.",
434 "",
435 "Multiply",
436 " The result of image * image window. This",
437 " useful for the creation of drop-shadows.",
438 "",
439 "Bumpmap The result of surface normals from image * image",
440 " window.",
441 "",
442 "Copy The resulting image is image window replaced with",
443 " image. Here the matte information is ignored.",
444 "",
445 "CopyRed The red layer of the image window is replace with",
446 " the red layer of the image. The other layers are",
447 " untouched.",
448 "",
449 "CopyGreen",
450 " The green layer of the image window is replace with",
451 " the green layer of the image. The other layers are",
452 " untouched.",
453 "",
454 "CopyBlue The blue layer of the image window is replace with",
455 " the blue layer of the image. The other layers are",
456 " untouched.",
457 "",
458 "CopyOpacity",
459 " The matte layer of the image window is replace with",
460 " the matte layer of the image. The other layers are",
461 " untouched.",
462 "",
463 "The image compositor requires a matte, or alpha channel in",
464 "the image for some operations. This extra channel usually",
465 "defines a mask which represents a sort of a cookie-cutter",
466 "for the image. This the case when matte is opaque (full",
467 "coverage) for pixels inside the shape, zero outside, and",
468 "between 0 and QuantumRange on the boundary. If image does not",
469 "have a matte channel, it is initialized with 0 for any pixel",
470 "matching in color to pixel location (0,0), otherwise QuantumRange.",
471 "",
472 "If you choose Dissolve, the composite operator becomes Over. The",
473 "image matte channel percent transparency is initialized to factor.",
474 "The image window is initialized to (100-factor). Where factor is the",
475 "value you specify in the Dialog widget.",
476 "",
477 "Displace shifts the image pixels as defined by a displacement",
478 "map. With this option, image is used as a displacement map.",
479 "Black, within the displacement map, is a maximum positive",
480 "displacement. White is a maximum negative displacement and",
481 "middle gray is neutral. The displacement is scaled to determine",
482 "the pixel shift. By default, the displacement applies in both the",
483 "horizontal and vertical directions. However, if you specify a mask,",
484 "image is the horizontal X displacement and mask the vertical Y",
485 "displacement.",
486 "",
487 "Note that matte information for image window is not retained",
488 "for colormapped X server visuals (e.g. StaticColor,",
489 "StaticColor, GrayScale, PseudoColor). Correct compositing",
490 "behavior may require a TrueColor or DirectColor visual or a",
491 "Standard Colormap.",
492 "",
493 "Choosing a composite operator is optional. The default",
494 "operator is replace. However, you must choose a location to",
495 "composite your image and press button 1. Press and hold the",
496 "button before releasing and an outline of the image will",
497 "appear to help you identify your location.",
498 "",
499 "The actual colors of the composite image is saved. However,",
500 "the color that appears in image window may be different.",
501 "For example, on a monochrome screen image window will appear",
502 "black or white even though your composited image may have",
503 "many colors. If the image is saved to a file it is written",
504 "with the correct colors. To assure the correct colors are",
505 "saved in the final image, any PseudoClass image is promoted",
506 "to DirectClass (see miff(5)). To force a PseudoClass image",
507 "to remain PseudoClass, use -colors.",
508 (char *) NULL,
509 },
510 *ImageCutHelp[] =
511 {
512 "In cut mode, the Command widget has these options:",
513 "",
514 " Help",
515 " Dismiss",
516 "",
517 "To define a cut region, press button 1 and drag. The",
518 "cut region is defined by a highlighted rectangle that",
519 "expands or contracts as it follows the pointer. Once you",
520 "are satisfied with the cut region, release the button.",
521 "You are now in rectify mode. In rectify mode, the Command",
522 "widget has these options:",
523 "",
524 " Cut",
525 " Help",
526 " Dismiss",
527 "",
528 "You can make adjustments by moving the pointer to one of the",
529 "cut rectangle corners, pressing a button, and dragging.",
530 "Finally, press Cut to commit your copy region. To",
531 "exit without cutting the image, press Dismiss.",
532 (char *) NULL,
533 },
534 *ImageCopyHelp[] =
535 {
536 "In copy mode, the Command widget has these options:",
537 "",
538 " Help",
539 " Dismiss",
540 "",
541 "To define a copy region, press button 1 and drag. The",
542 "copy region is defined by a highlighted rectangle that",
543 "expands or contracts as it follows the pointer. Once you",
544 "are satisfied with the copy region, release the button.",
545 "You are now in rectify mode. In rectify mode, the Command",
546 "widget has these options:",
547 "",
548 " Copy",
549 " Help",
550 " Dismiss",
551 "",
552 "You can make adjustments by moving the pointer to one of the",
553 "copy rectangle corners, pressing a button, and dragging.",
554 "Finally, press Copy to commit your copy region. To",
555 "exit without copying the image, press Dismiss.",
556 (char *) NULL,
557 },
558 *ImageCropHelp[] =
559 {
560 "In crop mode, the Command widget has these options:",
561 "",
562 " Help",
563 " Dismiss",
564 "",
565 "To define a cropping region, press button 1 and drag. The",
566 "cropping region is defined by a highlighted rectangle that",
567 "expands or contracts as it follows the pointer. Once you",
568 "are satisfied with the cropping region, release the button.",
569 "You are now in rectify mode. In rectify mode, the Command",
570 "widget has these options:",
571 "",
572 " Crop",
573 " Help",
574 " Dismiss",
575 "",
576 "You can make adjustments by moving the pointer to one of the",
577 "cropping rectangle corners, pressing a button, and dragging.",
578 "Finally, press Crop to commit your cropping region. To",
579 "exit without cropping the image, press Dismiss.",
580 (char *) NULL,
581 },
582 *ImageDrawHelp[] =
583 {
584 "The cursor changes to a crosshair to indicate you are in",
585 "draw mode. To exit immediately, press Dismiss. In draw mode,",
586 "the Command widget has these options:",
587 "",
588 " Element",
589 " point",
590 " line",
591 " rectangle",
592 " fill rectangle",
593 " circle",
594 " fill circle",
595 " ellipse",
596 " fill ellipse",
597 " polygon",
598 " fill polygon",
599 " Color",
600 " black",
601 " blue",
602 " cyan",
603 " green",
604 " gray",
605 " red",
606 " magenta",
607 " yellow",
608 " white",
609 " transparent",
610 " Browser...",
611 " Stipple",
612 " Brick",
613 " Diagonal",
614 " Scales",
615 " Vertical",
616 " Wavy",
617 " Translucent",
618 " Opaque",
619 " Open...",
620 " Width",
621 " 1",
622 " 2",
623 " 4",
624 " 8",
625 " 16",
626 " Dialog...",
627 " Undo",
628 " Help",
629 " Dismiss",
630 "",
631 "Choose a drawing primitive from the Element sub-menu.",
632 "",
633 "Choose a color from the Color sub-menu. Additional",
634 "colors can be specified with the color browser.",
635 "",
636 "If you choose the color browser and press Grab, you can",
637 "select the color by moving the pointer to the desired",
638 "color on the screen and press any button. The transparent",
639 "color updates the image matte channel and is useful for",
640 "image compositing.",
641 "",
642 "Choose a stipple, if appropriate, from the Stipple sub-menu.",
643 "Additional stipples can be specified with the file browser.",
644 "Stipples obtained from the file browser must be on disk in the",
645 "X11 bitmap format.",
646 "",
647 "Choose a width, if appropriate, from the Width sub-menu. To",
648 "choose a specific width select the Dialog widget.",
649 "",
650 "Choose a point in the Image window and press button 1 and",
651 "hold. Next, move the pointer to another location in the",
652 "image. As you move, a line connects the initial location and",
653 "the pointer. When you release the button, the image is",
654 "updated with the primitive you just drew. For polygons, the",
655 "image is updated when you press and release the button without",
656 "moving the pointer.",
657 "",
658 "To cancel image drawing, move the pointer back to the",
659 "starting point of the line and release the button.",
660 (char *) NULL,
661 },
662 *DisplayHelp[] =
663 {
664 "BUTTONS",
665 " The effects of each button press is described below. Three",
666 " buttons are required. If you have a two button mouse,",
667 " button 1 and 3 are returned. Press ALT and button 3 to",
668 " simulate button 2.",
669 "",
670 " 1 Press this button to map or unmap the Command widget.",
671 "",
672 " 2 Press and drag to define a region of the image to",
673 " magnify.",
674 "",
675 " 3 Press and drag to choose from a select set of commands.",
676 " This button behaves differently if the image being",
677 " displayed is a visual image directory. Here, choose a",
678 " particular tile of the directory and press this button and",
679 " drag to select a command from a pop-up menu. Choose from",
680 " these menu items:",
681 "",
682 " Open",
683 " Next",
684 " Former",
685 " Delete",
686 " Update",
687 "",
688 " If you choose Open, the image represented by the tile is",
689 " displayed. To return to the visual image directory, choose",
690 " Next from the Command widget. Next and Former moves to the",
691 " next or former image respectively. Choose Delete to delete",
692 " a particular image tile. Finally, choose Update to",
693 " synchronize all the image tiles with their respective",
694 " images.",
695 "",
696 "COMMAND WIDGET",
697 " The Command widget lists a number of sub-menus and commands.",
698 " They are",
699 "",
700 " File",
701 " Open...",
702 " Next",
703 " Former",
704 " Select...",
705 " Save...",
706 " Print...",
707 " Delete...",
708 " New...",
709 " Visual Directory...",
710 " Quit",
711 " Edit",
712 " Undo",
713 " Redo",
714 " Cut",
715 " Copy",
716 " Paste",
717 " View",
718 " Half Size",
719 " Original Size",
720 " Double Size",
721 " Resize...",
722 " Apply",
723 " Refresh",
724 " Restore",
725 " Transform",
726 " Crop",
727 " Chop",
728 " Flop",
729 " Flip",
730 " Rotate Right",
731 " Rotate Left",
732 " Rotate...",
733 " Shear...",
734 " Roll...",
735 " Trim Edges",
736 " Enhance",
737 " Brightness...",
738 " Saturation...",
739 " Hue...",
740 " Gamma...",
741 " Sharpen...",
742 " Dull",
743 " Contrast Stretch...",
744 " Sigmoidal Contrast...",
745 " Normalize",
746 " Equalize",
747 " Negate",
748 " Grayscale",
749 " Map...",
750 " Quantize...",
751 " Effects",
752 " Despeckle",
753 " Emboss",
754 " Reduce Noise",
755 " Add Noise",
756 " Sharpen...",
757 " Blur...",
758 " Threshold...",
759 " Edge Detect...",
760 " Spread...",
761 " Shade...",
762 " Painting...",
763 " Segment...",
764 " F/X",
765 " Solarize...",
766 " Sepia Tone...",
767 " Swirl...",
768 " Implode...",
769 " Vignette...",
770 " Wave...",
771 " Oil Painting...",
772 " Charcoal Drawing...",
773 " Image Edit",
774 " Annotate...",
775 " Draw...",
776 " Color...",
777 " Matte...",
778 " Composite...",
779 " Add Border...",
780 " Add Frame...",
781 " Comment...",
782 " Launch...",
783 " Region of Interest...",
784 " Miscellany",
785 " Image Info",
786 " Zoom Image",
787 " Show Preview...",
788 " Show Histogram",
789 " Show Matte",
790 " Background...",
791 " Slide Show",
792 " Preferences...",
793 " Help",
794 " Overview",
795 " Browse Documentation",
796 " About Display",
797 "",
798 " Menu items with a indented triangle have a sub-menu. They",
799 " are represented above as the indented items. To access a",
800 " sub-menu item, move the pointer to the appropriate menu and",
801 " press a button and drag. When you find the desired sub-menu",
802 " item, release the button and the command is executed. Move",
803 " the pointer away from the sub-menu if you decide not to",
804 " execute a particular command.",
805 "",
806 "KEYBOARD ACCELERATORS",
807 " Accelerators are one or two key presses that effect a",
808 " particular command. The keyboard accelerators that",
809 " display(1) understands is:",
810 "",
811 " Ctl+O Press to open an image from a file.",
812 "",
813 " space Press to display the next image.",
814 "",
815 " If the image is a multi-paged document such as a Postscript",
816 " document, you can skip ahead several pages by preceding",
817 " this command with a number. For example to display the",
818 " third page beyond the current page, press 3<space>.",
819 "",
820 " backspace Press to display the former image.",
821 "",
822 " If the image is a multi-paged document such as a Postscript",
823 " document, you can skip behind several pages by preceding",
824 " this command with a number. For example to display the",
825 " third page preceding the current page, press 3<backspace>.",
826 "",
827 " Ctl+S Press to write the image to a file.",
828 "",
829 " Ctl+P Press to print the image to a Postscript printer.",
830 "",
831 " Ctl+D Press to delete an image file.",
832 "",
833 " Ctl+N Press to create a blank canvas.",
834 "",
835 " Ctl+Q Press to discard all images and exit program.",
836 "",
837 " Ctl+Z Press to undo last image transformation.",
838 "",
839 " Ctl+R Press to redo last image transformation.",
840 "",
841 " Ctl+X Press to cut a region of the image.",
842 "",
843 " Ctl+C Press to copy a region of the image.",
844 "",
845 " Ctl+V Press to paste a region to the image.",
846 "",
847 " < Press to half the image size.",
848 "",
849 " - Press to return to the original image size.",
850 "",
851 " > Press to double the image size.",
852 "",
853 " % Press to resize the image to a width and height you",
854 " specify.",
855 "",
856 "Cmd-A Press to make any image transformations permanent."
857 "",
858 " By default, any image size transformations are applied",
859 " to the original image to create the image displayed on",
860 " the X server. However, the transformations are not",
861 " permanent (i.e. the original image does not change",
862 " size only the X image does). For example, if you",
863 " press > the X image will appear to double in size,",
864 " but the original image will in fact remain the same size.",
865 " To force the original image to double in size, press >",
866 " followed by Cmd-A.",
867 "",
868 " @ Press to refresh the image window.",
869 "",
870 " C Press to cut out a rectangular region of the image.",
871 "",
872 " [ Press to chop the image.",
873 "",
874 " H Press to flop image in the horizontal direction.",
875 "",
876 " V Press to flip image in the vertical direction.",
877 "",
878 " / Press to rotate the image 90 degrees clockwise.",
879 "",
880 " \\ Press to rotate the image 90 degrees counter-clockwise.",
881 "",
882 " * Press to rotate the image the number of degrees you",
883 " specify.",
884 "",
885 " S Press to shear the image the number of degrees you",
886 " specify.",
887 "",
888 " R Press to roll the image.",
889 "",
890 " T Press to trim the image edges.",
891 "",
892 " Shft-H Press to vary the image hue.",
893 "",
894 " Shft-S Press to vary the color saturation.",
895 "",
896 " Shft-L Press to vary the color brightness.",
897 "",
898 " Shft-G Press to gamma correct the image.",
899 "",
900 " Shft-C Press to sharpen the image contrast.",
901 "",
902 " Shft-Z Press to dull the image contrast.",
903 "",
904 " = Press to perform histogram equalization on the image.",
905 "",
906 " Shft-N Press to perform histogram normalization on the image.",
907 "",
908 " Shft-~ Press to negate the colors of the image.",
909 "",
910 " . Press to convert the image colors to gray.",
911 "",
912 " Shft-# Press to set the maximum number of unique colors in the",
913 " image.",
914 "",
915 " F2 Press to reduce the speckles in an image.",
916 "",
917 " F3 Press to eliminate peak noise from an image.",
918 "",
919 " F4 Press to add noise to an image.",
920 "",
921 " F5 Press to sharpen an image.",
922 "",
923 " F6 Press to delete an image file.",
924 "",
925 " F7 Press to threshold the image.",
926 "",
927 " F8 Press to detect edges within an image.",
928 "",
929 " F9 Press to emboss an image.",
930 "",
931 " F10 Press to displace pixels by a random amount.",
932 "",
933 " F11 Press to negate all pixels above the threshold level.",
934 "",
935 " F12 Press to shade the image using a distant light source.",
936 "",
937 " F13 Press to lighten or darken image edges to create a 3-D effect.",
938 "",
939 " F14 Press to segment the image by color.",
940 "",
941 " Meta-S Press to swirl image pixels about the center.",
942 "",
943 " Meta-I Press to implode image pixels about the center.",
944 "",
cristycee97112010-05-28 00:44:52 +0000945 " Meta-W Press to alter an image along a sine wave.",
cristy3ed852e2009-09-05 21:47:34 +0000946 "",
947 " Meta-P Press to simulate an oil painting.",
948 "",
949 " Meta-C Press to simulate a charcoal drawing.",
950 "",
951 " Alt-A Press to annotate the image with text.",
952 "",
953 " Alt-D Press to draw on an image.",
954 "",
955 " Alt-P Press to edit an image pixel color.",
956 "",
957 " Alt-M Press to edit the image matte information.",
958 "",
959 " Alt-V Press to composite the image with another.",
960 "",
961 " Alt-B Press to add a border to the image.",
962 "",
963 " Alt-F Press to add an ornamental border to the image.",
964 "",
965 " Alt-Shft-!",
966 " Press to add an image comment.",
967 "",
968 " Ctl-A Press to apply image processing techniques to a region",
969 " of interest.",
970 "",
971 " Shft-? Press to display information about the image.",
972 "",
973 " Shft-+ Press to map the zoom image window.",
974 "",
975 " Shft-P Press to preview an image enhancement, effect, or f/x.",
976 "",
977 " F1 Press to display helpful information about display(1).",
978 "",
979 " Find Press to browse documentation about ImageMagick.",
980 "",
981 " 1-9 Press to change the level of magnification.",
982 "",
983 " Use the arrow keys to move the image one pixel up, down,",
984 " left, or right within the magnify window. Be sure to first",
985 " map the magnify window by pressing button 2.",
986 "",
987 " Press ALT and one of the arrow keys to trim off one pixel",
988 " from any side of the image.",
989 (char *) NULL,
990 },
991 *ImageMatteEditHelp[] =
992 {
993 "Matte information within an image is useful for some",
994 "operations such as image compositing (See IMAGE",
995 "COMPOSITING). This extra channel usually defines a mask",
996 "which represents a sort of a cookie-cutter for the image.",
997 "This the case when matte is opaque (full coverage) for",
998 "pixels inside the shape, zero outside, and between 0 and",
999 "QuantumRange on the boundary.",
1000 "",
1001 "A small window appears showing the location of the cursor in",
1002 "the image window. You are now in matte edit mode. To exit",
1003 "immediately, press Dismiss. In matte edit mode, the Command",
1004 "widget has these options:",
1005 "",
1006 " Method",
1007 " point",
1008 " replace",
1009 " floodfill",
1010 " filltoborder",
1011 " reset",
1012 " Border Color",
1013 " black",
1014 " blue",
1015 " cyan",
1016 " green",
1017 " gray",
1018 " red",
1019 " magenta",
1020 " yellow",
1021 " white",
1022 " Browser...",
1023 " Fuzz",
1024 " 0%",
1025 " 2%",
1026 " 5%",
1027 " 10%",
1028 " 15%",
1029 " Dialog...",
1030 " Matte",
1031 " Opaque",
1032 " Transparent",
1033 " Dialog...",
1034 " Undo",
1035 " Help",
1036 " Dismiss",
1037 "",
1038 "Choose a matte editing method from the Method sub-menu of",
1039 "the Command widget. The point method changes the matte value",
1040 "of any pixel selected with the pointer until the button is",
1041 "is released. The replace method changes the matte value of",
1042 "any pixel that matches the color of the pixel you select with",
1043 "a button press. Floodfill changes the matte value of any pixel",
1044 "that matches the color of the pixel you select with a button",
1045 "press and is a neighbor. Whereas filltoborder changes the matte",
1046 "value any neighbor pixel that is not the border color. Finally",
1047 "reset changes the entire image to the designated matte value.",
1048 "",
1049 "Choose Matte Value and pick Opaque or Transarent. For other values",
1050 "select the Dialog entry. Here a dialog appears requesting a matte",
1051 "value. The value you select is assigned as the opacity value of the",
1052 "selected pixel or pixels.",
1053 "",
1054 "Now, press any button to select a pixel within the image",
1055 "window to change its matte value.",
1056 "",
1057 "If the Magnify widget is mapped, it can be helpful in positioning",
1058 "your pointer within the image (refer to button 2).",
1059 "",
1060 "Matte information is only valid in a DirectClass image.",
1061 "Therefore, any PseudoClass image is promoted to DirectClass",
1062 "(see miff(5)). Note that matte information for PseudoClass",
1063 "is not retained for colormapped X server visuals (e.g.",
1064 "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1065 "immediately save your image to a file (refer to Write).",
1066 "Correct matte editing behavior may require a TrueColor or",
1067 "DirectColor visual or a Standard Colormap.",
1068 (char *) NULL,
1069 },
1070 *ImagePanHelp[] =
1071 {
1072 "When an image exceeds the width or height of the X server",
1073 "screen, display maps a small panning icon. The rectangle",
1074 "within the panning icon shows the area that is currently",
1075 "displayed in the image window. To pan about the image,",
1076 "press any button and drag the pointer within the panning",
1077 "icon. The pan rectangle moves with the pointer and the",
1078 "image window is updated to reflect the location of the",
1079 "rectangle within the panning icon. When you have selected",
1080 "the area of the image you wish to view, release the button.",
1081 "",
1082 "Use the arrow keys to pan the image one pixel up, down,",
1083 "left, or right within the image window.",
1084 "",
1085 "The panning icon is withdrawn if the image becomes smaller",
1086 "than the dimensions of the X server screen.",
1087 (char *) NULL,
1088 },
1089 *ImagePasteHelp[] =
1090 {
1091 "A small window appears showing the location of the cursor in",
1092 "the image window. You are now in paste mode. To exit",
1093 "immediately, press Dismiss. In paste mode, the Command",
1094 "widget has these options:",
1095 "",
1096 " Operators",
1097 " over",
1098 " in",
1099 " out",
1100 " atop",
1101 " xor",
1102 " plus",
1103 " minus",
1104 " add",
1105 " subtract",
1106 " difference",
1107 " replace",
1108 " Help",
1109 " Dismiss",
1110 "",
1111 "Choose a composite operation from the Operators sub-menu of",
1112 "the Command widget. How each operator behaves is described",
1113 "below. Image window is the image currently displayed on",
1114 "your X server and image is the image obtained with the File",
1115 "Browser widget.",
1116 "",
1117 "Over The result is the union of the two image shapes,",
1118 " with image obscuring image window in the region of",
1119 " overlap.",
1120 "",
1121 "In The result is simply image cut by the shape of",
1122 " image window. None of the image data of image",
1123 " window is in the result.",
1124 "",
1125 "Out The resulting image is image with the shape of",
1126 " image window cut out.",
1127 "",
1128 "Atop The result is the same shape as image image window,",
1129 " with image obscuring image window where the image",
1130 " shapes overlap. Note this differs from over",
1131 " because the portion of image outside image window's",
1132 " shape does not appear in the result.",
1133 "",
1134 "Xor The result is the image data from both image and",
1135 " image window that is outside the overlap region.",
1136 " The overlap region is blank.",
1137 "",
1138 "Plus The result is just the sum of the image data.",
1139 " Output values are cropped to QuantumRange (no overflow).",
1140 " This operation is independent of the matte",
1141 " channels.",
1142 "",
1143 "Minus The result of image - image window, with underflow",
1144 " cropped to zero.",
1145 "",
1146 "Add The result of image + image window, with overflow",
1147 " wrapping around (mod 256).",
1148 "",
1149 "Subtract The result of image - image window, with underflow",
1150 " wrapping around (mod 256). The add and subtract",
1151 " operators can be used to perform reversible",
1152 " transformations.",
1153 "",
1154 "Difference",
1155 " The result of abs(image - image window). This",
1156 " useful for comparing two very similar images.",
1157 "",
1158 "Copy The resulting image is image window replaced with",
1159 " image. Here the matte information is ignored.",
1160 "",
1161 "CopyRed The red layer of the image window is replace with",
1162 " the red layer of the image. The other layers are",
1163 " untouched.",
1164 "",
1165 "CopyGreen",
1166 " The green layer of the image window is replace with",
1167 " the green layer of the image. The other layers are",
1168 " untouched.",
1169 "",
1170 "CopyBlue The blue layer of the image window is replace with",
1171 " the blue layer of the image. The other layers are",
1172 " untouched.",
1173 "",
1174 "CopyOpacity",
1175 " The matte layer of the image window is replace with",
1176 " the matte layer of the image. The other layers are",
1177 " untouched.",
1178 "",
1179 "The image compositor requires a matte, or alpha channel in",
1180 "the image for some operations. This extra channel usually",
1181 "defines a mask which represents a sort of a cookie-cutter",
1182 "for the image. This the case when matte is opaque (full",
1183 "coverage) for pixels inside the shape, zero outside, and",
1184 "between 0 and QuantumRange on the boundary. If image does not",
1185 "have a matte channel, it is initialized with 0 for any pixel",
1186 "matching in color to pixel location (0,0), otherwise QuantumRange.",
1187 "",
1188 "Note that matte information for image window is not retained",
1189 "for colormapped X server visuals (e.g. StaticColor,",
1190 "StaticColor, GrayScale, PseudoColor). Correct compositing",
1191 "behavior may require a TrueColor or DirectColor visual or a",
1192 "Standard Colormap.",
1193 "",
1194 "Choosing a composite operator is optional. The default",
1195 "operator is replace. However, you must choose a location to",
1196 "paste your image and press button 1. Press and hold the",
1197 "button before releasing and an outline of the image will",
1198 "appear to help you identify your location.",
1199 "",
1200 "The actual colors of the pasted image is saved. However,",
1201 "the color that appears in image window may be different.",
1202 "For example, on a monochrome screen image window will appear",
1203 "black or white even though your pasted image may have",
1204 "many colors. If the image is saved to a file it is written",
1205 "with the correct colors. To assure the correct colors are",
1206 "saved in the final image, any PseudoClass image is promoted",
1207 "to DirectClass (see miff(5)). To force a PseudoClass image",
1208 "to remain PseudoClass, use -colors.",
1209 (char *) NULL,
1210 },
1211 *ImageROIHelp[] =
1212 {
1213 "In region of interest mode, the Command widget has these",
1214 "options:",
1215 "",
1216 " Help",
1217 " Dismiss",
1218 "",
1219 "To define a region of interest, press button 1 and drag.",
1220 "The region of interest is defined by a highlighted rectangle",
1221 "that expands or contracts as it follows the pointer. Once",
1222 "you are satisfied with the region of interest, release the",
1223 "button. You are now in apply mode. In apply mode the",
1224 "Command widget has these options:",
1225 "",
1226 " File",
1227 " Save...",
1228 " Print...",
1229 " Edit",
1230 " Undo",
1231 " Redo",
1232 " Transform",
1233 " Flop",
1234 " Flip",
1235 " Rotate Right",
1236 " Rotate Left",
1237 " Enhance",
1238 " Hue...",
1239 " Saturation...",
1240 " Brightness...",
1241 " Gamma...",
1242 " Spiff",
1243 " Dull",
1244 " Contrast Stretch",
1245 " Sigmoidal Contrast...",
1246 " Normalize",
1247 " Equalize",
1248 " Negate",
1249 " Grayscale",
1250 " Map...",
1251 " Quantize...",
1252 " Effects",
1253 " Despeckle",
1254 " Emboss",
1255 " Reduce Noise",
1256 " Sharpen...",
1257 " Blur...",
1258 " Threshold...",
1259 " Edge Detect...",
1260 " Spread...",
1261 " Shade...",
1262 " Raise...",
1263 " Segment...",
1264 " F/X",
1265 " Solarize...",
1266 " Sepia Tone...",
1267 " Swirl...",
1268 " Implode...",
1269 " Vignette...",
1270 " Wave...",
1271 " Oil Painting...",
1272 " Charcoal Drawing...",
1273 " Miscellany",
1274 " Image Info",
1275 " Zoom Image",
1276 " Show Preview...",
1277 " Show Histogram",
1278 " Show Matte",
1279 " Help",
1280 " Dismiss",
1281 "",
1282 "You can make adjustments to the region of interest by moving",
1283 "the pointer to one of the rectangle corners, pressing a",
1284 "button, and dragging. Finally, choose an image processing",
1285 "technique from the Command widget. You can choose more than",
1286 "one image processing technique to apply to an area.",
1287 "Alternatively, you can move the region of interest before",
1288 "applying another image processing technique. To exit, press",
1289 "Dismiss.",
1290 (char *) NULL,
1291 },
1292 *ImageRotateHelp[] =
1293 {
1294 "In rotate mode, the Command widget has these options:",
1295 "",
1296 " Pixel Color",
1297 " black",
1298 " blue",
1299 " cyan",
1300 " green",
1301 " gray",
1302 " red",
1303 " magenta",
1304 " yellow",
1305 " white",
1306 " Browser...",
1307 " Direction",
1308 " horizontal",
1309 " vertical",
1310 " Help",
1311 " Dismiss",
1312 "",
1313 "Choose a background color from the Pixel Color sub-menu.",
1314 "Additional background colors can be specified with the color",
1315 "browser. You can change the menu colors by setting the X",
1316 "resources pen1 through pen9.",
1317 "",
1318 "If you choose the color browser and press Grab, you can",
1319 "select the background color by moving the pointer to the",
1320 "desired color on the screen and press any button.",
1321 "",
1322 "Choose a point in the image window and press this button and",
1323 "hold. Next, move the pointer to another location in the",
1324 "image. As you move a line connects the initial location and",
1325 "the pointer. When you release the button, the degree of",
1326 "image rotation is determined by the slope of the line you",
1327 "just drew. The slope is relative to the direction you",
1328 "choose from the Direction sub-menu of the Command widget.",
1329 "",
1330 "To cancel the image rotation, move the pointer back to the",
1331 "starting point of the line and release the button.",
1332 (char *) NULL,
1333 };
1334
1335/*
1336 Enumeration declarations.
1337*/
1338typedef enum
1339{
1340 CopyMode,
1341 CropMode,
1342 CutMode
1343} ClipboardMode;
1344
1345typedef enum
1346{
1347 OpenCommand,
1348 NextCommand,
1349 FormerCommand,
1350 SelectCommand,
1351 SaveCommand,
1352 PrintCommand,
1353 DeleteCommand,
1354 NewCommand,
1355 VisualDirectoryCommand,
1356 QuitCommand,
1357 UndoCommand,
1358 RedoCommand,
1359 CutCommand,
1360 CopyCommand,
1361 PasteCommand,
1362 HalfSizeCommand,
1363 OriginalSizeCommand,
1364 DoubleSizeCommand,
1365 ResizeCommand,
1366 ApplyCommand,
1367 RefreshCommand,
1368 RestoreCommand,
1369 CropCommand,
1370 ChopCommand,
1371 FlopCommand,
1372 FlipCommand,
1373 RotateRightCommand,
1374 RotateLeftCommand,
1375 RotateCommand,
1376 ShearCommand,
1377 RollCommand,
1378 TrimCommand,
1379 HueCommand,
1380 SaturationCommand,
1381 BrightnessCommand,
1382 GammaCommand,
1383 SpiffCommand,
1384 DullCommand,
1385 ContrastStretchCommand,
1386 SigmoidalContrastCommand,
1387 NormalizeCommand,
1388 EqualizeCommand,
1389 NegateCommand,
1390 GrayscaleCommand,
1391 MapCommand,
1392 QuantizeCommand,
1393 DespeckleCommand,
1394 EmbossCommand,
1395 ReduceNoiseCommand,
1396 AddNoiseCommand,
1397 SharpenCommand,
1398 BlurCommand,
1399 ThresholdCommand,
1400 EdgeDetectCommand,
1401 SpreadCommand,
1402 ShadeCommand,
1403 RaiseCommand,
1404 SegmentCommand,
1405 SolarizeCommand,
1406 SepiaToneCommand,
1407 SwirlCommand,
1408 ImplodeCommand,
1409 VignetteCommand,
1410 WaveCommand,
1411 OilPaintCommand,
1412 CharcoalDrawCommand,
1413 AnnotateCommand,
1414 DrawCommand,
1415 ColorCommand,
1416 MatteCommand,
1417 CompositeCommand,
1418 AddBorderCommand,
1419 AddFrameCommand,
1420 CommentCommand,
1421 LaunchCommand,
1422 RegionofInterestCommand,
1423 ROIHelpCommand,
1424 ROIDismissCommand,
1425 InfoCommand,
1426 ZoomCommand,
1427 ShowPreviewCommand,
1428 ShowHistogramCommand,
1429 ShowMatteCommand,
1430 BackgroundCommand,
1431 SlideShowCommand,
1432 PreferencesCommand,
1433 HelpCommand,
1434 BrowseDocumentationCommand,
1435 VersionCommand,
1436 SaveToUndoBufferCommand,
1437 FreeBuffersCommand,
1438 NullCommand
1439} CommandType;
1440
1441typedef enum
1442{
1443 AnnotateNameCommand,
1444 AnnotateFontColorCommand,
1445 AnnotateBackgroundColorCommand,
1446 AnnotateRotateCommand,
1447 AnnotateHelpCommand,
1448 AnnotateDismissCommand,
1449 TextHelpCommand,
1450 TextApplyCommand,
1451 ChopDirectionCommand,
1452 ChopHelpCommand,
1453 ChopDismissCommand,
1454 HorizontalChopCommand,
1455 VerticalChopCommand,
1456 ColorEditMethodCommand,
1457 ColorEditColorCommand,
1458 ColorEditBorderCommand,
1459 ColorEditFuzzCommand,
1460 ColorEditUndoCommand,
1461 ColorEditHelpCommand,
1462 ColorEditDismissCommand,
1463 CompositeOperatorsCommand,
1464 CompositeDissolveCommand,
1465 CompositeDisplaceCommand,
1466 CompositeHelpCommand,
1467 CompositeDismissCommand,
1468 CropHelpCommand,
1469 CropDismissCommand,
1470 RectifyCopyCommand,
1471 RectifyHelpCommand,
1472 RectifyDismissCommand,
1473 DrawElementCommand,
1474 DrawColorCommand,
1475 DrawStippleCommand,
1476 DrawWidthCommand,
1477 DrawUndoCommand,
1478 DrawHelpCommand,
1479 DrawDismissCommand,
1480 MatteEditMethod,
1481 MatteEditBorderCommand,
1482 MatteEditFuzzCommand,
1483 MatteEditValueCommand,
1484 MatteEditUndoCommand,
1485 MatteEditHelpCommand,
1486 MatteEditDismissCommand,
1487 PasteOperatorsCommand,
1488 PasteHelpCommand,
1489 PasteDismissCommand,
1490 RotateColorCommand,
1491 RotateDirectionCommand,
1492 RotateCropCommand,
1493 RotateSharpenCommand,
1494 RotateHelpCommand,
1495 RotateDismissCommand,
1496 HorizontalRotateCommand,
1497 VerticalRotateCommand,
1498 TileLoadCommand,
1499 TileNextCommand,
1500 TileFormerCommand,
1501 TileDeleteCommand,
1502 TileUpdateCommand
1503} ModeType;
1504
1505/*
1506 Stipples.
1507*/
1508#define BricksWidth 20
1509#define BricksHeight 20
1510#define DiagonalWidth 16
1511#define DiagonalHeight 16
1512#define HighlightWidth 8
1513#define HighlightHeight 8
cristydd05beb2010-11-21 21:23:39 +00001514#define OpaqueWidth 8
1515#define OpaqueHeight 8
cristy3ed852e2009-09-05 21:47:34 +00001516#define ScalesWidth 16
1517#define ScalesHeight 16
1518#define ShadowWidth 8
1519#define ShadowHeight 8
1520#define VerticalWidth 16
1521#define VerticalHeight 16
1522#define WavyWidth 16
1523#define WavyHeight 16
1524
1525/*
1526 Constant declaration.
1527*/
1528static const int
1529 RoiDelta = 8;
1530
1531static const unsigned char
1532 BricksBitmap[] =
1533 {
1534 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1535 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1536 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1537 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1538 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1539 },
1540 DiagonalBitmap[] =
1541 {
1542 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1543 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1544 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1545 },
1546 ScalesBitmap[] =
1547 {
1548 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1549 0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1550 0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1551 },
1552 VerticalBitmap[] =
1553 {
1554 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1555 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1556 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1557 },
1558 WavyBitmap[] =
1559 {
1560 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1561 0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1562 0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1563 };
1564
1565/*
1566 Function prototypes.
1567*/
1568static CommandType
1569 XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
cristy051718b2011-08-28 22:49:25 +00001570 const MagickStatusType,KeySym,Image **,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +00001571
1572static Image
1573 *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
cristy051718b2011-08-28 22:49:25 +00001574 Image **,ExceptionInfo *),
cristy3ed852e2009-09-05 21:47:34 +00001575 *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
cristy051718b2011-08-28 22:49:25 +00001576 *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1577 ExceptionInfo *),
1578 *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1579 ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +00001580
1581static MagickBooleanType
cristy051718b2011-08-28 22:49:25 +00001582 XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1583 ExceptionInfo *),
1584 XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1585 ExceptionInfo *),
1586 XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1587 ExceptionInfo *),
1588 XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1589 ExceptionInfo *),
1590 XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1591 ExceptionInfo *),
1592 XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1593 ExceptionInfo *),
1594 XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1595 XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1596 ExceptionInfo *),
1597 XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1598 ExceptionInfo *),
1599 XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1600 XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1601 XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1602 ExceptionInfo *),
1603 XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1604 XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1605 XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +00001606
1607static void
1608 XDrawPanRectangle(Display *,XWindows *),
cristy051718b2011-08-28 22:49:25 +00001609 XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1610 ExceptionInfo *),
cristy6710d842011-10-20 23:23:00 +00001611 XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
cristy051718b2011-08-28 22:49:25 +00001612 XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
cristy6710d842011-10-20 23:23:00 +00001613 XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
cristy3ed852e2009-09-05 21:47:34 +00001614 XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
cristy6710d842011-10-20 23:23:00 +00001615 const KeySym,ExceptionInfo *),
cristy3ed852e2009-09-05 21:47:34 +00001616 XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
cristy6710d842011-10-20 23:23:00 +00001617 XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
cristy3ed852e2009-09-05 21:47:34 +00001618 XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1619
1620/*
1621%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1622% %
1623% %
1624% %
1625% D i s p l a y I m a g e s %
1626% %
1627% %
1628% %
1629%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1630%
1631% DisplayImages() displays an image sequence to any X window screen. It
1632% returns a value other than 0 if successful. Check the exception member
1633% of image to determine the reason for any failure.
1634%
1635% The format of the DisplayImages method is:
1636%
1637% MagickBooleanType DisplayImages(const ImageInfo *image_info,
cristy051718b2011-08-28 22:49:25 +00001638% Image *images,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001639%
1640% A description of each parameter follows:
1641%
1642% o image_info: the image info.
1643%
1644% o image: the image.
1645%
cristy051718b2011-08-28 22:49:25 +00001646% o exception: return any errors or warnings in this structure.
1647%
cristy3ed852e2009-09-05 21:47:34 +00001648*/
1649MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
cristy051718b2011-08-28 22:49:25 +00001650 Image *images,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001651{
1652 char
1653 *argv[1];
1654
1655 Display
1656 *display;
1657
1658 Image
1659 *image;
1660
cristybb503372010-05-27 20:51:26 +00001661 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001662 i;
1663
cristybb503372010-05-27 20:51:26 +00001664 size_t
cristy3ed852e2009-09-05 21:47:34 +00001665 state;
1666
1667 XrmDatabase
1668 resource_database;
1669
1670 XResourceInfo
1671 resource_info;
1672
1673 assert(image_info != (const ImageInfo *) NULL);
1674 assert(image_info->signature == MagickSignature);
1675 assert(images != (Image *) NULL);
1676 assert(images->signature == MagickSignature);
1677 if (images->debug != MagickFalse)
1678 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1679 display=XOpenDisplay(image_info->server_name);
1680 if (display == (Display *) NULL)
1681 {
cristy051718b2011-08-28 22:49:25 +00001682 (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1683 "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
cristy3ed852e2009-09-05 21:47:34 +00001684 return(MagickFalse);
1685 }
cristy051718b2011-08-28 22:49:25 +00001686 if (exception->severity != UndefinedException)
1687 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00001688 (void) XSetErrorHandler(XError);
1689 resource_database=XGetResourceDatabase(display,GetClientName());
1690 (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1691 XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1692 if (image_info->page != (char *) NULL)
1693 resource_info.image_geometry=AcquireString(image_info->page);
1694 resource_info.immutable=MagickTrue;
1695 argv[0]=AcquireString(GetClientName());
1696 state=DefaultState;
1697 for (i=0; (state & ExitState) == 0; i++)
1698 {
cristybb503372010-05-27 20:51:26 +00001699 if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
cristy3ed852e2009-09-05 21:47:34 +00001700 break;
1701 image=GetImageFromList(images,i % GetImageListLength(images));
cristy051718b2011-08-28 22:49:25 +00001702 (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
cristy3ed852e2009-09-05 21:47:34 +00001703 }
cristy01cdc902011-08-31 01:05:44 +00001704 SetErrorHandler((ErrorHandler) NULL);
1705 SetWarningHandler((WarningHandler) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001706 argv[0]=DestroyString(argv[0]);
1707 (void) XCloseDisplay(display);
1708 XDestroyResourceInfo(&resource_info);
cristy051718b2011-08-28 22:49:25 +00001709 if (exception->severity != UndefinedException)
cristy3ed852e2009-09-05 21:47:34 +00001710 return(MagickFalse);
1711 return(MagickTrue);
1712}
1713
1714/*
1715%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1716% %
1717% %
1718% %
1719% R e m o t e D i s p l a y C o m m a n d %
1720% %
1721% %
1722% %
1723%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1724%
1725% RemoteDisplayCommand() encourages a remote display program to display the
1726% specified image filename.
1727%
1728% The format of the RemoteDisplayCommand method is:
1729%
1730% MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1731% const char *window,const char *filename,ExceptionInfo *exception)
1732%
1733% A description of each parameter follows:
1734%
1735% o image_info: the image info.
1736%
1737% o window: Specifies the name or id of an X window.
1738%
1739% o filename: the name of the image filename to display.
1740%
1741% o exception: return any errors or warnings in this structure.
1742%
1743*/
1744MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1745 const char *window,const char *filename,ExceptionInfo *exception)
1746{
1747 Display
1748 *display;
1749
1750 MagickStatusType
1751 status;
1752
1753 assert(image_info != (const ImageInfo *) NULL);
1754 assert(image_info->signature == MagickSignature);
1755 assert(filename != (char *) NULL);
1756 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1757 display=XOpenDisplay(image_info->server_name);
1758 if (display == (Display *) NULL)
1759 {
1760 (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1761 "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1762 return(MagickFalse);
1763 }
1764 (void) XSetErrorHandler(XError);
1765 status=XRemoteCommand(display,window,filename);
1766 (void) XCloseDisplay(display);
1767 return(status != 0 ? MagickTrue : MagickFalse);
1768}
1769
1770/*
1771%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1772% %
1773% %
1774% %
1775+ X A n n o t a t e E d i t I m a g e %
1776% %
1777% %
1778% %
1779%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1780%
1781% XAnnotateEditImage() annotates the image with text.
1782%
1783% The format of the XAnnotateEditImage method is:
1784%
1785% MagickBooleanType XAnnotateEditImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00001786% XResourceInfo *resource_info,XWindows *windows,Image *image,
1787% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001788%
1789% A description of each parameter follows:
1790%
1791% o display: Specifies a connection to an X server; returned from
1792% XOpenDisplay.
1793%
1794% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1795%
1796% o windows: Specifies a pointer to a XWindows structure.
1797%
1798% o image: the image; returned from ReadImage.
1799%
1800*/
1801
cristybb503372010-05-27 20:51:26 +00001802static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001803{
1804 if (x > y)
1805 return(x);
1806 return(y);
1807}
1808
cristybb503372010-05-27 20:51:26 +00001809static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001810{
1811 if (x < y)
1812 return(x);
1813 return(y);
1814}
1815
1816static MagickBooleanType XAnnotateEditImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00001817 XResourceInfo *resource_info,XWindows *windows,Image *image,
1818 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001819{
1820 static const char
1821 *AnnotateMenu[] =
1822 {
1823 "Font Name",
1824 "Font Color",
1825 "Box Color",
1826 "Rotate Text",
1827 "Help",
1828 "Dismiss",
1829 (char *) NULL
1830 },
1831 *TextMenu[] =
1832 {
1833 "Help",
1834 "Apply",
1835 (char *) NULL
1836 };
1837
1838 static const ModeType
1839 AnnotateCommands[] =
1840 {
1841 AnnotateNameCommand,
1842 AnnotateFontColorCommand,
1843 AnnotateBackgroundColorCommand,
1844 AnnotateRotateCommand,
1845 AnnotateHelpCommand,
1846 AnnotateDismissCommand
1847 },
1848 TextCommands[] =
1849 {
1850 TextHelpCommand,
1851 TextApplyCommand
1852 };
1853
1854 static MagickBooleanType
1855 transparent_box = MagickTrue,
1856 transparent_pen = MagickFalse;
1857
1858 static MagickRealType
1859 degrees = 0.0;
1860
1861 static unsigned int
1862 box_id = MaxNumberPens-2,
1863 font_id = 0,
1864 pen_id = 0;
1865
1866 char
1867 command[MaxTextExtent],
1868 text[MaxTextExtent];
1869
1870 const char
1871 *ColorMenu[MaxNumberPens+1];
1872
1873 Cursor
1874 cursor;
1875
1876 GC
1877 annotate_context;
1878
1879 int
1880 id,
1881 pen_number,
1882 status,
1883 x,
1884 y;
1885
1886 KeySym
1887 key_symbol;
1888
1889 register char
1890 *p;
1891
cristybb503372010-05-27 20:51:26 +00001892 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001893 i;
1894
1895 unsigned int
1896 height,
1897 width;
1898
cristybb503372010-05-27 20:51:26 +00001899 size_t
cristy3ed852e2009-09-05 21:47:34 +00001900 state;
1901
1902 XAnnotateInfo
1903 *annotate_info,
1904 *previous_info;
1905
1906 XColor
1907 color;
1908
1909 XFontStruct
1910 *font_info;
1911
1912 XEvent
1913 event,
1914 text_event;
1915
1916 /*
1917 Map Command widget.
1918 */
1919 (void) CloneString(&windows->command.name,"Annotate");
1920 windows->command.data=4;
1921 (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1922 (void) XMapRaised(display,windows->command.id);
1923 XClientMessage(display,windows->image.id,windows->im_protocols,
1924 windows->im_update_widget,CurrentTime);
1925 /*
1926 Track pointer until button 1 is pressed.
1927 */
1928 XQueryPosition(display,windows->image.id,&x,&y);
1929 (void) XSelectInput(display,windows->image.id,
1930 windows->image.attributes.event_mask | PointerMotionMask);
1931 cursor=XCreateFontCursor(display,XC_left_side);
1932 (void) XCheckDefineCursor(display,windows->image.id,cursor);
1933 state=DefaultState;
1934 do
1935 {
1936 if (windows->info.mapped != MagickFalse)
1937 {
1938 /*
1939 Display pointer position.
1940 */
cristyb51dff52011-05-19 16:55:47 +00001941 (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
cristy3ed852e2009-09-05 21:47:34 +00001942 x+windows->image.x,y+windows->image.y);
1943 XInfoWidget(display,windows,text);
1944 }
1945 /*
1946 Wait for next event.
1947 */
cristy6710d842011-10-20 23:23:00 +00001948 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +00001949 if (event.xany.window == windows->command.id)
1950 {
1951 /*
1952 Select a command from the Command widget.
1953 */
1954 id=XCommandWidget(display,windows,AnnotateMenu,&event);
1955 (void) XCheckDefineCursor(display,windows->image.id,cursor);
1956 if (id < 0)
1957 continue;
1958 switch (AnnotateCommands[id])
1959 {
1960 case AnnotateNameCommand:
1961 {
1962 const char
1963 *FontMenu[MaxNumberFonts];
1964
1965 int
1966 font_number;
1967
1968 /*
1969 Initialize menu selections.
1970 */
1971 for (i=0; i < MaxNumberFonts; i++)
1972 FontMenu[i]=resource_info->font_name[i];
1973 FontMenu[MaxNumberFonts-2]="Browser...";
1974 FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1975 /*
1976 Select a font name from the pop-up menu.
1977 */
1978 font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1979 (const char **) FontMenu,command);
1980 if (font_number < 0)
1981 break;
1982 if (font_number == (MaxNumberFonts-2))
1983 {
1984 static char
1985 font_name[MaxTextExtent] = "fixed";
1986
1987 /*
1988 Select a font name from a browser.
1989 */
1990 resource_info->font_name[font_number]=font_name;
1991 XFontBrowserWidget(display,windows,"Select",font_name);
1992 if (*font_name == '\0')
1993 break;
1994 }
1995 /*
1996 Initialize font info.
1997 */
1998 font_info=XLoadQueryFont(display,resource_info->font_name[
1999 font_number]);
2000 if (font_info == (XFontStruct *) NULL)
2001 {
2002 XNoticeWidget(display,windows,"Unable to load font:",
2003 resource_info->font_name[font_number]);
2004 break;
2005 }
2006 font_id=(unsigned int) font_number;
2007 (void) XFreeFont(display,font_info);
2008 break;
2009 }
2010 case AnnotateFontColorCommand:
2011 {
2012 /*
2013 Initialize menu selections.
2014 */
2015 for (i=0; i < (int) (MaxNumberPens-2); i++)
2016 ColorMenu[i]=resource_info->pen_colors[i];
2017 ColorMenu[MaxNumberPens-2]="transparent";
2018 ColorMenu[MaxNumberPens-1]="Browser...";
2019 ColorMenu[MaxNumberPens]=(const char *) NULL;
2020 /*
2021 Select a pen color from the pop-up menu.
2022 */
2023 pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2024 (const char **) ColorMenu,command);
2025 if (pen_number < 0)
2026 break;
2027 transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2028 MagickFalse;
2029 if (transparent_pen != MagickFalse)
2030 break;
2031 if (pen_number == (MaxNumberPens-1))
2032 {
2033 static char
2034 color_name[MaxTextExtent] = "gray";
2035
2036 /*
2037 Select a pen color from a dialog.
2038 */
2039 resource_info->pen_colors[pen_number]=color_name;
2040 XColorBrowserWidget(display,windows,"Select",color_name);
2041 if (*color_name == '\0')
2042 break;
2043 }
2044 /*
2045 Set pen color.
2046 */
2047 (void) XParseColor(display,windows->map_info->colormap,
2048 resource_info->pen_colors[pen_number],&color);
2049 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2050 (unsigned int) MaxColors,&color);
2051 windows->pixel_info->pen_colors[pen_number]=color;
2052 pen_id=(unsigned int) pen_number;
2053 break;
2054 }
2055 case AnnotateBackgroundColorCommand:
2056 {
2057 /*
2058 Initialize menu selections.
2059 */
2060 for (i=0; i < (int) (MaxNumberPens-2); i++)
2061 ColorMenu[i]=resource_info->pen_colors[i];
2062 ColorMenu[MaxNumberPens-2]="transparent";
2063 ColorMenu[MaxNumberPens-1]="Browser...";
2064 ColorMenu[MaxNumberPens]=(const char *) NULL;
2065 /*
2066 Select a pen color from the pop-up menu.
2067 */
2068 pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2069 (const char **) ColorMenu,command);
2070 if (pen_number < 0)
2071 break;
2072 transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2073 MagickFalse;
2074 if (transparent_box != MagickFalse)
2075 break;
2076 if (pen_number == (MaxNumberPens-1))
2077 {
2078 static char
2079 color_name[MaxTextExtent] = "gray";
2080
2081 /*
2082 Select a pen color from a dialog.
2083 */
2084 resource_info->pen_colors[pen_number]=color_name;
2085 XColorBrowserWidget(display,windows,"Select",color_name);
2086 if (*color_name == '\0')
2087 break;
2088 }
2089 /*
2090 Set pen color.
2091 */
2092 (void) XParseColor(display,windows->map_info->colormap,
2093 resource_info->pen_colors[pen_number],&color);
2094 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2095 (unsigned int) MaxColors,&color);
2096 windows->pixel_info->pen_colors[pen_number]=color;
2097 box_id=(unsigned int) pen_number;
2098 break;
2099 }
2100 case AnnotateRotateCommand:
2101 {
2102 int
2103 entry;
2104
2105 static char
2106 angle[MaxTextExtent] = "30.0";
2107
2108 static const char
2109 *RotateMenu[] =
2110 {
2111 "-90",
2112 "-45",
2113 "-30",
2114 "0",
2115 "30",
2116 "45",
2117 "90",
2118 "180",
2119 "Dialog...",
2120 (char *) NULL,
2121 };
2122
2123 /*
2124 Select a command from the pop-up menu.
2125 */
2126 entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2127 command);
2128 if (entry < 0)
2129 break;
2130 if (entry != 8)
2131 {
cristydbdd0e32011-11-04 23:29:40 +00002132 degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002133 break;
2134 }
2135 (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2136 angle);
2137 if (*angle == '\0')
2138 break;
cristydbdd0e32011-11-04 23:29:40 +00002139 degrees=StringToDouble(angle,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002140 break;
2141 }
2142 case AnnotateHelpCommand:
2143 {
2144 XTextViewWidget(display,resource_info,windows,MagickFalse,
2145 "Help Viewer - Image Annotation",ImageAnnotateHelp);
2146 break;
2147 }
2148 case AnnotateDismissCommand:
2149 {
2150 /*
2151 Prematurely exit.
2152 */
2153 state|=EscapeState;
2154 state|=ExitState;
2155 break;
2156 }
2157 default:
2158 break;
2159 }
2160 continue;
2161 }
2162 switch (event.type)
2163 {
2164 case ButtonPress:
2165 {
2166 if (event.xbutton.button != Button1)
2167 break;
2168 if (event.xbutton.window != windows->image.id)
2169 break;
2170 /*
2171 Change to text entering mode.
2172 */
2173 x=event.xbutton.x;
2174 y=event.xbutton.y;
2175 state|=ExitState;
2176 break;
2177 }
2178 case ButtonRelease:
2179 break;
2180 case Expose:
2181 break;
2182 case KeyPress:
2183 {
2184 if (event.xkey.window != windows->image.id)
2185 break;
2186 /*
2187 Respond to a user key press.
2188 */
2189 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2190 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2191 switch ((int) key_symbol)
2192 {
2193 case XK_Escape:
2194 case XK_F20:
2195 {
2196 /*
2197 Prematurely exit.
2198 */
2199 state|=EscapeState;
2200 state|=ExitState;
2201 break;
2202 }
2203 case XK_F1:
2204 case XK_Help:
2205 {
2206 XTextViewWidget(display,resource_info,windows,MagickFalse,
2207 "Help Viewer - Image Annotation",ImageAnnotateHelp);
2208 break;
2209 }
2210 default:
2211 {
2212 (void) XBell(display,0);
2213 break;
2214 }
2215 }
2216 break;
2217 }
2218 case MotionNotify:
2219 {
2220 /*
2221 Map and unmap Info widget as cursor crosses its boundaries.
2222 */
2223 x=event.xmotion.x;
2224 y=event.xmotion.y;
2225 if (windows->info.mapped != MagickFalse)
2226 {
2227 if ((x < (int) (windows->info.x+windows->info.width)) &&
2228 (y < (int) (windows->info.y+windows->info.height)))
2229 (void) XWithdrawWindow(display,windows->info.id,
2230 windows->info.screen);
2231 }
2232 else
2233 if ((x > (int) (windows->info.x+windows->info.width)) ||
2234 (y > (int) (windows->info.y+windows->info.height)))
2235 (void) XMapWindow(display,windows->info.id);
2236 break;
2237 }
2238 default:
2239 break;
2240 }
2241 } while ((state & ExitState) == 0);
2242 (void) XSelectInput(display,windows->image.id,
2243 windows->image.attributes.event_mask);
2244 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2245 if ((state & EscapeState) != 0)
2246 return(MagickTrue);
2247 /*
2248 Set font info and check boundary conditions.
2249 */
2250 font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2251 if (font_info == (XFontStruct *) NULL)
2252 {
2253 XNoticeWidget(display,windows,"Unable to load font:",
2254 resource_info->font_name[font_id]);
2255 font_info=windows->font_info;
2256 }
2257 if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2258 x=(int) windows->image.width-font_info->max_bounds.width;
2259 if (y < (int) (font_info->ascent+font_info->descent))
2260 y=(int) font_info->ascent+font_info->descent;
2261 if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2262 ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2263 return(MagickFalse);
2264 /*
2265 Initialize annotate structure.
2266 */
cristy73bd4a52010-10-05 11:24:23 +00002267 annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
cristy3ed852e2009-09-05 21:47:34 +00002268 if (annotate_info == (XAnnotateInfo *) NULL)
2269 return(MagickFalse);
2270 XGetAnnotateInfo(annotate_info);
2271 annotate_info->x=x;
2272 annotate_info->y=y;
2273 if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2274 annotate_info->stencil=OpaqueStencil;
2275 else
2276 if (transparent_box == MagickFalse)
2277 annotate_info->stencil=BackgroundStencil;
2278 else
2279 annotate_info->stencil=ForegroundStencil;
2280 annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2281 annotate_info->degrees=degrees;
2282 annotate_info->font_info=font_info;
2283 annotate_info->text=(char *) AcquireQuantumMemory((size_t)
cristy49e2d862010-11-12 02:50:30 +00002284 windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
cristy3ed852e2009-09-05 21:47:34 +00002285 sizeof(*annotate_info->text));
2286 if (annotate_info->text == (char *) NULL)
2287 return(MagickFalse);
2288 /*
2289 Create cursor and set graphic context.
2290 */
2291 cursor=XCreateFontCursor(display,XC_pencil);
2292 (void) XCheckDefineCursor(display,windows->image.id,cursor);
2293 annotate_context=windows->image.annotate_context;
2294 (void) XSetFont(display,annotate_context,font_info->fid);
2295 (void) XSetBackground(display,annotate_context,
2296 windows->pixel_info->pen_colors[box_id].pixel);
2297 (void) XSetForeground(display,annotate_context,
2298 windows->pixel_info->pen_colors[pen_id].pixel);
2299 /*
2300 Begin annotating the image with text.
2301 */
2302 (void) CloneString(&windows->command.name,"Text");
2303 windows->command.data=0;
2304 (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2305 state=DefaultState;
2306 (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2307 text_event.xexpose.width=(int) font_info->max_bounds.width;
2308 text_event.xexpose.height=font_info->max_bounds.ascent+
2309 font_info->max_bounds.descent;
2310 p=annotate_info->text;
2311 do
2312 {
2313 /*
2314 Display text cursor.
2315 */
2316 *p='\0';
2317 (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2318 /*
2319 Wait for next event.
2320 */
cristy6710d842011-10-20 23:23:00 +00002321 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +00002322 if (event.xany.window == windows->command.id)
2323 {
2324 /*
2325 Select a command from the Command widget.
2326 */
2327 (void) XSetBackground(display,annotate_context,
2328 windows->pixel_info->background_color.pixel);
2329 (void) XSetForeground(display,annotate_context,
2330 windows->pixel_info->foreground_color.pixel);
2331 id=XCommandWidget(display,windows,AnnotateMenu,&event);
2332 (void) XSetBackground(display,annotate_context,
2333 windows->pixel_info->pen_colors[box_id].pixel);
2334 (void) XSetForeground(display,annotate_context,
2335 windows->pixel_info->pen_colors[pen_id].pixel);
2336 if (id < 0)
2337 continue;
2338 switch (TextCommands[id])
2339 {
2340 case TextHelpCommand:
2341 {
2342 XTextViewWidget(display,resource_info,windows,MagickFalse,
2343 "Help Viewer - Image Annotation",ImageAnnotateHelp);
2344 (void) XCheckDefineCursor(display,windows->image.id,cursor);
2345 break;
2346 }
2347 case TextApplyCommand:
2348 {
2349 /*
2350 Finished annotating.
2351 */
2352 annotate_info->width=(unsigned int) XTextWidth(font_info,
2353 annotate_info->text,(int) strlen(annotate_info->text));
2354 XRefreshWindow(display,&windows->image,&text_event);
2355 state|=ExitState;
2356 break;
2357 }
2358 default:
2359 break;
2360 }
2361 continue;
2362 }
2363 /*
2364 Erase text cursor.
2365 */
2366 text_event.xexpose.x=x;
2367 text_event.xexpose.y=y-font_info->max_bounds.ascent;
2368 (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2369 (unsigned int) text_event.xexpose.width,(unsigned int)
2370 text_event.xexpose.height,MagickFalse);
2371 XRefreshWindow(display,&windows->image,&text_event);
2372 switch (event.type)
2373 {
2374 case ButtonPress:
2375 {
2376 if (event.xbutton.window != windows->image.id)
2377 break;
2378 if (event.xbutton.button == Button2)
2379 {
2380 /*
2381 Request primary selection.
2382 */
2383 (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2384 windows->image.id,CurrentTime);
2385 break;
2386 }
2387 break;
2388 }
2389 case Expose:
2390 {
2391 if (event.xexpose.count == 0)
2392 {
2393 XAnnotateInfo
2394 *text_info;
2395
2396 /*
2397 Refresh Image window.
2398 */
2399 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2400 text_info=annotate_info;
2401 while (text_info != (XAnnotateInfo *) NULL)
2402 {
2403 if (annotate_info->stencil == ForegroundStencil)
2404 (void) XDrawString(display,windows->image.id,annotate_context,
2405 text_info->x,text_info->y,text_info->text,
2406 (int) strlen(text_info->text));
2407 else
2408 (void) XDrawImageString(display,windows->image.id,
2409 annotate_context,text_info->x,text_info->y,text_info->text,
2410 (int) strlen(text_info->text));
2411 text_info=text_info->previous;
2412 }
2413 (void) XDrawString(display,windows->image.id,annotate_context,
2414 x,y,"_",1);
2415 }
2416 break;
2417 }
2418 case KeyPress:
2419 {
2420 int
2421 length;
2422
2423 if (event.xkey.window != windows->image.id)
2424 break;
2425 /*
2426 Respond to a user key press.
2427 */
2428 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2429 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2430 *(command+length)='\0';
2431 if (((event.xkey.state & ControlMask) != 0) ||
2432 ((event.xkey.state & Mod1Mask) != 0))
2433 state|=ModifierState;
2434 if ((state & ModifierState) != 0)
2435 switch ((int) key_symbol)
2436 {
2437 case XK_u:
2438 case XK_U:
2439 {
2440 key_symbol=DeleteCommand;
2441 break;
2442 }
2443 default:
2444 break;
2445 }
2446 switch ((int) key_symbol)
2447 {
2448 case XK_BackSpace:
2449 {
2450 /*
2451 Erase one character.
2452 */
2453 if (p == annotate_info->text)
2454 {
2455 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2456 break;
2457 else
2458 {
2459 /*
2460 Go to end of the previous line of text.
2461 */
2462 annotate_info=annotate_info->previous;
2463 p=annotate_info->text;
2464 x=annotate_info->x+annotate_info->width;
2465 y=annotate_info->y;
2466 if (annotate_info->width != 0)
2467 p+=strlen(annotate_info->text);
2468 break;
2469 }
2470 }
2471 p--;
2472 x-=XTextWidth(font_info,p,1);
2473 text_event.xexpose.x=x;
2474 text_event.xexpose.y=y-font_info->max_bounds.ascent;
2475 XRefreshWindow(display,&windows->image,&text_event);
2476 break;
2477 }
2478 case XK_bracketleft:
2479 {
2480 key_symbol=XK_Escape;
2481 break;
2482 }
2483 case DeleteCommand:
2484 {
2485 /*
2486 Erase the entire line of text.
2487 */
2488 while (p != annotate_info->text)
2489 {
2490 p--;
2491 x-=XTextWidth(font_info,p,1);
2492 text_event.xexpose.x=x;
2493 XRefreshWindow(display,&windows->image,&text_event);
2494 }
2495 break;
2496 }
2497 case XK_Escape:
2498 case XK_F20:
2499 {
2500 /*
2501 Finished annotating.
2502 */
2503 annotate_info->width=(unsigned int) XTextWidth(font_info,
2504 annotate_info->text,(int) strlen(annotate_info->text));
2505 XRefreshWindow(display,&windows->image,&text_event);
2506 state|=ExitState;
2507 break;
2508 }
2509 default:
2510 {
2511 /*
2512 Draw a single character on the Image window.
2513 */
2514 if ((state & ModifierState) != 0)
2515 break;
2516 if (*command == '\0')
2517 break;
2518 *p=(*command);
2519 if (annotate_info->stencil == ForegroundStencil)
2520 (void) XDrawString(display,windows->image.id,annotate_context,
2521 x,y,p,1);
2522 else
2523 (void) XDrawImageString(display,windows->image.id,
2524 annotate_context,x,y,p,1);
2525 x+=XTextWidth(font_info,p,1);
2526 p++;
2527 if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2528 break;
2529 }
2530 case XK_Return:
2531 case XK_KP_Enter:
2532 {
2533 /*
2534 Advance to the next line of text.
2535 */
2536 *p='\0';
2537 annotate_info->width=(unsigned int) XTextWidth(font_info,
2538 annotate_info->text,(int) strlen(annotate_info->text));
2539 if (annotate_info->next != (XAnnotateInfo *) NULL)
2540 {
2541 /*
2542 Line of text already exists.
2543 */
2544 annotate_info=annotate_info->next;
2545 x=annotate_info->x;
2546 y=annotate_info->y;
2547 p=annotate_info->text;
2548 break;
2549 }
2550 annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2551 sizeof(*annotate_info->next));
2552 if (annotate_info->next == (XAnnotateInfo *) NULL)
2553 return(MagickFalse);
2554 *annotate_info->next=(*annotate_info);
2555 annotate_info->next->previous=annotate_info;
2556 annotate_info=annotate_info->next;
2557 annotate_info->text=(char *) AcquireQuantumMemory((size_t)
cristy49e2d862010-11-12 02:50:30 +00002558 windows->image.width/MagickMax((ssize_t)
2559 font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
cristy3ed852e2009-09-05 21:47:34 +00002560 if (annotate_info->text == (char *) NULL)
2561 return(MagickFalse);
2562 annotate_info->y+=annotate_info->height;
2563 if (annotate_info->y > (int) windows->image.height)
2564 annotate_info->y=(int) annotate_info->height;
2565 annotate_info->next=(XAnnotateInfo *) NULL;
2566 x=annotate_info->x;
2567 y=annotate_info->y;
2568 p=annotate_info->text;
2569 break;
2570 }
2571 }
2572 break;
2573 }
2574 case KeyRelease:
2575 {
2576 /*
2577 Respond to a user key release.
2578 */
2579 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2580 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2581 state&=(~ModifierState);
2582 break;
2583 }
2584 case SelectionNotify:
2585 {
2586 Atom
2587 type;
2588
2589 int
2590 format;
2591
2592 unsigned char
2593 *data;
2594
cristyf2faecf2010-05-28 19:19:36 +00002595 unsigned long
cristy3ed852e2009-09-05 21:47:34 +00002596 after,
2597 length;
2598
2599 /*
2600 Obtain response from primary selection.
2601 */
2602 if (event.xselection.property == (Atom) None)
2603 break;
2604 status=XGetWindowProperty(display,event.xselection.requestor,
cristyecd0ab52010-05-30 14:59:20 +00002605 event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
cristy3ed852e2009-09-05 21:47:34 +00002606 &type,&format,&length,&after,&data);
2607 if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2608 (length == 0))
2609 break;
2610 /*
2611 Annotate Image window with primary selection.
2612 */
cristybb503372010-05-27 20:51:26 +00002613 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00002614 {
2615 if ((char) data[i] != '\n')
2616 {
2617 /*
2618 Draw a single character on the Image window.
2619 */
2620 *p=(char) data[i];
2621 (void) XDrawString(display,windows->image.id,annotate_context,
2622 x,y,p,1);
2623 x+=XTextWidth(font_info,p,1);
2624 p++;
2625 if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2626 continue;
2627 }
2628 /*
2629 Advance to the next line of text.
2630 */
2631 *p='\0';
2632 annotate_info->width=(unsigned int) XTextWidth(font_info,
2633 annotate_info->text,(int) strlen(annotate_info->text));
2634 if (annotate_info->next != (XAnnotateInfo *) NULL)
2635 {
2636 /*
2637 Line of text already exists.
2638 */
2639 annotate_info=annotate_info->next;
2640 x=annotate_info->x;
2641 y=annotate_info->y;
2642 p=annotate_info->text;
2643 continue;
2644 }
2645 annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2646 sizeof(*annotate_info->next));
2647 if (annotate_info->next == (XAnnotateInfo *) NULL)
2648 return(MagickFalse);
2649 *annotate_info->next=(*annotate_info);
2650 annotate_info->next->previous=annotate_info;
2651 annotate_info=annotate_info->next;
2652 annotate_info->text=(char *) AcquireQuantumMemory((size_t)
cristy49e2d862010-11-12 02:50:30 +00002653 windows->image.width/MagickMax((ssize_t)
2654 font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
cristy3ed852e2009-09-05 21:47:34 +00002655 if (annotate_info->text == (char *) NULL)
2656 return(MagickFalse);
2657 annotate_info->y+=annotate_info->height;
2658 if (annotate_info->y > (int) windows->image.height)
2659 annotate_info->y=(int) annotate_info->height;
2660 annotate_info->next=(XAnnotateInfo *) NULL;
2661 x=annotate_info->x;
2662 y=annotate_info->y;
2663 p=annotate_info->text;
2664 }
2665 (void) XFree((void *) data);
2666 break;
2667 }
2668 default:
2669 break;
2670 }
2671 } while ((state & ExitState) == 0);
2672 (void) XFreeCursor(display,cursor);
2673 /*
2674 Annotation is relative to image configuration.
2675 */
2676 width=(unsigned int) image->columns;
2677 height=(unsigned int) image->rows;
2678 x=0;
2679 y=0;
2680 if (windows->image.crop_geometry != (char *) NULL)
2681 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2682 /*
2683 Initialize annotated image.
2684 */
2685 XSetCursorState(display,windows,MagickTrue);
2686 XCheckRefreshWindows(display,windows);
2687 while (annotate_info != (XAnnotateInfo *) NULL)
2688 {
2689 if (annotate_info->width == 0)
2690 {
2691 /*
2692 No text on this line-- go to the next line of text.
2693 */
2694 previous_info=annotate_info->previous;
2695 annotate_info->text=(char *)
2696 RelinquishMagickMemory(annotate_info->text);
2697 annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2698 annotate_info=previous_info;
2699 continue;
2700 }
2701 /*
2702 Determine pixel index for box and pen color.
2703 */
2704 windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2705 if (windows->pixel_info->colors != 0)
cristybb503372010-05-27 20:51:26 +00002706 for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002707 if (windows->pixel_info->pixels[i] ==
2708 windows->pixel_info->pen_colors[box_id].pixel)
2709 {
2710 windows->pixel_info->box_index=(unsigned short) i;
2711 break;
2712 }
2713 windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2714 if (windows->pixel_info->colors != 0)
cristybb503372010-05-27 20:51:26 +00002715 for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002716 if (windows->pixel_info->pixels[i] ==
2717 windows->pixel_info->pen_colors[pen_id].pixel)
2718 {
2719 windows->pixel_info->pen_index=(unsigned short) i;
2720 break;
2721 }
2722 /*
2723 Define the annotate geometry string.
2724 */
2725 annotate_info->x=(int)
2726 width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2727 annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2728 windows->image.y)/windows->image.ximage->height;
cristyb51dff52011-05-19 16:55:47 +00002729 (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00002730 "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2731 height*annotate_info->height/windows->image.ximage->height,
2732 annotate_info->x+x,annotate_info->y+y);
2733 /*
2734 Annotate image with text.
2735 */
cristy7c3af952011-10-20 16:04:16 +00002736 status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2737 exception);
cristy3ed852e2009-09-05 21:47:34 +00002738 if (status == 0)
2739 return(MagickFalse);
2740 /*
2741 Free up memory.
2742 */
2743 previous_info=annotate_info->previous;
2744 annotate_info->text=DestroyString(annotate_info->text);
2745 annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2746 annotate_info=previous_info;
2747 }
2748 (void) XSetForeground(display,annotate_context,
2749 windows->pixel_info->foreground_color.pixel);
2750 (void) XSetBackground(display,annotate_context,
2751 windows->pixel_info->background_color.pixel);
2752 (void) XSetFont(display,annotate_context,windows->font_info->fid);
2753 XSetCursorState(display,windows,MagickFalse);
2754 (void) XFreeFont(display,font_info);
2755 /*
2756 Update image configuration.
2757 */
cristy6710d842011-10-20 23:23:00 +00002758 XConfigureImageColormap(display,resource_info,windows,image,exception);
cristy051718b2011-08-28 22:49:25 +00002759 (void) XConfigureImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00002760 return(MagickTrue);
2761}
2762
2763/*
2764%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2765% %
2766% %
2767% %
2768+ X B a c k g r o u n d I m a g e %
2769% %
2770% %
2771% %
2772%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2773%
2774% XBackgroundImage() displays the image in the background of a window.
2775%
2776% The format of the XBackgroundImage method is:
2777%
2778% MagickBooleanType XBackgroundImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00002779% XResourceInfo *resource_info,XWindows *windows,Image **image,
2780% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002781%
2782% A description of each parameter follows:
2783%
2784% o display: Specifies a connection to an X server; returned from
2785% XOpenDisplay.
2786%
2787% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2788%
2789% o windows: Specifies a pointer to a XWindows structure.
2790%
2791% o image: the image.
2792%
cristy051718b2011-08-28 22:49:25 +00002793% o exception: return any errors or warnings in this structure.
2794%
cristy3ed852e2009-09-05 21:47:34 +00002795*/
2796static MagickBooleanType XBackgroundImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00002797 XResourceInfo *resource_info,XWindows *windows,Image **image,
2798 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002799{
2800#define BackgroundImageTag "Background/Image"
2801
2802 int
2803 status;
2804
2805 static char
2806 window_id[MaxTextExtent] = "root";
2807
2808 XResourceInfo
2809 background_resources;
2810
2811 /*
2812 Put image in background.
2813 */
2814 status=XDialogWidget(display,windows,"Background",
2815 "Enter window id (id 0x00 selects window with pointer):",window_id);
2816 if (*window_id == '\0')
2817 return(MagickFalse);
cristy051718b2011-08-28 22:49:25 +00002818 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2819 exception);
cristy3ed852e2009-09-05 21:47:34 +00002820 XInfoWidget(display,windows,BackgroundImageTag);
2821 XSetCursorState(display,windows,MagickTrue);
2822 XCheckRefreshWindows(display,windows);
2823 background_resources=(*resource_info);
2824 background_resources.window_id=window_id;
2825 background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
cristy051718b2011-08-28 22:49:25 +00002826 status=XDisplayBackgroundImage(display,&background_resources,*image,
2827 exception);
cristy3ed852e2009-09-05 21:47:34 +00002828 if (status != MagickFalse)
2829 XClientMessage(display,windows->image.id,windows->im_protocols,
2830 windows->im_retain_colors,CurrentTime);
2831 XSetCursorState(display,windows,MagickFalse);
cristy051718b2011-08-28 22:49:25 +00002832 (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2833 exception);
cristy3ed852e2009-09-05 21:47:34 +00002834 return(MagickTrue);
2835}
2836
2837/*
2838%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2839% %
2840% %
2841% %
2842+ X C h o p I m a g e %
2843% %
2844% %
2845% %
2846%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2847%
2848% XChopImage() chops the X image.
2849%
2850% The format of the XChopImage method is:
2851%
2852% MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +00002853% XWindows *windows,Image **image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002854%
2855% A description of each parameter follows:
2856%
2857% o display: Specifies a connection to an X server; returned from
2858% XOpenDisplay.
2859%
2860% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2861%
2862% o windows: Specifies a pointer to a XWindows structure.
2863%
2864% o image: the image.
2865%
cristy051718b2011-08-28 22:49:25 +00002866% o exception: return any errors or warnings in this structure.
2867%
cristy3ed852e2009-09-05 21:47:34 +00002868*/
2869static MagickBooleanType XChopImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00002870 XResourceInfo *resource_info,XWindows *windows,Image **image,
2871 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002872{
2873 static const char
2874 *ChopMenu[] =
2875 {
2876 "Direction",
2877 "Help",
2878 "Dismiss",
2879 (char *) NULL
2880 };
2881
2882 static ModeType
2883 direction = HorizontalChopCommand;
2884
2885 static const ModeType
2886 ChopCommands[] =
2887 {
2888 ChopDirectionCommand,
2889 ChopHelpCommand,
2890 ChopDismissCommand
2891 },
2892 DirectionCommands[] =
2893 {
2894 HorizontalChopCommand,
2895 VerticalChopCommand
2896 };
2897
2898 char
2899 text[MaxTextExtent];
2900
2901 Image
2902 *chop_image;
2903
2904 int
2905 id,
2906 x,
2907 y;
2908
2909 MagickRealType
2910 scale_factor;
2911
2912 RectangleInfo
2913 chop_info;
2914
2915 unsigned int
2916 distance,
2917 height,
2918 width;
2919
cristybb503372010-05-27 20:51:26 +00002920 size_t
cristy3ed852e2009-09-05 21:47:34 +00002921 state;
2922
2923 XEvent
2924 event;
2925
2926 XSegment
2927 segment_info;
2928
2929 /*
2930 Map Command widget.
2931 */
2932 (void) CloneString(&windows->command.name,"Chop");
2933 windows->command.data=1;
2934 (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2935 (void) XMapRaised(display,windows->command.id);
2936 XClientMessage(display,windows->image.id,windows->im_protocols,
2937 windows->im_update_widget,CurrentTime);
2938 /*
2939 Track pointer until button 1 is pressed.
2940 */
2941 XQueryPosition(display,windows->image.id,&x,&y);
2942 (void) XSelectInput(display,windows->image.id,
2943 windows->image.attributes.event_mask | PointerMotionMask);
2944 state=DefaultState;
2945 do
2946 {
2947 if (windows->info.mapped != MagickFalse)
2948 {
2949 /*
2950 Display pointer position.
2951 */
cristyb51dff52011-05-19 16:55:47 +00002952 (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
cristy3ed852e2009-09-05 21:47:34 +00002953 x+windows->image.x,y+windows->image.y);
2954 XInfoWidget(display,windows,text);
2955 }
2956 /*
2957 Wait for next event.
2958 */
cristy6710d842011-10-20 23:23:00 +00002959 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +00002960 if (event.xany.window == windows->command.id)
2961 {
2962 /*
2963 Select a command from the Command widget.
2964 */
2965 id=XCommandWidget(display,windows,ChopMenu,&event);
2966 if (id < 0)
2967 continue;
2968 switch (ChopCommands[id])
2969 {
2970 case ChopDirectionCommand:
2971 {
2972 char
2973 command[MaxTextExtent];
2974
2975 static const char
2976 *Directions[] =
2977 {
2978 "horizontal",
2979 "vertical",
2980 (char *) NULL,
2981 };
2982
2983 /*
2984 Select a command from the pop-up menu.
2985 */
2986 id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2987 if (id >= 0)
2988 direction=DirectionCommands[id];
2989 break;
2990 }
2991 case ChopHelpCommand:
2992 {
2993 XTextViewWidget(display,resource_info,windows,MagickFalse,
2994 "Help Viewer - Image Chop",ImageChopHelp);
2995 break;
2996 }
2997 case ChopDismissCommand:
2998 {
2999 /*
3000 Prematurely exit.
3001 */
3002 state|=EscapeState;
3003 state|=ExitState;
3004 break;
3005 }
3006 default:
3007 break;
3008 }
3009 continue;
3010 }
3011 switch (event.type)
3012 {
3013 case ButtonPress:
3014 {
3015 if (event.xbutton.button != Button1)
3016 break;
3017 if (event.xbutton.window != windows->image.id)
3018 break;
3019 /*
3020 User has committed to start point of chopping line.
3021 */
3022 segment_info.x1=(short int) event.xbutton.x;
3023 segment_info.x2=(short int) event.xbutton.x;
3024 segment_info.y1=(short int) event.xbutton.y;
3025 segment_info.y2=(short int) event.xbutton.y;
3026 state|=ExitState;
3027 break;
3028 }
3029 case ButtonRelease:
3030 break;
3031 case Expose:
3032 break;
3033 case KeyPress:
3034 {
3035 char
3036 command[MaxTextExtent];
3037
3038 KeySym
3039 key_symbol;
3040
3041 if (event.xkey.window != windows->image.id)
3042 break;
3043 /*
3044 Respond to a user key press.
3045 */
3046 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3047 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3048 switch ((int) key_symbol)
3049 {
3050 case XK_Escape:
3051 case XK_F20:
3052 {
3053 /*
3054 Prematurely exit.
3055 */
3056 state|=EscapeState;
3057 state|=ExitState;
3058 break;
3059 }
3060 case XK_F1:
3061 case XK_Help:
3062 {
3063 (void) XSetFunction(display,windows->image.highlight_context,
3064 GXcopy);
3065 XTextViewWidget(display,resource_info,windows,MagickFalse,
3066 "Help Viewer - Image Chop",ImageChopHelp);
3067 (void) XSetFunction(display,windows->image.highlight_context,
3068 GXinvert);
3069 break;
3070 }
3071 default:
3072 {
3073 (void) XBell(display,0);
3074 break;
3075 }
3076 }
3077 break;
3078 }
3079 case MotionNotify:
3080 {
3081 /*
3082 Map and unmap Info widget as text cursor crosses its boundaries.
3083 */
3084 x=event.xmotion.x;
3085 y=event.xmotion.y;
3086 if (windows->info.mapped != MagickFalse)
3087 {
3088 if ((x < (int) (windows->info.x+windows->info.width)) &&
3089 (y < (int) (windows->info.y+windows->info.height)))
3090 (void) XWithdrawWindow(display,windows->info.id,
3091 windows->info.screen);
3092 }
3093 else
3094 if ((x > (int) (windows->info.x+windows->info.width)) ||
3095 (y > (int) (windows->info.y+windows->info.height)))
3096 (void) XMapWindow(display,windows->info.id);
3097 }
3098 }
3099 } while ((state & ExitState) == 0);
3100 (void) XSelectInput(display,windows->image.id,
3101 windows->image.attributes.event_mask);
3102 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3103 if ((state & EscapeState) != 0)
3104 return(MagickTrue);
3105 /*
3106 Draw line as pointer moves until the mouse button is released.
3107 */
3108 chop_info.width=0;
3109 chop_info.height=0;
3110 chop_info.x=0;
3111 chop_info.y=0;
3112 distance=0;
3113 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3114 state=DefaultState;
3115 do
3116 {
3117 if (distance > 9)
3118 {
3119 /*
3120 Display info and draw chopping line.
3121 */
3122 if (windows->info.mapped == MagickFalse)
3123 (void) XMapWindow(display,windows->info.id);
cristyb51dff52011-05-19 16:55:47 +00003124 (void) FormatLocaleString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00003125 " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +00003126 chop_info.height,(double) chop_info.x,(double) chop_info.y);
cristy3ed852e2009-09-05 21:47:34 +00003127 XInfoWidget(display,windows,text);
3128 XHighlightLine(display,windows->image.id,
3129 windows->image.highlight_context,&segment_info);
3130 }
3131 else
3132 if (windows->info.mapped != MagickFalse)
3133 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3134 /*
3135 Wait for next event.
3136 */
cristy6710d842011-10-20 23:23:00 +00003137 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +00003138 if (distance > 9)
3139 XHighlightLine(display,windows->image.id,
3140 windows->image.highlight_context,&segment_info);
3141 switch (event.type)
3142 {
3143 case ButtonPress:
3144 {
3145 segment_info.x2=(short int) event.xmotion.x;
3146 segment_info.y2=(short int) event.xmotion.y;
3147 break;
3148 }
3149 case ButtonRelease:
3150 {
3151 /*
3152 User has committed to chopping line.
3153 */
3154 segment_info.x2=(short int) event.xbutton.x;
3155 segment_info.y2=(short int) event.xbutton.y;
3156 state|=ExitState;
3157 break;
3158 }
3159 case Expose:
3160 break;
3161 case MotionNotify:
3162 {
3163 segment_info.x2=(short int) event.xmotion.x;
3164 segment_info.y2=(short int) event.xmotion.y;
3165 }
3166 default:
3167 break;
3168 }
3169 /*
3170 Check boundary conditions.
3171 */
3172 if (segment_info.x2 < 0)
3173 segment_info.x2=0;
3174 else
3175 if (segment_info.x2 > windows->image.ximage->width)
3176 segment_info.x2=windows->image.ximage->width;
3177 if (segment_info.y2 < 0)
3178 segment_info.y2=0;
3179 else
3180 if (segment_info.y2 > windows->image.ximage->height)
3181 segment_info.y2=windows->image.ximage->height;
3182 distance=(unsigned int)
3183 (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3184 ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3185 /*
3186 Compute chopping geometry.
3187 */
3188 if (direction == HorizontalChopCommand)
3189 {
cristybb503372010-05-27 20:51:26 +00003190 chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
cristy49e2d862010-11-12 02:50:30 +00003191 chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
cristy3ed852e2009-09-05 21:47:34 +00003192 chop_info.height=0;
3193 chop_info.y=0;
3194 if (segment_info.x1 > (int) segment_info.x2)
3195 {
cristybb503372010-05-27 20:51:26 +00003196 chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
cristy49e2d862010-11-12 02:50:30 +00003197 chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
cristy3ed852e2009-09-05 21:47:34 +00003198 }
3199 }
3200 else
3201 {
3202 chop_info.width=0;
cristybb503372010-05-27 20:51:26 +00003203 chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
cristy3ed852e2009-09-05 21:47:34 +00003204 chop_info.x=0;
cristy49e2d862010-11-12 02:50:30 +00003205 chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
cristy3ed852e2009-09-05 21:47:34 +00003206 if (segment_info.y1 > segment_info.y2)
3207 {
cristy49e2d862010-11-12 02:50:30 +00003208 chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3209 chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
cristy3ed852e2009-09-05 21:47:34 +00003210 }
3211 }
3212 } while ((state & ExitState) == 0);
3213 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3214 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3215 if (distance <= 9)
3216 return(MagickTrue);
3217 /*
3218 Image chopping is relative to image configuration.
3219 */
cristy051718b2011-08-28 22:49:25 +00003220 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3221 exception);
cristy3ed852e2009-09-05 21:47:34 +00003222 XSetCursorState(display,windows,MagickTrue);
3223 XCheckRefreshWindows(display,windows);
3224 windows->image.window_changes.width=windows->image.ximage->width-
3225 (unsigned int) chop_info.width;
3226 windows->image.window_changes.height=windows->image.ximage->height-
3227 (unsigned int) chop_info.height;
3228 width=(unsigned int) (*image)->columns;
3229 height=(unsigned int) (*image)->rows;
3230 x=0;
3231 y=0;
3232 if (windows->image.crop_geometry != (char *) NULL)
3233 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3234 scale_factor=(MagickRealType) width/windows->image.ximage->width;
3235 chop_info.x+=x;
cristy49e2d862010-11-12 02:50:30 +00003236 chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003237 chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3238 scale_factor=(MagickRealType) height/windows->image.ximage->height;
3239 chop_info.y+=y;
cristy49e2d862010-11-12 02:50:30 +00003240 chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003241 chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3242 /*
3243 Chop image.
3244 */
cristy051718b2011-08-28 22:49:25 +00003245 chop_image=ChopImage(*image,&chop_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003246 XSetCursorState(display,windows,MagickFalse);
3247 if (chop_image == (Image *) NULL)
3248 return(MagickFalse);
3249 *image=DestroyImage(*image);
3250 *image=chop_image;
3251 /*
3252 Update image configuration.
3253 */
cristy6710d842011-10-20 23:23:00 +00003254 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00003255 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003256 return(MagickTrue);
3257}
3258
3259/*
3260%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3261% %
3262% %
3263% %
3264+ X C o l o r E d i t I m a g e %
3265% %
3266% %
3267% %
3268%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3269%
3270% XColorEditImage() allows the user to interactively change the color of one
3271% pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3272%
3273% The format of the XColorEditImage method is:
3274%
3275% MagickBooleanType XColorEditImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00003276% XResourceInfo *resource_info,XWindows *windows,Image **image,
3277% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003278%
3279% A description of each parameter follows:
3280%
3281% o display: Specifies a connection to an X server; returned from
3282% XOpenDisplay.
3283%
3284% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3285%
3286% o windows: Specifies a pointer to a XWindows structure.
3287%
3288% o image: the image; returned from ReadImage.
3289%
cristy051718b2011-08-28 22:49:25 +00003290% o exception: return any errors or warnings in this structure.
3291%
cristy3ed852e2009-09-05 21:47:34 +00003292*/
cristy3ed852e2009-09-05 21:47:34 +00003293static MagickBooleanType XColorEditImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00003294 XResourceInfo *resource_info,XWindows *windows,Image **image,
3295 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003296{
3297 static const char
3298 *ColorEditMenu[] =
3299 {
3300 "Method",
3301 "Pixel Color",
3302 "Border Color",
3303 "Fuzz",
3304 "Undo",
3305 "Help",
3306 "Dismiss",
3307 (char *) NULL
3308 };
3309
3310 static const ModeType
3311 ColorEditCommands[] =
3312 {
3313 ColorEditMethodCommand,
3314 ColorEditColorCommand,
3315 ColorEditBorderCommand,
3316 ColorEditFuzzCommand,
3317 ColorEditUndoCommand,
3318 ColorEditHelpCommand,
3319 ColorEditDismissCommand
3320 };
3321
3322 static PaintMethod
3323 method = PointMethod;
3324
3325 static unsigned int
3326 pen_id = 0;
3327
3328 static XColor
3329 border_color = { 0, 0, 0, 0, 0, 0 };
3330
3331 char
3332 command[MaxTextExtent],
3333 text[MaxTextExtent];
3334
3335 Cursor
3336 cursor;
3337
cristy3ed852e2009-09-05 21:47:34 +00003338 int
3339 entry,
3340 id,
3341 x,
3342 x_offset,
3343 y,
3344 y_offset;
3345
cristy4c08aed2011-07-01 19:47:50 +00003346 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00003347 *q;
3348
cristybb503372010-05-27 20:51:26 +00003349 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003350 i;
3351
3352 unsigned int
3353 height,
3354 width;
3355
cristybb503372010-05-27 20:51:26 +00003356 size_t
cristy3ed852e2009-09-05 21:47:34 +00003357 state;
3358
3359 XColor
3360 color;
3361
3362 XEvent
3363 event;
3364
3365 /*
3366 Map Command widget.
3367 */
3368 (void) CloneString(&windows->command.name,"Color Edit");
3369 windows->command.data=4;
3370 (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3371 (void) XMapRaised(display,windows->command.id);
3372 XClientMessage(display,windows->image.id,windows->im_protocols,
3373 windows->im_update_widget,CurrentTime);
3374 /*
3375 Make cursor.
3376 */
3377 cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3378 resource_info->background_color,resource_info->foreground_color);
3379 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3380 /*
3381 Track pointer until button 1 is pressed.
3382 */
3383 XQueryPosition(display,windows->image.id,&x,&y);
3384 (void) XSelectInput(display,windows->image.id,
3385 windows->image.attributes.event_mask | PointerMotionMask);
3386 state=DefaultState;
3387 do
3388 {
3389 if (windows->info.mapped != MagickFalse)
3390 {
3391 /*
3392 Display pointer position.
3393 */
cristyb51dff52011-05-19 16:55:47 +00003394 (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
cristy3ed852e2009-09-05 21:47:34 +00003395 x+windows->image.x,y+windows->image.y);
3396 XInfoWidget(display,windows,text);
3397 }
3398 /*
3399 Wait for next event.
3400 */
cristy6710d842011-10-20 23:23:00 +00003401 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +00003402 if (event.xany.window == windows->command.id)
3403 {
3404 /*
3405 Select a command from the Command widget.
3406 */
3407 id=XCommandWidget(display,windows,ColorEditMenu,&event);
3408 if (id < 0)
3409 {
3410 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3411 continue;
3412 }
3413 switch (ColorEditCommands[id])
3414 {
3415 case ColorEditMethodCommand:
3416 {
3417 char
3418 **methods;
3419
3420 /*
3421 Select a method from the pop-up menu.
3422 */
cristy042ee782011-04-22 18:48:30 +00003423 methods=(char **) GetCommandOptions(MagickMethodOptions);
cristy3ed852e2009-09-05 21:47:34 +00003424 if (methods == (char **) NULL)
3425 break;
3426 entry=XMenuWidget(display,windows,ColorEditMenu[id],
3427 (const char **) methods,command);
3428 if (entry >= 0)
cristy042ee782011-04-22 18:48:30 +00003429 method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
cristy3ed852e2009-09-05 21:47:34 +00003430 MagickFalse,methods[entry]);
3431 methods=DestroyStringList(methods);
3432 break;
3433 }
3434 case ColorEditColorCommand:
3435 {
3436 const char
3437 *ColorMenu[MaxNumberPens];
3438
3439 int
3440 pen_number;
3441
3442 /*
3443 Initialize menu selections.
3444 */
3445 for (i=0; i < (int) (MaxNumberPens-2); i++)
3446 ColorMenu[i]=resource_info->pen_colors[i];
3447 ColorMenu[MaxNumberPens-2]="Browser...";
3448 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3449 /*
3450 Select a pen color from the pop-up menu.
3451 */
3452 pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3453 (const char **) ColorMenu,command);
3454 if (pen_number < 0)
3455 break;
3456 if (pen_number == (MaxNumberPens-2))
3457 {
3458 static char
3459 color_name[MaxTextExtent] = "gray";
3460
3461 /*
3462 Select a pen color from a dialog.
3463 */
3464 resource_info->pen_colors[pen_number]=color_name;
3465 XColorBrowserWidget(display,windows,"Select",color_name);
3466 if (*color_name == '\0')
3467 break;
3468 }
3469 /*
3470 Set pen color.
3471 */
3472 (void) XParseColor(display,windows->map_info->colormap,
3473 resource_info->pen_colors[pen_number],&color);
3474 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3475 (unsigned int) MaxColors,&color);
3476 windows->pixel_info->pen_colors[pen_number]=color;
3477 pen_id=(unsigned int) pen_number;
3478 break;
3479 }
3480 case ColorEditBorderCommand:
3481 {
3482 const char
3483 *ColorMenu[MaxNumberPens];
3484
3485 int
3486 pen_number;
3487
3488 /*
3489 Initialize menu selections.
3490 */
3491 for (i=0; i < (int) (MaxNumberPens-2); i++)
3492 ColorMenu[i]=resource_info->pen_colors[i];
3493 ColorMenu[MaxNumberPens-2]="Browser...";
3494 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3495 /*
3496 Select a pen color from the pop-up menu.
3497 */
3498 pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3499 (const char **) ColorMenu,command);
3500 if (pen_number < 0)
3501 break;
3502 if (pen_number == (MaxNumberPens-2))
3503 {
3504 static char
3505 color_name[MaxTextExtent] = "gray";
3506
3507 /*
3508 Select a pen color from a dialog.
3509 */
3510 resource_info->pen_colors[pen_number]=color_name;
3511 XColorBrowserWidget(display,windows,"Select",color_name);
3512 if (*color_name == '\0')
3513 break;
3514 }
3515 /*
3516 Set border color.
3517 */
3518 (void) XParseColor(display,windows->map_info->colormap,
3519 resource_info->pen_colors[pen_number],&border_color);
3520 break;
3521 }
3522 case ColorEditFuzzCommand:
3523 {
3524 static char
3525 fuzz[MaxTextExtent];
3526
3527 static const char
3528 *FuzzMenu[] =
3529 {
3530 "0%",
3531 "2%",
3532 "5%",
3533 "10%",
3534 "15%",
3535 "Dialog...",
3536 (char *) NULL,
3537 };
3538
3539 /*
3540 Select a command from the pop-up menu.
3541 */
3542 entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3543 command);
3544 if (entry < 0)
3545 break;
3546 if (entry != 5)
3547 {
cristydbdd0e32011-11-04 23:29:40 +00003548 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
cristy40a08ad2010-02-09 02:27:44 +00003549 QuantumRange+1.0);
cristy3ed852e2009-09-05 21:47:34 +00003550 break;
3551 }
3552 (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3553 (void) XDialogWidget(display,windows,"Ok",
3554 "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3555 if (*fuzz == '\0')
3556 break;
3557 (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
cristy9b34e302011-11-05 02:15:45 +00003558 (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3559 1.0);
cristy3ed852e2009-09-05 21:47:34 +00003560 break;
3561 }
3562 case ColorEditUndoCommand:
3563 {
3564 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
cristy051718b2011-08-28 22:49:25 +00003565 image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003566 break;
3567 }
3568 case ColorEditHelpCommand:
3569 default:
3570 {
3571 XTextViewWidget(display,resource_info,windows,MagickFalse,
3572 "Help Viewer - Image Annotation",ImageColorEditHelp);
3573 break;
3574 }
3575 case ColorEditDismissCommand:
3576 {
3577 /*
3578 Prematurely exit.
3579 */
3580 state|=EscapeState;
3581 state|=ExitState;
3582 break;
3583 }
3584 }
3585 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3586 continue;
3587 }
3588 switch (event.type)
3589 {
3590 case ButtonPress:
3591 {
3592 if (event.xbutton.button != Button1)
3593 break;
3594 if ((event.xbutton.window != windows->image.id) &&
3595 (event.xbutton.window != windows->magnify.id))
3596 break;
3597 /*
3598 exit loop.
3599 */
3600 x=event.xbutton.x;
3601 y=event.xbutton.y;
3602 (void) XMagickCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +00003603 SaveToUndoBufferCommand,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003604 state|=UpdateConfigurationState;
3605 break;
3606 }
3607 case ButtonRelease:
3608 {
3609 if (event.xbutton.button != Button1)
3610 break;
3611 if ((event.xbutton.window != windows->image.id) &&
3612 (event.xbutton.window != windows->magnify.id))
3613 break;
3614 /*
3615 Update colormap information.
3616 */
3617 x=event.xbutton.x;
3618 y=event.xbutton.y;
cristy6710d842011-10-20 23:23:00 +00003619 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00003620 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003621 XInfoWidget(display,windows,text);
3622 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3623 state&=(~UpdateConfigurationState);
3624 break;
3625 }
3626 case Expose:
3627 break;
3628 case KeyPress:
3629 {
3630 KeySym
3631 key_symbol;
3632
3633 if (event.xkey.window == windows->magnify.id)
3634 {
3635 Window
3636 window;
3637
3638 window=windows->magnify.id;
3639 while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3640 }
3641 if (event.xkey.window != windows->image.id)
3642 break;
3643 /*
3644 Respond to a user key press.
3645 */
3646 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3647 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3648 switch ((int) key_symbol)
3649 {
3650 case XK_Escape:
3651 case XK_F20:
3652 {
3653 /*
3654 Prematurely exit.
3655 */
3656 state|=ExitState;
3657 break;
3658 }
3659 case XK_F1:
3660 case XK_Help:
3661 {
3662 XTextViewWidget(display,resource_info,windows,MagickFalse,
3663 "Help Viewer - Image Annotation",ImageColorEditHelp);
3664 break;
3665 }
3666 default:
3667 {
3668 (void) XBell(display,0);
3669 break;
3670 }
3671 }
3672 break;
3673 }
3674 case MotionNotify:
3675 {
3676 /*
3677 Map and unmap Info widget as cursor crosses its boundaries.
3678 */
3679 x=event.xmotion.x;
3680 y=event.xmotion.y;
3681 if (windows->info.mapped != MagickFalse)
3682 {
3683 if ((x < (int) (windows->info.x+windows->info.width)) &&
3684 (y < (int) (windows->info.y+windows->info.height)))
3685 (void) XWithdrawWindow(display,windows->info.id,
3686 windows->info.screen);
3687 }
3688 else
3689 if ((x > (int) (windows->info.x+windows->info.width)) ||
3690 (y > (int) (windows->info.y+windows->info.height)))
3691 (void) XMapWindow(display,windows->info.id);
3692 break;
3693 }
3694 default:
3695 break;
3696 }
3697 if (event.xany.window == windows->magnify.id)
3698 {
3699 x=windows->magnify.x-windows->image.x;
3700 y=windows->magnify.y-windows->image.y;
3701 }
3702 x_offset=x;
3703 y_offset=y;
3704 if ((state & UpdateConfigurationState) != 0)
3705 {
cristy49e2d862010-11-12 02:50:30 +00003706 CacheView
3707 *image_view;
3708
cristy3ed852e2009-09-05 21:47:34 +00003709 int
3710 x,
3711 y;
3712
3713 /*
3714 Pixel edit is relative to image configuration.
3715 */
3716 (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3717 MagickTrue);
3718 color=windows->pixel_info->pen_colors[pen_id];
3719 XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3720 width=(unsigned int) (*image)->columns;
3721 height=(unsigned int) (*image)->rows;
3722 x=0;
3723 y=0;
3724 if (windows->image.crop_geometry != (char *) NULL)
3725 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3726 &width,&height);
3727 x_offset=(int)
3728 (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3729 y_offset=(int)
3730 (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3731 if ((x_offset < 0) || (y_offset < 0))
3732 continue;
cristy49e2d862010-11-12 02:50:30 +00003733 if ((x_offset >= (int) (*image)->columns) ||
3734 (y_offset >= (int) (*image)->rows))
cristy3ed852e2009-09-05 21:47:34 +00003735 continue;
cristy49e2d862010-11-12 02:50:30 +00003736 image_view=AcquireCacheView(*image);
cristy3ed852e2009-09-05 21:47:34 +00003737 switch (method)
3738 {
3739 case PointMethod:
3740 default:
3741 {
3742 /*
3743 Update color information using point algorithm.
3744 */
cristy574cc262011-08-05 01:23:58 +00003745 if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003746 return(MagickFalse);
cristy49e2d862010-11-12 02:50:30 +00003747 q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
cristy574cc262011-08-05 01:23:58 +00003748 (ssize_t) y_offset,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003749 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003750 break;
cristy4c08aed2011-07-01 19:47:50 +00003751 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3752 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3753 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
cristy051718b2011-08-28 22:49:25 +00003754 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003755 break;
3756 }
3757 case ReplaceMethod:
3758 {
cristy101ab702011-10-13 13:06:32 +00003759 PixelInfo
cristy4c08aed2011-07-01 19:47:50 +00003760 pixel,
cristy3ed852e2009-09-05 21:47:34 +00003761 target;
3762
cristy2ed42f62011-10-02 19:49:57 +00003763 Quantum
cristy5f95f4f2011-10-23 01:01:01 +00003764 virtual_pixel[CompositePixelChannel];
cristy2ed42f62011-10-02 19:49:57 +00003765
cristy3ed852e2009-09-05 21:47:34 +00003766 /*
3767 Update color information using replace algorithm.
3768 */
cristy49e2d862010-11-12 02:50:30 +00003769 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
cristy2ed42f62011-10-02 19:49:57 +00003770 (ssize_t) y_offset,virtual_pixel,exception);
3771 target.red=virtual_pixel[RedPixelChannel];
3772 target.green=virtual_pixel[GreenPixelChannel];
3773 target.blue=virtual_pixel[BluePixelChannel];
3774 target.alpha=virtual_pixel[AlphaPixelChannel];
cristy3ed852e2009-09-05 21:47:34 +00003775 if ((*image)->storage_class == DirectClass)
3776 {
cristy49e2d862010-11-12 02:50:30 +00003777 for (y=0; y < (int) (*image)->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003778 {
cristy49e2d862010-11-12 02:50:30 +00003779 q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3780 (*image)->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003781 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003782 break;
3783 for (x=0; x < (int) (*image)->columns; x++)
3784 {
cristy101ab702011-10-13 13:06:32 +00003785 GetPixelInfoPixel(*image,q,&pixel);
3786 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
cristy3ed852e2009-09-05 21:47:34 +00003787 {
cristy4c08aed2011-07-01 19:47:50 +00003788 SetPixelRed(*image,ScaleShortToQuantum(
3789 color.red),q);
3790 SetPixelGreen(*image,ScaleShortToQuantum(
3791 color.green),q);
3792 SetPixelBlue(*image,ScaleShortToQuantum(
3793 color.blue),q);
cristy3ed852e2009-09-05 21:47:34 +00003794 }
cristyed231572011-07-14 02:18:59 +00003795 q+=GetPixelChannels(*image);
cristy3ed852e2009-09-05 21:47:34 +00003796 }
cristy49e2d862010-11-12 02:50:30 +00003797 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003798 break;
3799 }
3800 }
3801 else
3802 {
cristy49e2d862010-11-12 02:50:30 +00003803 for (i=0; i < (ssize_t) (*image)->colors; i++)
cristy101ab702011-10-13 13:06:32 +00003804 if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
cristy3ed852e2009-09-05 21:47:34 +00003805 {
cristy4c08aed2011-07-01 19:47:50 +00003806 (*image)->colormap[i].red=ScaleShortToQuantum(
3807 color.red);
cristy3ed852e2009-09-05 21:47:34 +00003808 (*image)->colormap[i].green=ScaleShortToQuantum(
3809 color.green);
3810 (*image)->colormap[i].blue=ScaleShortToQuantum(
3811 color.blue);
3812 }
cristyea1a8aa2011-10-20 13:24:06 +00003813 (void) SyncImage(*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003814 }
3815 break;
3816 }
3817 case FloodfillMethod:
3818 case FillToBorderMethod:
3819 {
3820 DrawInfo
3821 *draw_info;
3822
cristy4c08aed2011-07-01 19:47:50 +00003823 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003824 target;
3825
3826 /*
3827 Update color information using floodfill algorithm.
3828 */
cristy52010022011-10-21 18:07:37 +00003829 (void) GetOneVirtualMagickPixel(*image,
3830 GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3831 y_offset,&target,exception);
cristy3ed852e2009-09-05 21:47:34 +00003832 if (method == FillToBorderMethod)
3833 {
3834 target.red=(MagickRealType)
3835 ScaleShortToQuantum(border_color.red);
3836 target.green=(MagickRealType)
3837 ScaleShortToQuantum(border_color.green);
3838 target.blue=(MagickRealType)
3839 ScaleShortToQuantum(border_color.blue);
3840 }
3841 draw_info=CloneDrawInfo(resource_info->image_info,
3842 (DrawInfo *) NULL);
cristy9950d572011-10-01 18:22:35 +00003843 (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3844 AllCompliance,&draw_info->fill,exception);
cristyd42d9952011-07-08 14:21:50 +00003845 (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
3846 x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
cristy189e84c2011-08-27 18:08:53 +00003847 MagickFalse : MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00003848 draw_info=DestroyDrawInfo(draw_info);
3849 break;
3850 }
3851 case ResetMethod:
3852 {
3853 /*
3854 Update color information using reset algorithm.
3855 */
cristy574cc262011-08-05 01:23:58 +00003856 if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003857 return(MagickFalse);
cristy49e2d862010-11-12 02:50:30 +00003858 for (y=0; y < (int) (*image)->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003859 {
cristy49e2d862010-11-12 02:50:30 +00003860 q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3861 (*image)->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003862 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003863 break;
3864 for (x=0; x < (int) (*image)->columns; x++)
3865 {
cristy4c08aed2011-07-01 19:47:50 +00003866 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3867 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3868 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
cristyed231572011-07-14 02:18:59 +00003869 q+=GetPixelChannels(*image);
cristy3ed852e2009-09-05 21:47:34 +00003870 }
cristy49e2d862010-11-12 02:50:30 +00003871 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003872 break;
3873 }
3874 break;
3875 }
3876 }
cristy49e2d862010-11-12 02:50:30 +00003877 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003878 state&=(~UpdateConfigurationState);
3879 }
3880 } while ((state & ExitState) == 0);
3881 (void) XSelectInput(display,windows->image.id,
3882 windows->image.attributes.event_mask);
3883 XSetCursorState(display,windows,MagickFalse);
3884 (void) XFreeCursor(display,cursor);
3885 return(MagickTrue);
3886}
3887
3888/*
3889%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3890% %
3891% %
3892% %
3893+ X C o m p o s i t e I m a g e %
3894% %
3895% %
3896% %
3897%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3898%
3899% XCompositeImage() requests an image name from the user, reads the image and
3900% composites it with the X window image at a location the user chooses with
3901% the pointer.
3902%
3903% The format of the XCompositeImage method is:
3904%
3905% MagickBooleanType XCompositeImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00003906% XResourceInfo *resource_info,XWindows *windows,Image *image,
3907% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003908%
3909% A description of each parameter follows:
3910%
3911% o display: Specifies a connection to an X server; returned from
3912% XOpenDisplay.
3913%
3914% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3915%
3916% o windows: Specifies a pointer to a XWindows structure.
3917%
3918% o image: the image; returned from ReadImage.
3919%
cristy051718b2011-08-28 22:49:25 +00003920% o exception: return any errors or warnings in this structure.
3921%
cristy3ed852e2009-09-05 21:47:34 +00003922*/
3923static MagickBooleanType XCompositeImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00003924 XResourceInfo *resource_info,XWindows *windows,Image *image,
3925 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003926{
3927 static char
3928 displacement_geometry[MaxTextExtent] = "30x30",
3929 filename[MaxTextExtent] = "\0";
3930
3931 static const char
3932 *CompositeMenu[] =
3933 {
3934 "Operators",
3935 "Dissolve",
3936 "Displace",
3937 "Help",
3938 "Dismiss",
3939 (char *) NULL
3940 };
3941
3942 static CompositeOperator
3943 compose = CopyCompositeOp;
3944
3945 static const ModeType
3946 CompositeCommands[] =
3947 {
3948 CompositeOperatorsCommand,
3949 CompositeDissolveCommand,
3950 CompositeDisplaceCommand,
3951 CompositeHelpCommand,
3952 CompositeDismissCommand
3953 };
3954
3955 char
3956 text[MaxTextExtent];
3957
3958 Cursor
3959 cursor;
3960
3961 Image
3962 *composite_image;
3963
3964 int
3965 entry,
3966 id,
3967 x,
3968 y;
3969
3970 MagickRealType
3971 blend,
3972 scale_factor;
3973
3974 RectangleInfo
3975 highlight_info,
3976 composite_info;
3977
3978 unsigned int
3979 height,
3980 width;
3981
cristybb503372010-05-27 20:51:26 +00003982 size_t
cristy3ed852e2009-09-05 21:47:34 +00003983 state;
3984
3985 XEvent
3986 event;
3987
3988 /*
3989 Request image file name from user.
3990 */
3991 XFileBrowserWidget(display,windows,"Composite",filename);
3992 if (*filename == '\0')
3993 return(MagickTrue);
3994 /*
3995 Read image.
3996 */
3997 XSetCursorState(display,windows,MagickTrue);
3998 XCheckRefreshWindows(display,windows);
3999 (void) CopyMagickString(resource_info->image_info->filename,filename,
4000 MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +00004001 composite_image=ReadImage(resource_info->image_info,exception);
4002 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00004003 XSetCursorState(display,windows,MagickFalse);
4004 if (composite_image == (Image *) NULL)
4005 return(MagickFalse);
4006 /*
4007 Map Command widget.
4008 */
4009 (void) CloneString(&windows->command.name,"Composite");
4010 windows->command.data=1;
4011 (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
4012 (void) XMapRaised(display,windows->command.id);
4013 XClientMessage(display,windows->image.id,windows->im_protocols,
4014 windows->im_update_widget,CurrentTime);
4015 /*
4016 Track pointer until button 1 is pressed.
4017 */
4018 XQueryPosition(display,windows->image.id,&x,&y);
4019 (void) XSelectInput(display,windows->image.id,
4020 windows->image.attributes.event_mask | PointerMotionMask);
cristy49e2d862010-11-12 02:50:30 +00004021 composite_info.x=(ssize_t) windows->image.x+x;
4022 composite_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +00004023 composite_info.width=0;
4024 composite_info.height=0;
4025 cursor=XCreateFontCursor(display,XC_ul_angle);
4026 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4027 blend=0.0;
4028 state=DefaultState;
4029 do
4030 {
4031 if (windows->info.mapped != MagickFalse)
4032 {
4033 /*
4034 Display pointer position.
4035 */
cristyb51dff52011-05-19 16:55:47 +00004036 (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
cristyf2faecf2010-05-28 19:19:36 +00004037 (long) composite_info.x,(long) composite_info.y);
cristy3ed852e2009-09-05 21:47:34 +00004038 XInfoWidget(display,windows,text);
4039 }
4040 highlight_info=composite_info;
4041 highlight_info.x=composite_info.x-windows->image.x;
4042 highlight_info.y=composite_info.y-windows->image.y;
4043 XHighlightRectangle(display,windows->image.id,
4044 windows->image.highlight_context,&highlight_info);
4045 /*
4046 Wait for next event.
4047 */
cristy6710d842011-10-20 23:23:00 +00004048 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +00004049 XHighlightRectangle(display,windows->image.id,
4050 windows->image.highlight_context,&highlight_info);
4051 if (event.xany.window == windows->command.id)
4052 {
4053 /*
4054 Select a command from the Command widget.
4055 */
4056 id=XCommandWidget(display,windows,CompositeMenu,&event);
4057 if (id < 0)
4058 continue;
4059 switch (CompositeCommands[id])
4060 {
4061 case CompositeOperatorsCommand:
4062 {
4063 char
4064 command[MaxTextExtent],
4065 **operators;
4066
4067 /*
4068 Select a command from the pop-up menu.
4069 */
cristy042ee782011-04-22 18:48:30 +00004070 operators=GetCommandOptions(MagickComposeOptions);
cristy3ed852e2009-09-05 21:47:34 +00004071 if (operators == (char **) NULL)
4072 break;
4073 entry=XMenuWidget(display,windows,CompositeMenu[id],
4074 (const char **) operators,command);
4075 if (entry >= 0)
cristy042ee782011-04-22 18:48:30 +00004076 compose=(CompositeOperator) ParseCommandOption(
cristy3ed852e2009-09-05 21:47:34 +00004077 MagickComposeOptions,MagickFalse,operators[entry]);
4078 operators=DestroyStringList(operators);
4079 break;
4080 }
4081 case CompositeDissolveCommand:
4082 {
4083 static char
4084 factor[MaxTextExtent] = "20.0";
4085
4086 /*
4087 Dissolve the two images a given percent.
4088 */
4089 (void) XSetFunction(display,windows->image.highlight_context,
4090 GXcopy);
4091 (void) XDialogWidget(display,windows,"Dissolve",
4092 "Enter the blend factor (0.0 - 99.9%):",factor);
4093 (void) XSetFunction(display,windows->image.highlight_context,
4094 GXinvert);
4095 if (*factor == '\0')
4096 break;
cristydbdd0e32011-11-04 23:29:40 +00004097 blend=StringToDouble(factor,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004098 compose=DissolveCompositeOp;
4099 break;
4100 }
4101 case CompositeDisplaceCommand:
4102 {
4103 /*
4104 Get horizontal and vertical scale displacement geometry.
4105 */
4106 (void) XSetFunction(display,windows->image.highlight_context,
4107 GXcopy);
4108 (void) XDialogWidget(display,windows,"Displace",
4109 "Enter the horizontal and vertical scale:",displacement_geometry);
4110 (void) XSetFunction(display,windows->image.highlight_context,
4111 GXinvert);
4112 if (*displacement_geometry == '\0')
4113 break;
4114 compose=DisplaceCompositeOp;
4115 break;
4116 }
4117 case CompositeHelpCommand:
4118 {
4119 (void) XSetFunction(display,windows->image.highlight_context,
4120 GXcopy);
4121 XTextViewWidget(display,resource_info,windows,MagickFalse,
4122 "Help Viewer - Image Composite",ImageCompositeHelp);
4123 (void) XSetFunction(display,windows->image.highlight_context,
4124 GXinvert);
4125 break;
4126 }
4127 case CompositeDismissCommand:
4128 {
4129 /*
4130 Prematurely exit.
4131 */
4132 state|=EscapeState;
4133 state|=ExitState;
4134 break;
4135 }
4136 default:
4137 break;
4138 }
4139 continue;
4140 }
4141 switch (event.type)
4142 {
4143 case ButtonPress:
4144 {
4145 if (image->debug != MagickFalse)
4146 (void) LogMagickEvent(X11Event,GetMagickModule(),
4147 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4148 event.xbutton.button,event.xbutton.x,event.xbutton.y);
4149 if (event.xbutton.button != Button1)
4150 break;
4151 if (event.xbutton.window != windows->image.id)
4152 break;
4153 /*
4154 Change cursor.
4155 */
4156 composite_info.width=composite_image->columns;
4157 composite_info.height=composite_image->rows;
4158 (void) XCheckDefineCursor(display,windows->image.id,cursor);
cristy49e2d862010-11-12 02:50:30 +00004159 composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4160 composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +00004161 break;
4162 }
4163 case ButtonRelease:
4164 {
4165 if (image->debug != MagickFalse)
4166 (void) LogMagickEvent(X11Event,GetMagickModule(),
4167 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4168 event.xbutton.button,event.xbutton.x,event.xbutton.y);
4169 if (event.xbutton.button != Button1)
4170 break;
4171 if (event.xbutton.window != windows->image.id)
4172 break;
4173 if ((composite_info.width != 0) && (composite_info.height != 0))
4174 {
4175 /*
4176 User has selected the location of the composite image.
4177 */
cristy49e2d862010-11-12 02:50:30 +00004178 composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4179 composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +00004180 state|=ExitState;
4181 }
4182 break;
4183 }
4184 case Expose:
4185 break;
4186 case KeyPress:
4187 {
4188 char
4189 command[MaxTextExtent];
4190
4191 KeySym
4192 key_symbol;
4193
4194 int
4195 length;
4196
4197 if (event.xkey.window != windows->image.id)
4198 break;
4199 /*
4200 Respond to a user key press.
4201 */
4202 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4203 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4204 *(command+length)='\0';
4205 if (image->debug != MagickFalse)
4206 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00004207 "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
cristy3ed852e2009-09-05 21:47:34 +00004208 switch ((int) key_symbol)
4209 {
4210 case XK_Escape:
4211 case XK_F20:
4212 {
4213 /*
4214 Prematurely exit.
4215 */
4216 composite_image=DestroyImage(composite_image);
4217 state|=EscapeState;
4218 state|=ExitState;
4219 break;
4220 }
4221 case XK_F1:
4222 case XK_Help:
4223 {
4224 (void) XSetFunction(display,windows->image.highlight_context,
4225 GXcopy);
4226 XTextViewWidget(display,resource_info,windows,MagickFalse,
4227 "Help Viewer - Image Composite",ImageCompositeHelp);
4228 (void) XSetFunction(display,windows->image.highlight_context,
4229 GXinvert);
4230 break;
4231 }
4232 default:
4233 {
4234 (void) XBell(display,0);
4235 break;
4236 }
4237 }
4238 break;
4239 }
4240 case MotionNotify:
4241 {
4242 /*
4243 Map and unmap Info widget as text cursor crosses its boundaries.
4244 */
4245 x=event.xmotion.x;
4246 y=event.xmotion.y;
4247 if (windows->info.mapped != MagickFalse)
4248 {
4249 if ((x < (int) (windows->info.x+windows->info.width)) &&
4250 (y < (int) (windows->info.y+windows->info.height)))
4251 (void) XWithdrawWindow(display,windows->info.id,
4252 windows->info.screen);
4253 }
4254 else
4255 if ((x > (int) (windows->info.x+windows->info.width)) ||
4256 (y > (int) (windows->info.y+windows->info.height)))
4257 (void) XMapWindow(display,windows->info.id);
cristy49e2d862010-11-12 02:50:30 +00004258 composite_info.x=(ssize_t) windows->image.x+x;
4259 composite_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +00004260 break;
4261 }
4262 default:
4263 {
4264 if (image->debug != MagickFalse)
4265 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4266 event.type);
4267 break;
4268 }
4269 }
4270 } while ((state & ExitState) == 0);
4271 (void) XSelectInput(display,windows->image.id,
4272 windows->image.attributes.event_mask);
4273 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4274 XSetCursorState(display,windows,MagickFalse);
4275 (void) XFreeCursor(display,cursor);
4276 if ((state & EscapeState) != 0)
4277 return(MagickTrue);
4278 /*
4279 Image compositing is relative to image configuration.
4280 */
4281 XSetCursorState(display,windows,MagickTrue);
4282 XCheckRefreshWindows(display,windows);
4283 width=(unsigned int) image->columns;
4284 height=(unsigned int) image->rows;
4285 x=0;
4286 y=0;
4287 if (windows->image.crop_geometry != (char *) NULL)
4288 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4289 scale_factor=(MagickRealType) width/windows->image.ximage->width;
4290 composite_info.x+=x;
cristy49e2d862010-11-12 02:50:30 +00004291 composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +00004292 composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4293 scale_factor=(MagickRealType) height/windows->image.ximage->height;
4294 composite_info.y+=y;
cristy49e2d862010-11-12 02:50:30 +00004295 composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00004296 composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4297 if ((composite_info.width != composite_image->columns) ||
4298 (composite_info.height != composite_image->rows))
4299 {
4300 Image
4301 *resize_image;
4302
4303 /*
4304 Scale composite image.
4305 */
cristy15b98cd2010-09-12 19:42:50 +00004306 resize_image=ResizeImage(composite_image,composite_info.width,
4307 composite_info.height,composite_image->filter,composite_image->blur,
cristy051718b2011-08-28 22:49:25 +00004308 exception);
cristy3ed852e2009-09-05 21:47:34 +00004309 composite_image=DestroyImage(composite_image);
4310 if (resize_image == (Image *) NULL)
4311 {
4312 XSetCursorState(display,windows,MagickFalse);
4313 return(MagickFalse);
4314 }
4315 composite_image=resize_image;
4316 }
4317 if (compose == DisplaceCompositeOp)
4318 (void) SetImageArtifact(composite_image,"compose:args",
4319 displacement_geometry);
4320 if (blend != 0.0)
4321 {
cristy49e2d862010-11-12 02:50:30 +00004322 CacheView
4323 *image_view;
4324
cristy3ed852e2009-09-05 21:47:34 +00004325 int
4326 y;
4327
4328 Quantum
4329 opacity;
4330
4331 register int
4332 x;
4333
cristy4c08aed2011-07-01 19:47:50 +00004334 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004335 *q;
4336
4337 /*
4338 Create mattes for blending.
4339 */
cristy63240882011-08-05 19:05:27 +00004340 (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00004341 opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
cristybb503372010-05-27 20:51:26 +00004342 ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
cristy574cc262011-08-05 01:23:58 +00004343 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004344 return(MagickFalse);
4345 image->matte=MagickTrue;
cristy49e2d862010-11-12 02:50:30 +00004346 image_view=AcquireCacheView(image);
4347 for (y=0; y < (int) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004348 {
cristy49e2d862010-11-12 02:50:30 +00004349 q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4350 exception);
cristy4c08aed2011-07-01 19:47:50 +00004351 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004352 break;
4353 for (x=0; x < (int) image->columns; x++)
4354 {
cristy4c08aed2011-07-01 19:47:50 +00004355 SetPixelAlpha(image,opacity,q);
cristyed231572011-07-14 02:18:59 +00004356 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004357 }
cristy49e2d862010-11-12 02:50:30 +00004358 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004359 break;
4360 }
cristy49e2d862010-11-12 02:50:30 +00004361 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00004362 }
4363 /*
4364 Composite image with X Image window.
4365 */
4366 (void) CompositeImage(image,compose,composite_image,composite_info.x,
cristye941a752011-10-15 01:52:48 +00004367 composite_info.y,exception);
cristy3ed852e2009-09-05 21:47:34 +00004368 composite_image=DestroyImage(composite_image);
4369 XSetCursorState(display,windows,MagickFalse);
4370 /*
4371 Update image configuration.
4372 */
cristy6710d842011-10-20 23:23:00 +00004373 XConfigureImageColormap(display,resource_info,windows,image,exception);
cristy051718b2011-08-28 22:49:25 +00004374 (void) XConfigureImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00004375 return(MagickTrue);
4376}
4377
4378/*
4379%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4380% %
4381% %
4382% %
4383+ X C o n f i g u r e I m a g e %
4384% %
4385% %
4386% %
4387%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4388%
4389% XConfigureImage() creates a new X image. It also notifies the window
4390% manager of the new image size and configures the transient widows.
4391%
4392% The format of the XConfigureImage method is:
4393%
4394% MagickBooleanType XConfigureImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00004395% XResourceInfo *resource_info,XWindows *windows,Image *image,
4396% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004397%
4398% A description of each parameter follows:
4399%
4400% o display: Specifies a connection to an X server; returned from
4401% XOpenDisplay.
4402%
4403% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4404%
4405% o windows: Specifies a pointer to a XWindows structure.
4406%
4407% o image: the image.
4408%
cristy051718b2011-08-28 22:49:25 +00004409% o exception: return any errors or warnings in this structure.
4410%
4411% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00004412%
4413*/
4414static MagickBooleanType XConfigureImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00004415 XResourceInfo *resource_info,XWindows *windows,Image *image,
4416 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004417{
4418 char
4419 geometry[MaxTextExtent];
4420
cristy3ed852e2009-09-05 21:47:34 +00004421 MagickStatusType
4422 status;
4423
cristybb503372010-05-27 20:51:26 +00004424 size_t
cristy3ed852e2009-09-05 21:47:34 +00004425 mask,
4426 height,
4427 width;
4428
cristy9d314ff2011-03-09 01:30:28 +00004429 ssize_t
4430 x,
4431 y;
4432
cristy3ed852e2009-09-05 21:47:34 +00004433 XSizeHints
4434 *size_hints;
4435
4436 XWindowChanges
4437 window_changes;
4438
4439 /*
4440 Dismiss if window dimensions are zero.
4441 */
4442 width=(unsigned int) windows->image.window_changes.width;
4443 height=(unsigned int) windows->image.window_changes.height;
4444 if (image->debug != MagickFalse)
4445 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004446 "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4447 windows->image.ximage->height,(double) width,(double) height);
cristy3ed852e2009-09-05 21:47:34 +00004448 if ((width*height) == 0)
4449 return(MagickTrue);
4450 x=0;
4451 y=0;
4452 /*
4453 Resize image to fit Image window dimensions.
4454 */
4455 XSetCursorState(display,windows,MagickTrue);
4456 (void) XFlush(display);
4457 if (((int) width != windows->image.ximage->width) ||
4458 ((int) height != windows->image.ximage->height))
4459 image->taint=MagickTrue;
4460 windows->magnify.x=(int)
4461 width*windows->magnify.x/windows->image.ximage->width;
4462 windows->magnify.y=(int)
4463 height*windows->magnify.y/windows->image.ximage->height;
4464 windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4465 windows->image.y=(int)
4466 (height*windows->image.y/windows->image.ximage->height);
4467 status=XMakeImage(display,resource_info,&windows->image,image,
cristy051718b2011-08-28 22:49:25 +00004468 (unsigned int) width,(unsigned int) height,exception);
cristy3ed852e2009-09-05 21:47:34 +00004469 if (status == MagickFalse)
4470 XNoticeWidget(display,windows,"Unable to configure X image:",
4471 windows->image.name);
4472 /*
4473 Notify window manager of the new configuration.
4474 */
4475 if (resource_info->image_geometry != (char *) NULL)
cristyb51dff52011-05-19 16:55:47 +00004476 (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
cristy3ed852e2009-09-05 21:47:34 +00004477 resource_info->image_geometry);
4478 else
cristyb51dff52011-05-19 16:55:47 +00004479 (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
cristy3ed852e2009-09-05 21:47:34 +00004480 XDisplayWidth(display,windows->image.screen),
4481 XDisplayHeight(display,windows->image.screen));
4482 (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4483 window_changes.width=(int) width;
4484 if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4485 window_changes.width=XDisplayWidth(display,windows->image.screen);
4486 window_changes.height=(int) height;
4487 if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4488 window_changes.height=XDisplayHeight(display,windows->image.screen);
cristybb503372010-05-27 20:51:26 +00004489 mask=(size_t) (CWWidth | CWHeight);
cristy3ed852e2009-09-05 21:47:34 +00004490 if (resource_info->backdrop)
4491 {
4492 mask|=CWX | CWY;
4493 window_changes.x=(int)
4494 ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4495 window_changes.y=(int)
4496 ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4497 }
4498 (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4499 (unsigned int) mask,&window_changes);
4500 (void) XClearWindow(display,windows->image.id);
4501 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4502 /*
4503 Update Magnify window configuration.
4504 */
4505 if (windows->magnify.mapped != MagickFalse)
cristy6710d842011-10-20 23:23:00 +00004506 XMakeMagnifyImage(display,windows,exception);
cristy3ed852e2009-09-05 21:47:34 +00004507 windows->pan.crop_geometry=windows->image.crop_geometry;
4508 XBestIconSize(display,&windows->pan,image);
4509 while (((windows->pan.width << 1) < MaxIconSize) &&
4510 ((windows->pan.height << 1) < MaxIconSize))
4511 {
4512 windows->pan.width<<=1;
4513 windows->pan.height<<=1;
4514 }
4515 if (windows->pan.geometry != (char *) NULL)
4516 (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4517 &windows->pan.width,&windows->pan.height);
4518 window_changes.width=(int) windows->pan.width;
4519 window_changes.height=(int) windows->pan.height;
4520 size_hints=XAllocSizeHints();
4521 if (size_hints != (XSizeHints *) NULL)
4522 {
4523 /*
4524 Set new size hints.
4525 */
4526 size_hints->flags=PSize | PMinSize | PMaxSize;
4527 size_hints->width=window_changes.width;
4528 size_hints->height=window_changes.height;
4529 size_hints->min_width=size_hints->width;
4530 size_hints->min_height=size_hints->height;
4531 size_hints->max_width=size_hints->width;
4532 size_hints->max_height=size_hints->height;
4533 (void) XSetNormalHints(display,windows->pan.id,size_hints);
4534 (void) XFree((void *) size_hints);
4535 }
4536 (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4537 (unsigned int) (CWWidth | CWHeight),&window_changes);
4538 /*
4539 Update icon window configuration.
4540 */
4541 windows->icon.crop_geometry=windows->image.crop_geometry;
4542 XBestIconSize(display,&windows->icon,image);
4543 window_changes.width=(int) windows->icon.width;
4544 window_changes.height=(int) windows->icon.height;
4545 (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4546 (unsigned int) (CWWidth | CWHeight),&window_changes);
4547 XSetCursorState(display,windows,MagickFalse);
4548 return(status != 0 ? MagickTrue : MagickFalse);
4549}
4550
4551/*
4552%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4553% %
4554% %
4555% %
4556+ X C r o p I m a g e %
4557% %
4558% %
4559% %
4560%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4561%
4562% XCropImage() allows the user to select a region of the image and crop, copy,
4563% or cut it. For copy or cut, the image can subsequently be composited onto
4564% the image with XPasteImage.
4565%
4566% The format of the XCropImage method is:
4567%
4568% MagickBooleanType XCropImage(Display *display,
4569% XResourceInfo *resource_info,XWindows *windows,Image *image,
cristy051718b2011-08-28 22:49:25 +00004570% const ClipboardMode mode,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004571%
4572% A description of each parameter follows:
4573%
4574% o display: Specifies a connection to an X server; returned from
4575% XOpenDisplay.
4576%
4577% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4578%
4579% o windows: Specifies a pointer to a XWindows structure.
4580%
4581% o image: the image; returned from ReadImage.
4582%
4583% o mode: This unsigned value specified whether the image should be
4584% cropped, copied, or cut.
4585%
cristy051718b2011-08-28 22:49:25 +00004586% o exception: return any errors or warnings in this structure.
4587%
cristy3ed852e2009-09-05 21:47:34 +00004588*/
4589static MagickBooleanType XCropImage(Display *display,
4590 XResourceInfo *resource_info,XWindows *windows,Image *image,
cristy051718b2011-08-28 22:49:25 +00004591 const ClipboardMode mode,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004592{
4593 static const char
4594 *CropModeMenu[] =
4595 {
4596 "Help",
4597 "Dismiss",
4598 (char *) NULL
4599 },
4600 *RectifyModeMenu[] =
4601 {
4602 "Crop",
4603 "Help",
4604 "Dismiss",
4605 (char *) NULL
4606 };
4607
4608 static const ModeType
4609 CropCommands[] =
4610 {
4611 CropHelpCommand,
4612 CropDismissCommand
4613 },
4614 RectifyCommands[] =
4615 {
4616 RectifyCopyCommand,
4617 RectifyHelpCommand,
4618 RectifyDismissCommand
4619 };
4620
cristy49e2d862010-11-12 02:50:30 +00004621 CacheView
4622 *image_view;
4623
cristy3ed852e2009-09-05 21:47:34 +00004624 char
4625 command[MaxTextExtent],
4626 text[MaxTextExtent];
4627
4628 Cursor
4629 cursor;
4630
cristy3ed852e2009-09-05 21:47:34 +00004631 int
4632 id,
4633 x,
4634 y;
4635
4636 KeySym
4637 key_symbol;
4638
4639 Image
4640 *crop_image;
4641
4642 MagickRealType
4643 scale_factor;
4644
4645 RectangleInfo
4646 crop_info,
4647 highlight_info;
4648
cristy4c08aed2011-07-01 19:47:50 +00004649 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004650 *q;
4651
4652 unsigned int
4653 height,
4654 width;
4655
cristybb503372010-05-27 20:51:26 +00004656 size_t
cristy3ed852e2009-09-05 21:47:34 +00004657 state;
4658
4659 XEvent
4660 event;
4661
4662 /*
4663 Map Command widget.
4664 */
4665 switch (mode)
4666 {
4667 case CopyMode:
4668 {
4669 (void) CloneString(&windows->command.name,"Copy");
4670 break;
4671 }
4672 case CropMode:
4673 {
4674 (void) CloneString(&windows->command.name,"Crop");
4675 break;
4676 }
4677 case CutMode:
4678 {
4679 (void) CloneString(&windows->command.name,"Cut");
4680 break;
4681 }
4682 }
4683 RectifyModeMenu[0]=windows->command.name;
4684 windows->command.data=0;
4685 (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4686 (void) XMapRaised(display,windows->command.id);
4687 XClientMessage(display,windows->image.id,windows->im_protocols,
4688 windows->im_update_widget,CurrentTime);
4689 /*
4690 Track pointer until button 1 is pressed.
4691 */
4692 XQueryPosition(display,windows->image.id,&x,&y);
4693 (void) XSelectInput(display,windows->image.id,
4694 windows->image.attributes.event_mask | PointerMotionMask);
cristy49e2d862010-11-12 02:50:30 +00004695 crop_info.x=(ssize_t) windows->image.x+x;
4696 crop_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +00004697 crop_info.width=0;
4698 crop_info.height=0;
4699 cursor=XCreateFontCursor(display,XC_fleur);
4700 state=DefaultState;
4701 do
4702 {
4703 if (windows->info.mapped != MagickFalse)
4704 {
4705 /*
4706 Display pointer position.
4707 */
cristyb51dff52011-05-19 16:55:47 +00004708 (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
cristyf2faecf2010-05-28 19:19:36 +00004709 (long) crop_info.x,(long) crop_info.y);
cristy3ed852e2009-09-05 21:47:34 +00004710 XInfoWidget(display,windows,text);
4711 }
4712 /*
4713 Wait for next event.
4714 */
cristy6710d842011-10-20 23:23:00 +00004715 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +00004716 if (event.xany.window == windows->command.id)
4717 {
4718 /*
4719 Select a command from the Command widget.
4720 */
4721 id=XCommandWidget(display,windows,CropModeMenu,&event);
4722 if (id < 0)
4723 continue;
4724 switch (CropCommands[id])
4725 {
4726 case CropHelpCommand:
4727 {
4728 switch (mode)
4729 {
4730 case CopyMode:
4731 {
4732 XTextViewWidget(display,resource_info,windows,MagickFalse,
4733 "Help Viewer - Image Copy",ImageCopyHelp);
4734 break;
4735 }
4736 case CropMode:
4737 {
4738 XTextViewWidget(display,resource_info,windows,MagickFalse,
4739 "Help Viewer - Image Crop",ImageCropHelp);
4740 break;
4741 }
4742 case CutMode:
4743 {
4744 XTextViewWidget(display,resource_info,windows,MagickFalse,
4745 "Help Viewer - Image Cut",ImageCutHelp);
4746 break;
4747 }
4748 }
4749 break;
4750 }
4751 case CropDismissCommand:
4752 {
4753 /*
4754 Prematurely exit.
4755 */
4756 state|=EscapeState;
4757 state|=ExitState;
4758 break;
4759 }
4760 default:
4761 break;
4762 }
4763 continue;
4764 }
4765 switch (event.type)
4766 {
4767 case ButtonPress:
4768 {
4769 if (event.xbutton.button != Button1)
4770 break;
4771 if (event.xbutton.window != windows->image.id)
4772 break;
4773 /*
4774 Note first corner of cropping rectangle-- exit loop.
4775 */
4776 (void) XCheckDefineCursor(display,windows->image.id,cursor);
cristy49e2d862010-11-12 02:50:30 +00004777 crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4778 crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +00004779 state|=ExitState;
4780 break;
4781 }
4782 case ButtonRelease:
4783 break;
4784 case Expose:
4785 break;
4786 case KeyPress:
4787 {
4788 if (event.xkey.window != windows->image.id)
4789 break;
4790 /*
4791 Respond to a user key press.
4792 */
4793 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4794 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4795 switch ((int) key_symbol)
4796 {
4797 case XK_Escape:
4798 case XK_F20:
4799 {
4800 /*
4801 Prematurely exit.
4802 */
4803 state|=EscapeState;
4804 state|=ExitState;
4805 break;
4806 }
4807 case XK_F1:
4808 case XK_Help:
4809 {
4810 switch (mode)
4811 {
4812 case CopyMode:
4813 {
4814 XTextViewWidget(display,resource_info,windows,MagickFalse,
4815 "Help Viewer - Image Copy",ImageCopyHelp);
4816 break;
4817 }
4818 case CropMode:
4819 {
4820 XTextViewWidget(display,resource_info,windows,MagickFalse,
4821 "Help Viewer - Image Crop",ImageCropHelp);
4822 break;
4823 }
4824 case CutMode:
4825 {
4826 XTextViewWidget(display,resource_info,windows,MagickFalse,
4827 "Help Viewer - Image Cut",ImageCutHelp);
4828 break;
4829 }
4830 }
4831 break;
4832 }
4833 default:
4834 {
4835 (void) XBell(display,0);
4836 break;
4837 }
4838 }
4839 break;
4840 }
4841 case MotionNotify:
4842 {
4843 if (event.xmotion.window != windows->image.id)
4844 break;
4845 /*
4846 Map and unmap Info widget as text cursor crosses its boundaries.
4847 */
4848 x=event.xmotion.x;
4849 y=event.xmotion.y;
4850 if (windows->info.mapped != MagickFalse)
4851 {
4852 if ((x < (int) (windows->info.x+windows->info.width)) &&
4853 (y < (int) (windows->info.y+windows->info.height)))
4854 (void) XWithdrawWindow(display,windows->info.id,
4855 windows->info.screen);
4856 }
4857 else
4858 if ((x > (int) (windows->info.x+windows->info.width)) ||
4859 (y > (int) (windows->info.y+windows->info.height)))
4860 (void) XMapWindow(display,windows->info.id);
cristy49e2d862010-11-12 02:50:30 +00004861 crop_info.x=(ssize_t) windows->image.x+x;
4862 crop_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +00004863 break;
4864 }
4865 default:
4866 break;
4867 }
4868 } while ((state & ExitState) == 0);
4869 (void) XSelectInput(display,windows->image.id,
4870 windows->image.attributes.event_mask);
4871 if ((state & EscapeState) != 0)
4872 {
4873 /*
4874 User want to exit without cropping.
4875 */
4876 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4877 (void) XFreeCursor(display,cursor);
4878 return(MagickTrue);
4879 }
4880 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4881 do
4882 {
4883 /*
4884 Size rectangle as pointer moves until the mouse button is released.
4885 */
4886 x=(int) crop_info.x;
4887 y=(int) crop_info.y;
4888 crop_info.width=0;
4889 crop_info.height=0;
4890 state=DefaultState;
4891 do
4892 {
4893 highlight_info=crop_info;
4894 highlight_info.x=crop_info.x-windows->image.x;
4895 highlight_info.y=crop_info.y-windows->image.y;
4896 if ((highlight_info.width > 3) && (highlight_info.height > 3))
4897 {
4898 /*
4899 Display info and draw cropping rectangle.
4900 */
4901 if (windows->info.mapped == MagickFalse)
4902 (void) XMapWindow(display,windows->info.id);
cristyb51dff52011-05-19 16:55:47 +00004903 (void) FormatLocaleString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00004904 " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +00004905 crop_info.height,(double) crop_info.x,(double) crop_info.y);
cristy3ed852e2009-09-05 21:47:34 +00004906 XInfoWidget(display,windows,text);
4907 XHighlightRectangle(display,windows->image.id,
4908 windows->image.highlight_context,&highlight_info);
4909 }
4910 else
4911 if (windows->info.mapped != MagickFalse)
4912 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4913 /*
4914 Wait for next event.
4915 */
cristy6710d842011-10-20 23:23:00 +00004916 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +00004917 if ((highlight_info.width > 3) && (highlight_info.height > 3))
4918 XHighlightRectangle(display,windows->image.id,
4919 windows->image.highlight_context,&highlight_info);
4920 switch (event.type)
4921 {
4922 case ButtonPress:
4923 {
cristy49e2d862010-11-12 02:50:30 +00004924 crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4925 crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +00004926 break;
4927 }
4928 case ButtonRelease:
4929 {
4930 /*
4931 User has committed to cropping rectangle.
4932 */
cristy49e2d862010-11-12 02:50:30 +00004933 crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4934 crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +00004935 XSetCursorState(display,windows,MagickFalse);
4936 state|=ExitState;
4937 windows->command.data=0;
4938 (void) XCommandWidget(display,windows,RectifyModeMenu,
4939 (XEvent *) NULL);
4940 break;
4941 }
4942 case Expose:
4943 break;
4944 case MotionNotify:
4945 {
cristy49e2d862010-11-12 02:50:30 +00004946 crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4947 crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
cristy3ed852e2009-09-05 21:47:34 +00004948 }
4949 default:
4950 break;
4951 }
4952 if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4953 ((state & ExitState) != 0))
4954 {
4955 /*
4956 Check boundary conditions.
4957 */
4958 if (crop_info.x < 0)
4959 crop_info.x=0;
4960 else
cristy49e2d862010-11-12 02:50:30 +00004961 if (crop_info.x > (ssize_t) windows->image.ximage->width)
4962 crop_info.x=(ssize_t) windows->image.ximage->width;
cristy3ed852e2009-09-05 21:47:34 +00004963 if ((int) crop_info.x < x)
4964 crop_info.width=(unsigned int) (x-crop_info.x);
4965 else
4966 {
4967 crop_info.width=(unsigned int) (crop_info.x-x);
cristy49e2d862010-11-12 02:50:30 +00004968 crop_info.x=(ssize_t) x;
cristy3ed852e2009-09-05 21:47:34 +00004969 }
4970 if (crop_info.y < 0)
4971 crop_info.y=0;
4972 else
cristy49e2d862010-11-12 02:50:30 +00004973 if (crop_info.y > (ssize_t) windows->image.ximage->height)
4974 crop_info.y=(ssize_t) windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +00004975 if ((int) crop_info.y < y)
4976 crop_info.height=(unsigned int) (y-crop_info.y);
4977 else
4978 {
4979 crop_info.height=(unsigned int) (crop_info.y-y);
cristy49e2d862010-11-12 02:50:30 +00004980 crop_info.y=(ssize_t) y;
cristy3ed852e2009-09-05 21:47:34 +00004981 }
4982 }
4983 } while ((state & ExitState) == 0);
4984 /*
4985 Wait for user to grab a corner of the rectangle or press return.
4986 */
4987 state=DefaultState;
4988 (void) XMapWindow(display,windows->info.id);
4989 do
4990 {
4991 if (windows->info.mapped != MagickFalse)
4992 {
4993 /*
4994 Display pointer position.
4995 */
cristyb51dff52011-05-19 16:55:47 +00004996 (void) FormatLocaleString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00004997 " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +00004998 crop_info.height,(double) crop_info.x,(double) crop_info.y);
cristy3ed852e2009-09-05 21:47:34 +00004999 XInfoWidget(display,windows,text);
5000 }
5001 highlight_info=crop_info;
5002 highlight_info.x=crop_info.x-windows->image.x;
5003 highlight_info.y=crop_info.y-windows->image.y;
5004 if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
5005 {
5006 state|=EscapeState;
5007 state|=ExitState;
5008 break;
5009 }
5010 XHighlightRectangle(display,windows->image.id,
5011 windows->image.highlight_context,&highlight_info);
cristy6710d842011-10-20 23:23:00 +00005012 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +00005013 if (event.xany.window == windows->command.id)
5014 {
5015 /*
5016 Select a command from the Command widget.
5017 */
5018 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5019 id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5020 (void) XSetFunction(display,windows->image.highlight_context,
5021 GXinvert);
5022 XHighlightRectangle(display,windows->image.id,
5023 windows->image.highlight_context,&highlight_info);
5024 if (id >= 0)
5025 switch (RectifyCommands[id])
5026 {
5027 case RectifyCopyCommand:
5028 {
5029 state|=ExitState;
5030 break;
5031 }
5032 case RectifyHelpCommand:
5033 {
5034 (void) XSetFunction(display,windows->image.highlight_context,
5035 GXcopy);
5036 switch (mode)
5037 {
5038 case CopyMode:
5039 {
5040 XTextViewWidget(display,resource_info,windows,MagickFalse,
5041 "Help Viewer - Image Copy",ImageCopyHelp);
5042 break;
5043 }
5044 case CropMode:
5045 {
5046 XTextViewWidget(display,resource_info,windows,MagickFalse,
5047 "Help Viewer - Image Crop",ImageCropHelp);
5048 break;
5049 }
5050 case CutMode:
5051 {
5052 XTextViewWidget(display,resource_info,windows,MagickFalse,
5053 "Help Viewer - Image Cut",ImageCutHelp);
5054 break;
5055 }
5056 }
5057 (void) XSetFunction(display,windows->image.highlight_context,
5058 GXinvert);
5059 break;
5060 }
5061 case RectifyDismissCommand:
5062 {
5063 /*
5064 Prematurely exit.
5065 */
5066 state|=EscapeState;
5067 state|=ExitState;
5068 break;
5069 }
5070 default:
5071 break;
5072 }
5073 continue;
5074 }
5075 XHighlightRectangle(display,windows->image.id,
5076 windows->image.highlight_context,&highlight_info);
5077 switch (event.type)
5078 {
5079 case ButtonPress:
5080 {
5081 if (event.xbutton.button != Button1)
5082 break;
5083 if (event.xbutton.window != windows->image.id)
5084 break;
5085 x=windows->image.x+event.xbutton.x;
5086 y=windows->image.y+event.xbutton.y;
5087 if ((x < (int) (crop_info.x+RoiDelta)) &&
5088 (x > (int) (crop_info.x-RoiDelta)) &&
5089 (y < (int) (crop_info.y+RoiDelta)) &&
5090 (y > (int) (crop_info.y-RoiDelta)))
5091 {
cristybb503372010-05-27 20:51:26 +00005092 crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5093 crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
cristy3ed852e2009-09-05 21:47:34 +00005094 state|=UpdateConfigurationState;
5095 break;
5096 }
5097 if ((x < (int) (crop_info.x+RoiDelta)) &&
5098 (x > (int) (crop_info.x-RoiDelta)) &&
5099 (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5100 (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5101 {
cristybb503372010-05-27 20:51:26 +00005102 crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
cristy3ed852e2009-09-05 21:47:34 +00005103 state|=UpdateConfigurationState;
5104 break;
5105 }
5106 if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5107 (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5108 (y < (int) (crop_info.y+RoiDelta)) &&
5109 (y > (int) (crop_info.y-RoiDelta)))
5110 {
cristybb503372010-05-27 20:51:26 +00005111 crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
cristy3ed852e2009-09-05 21:47:34 +00005112 state|=UpdateConfigurationState;
5113 break;
5114 }
5115 if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5116 (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5117 (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5118 (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5119 {
5120 state|=UpdateConfigurationState;
5121 break;
5122 }
5123 }
5124 case ButtonRelease:
5125 {
5126 if (event.xbutton.window == windows->pan.id)
5127 if ((highlight_info.x != crop_info.x-windows->image.x) ||
5128 (highlight_info.y != crop_info.y-windows->image.y))
5129 XHighlightRectangle(display,windows->image.id,
5130 windows->image.highlight_context,&highlight_info);
5131 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5132 event.xbutton.time);
5133 break;
5134 }
5135 case Expose:
5136 {
5137 if (event.xexpose.window == windows->image.id)
5138 if (event.xexpose.count == 0)
5139 {
5140 event.xexpose.x=(int) highlight_info.x;
5141 event.xexpose.y=(int) highlight_info.y;
5142 event.xexpose.width=(int) highlight_info.width;
5143 event.xexpose.height=(int) highlight_info.height;
5144 XRefreshWindow(display,&windows->image,&event);
5145 }
5146 if (event.xexpose.window == windows->info.id)
5147 if (event.xexpose.count == 0)
5148 XInfoWidget(display,windows,text);
5149 break;
5150 }
5151 case KeyPress:
5152 {
5153 if (event.xkey.window != windows->image.id)
5154 break;
5155 /*
5156 Respond to a user key press.
5157 */
5158 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5159 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5160 switch ((int) key_symbol)
5161 {
5162 case XK_Escape:
5163 case XK_F20:
5164 state|=EscapeState;
5165 case XK_Return:
5166 {
5167 state|=ExitState;
5168 break;
5169 }
5170 case XK_Home:
5171 case XK_KP_Home:
5172 {
cristy49e2d862010-11-12 02:50:30 +00005173 crop_info.x=(ssize_t) (windows->image.width/2L-
5174 crop_info.width/2L);
5175 crop_info.y=(ssize_t) (windows->image.height/2L-
5176 crop_info.height/2L);
cristy3ed852e2009-09-05 21:47:34 +00005177 break;
5178 }
5179 case XK_Left:
5180 case XK_KP_Left:
5181 {
5182 crop_info.x--;
5183 break;
5184 }
5185 case XK_Up:
5186 case XK_KP_Up:
5187 case XK_Next:
5188 {
5189 crop_info.y--;
5190 break;
5191 }
5192 case XK_Right:
5193 case XK_KP_Right:
5194 {
5195 crop_info.x++;
5196 break;
5197 }
5198 case XK_Prior:
5199 case XK_Down:
5200 case XK_KP_Down:
5201 {
5202 crop_info.y++;
5203 break;
5204 }
5205 case XK_F1:
5206 case XK_Help:
5207 {
5208 (void) XSetFunction(display,windows->image.highlight_context,
5209 GXcopy);
5210 switch (mode)
5211 {
5212 case CopyMode:
5213 {
5214 XTextViewWidget(display,resource_info,windows,MagickFalse,
5215 "Help Viewer - Image Copy",ImageCopyHelp);
5216 break;
5217 }
5218 case CropMode:
5219 {
5220 XTextViewWidget(display,resource_info,windows,MagickFalse,
5221 "Help Viewer - Image Cropg",ImageCropHelp);
5222 break;
5223 }
5224 case CutMode:
5225 {
5226 XTextViewWidget(display,resource_info,windows,MagickFalse,
5227 "Help Viewer - Image Cutg",ImageCutHelp);
5228 break;
5229 }
5230 }
5231 (void) XSetFunction(display,windows->image.highlight_context,
5232 GXinvert);
5233 break;
5234 }
5235 default:
5236 {
5237 (void) XBell(display,0);
5238 break;
5239 }
5240 }
5241 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5242 event.xkey.time);
5243 break;
5244 }
5245 case KeyRelease:
5246 break;
5247 case MotionNotify:
5248 {
5249 if (event.xmotion.window != windows->image.id)
5250 break;
5251 /*
5252 Map and unmap Info widget as text cursor crosses its boundaries.
5253 */
5254 x=event.xmotion.x;
5255 y=event.xmotion.y;
5256 if (windows->info.mapped != MagickFalse)
5257 {
5258 if ((x < (int) (windows->info.x+windows->info.width)) &&
5259 (y < (int) (windows->info.y+windows->info.height)))
5260 (void) XWithdrawWindow(display,windows->info.id,
5261 windows->info.screen);
5262 }
5263 else
5264 if ((x > (int) (windows->info.x+windows->info.width)) ||
5265 (y > (int) (windows->info.y+windows->info.height)))
5266 (void) XMapWindow(display,windows->info.id);
cristy49e2d862010-11-12 02:50:30 +00005267 crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5268 crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
cristy3ed852e2009-09-05 21:47:34 +00005269 break;
5270 }
5271 case SelectionRequest:
5272 {
5273 XSelectionEvent
5274 notify;
5275
5276 XSelectionRequestEvent
5277 *request;
5278
5279 /*
5280 Set primary selection.
5281 */
cristyb51dff52011-05-19 16:55:47 +00005282 (void) FormatLocaleString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00005283 "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +00005284 crop_info.height,(double) crop_info.x,(double) crop_info.y);
cristy3ed852e2009-09-05 21:47:34 +00005285 request=(&(event.xselectionrequest));
5286 (void) XChangeProperty(request->display,request->requestor,
5287 request->property,request->target,8,PropModeReplace,
5288 (unsigned char *) text,(int) strlen(text));
5289 notify.type=SelectionNotify;
5290 notify.display=request->display;
5291 notify.requestor=request->requestor;
5292 notify.selection=request->selection;
5293 notify.target=request->target;
5294 notify.time=request->time;
5295 if (request->property == None)
5296 notify.property=request->target;
5297 else
5298 notify.property=request->property;
5299 (void) XSendEvent(request->display,request->requestor,False,0,
5300 (XEvent *) &notify);
5301 }
5302 default:
5303 break;
5304 }
5305 if ((state & UpdateConfigurationState) != 0)
5306 {
5307 (void) XPutBackEvent(display,&event);
5308 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5309 break;
5310 }
5311 } while ((state & ExitState) == 0);
5312 } while ((state & ExitState) == 0);
5313 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5314 XSetCursorState(display,windows,MagickFalse);
5315 if ((state & EscapeState) != 0)
5316 return(MagickTrue);
5317 if (mode == CropMode)
5318 if (((int) crop_info.width != windows->image.ximage->width) ||
5319 ((int) crop_info.height != windows->image.ximage->height))
5320 {
5321 /*
5322 Reconfigure Image window as defined by cropping rectangle.
5323 */
5324 XSetCropGeometry(display,windows,&crop_info,image);
5325 windows->image.window_changes.width=(int) crop_info.width;
5326 windows->image.window_changes.height=(int) crop_info.height;
cristy051718b2011-08-28 22:49:25 +00005327 (void) XConfigureImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005328 return(MagickTrue);
5329 }
5330 /*
5331 Copy image before applying image transforms.
5332 */
5333 XSetCursorState(display,windows,MagickTrue);
5334 XCheckRefreshWindows(display,windows);
5335 width=(unsigned int) image->columns;
5336 height=(unsigned int) image->rows;
5337 x=0;
5338 y=0;
5339 if (windows->image.crop_geometry != (char *) NULL)
5340 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5341 scale_factor=(MagickRealType) width/windows->image.ximage->width;
5342 crop_info.x+=x;
cristy49e2d862010-11-12 02:50:30 +00005343 crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +00005344 crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5345 scale_factor=(MagickRealType) height/windows->image.ximage->height;
5346 crop_info.y+=y;
cristy49e2d862010-11-12 02:50:30 +00005347 crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00005348 crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
cristy051718b2011-08-28 22:49:25 +00005349 crop_image=CropImage(image,&crop_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00005350 XSetCursorState(display,windows,MagickFalse);
5351 if (crop_image == (Image *) NULL)
5352 return(MagickFalse);
5353 if (resource_info->copy_image != (Image *) NULL)
5354 resource_info->copy_image=DestroyImage(resource_info->copy_image);
5355 resource_info->copy_image=crop_image;
5356 if (mode == CopyMode)
5357 {
cristy051718b2011-08-28 22:49:25 +00005358 (void) XConfigureImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005359 return(MagickTrue);
5360 }
5361 /*
5362 Cut image.
5363 */
cristy574cc262011-08-05 01:23:58 +00005364 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005365 return(MagickFalse);
5366 image->matte=MagickTrue;
cristy49e2d862010-11-12 02:50:30 +00005367 image_view=AcquireCacheView(image);
5368 for (y=0; y < (int) crop_info.height; y++)
cristy3ed852e2009-09-05 21:47:34 +00005369 {
cristy49e2d862010-11-12 02:50:30 +00005370 q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5371 crop_info.width,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00005372 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005373 break;
5374 for (x=0; x < (int) crop_info.width; x++)
5375 {
cristy4c08aed2011-07-01 19:47:50 +00005376 SetPixelAlpha(image,TransparentAlpha,q);
cristyed231572011-07-14 02:18:59 +00005377 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00005378 }
cristy49e2d862010-11-12 02:50:30 +00005379 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005380 break;
5381 }
cristy49e2d862010-11-12 02:50:30 +00005382 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005383 /*
5384 Update image configuration.
5385 */
cristy6710d842011-10-20 23:23:00 +00005386 XConfigureImageColormap(display,resource_info,windows,image,exception);
cristy051718b2011-08-28 22:49:25 +00005387 (void) XConfigureImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005388 return(MagickTrue);
5389}
5390
5391/*
5392%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5393% %
5394% %
5395% %
5396+ X D r a w I m a g e %
5397% %
5398% %
5399% %
5400%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5401%
5402% XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5403% the image.
5404%
5405% The format of the XDrawEditImage method is:
5406%
5407% MagickBooleanType XDrawEditImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00005408% XResourceInfo *resource_info,XWindows *windows,Image **image,
5409% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005410%
5411% A description of each parameter follows:
5412%
5413% o display: Specifies a connection to an X server; returned from
5414% XOpenDisplay.
5415%
5416% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5417%
5418% o windows: Specifies a pointer to a XWindows structure.
5419%
5420% o image: the image.
5421%
cristy051718b2011-08-28 22:49:25 +00005422% o exception: return any errors or warnings in this structure.
5423%
cristy3ed852e2009-09-05 21:47:34 +00005424*/
5425static MagickBooleanType XDrawEditImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00005426 XResourceInfo *resource_info,XWindows *windows,Image **image,
5427 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005428{
5429 static const char
5430 *DrawMenu[] =
5431 {
5432 "Element",
5433 "Color",
5434 "Stipple",
5435 "Width",
5436 "Undo",
5437 "Help",
5438 "Dismiss",
5439 (char *) NULL
5440 };
5441
5442 static ElementType
5443 element = PointElement;
5444
5445 static const ModeType
5446 DrawCommands[] =
5447 {
5448 DrawElementCommand,
5449 DrawColorCommand,
5450 DrawStippleCommand,
5451 DrawWidthCommand,
5452 DrawUndoCommand,
5453 DrawHelpCommand,
5454 DrawDismissCommand
5455 };
5456
5457 static Pixmap
5458 stipple = (Pixmap) NULL;
5459
5460 static unsigned int
5461 pen_id = 0,
5462 line_width = 1;
5463
5464 char
5465 command[MaxTextExtent],
5466 text[MaxTextExtent];
5467
5468 Cursor
5469 cursor;
5470
5471 int
5472 entry,
5473 id,
5474 number_coordinates,
5475 x,
5476 y;
5477
5478 MagickRealType
5479 degrees;
5480
5481 MagickStatusType
5482 status;
5483
5484 RectangleInfo
5485 rectangle_info;
5486
5487 register int
5488 i;
5489
5490 unsigned int
5491 distance,
5492 height,
5493 max_coordinates,
5494 width;
5495
cristybb503372010-05-27 20:51:26 +00005496 size_t
cristy3ed852e2009-09-05 21:47:34 +00005497 state;
5498
5499 Window
5500 root_window;
5501
5502 XDrawInfo
5503 draw_info;
5504
5505 XEvent
5506 event;
5507
5508 XPoint
5509 *coordinate_info;
5510
5511 XSegment
5512 line_info;
5513
5514 /*
5515 Allocate polygon info.
5516 */
5517 max_coordinates=2048;
5518 coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5519 sizeof(*coordinate_info));
5520 if (coordinate_info == (XPoint *) NULL)
5521 {
cristy051718b2011-08-28 22:49:25 +00005522 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005523 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5524 return(MagickFalse);
5525 }
5526 /*
5527 Map Command widget.
5528 */
5529 (void) CloneString(&windows->command.name,"Draw");
5530 windows->command.data=4;
5531 (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5532 (void) XMapRaised(display,windows->command.id);
5533 XClientMessage(display,windows->image.id,windows->im_protocols,
5534 windows->im_update_widget,CurrentTime);
5535 /*
5536 Wait for first button press.
5537 */
5538 root_window=XRootWindow(display,XDefaultScreen(display));
5539 draw_info.stencil=OpaqueStencil;
5540 status=MagickTrue;
5541 cursor=XCreateFontCursor(display,XC_tcross);
5542 for ( ; ; )
5543 {
5544 XQueryPosition(display,windows->image.id,&x,&y);
5545 (void) XSelectInput(display,windows->image.id,
5546 windows->image.attributes.event_mask | PointerMotionMask);
5547 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5548 state=DefaultState;
5549 do
5550 {
5551 if (windows->info.mapped != MagickFalse)
5552 {
5553 /*
5554 Display pointer position.
5555 */
cristyb51dff52011-05-19 16:55:47 +00005556 (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
cristy3ed852e2009-09-05 21:47:34 +00005557 x+windows->image.x,y+windows->image.y);
5558 XInfoWidget(display,windows,text);
5559 }
5560 /*
5561 Wait for next event.
5562 */
cristy6710d842011-10-20 23:23:00 +00005563 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +00005564 if (event.xany.window == windows->command.id)
5565 {
5566 /*
5567 Select a command from the Command widget.
5568 */
5569 id=XCommandWidget(display,windows,DrawMenu,&event);
5570 if (id < 0)
5571 continue;
5572 switch (DrawCommands[id])
5573 {
5574 case DrawElementCommand:
5575 {
5576 static const char
5577 *Elements[] =
5578 {
5579 "point",
5580 "line",
5581 "rectangle",
5582 "fill rectangle",
5583 "circle",
5584 "fill circle",
5585 "ellipse",
5586 "fill ellipse",
5587 "polygon",
5588 "fill polygon",
5589 (char *) NULL,
5590 };
5591
5592 /*
5593 Select a command from the pop-up menu.
5594 */
5595 element=(ElementType) (XMenuWidget(display,windows,
5596 DrawMenu[id],Elements,command)+1);
5597 break;
5598 }
5599 case DrawColorCommand:
5600 {
5601 const char
5602 *ColorMenu[MaxNumberPens+1];
5603
5604 int
5605 pen_number;
5606
5607 MagickBooleanType
5608 transparent;
5609
5610 XColor
5611 color;
5612
5613 /*
5614 Initialize menu selections.
5615 */
5616 for (i=0; i < (int) (MaxNumberPens-2); i++)
5617 ColorMenu[i]=resource_info->pen_colors[i];
5618 ColorMenu[MaxNumberPens-2]="transparent";
5619 ColorMenu[MaxNumberPens-1]="Browser...";
5620 ColorMenu[MaxNumberPens]=(char *) NULL;
5621 /*
5622 Select a pen color from the pop-up menu.
5623 */
5624 pen_number=XMenuWidget(display,windows,DrawMenu[id],
5625 (const char **) ColorMenu,command);
5626 if (pen_number < 0)
5627 break;
5628 transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5629 MagickFalse;
5630 if (transparent != MagickFalse)
5631 {
5632 draw_info.stencil=TransparentStencil;
5633 break;
5634 }
5635 if (pen_number == (MaxNumberPens-1))
5636 {
5637 static char
5638 color_name[MaxTextExtent] = "gray";
5639
5640 /*
5641 Select a pen color from a dialog.
5642 */
5643 resource_info->pen_colors[pen_number]=color_name;
5644 XColorBrowserWidget(display,windows,"Select",color_name);
5645 if (*color_name == '\0')
5646 break;
5647 }
5648 /*
5649 Set pen color.
5650 */
5651 (void) XParseColor(display,windows->map_info->colormap,
5652 resource_info->pen_colors[pen_number],&color);
5653 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5654 (unsigned int) MaxColors,&color);
5655 windows->pixel_info->pen_colors[pen_number]=color;
5656 pen_id=(unsigned int) pen_number;
5657 draw_info.stencil=OpaqueStencil;
5658 break;
5659 }
5660 case DrawStippleCommand:
5661 {
5662 Image
5663 *stipple_image;
5664
5665 ImageInfo
5666 *image_info;
5667
5668 int
5669 status;
5670
5671 static char
5672 filename[MaxTextExtent] = "\0";
5673
5674 static const char
5675 *StipplesMenu[] =
5676 {
5677 "Brick",
5678 "Diagonal",
5679 "Scales",
5680 "Vertical",
5681 "Wavy",
5682 "Translucent",
5683 "Opaque",
5684 (char *) NULL,
5685 (char *) NULL,
5686 };
5687
5688 /*
5689 Select a command from the pop-up menu.
5690 */
5691 StipplesMenu[7]="Open...";
5692 entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5693 command);
5694 if (entry < 0)
5695 break;
5696 if (stipple != (Pixmap) NULL)
5697 (void) XFreePixmap(display,stipple);
5698 stipple=(Pixmap) NULL;
cristy3ed852e2009-09-05 21:47:34 +00005699 if (entry != 7)
5700 {
5701 switch (entry)
5702 {
5703 case 0:
5704 {
5705 stipple=XCreateBitmapFromData(display,root_window,
5706 (char *) BricksBitmap,BricksWidth,BricksHeight);
5707 break;
5708 }
5709 case 1:
5710 {
5711 stipple=XCreateBitmapFromData(display,root_window,
5712 (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5713 break;
5714 }
5715 case 2:
5716 {
5717 stipple=XCreateBitmapFromData(display,root_window,
5718 (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5719 break;
5720 }
5721 case 3:
5722 {
5723 stipple=XCreateBitmapFromData(display,root_window,
5724 (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5725 break;
5726 }
5727 case 4:
5728 {
5729 stipple=XCreateBitmapFromData(display,root_window,
5730 (char *) WavyBitmap,WavyWidth,WavyHeight);
5731 break;
5732 }
5733 case 5:
cristy3ed852e2009-09-05 21:47:34 +00005734 {
5735 stipple=XCreateBitmapFromData(display,root_window,
5736 (char *) HighlightBitmap,HighlightWidth,
5737 HighlightHeight);
5738 break;
5739 }
cristydd05beb2010-11-21 21:23:39 +00005740 case 6:
5741 default:
5742 {
5743 stipple=XCreateBitmapFromData(display,root_window,
5744 (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5745 break;
5746 }
cristy3ed852e2009-09-05 21:47:34 +00005747 }
5748 break;
5749 }
5750 XFileBrowserWidget(display,windows,"Stipple",filename);
5751 if (*filename == '\0')
5752 break;
5753 /*
5754 Read image.
5755 */
5756 XSetCursorState(display,windows,MagickTrue);
5757 XCheckRefreshWindows(display,windows);
5758 image_info=AcquireImageInfo();
5759 (void) CopyMagickString(image_info->filename,filename,
5760 MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +00005761 stipple_image=ReadImage(image_info,exception);
5762 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00005763 XSetCursorState(display,windows,MagickFalse);
5764 if (stipple_image == (Image *) NULL)
5765 break;
5766 (void) AcquireUniqueFileResource(filename);
cristyb51dff52011-05-19 16:55:47 +00005767 (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00005768 "xbm:%s",filename);
cristy051718b2011-08-28 22:49:25 +00005769 (void) WriteImage(image_info,stipple_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005770 stipple_image=DestroyImage(stipple_image);
5771 image_info=DestroyImageInfo(image_info);
5772 status=XReadBitmapFile(display,root_window,filename,&width,
5773 &height,&stipple,&x,&y);
5774 (void) RelinquishUniqueFileResource(filename);
5775 if ((status != BitmapSuccess) != 0)
5776 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5777 filename);
5778 break;
5779 }
5780 case DrawWidthCommand:
5781 {
5782 static char
5783 width[MaxTextExtent] = "0";
5784
5785 static const char
5786 *WidthsMenu[] =
5787 {
5788 "1",
5789 "2",
5790 "4",
5791 "8",
5792 "16",
5793 "Dialog...",
5794 (char *) NULL,
5795 };
5796
5797 /*
5798 Select a command from the pop-up menu.
5799 */
5800 entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5801 command);
5802 if (entry < 0)
5803 break;
5804 if (entry != 5)
5805 {
cristydd05beb2010-11-21 21:23:39 +00005806 line_width=(unsigned int) StringToUnsignedLong(
5807 WidthsMenu[entry]);
cristy3ed852e2009-09-05 21:47:34 +00005808 break;
5809 }
5810 (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5811 width);
5812 if (*width == '\0')
5813 break;
cristye27293e2009-12-18 02:53:20 +00005814 line_width=(unsigned int) StringToUnsignedLong(width);
cristy3ed852e2009-09-05 21:47:34 +00005815 break;
5816 }
5817 case DrawUndoCommand:
5818 {
5819 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
cristy051718b2011-08-28 22:49:25 +00005820 image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005821 break;
5822 }
5823 case DrawHelpCommand:
5824 {
5825 XTextViewWidget(display,resource_info,windows,MagickFalse,
5826 "Help Viewer - Image Rotation",ImageDrawHelp);
5827 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5828 break;
5829 }
5830 case DrawDismissCommand:
5831 {
5832 /*
5833 Prematurely exit.
5834 */
5835 state|=EscapeState;
5836 state|=ExitState;
5837 break;
5838 }
5839 default:
5840 break;
5841 }
5842 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5843 continue;
5844 }
5845 switch (event.type)
5846 {
5847 case ButtonPress:
5848 {
5849 if (event.xbutton.button != Button1)
5850 break;
5851 if (event.xbutton.window != windows->image.id)
5852 break;
5853 /*
5854 exit loop.
5855 */
5856 x=event.xbutton.x;
5857 y=event.xbutton.y;
5858 state|=ExitState;
5859 break;
5860 }
5861 case ButtonRelease:
5862 break;
5863 case Expose:
5864 break;
5865 case KeyPress:
5866 {
5867 KeySym
5868 key_symbol;
5869
5870 if (event.xkey.window != windows->image.id)
5871 break;
5872 /*
5873 Respond to a user key press.
5874 */
5875 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5876 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5877 switch ((int) key_symbol)
5878 {
5879 case XK_Escape:
5880 case XK_F20:
5881 {
5882 /*
5883 Prematurely exit.
5884 */
5885 state|=EscapeState;
5886 state|=ExitState;
5887 break;
5888 }
5889 case XK_F1:
5890 case XK_Help:
5891 {
5892 XTextViewWidget(display,resource_info,windows,MagickFalse,
5893 "Help Viewer - Image Rotation",ImageDrawHelp);
5894 break;
5895 }
5896 default:
5897 {
5898 (void) XBell(display,0);
5899 break;
5900 }
5901 }
5902 break;
5903 }
5904 case MotionNotify:
5905 {
5906 /*
5907 Map and unmap Info widget as text cursor crosses its boundaries.
5908 */
5909 x=event.xmotion.x;
5910 y=event.xmotion.y;
5911 if (windows->info.mapped != MagickFalse)
5912 {
5913 if ((x < (int) (windows->info.x+windows->info.width)) &&
5914 (y < (int) (windows->info.y+windows->info.height)))
5915 (void) XWithdrawWindow(display,windows->info.id,
5916 windows->info.screen);
5917 }
5918 else
5919 if ((x > (int) (windows->info.x+windows->info.width)) ||
5920 (y > (int) (windows->info.y+windows->info.height)))
5921 (void) XMapWindow(display,windows->info.id);
5922 break;
5923 }
5924 }
5925 } while ((state & ExitState) == 0);
5926 (void) XSelectInput(display,windows->image.id,
5927 windows->image.attributes.event_mask);
5928 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5929 if ((state & EscapeState) != 0)
5930 break;
5931 /*
5932 Draw element as pointer moves until the button is released.
5933 */
5934 distance=0;
5935 degrees=0.0;
5936 line_info.x1=x;
5937 line_info.y1=y;
5938 line_info.x2=x;
5939 line_info.y2=y;
cristy49e2d862010-11-12 02:50:30 +00005940 rectangle_info.x=(ssize_t) x;
5941 rectangle_info.y=(ssize_t) y;
cristy3ed852e2009-09-05 21:47:34 +00005942 rectangle_info.width=0;
5943 rectangle_info.height=0;
5944 number_coordinates=1;
5945 coordinate_info->x=x;
5946 coordinate_info->y=y;
5947 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5948 state=DefaultState;
5949 do
5950 {
5951 switch (element)
5952 {
5953 case PointElement:
5954 default:
5955 {
5956 if (number_coordinates > 1)
5957 {
5958 (void) XDrawLines(display,windows->image.id,
5959 windows->image.highlight_context,coordinate_info,
5960 number_coordinates,CoordModeOrigin);
cristyb51dff52011-05-19 16:55:47 +00005961 (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
cristy3ed852e2009-09-05 21:47:34 +00005962 coordinate_info[number_coordinates-1].x,
5963 coordinate_info[number_coordinates-1].y);
5964 XInfoWidget(display,windows,text);
5965 }
5966 break;
5967 }
5968 case LineElement:
5969 {
5970 if (distance > 9)
5971 {
5972 /*
5973 Display angle of the line.
5974 */
5975 degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5976 line_info.y1),(double) (line_info.x2-line_info.x1)));
cristyb51dff52011-05-19 16:55:47 +00005977 (void) FormatLocaleString(text,MaxTextExtent," %g",
cristy3ed852e2009-09-05 21:47:34 +00005978 (double) degrees);
5979 XInfoWidget(display,windows,text);
5980 XHighlightLine(display,windows->image.id,
5981 windows->image.highlight_context,&line_info);
5982 }
5983 else
5984 if (windows->info.mapped != MagickFalse)
5985 (void) XWithdrawWindow(display,windows->info.id,
5986 windows->info.screen);
5987 break;
5988 }
5989 case RectangleElement:
5990 case FillRectangleElement:
5991 {
5992 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5993 {
5994 /*
5995 Display info and draw drawing rectangle.
5996 */
cristyb51dff52011-05-19 16:55:47 +00005997 (void) FormatLocaleString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00005998 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
cristye8c25f92010-06-03 00:53:06 +00005999 (double) rectangle_info.height,(double) rectangle_info.x,
6000 (double) rectangle_info.y);
cristy3ed852e2009-09-05 21:47:34 +00006001 XInfoWidget(display,windows,text);
6002 XHighlightRectangle(display,windows->image.id,
6003 windows->image.highlight_context,&rectangle_info);
6004 }
6005 else
6006 if (windows->info.mapped != MagickFalse)
6007 (void) XWithdrawWindow(display,windows->info.id,
6008 windows->info.screen);
6009 break;
6010 }
6011 case CircleElement:
6012 case FillCircleElement:
6013 case EllipseElement:
6014 case FillEllipseElement:
6015 {
6016 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6017 {
6018 /*
6019 Display info and draw drawing rectangle.
6020 */
cristyb51dff52011-05-19 16:55:47 +00006021 (void) FormatLocaleString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00006022 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
cristye8c25f92010-06-03 00:53:06 +00006023 (double) rectangle_info.height,(double) rectangle_info.x,
6024 (double) rectangle_info.y);
cristy3ed852e2009-09-05 21:47:34 +00006025 XInfoWidget(display,windows,text);
6026 XHighlightEllipse(display,windows->image.id,
6027 windows->image.highlight_context,&rectangle_info);
6028 }
6029 else
6030 if (windows->info.mapped != MagickFalse)
6031 (void) XWithdrawWindow(display,windows->info.id,
6032 windows->info.screen);
6033 break;
6034 }
6035 case PolygonElement:
6036 case FillPolygonElement:
6037 {
6038 if (number_coordinates > 1)
6039 (void) XDrawLines(display,windows->image.id,
6040 windows->image.highlight_context,coordinate_info,
6041 number_coordinates,CoordModeOrigin);
6042 if (distance > 9)
6043 {
6044 /*
6045 Display angle of the line.
6046 */
6047 degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6048 line_info.y1),(double) (line_info.x2-line_info.x1)));
cristyb51dff52011-05-19 16:55:47 +00006049 (void) FormatLocaleString(text,MaxTextExtent," %g",
cristy3ed852e2009-09-05 21:47:34 +00006050 (double) degrees);
6051 XInfoWidget(display,windows,text);
6052 XHighlightLine(display,windows->image.id,
6053 windows->image.highlight_context,&line_info);
6054 }
6055 else
6056 if (windows->info.mapped != MagickFalse)
6057 (void) XWithdrawWindow(display,windows->info.id,
6058 windows->info.screen);
6059 break;
6060 }
6061 }
6062 /*
6063 Wait for next event.
6064 */
cristy6710d842011-10-20 23:23:00 +00006065 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +00006066 switch (element)
6067 {
6068 case PointElement:
6069 default:
6070 {
6071 if (number_coordinates > 1)
6072 (void) XDrawLines(display,windows->image.id,
6073 windows->image.highlight_context,coordinate_info,
6074 number_coordinates,CoordModeOrigin);
6075 break;
6076 }
6077 case LineElement:
6078 {
6079 if (distance > 9)
6080 XHighlightLine(display,windows->image.id,
6081 windows->image.highlight_context,&line_info);
6082 break;
6083 }
6084 case RectangleElement:
6085 case FillRectangleElement:
6086 {
6087 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6088 XHighlightRectangle(display,windows->image.id,
6089 windows->image.highlight_context,&rectangle_info);
6090 break;
6091 }
6092 case CircleElement:
6093 case FillCircleElement:
6094 case EllipseElement:
6095 case FillEllipseElement:
6096 {
6097 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6098 XHighlightEllipse(display,windows->image.id,
6099 windows->image.highlight_context,&rectangle_info);
6100 break;
6101 }
6102 case PolygonElement:
6103 case FillPolygonElement:
6104 {
6105 if (number_coordinates > 1)
6106 (void) XDrawLines(display,windows->image.id,
6107 windows->image.highlight_context,coordinate_info,
6108 number_coordinates,CoordModeOrigin);
6109 if (distance > 9)
6110 XHighlightLine(display,windows->image.id,
6111 windows->image.highlight_context,&line_info);
6112 break;
6113 }
6114 }
6115 switch (event.type)
6116 {
6117 case ButtonPress:
6118 break;
6119 case ButtonRelease:
6120 {
6121 /*
6122 User has committed to element.
6123 */
6124 line_info.x2=event.xbutton.x;
6125 line_info.y2=event.xbutton.y;
cristy49e2d862010-11-12 02:50:30 +00006126 rectangle_info.x=(ssize_t) event.xbutton.x;
6127 rectangle_info.y=(ssize_t) event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +00006128 coordinate_info[number_coordinates].x=event.xbutton.x;
6129 coordinate_info[number_coordinates].y=event.xbutton.y;
6130 if (((element != PolygonElement) &&
6131 (element != FillPolygonElement)) || (distance <= 9))
6132 {
6133 state|=ExitState;
6134 break;
6135 }
6136 number_coordinates++;
6137 if (number_coordinates < (int) max_coordinates)
6138 {
6139 line_info.x1=event.xbutton.x;
6140 line_info.y1=event.xbutton.y;
6141 break;
6142 }
6143 max_coordinates<<=1;
6144 coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6145 max_coordinates,sizeof(*coordinate_info));
6146 if (coordinate_info == (XPoint *) NULL)
cristy051718b2011-08-28 22:49:25 +00006147 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006148 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6149 break;
6150 }
6151 case Expose:
6152 break;
6153 case MotionNotify:
6154 {
6155 if (event.xmotion.window != windows->image.id)
6156 break;
6157 if (element != PointElement)
6158 {
6159 line_info.x2=event.xmotion.x;
6160 line_info.y2=event.xmotion.y;
cristy49e2d862010-11-12 02:50:30 +00006161 rectangle_info.x=(ssize_t) event.xmotion.x;
6162 rectangle_info.y=(ssize_t) event.xmotion.y;
cristy3ed852e2009-09-05 21:47:34 +00006163 break;
6164 }
6165 coordinate_info[number_coordinates].x=event.xbutton.x;
6166 coordinate_info[number_coordinates].y=event.xbutton.y;
6167 number_coordinates++;
6168 if (number_coordinates < (int) max_coordinates)
6169 break;
6170 max_coordinates<<=1;
6171 coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6172 max_coordinates,sizeof(*coordinate_info));
6173 if (coordinate_info == (XPoint *) NULL)
cristy051718b2011-08-28 22:49:25 +00006174 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006175 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6176 break;
6177 }
6178 default:
6179 break;
6180 }
6181 /*
6182 Check boundary conditions.
6183 */
6184 if (line_info.x2 < 0)
6185 line_info.x2=0;
6186 else
6187 if (line_info.x2 > (int) windows->image.width)
6188 line_info.x2=(short) windows->image.width;
6189 if (line_info.y2 < 0)
6190 line_info.y2=0;
6191 else
6192 if (line_info.y2 > (int) windows->image.height)
6193 line_info.y2=(short) windows->image.height;
6194 distance=(unsigned int)
6195 (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6196 ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6197 if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6198 ((state & ExitState) != 0))
6199 {
6200 if (rectangle_info.x < 0)
6201 rectangle_info.x=0;
6202 else
cristy49e2d862010-11-12 02:50:30 +00006203 if (rectangle_info.x > (ssize_t) windows->image.width)
cristybb503372010-05-27 20:51:26 +00006204 rectangle_info.x=(ssize_t) windows->image.width;
cristy3ed852e2009-09-05 21:47:34 +00006205 if ((int) rectangle_info.x < x)
6206 rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6207 else
6208 {
6209 rectangle_info.width=(unsigned int) (rectangle_info.x-x);
cristy49e2d862010-11-12 02:50:30 +00006210 rectangle_info.x=(ssize_t) x;
cristy3ed852e2009-09-05 21:47:34 +00006211 }
6212 if (rectangle_info.y < 0)
6213 rectangle_info.y=0;
6214 else
cristy49e2d862010-11-12 02:50:30 +00006215 if (rectangle_info.y > (ssize_t) windows->image.height)
cristybb503372010-05-27 20:51:26 +00006216 rectangle_info.y=(ssize_t) windows->image.height;
cristy3ed852e2009-09-05 21:47:34 +00006217 if ((int) rectangle_info.y < y)
6218 rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6219 else
6220 {
6221 rectangle_info.height=(unsigned int) (rectangle_info.y-y);
cristy49e2d862010-11-12 02:50:30 +00006222 rectangle_info.y=(ssize_t) y;
cristy3ed852e2009-09-05 21:47:34 +00006223 }
6224 }
6225 } while ((state & ExitState) == 0);
6226 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6227 if ((element == PointElement) || (element == PolygonElement) ||
6228 (element == FillPolygonElement))
6229 {
6230 /*
6231 Determine polygon bounding box.
6232 */
cristy49e2d862010-11-12 02:50:30 +00006233 rectangle_info.x=(ssize_t) coordinate_info->x;
6234 rectangle_info.y=(ssize_t) coordinate_info->y;
cristy3ed852e2009-09-05 21:47:34 +00006235 x=coordinate_info->x;
6236 y=coordinate_info->y;
6237 for (i=1; i < number_coordinates; i++)
6238 {
6239 if (coordinate_info[i].x > x)
6240 x=coordinate_info[i].x;
6241 if (coordinate_info[i].y > y)
6242 y=coordinate_info[i].y;
cristy49e2d862010-11-12 02:50:30 +00006243 if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6244 rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6245 if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6246 rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
cristy3ed852e2009-09-05 21:47:34 +00006247 }
cristybb503372010-05-27 20:51:26 +00006248 rectangle_info.width=(size_t) (x-rectangle_info.x);
6249 rectangle_info.height=(size_t) (y-rectangle_info.y);
cristy3ed852e2009-09-05 21:47:34 +00006250 for (i=0; i < number_coordinates; i++)
6251 {
6252 coordinate_info[i].x-=rectangle_info.x;
6253 coordinate_info[i].y-=rectangle_info.y;
6254 }
6255 }
6256 else
6257 if (distance <= 9)
6258 continue;
6259 else
6260 if ((element == RectangleElement) ||
6261 (element == CircleElement) || (element == EllipseElement))
6262 {
6263 rectangle_info.width--;
6264 rectangle_info.height--;
6265 }
6266 /*
6267 Drawing is relative to image configuration.
6268 */
6269 draw_info.x=(int) rectangle_info.x;
6270 draw_info.y=(int) rectangle_info.y;
6271 (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
cristy051718b2011-08-28 22:49:25 +00006272 image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006273 width=(unsigned int) (*image)->columns;
6274 height=(unsigned int) (*image)->rows;
6275 x=0;
6276 y=0;
6277 if (windows->image.crop_geometry != (char *) NULL)
6278 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6279 draw_info.x+=windows->image.x-(line_width/2);
6280 if (draw_info.x < 0)
6281 draw_info.x=0;
6282 draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6283 draw_info.y+=windows->image.y-(line_width/2);
6284 if (draw_info.y < 0)
6285 draw_info.y=0;
6286 draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6287 draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6288 if (draw_info.width > (unsigned int) (*image)->columns)
6289 draw_info.width=(unsigned int) (*image)->columns;
6290 draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6291 if (draw_info.height > (unsigned int) (*image)->rows)
6292 draw_info.height=(unsigned int) (*image)->rows;
cristyb51dff52011-05-19 16:55:47 +00006293 (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
cristy3ed852e2009-09-05 21:47:34 +00006294 width*draw_info.width/windows->image.ximage->width,
6295 height*draw_info.height/windows->image.ximage->height,
6296 draw_info.x+x,draw_info.y+y);
6297 /*
6298 Initialize drawing attributes.
6299 */
6300 draw_info.degrees=0.0;
6301 draw_info.element=element;
6302 draw_info.stipple=stipple;
6303 draw_info.line_width=line_width;
6304 draw_info.line_info=line_info;
6305 if (line_info.x1 > (int) (line_width/2))
6306 draw_info.line_info.x1=(short) line_width/2;
6307 if (line_info.y1 > (int) (line_width/2))
6308 draw_info.line_info.y1=(short) line_width/2;
6309 draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6310 draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6311 if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6312 {
6313 draw_info.line_info.x2=(-draw_info.line_info.x2);
6314 draw_info.line_info.y2=(-draw_info.line_info.y2);
6315 }
6316 if (draw_info.line_info.x2 < 0)
6317 {
6318 draw_info.line_info.x2=(-draw_info.line_info.x2);
6319 Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6320 }
6321 if (draw_info.line_info.y2 < 0)
6322 {
6323 draw_info.line_info.y2=(-draw_info.line_info.y2);
6324 Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6325 }
6326 draw_info.rectangle_info=rectangle_info;
cristy49e2d862010-11-12 02:50:30 +00006327 if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
cristybb503372010-05-27 20:51:26 +00006328 draw_info.rectangle_info.x=(ssize_t) line_width/2;
cristy49e2d862010-11-12 02:50:30 +00006329 if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
cristybb503372010-05-27 20:51:26 +00006330 draw_info.rectangle_info.y=(ssize_t) line_width/2;
cristy3ed852e2009-09-05 21:47:34 +00006331 draw_info.number_coordinates=(unsigned int) number_coordinates;
6332 draw_info.coordinate_info=coordinate_info;
6333 windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6334 /*
6335 Draw element on image.
6336 */
6337 XSetCursorState(display,windows,MagickTrue);
6338 XCheckRefreshWindows(display,windows);
cristy6710d842011-10-20 23:23:00 +00006339 status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006340 XSetCursorState(display,windows,MagickFalse);
6341 /*
6342 Update image colormap and return to image drawing.
6343 */
cristy6710d842011-10-20 23:23:00 +00006344 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00006345 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006346 }
6347 XSetCursorState(display,windows,MagickFalse);
6348 coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6349 return(status != 0 ? MagickTrue : MagickFalse);
6350}
6351
6352/*
6353%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6354% %
6355% %
6356% %
6357+ X D r a w P a n R e c t a n g l e %
6358% %
6359% %
6360% %
6361%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6362%
6363% XDrawPanRectangle() draws a rectangle in the pan window. The pan window
6364% displays a zoom image and the rectangle shows which portion of the image is
6365% displayed in the Image window.
6366%
6367% The format of the XDrawPanRectangle method is:
6368%
6369% XDrawPanRectangle(Display *display,XWindows *windows)
6370%
6371% A description of each parameter follows:
6372%
6373% o display: Specifies a connection to an X server; returned from
6374% XOpenDisplay.
6375%
6376% o windows: Specifies a pointer to a XWindows structure.
6377%
6378*/
6379static void XDrawPanRectangle(Display *display,XWindows *windows)
6380{
6381 MagickRealType
6382 scale_factor;
6383
6384 RectangleInfo
6385 highlight_info;
6386
6387 /*
6388 Determine dimensions of the panning rectangle.
6389 */
6390 scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
cristy49e2d862010-11-12 02:50:30 +00006391 highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +00006392 highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6393 scale_factor=(MagickRealType)
6394 windows->pan.height/windows->image.ximage->height;
cristy49e2d862010-11-12 02:50:30 +00006395 highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00006396 highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6397 /*
6398 Display the panning rectangle.
6399 */
6400 (void) XClearWindow(display,windows->pan.id);
6401 XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6402 &highlight_info);
6403}
6404
6405/*
6406%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6407% %
6408% %
6409% %
6410+ X I m a g e C a c h e %
6411% %
6412% %
6413% %
6414%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6415%
6416% XImageCache() handles the creation, manipulation, and destruction of the
6417% image cache (undo and redo buffers).
6418%
6419% The format of the XImageCache method is:
6420%
6421% void XImageCache(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +00006422% XWindows *windows,const CommandType command,Image **image,
6423% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00006424%
6425% A description of each parameter follows:
6426%
6427% o display: Specifies a connection to an X server; returned from
6428% XOpenDisplay.
6429%
6430% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6431%
6432% o windows: Specifies a pointer to a XWindows structure.
6433%
6434% o command: Specifies a command to perform.
6435%
cristya9a86bb2011-01-13 01:11:00 +00006436% o image: the image; XImageCache may transform the image and return a new
6437% image pointer.
cristy3ed852e2009-09-05 21:47:34 +00006438%
cristy051718b2011-08-28 22:49:25 +00006439% o exception: return any errors or warnings in this structure.
6440%
cristy3ed852e2009-09-05 21:47:34 +00006441*/
6442static void XImageCache(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +00006443 XWindows *windows,const CommandType command,Image **image,
6444 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00006445{
6446 Image
6447 *cache_image;
6448
6449 static Image
6450 *redo_image = (Image *) NULL,
6451 *undo_image = (Image *) NULL;
6452
6453 switch (command)
6454 {
6455 case FreeBuffersCommand:
6456 {
6457 /*
6458 Free memory from the undo and redo cache.
6459 */
6460 while (undo_image != (Image *) NULL)
6461 {
6462 cache_image=undo_image;
6463 undo_image=GetPreviousImageInList(undo_image);
6464 cache_image->list=DestroyImage(cache_image->list);
6465 cache_image=DestroyImage(cache_image);
6466 }
6467 undo_image=NewImageList();
6468 if (redo_image != (Image *) NULL)
6469 redo_image=DestroyImage(redo_image);
6470 redo_image=NewImageList();
6471 return;
6472 }
6473 case UndoCommand:
6474 {
cristya9a86bb2011-01-13 01:11:00 +00006475 char
6476 image_geometry[MaxTextExtent];
6477
cristy3ed852e2009-09-05 21:47:34 +00006478 /*
6479 Undo the last image transformation.
6480 */
6481 if (undo_image == (Image *) NULL)
6482 {
6483 (void) XBell(display,0);
6484 return;
6485 }
6486 cache_image=undo_image;
6487 undo_image=GetPreviousImageInList(undo_image);
6488 windows->image.window_changes.width=(int) cache_image->columns;
6489 windows->image.window_changes.height=(int) cache_image->rows;
cristyb51dff52011-05-19 16:55:47 +00006490 (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
cristya9a86bb2011-01-13 01:11:00 +00006491 windows->image.ximage->width,windows->image.ximage->height);
cristye941a752011-10-15 01:52:48 +00006492 (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6493 exception);
cristy3ed852e2009-09-05 21:47:34 +00006494 if (windows->image.crop_geometry != (char *) NULL)
cristye941a752011-10-15 01:52:48 +00006495 windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6496 windows->image.crop_geometry);
cristy3ed852e2009-09-05 21:47:34 +00006497 windows->image.crop_geometry=cache_image->geometry;
6498 if (redo_image != (Image *) NULL)
6499 redo_image=DestroyImage(redo_image);
6500 redo_image=(*image);
6501 *image=cache_image->list;
6502 cache_image=DestroyImage(cache_image);
6503 if (windows->image.orphan != MagickFalse)
6504 return;
cristy6710d842011-10-20 23:23:00 +00006505 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00006506 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006507 return;
6508 }
6509 case CutCommand:
6510 case PasteCommand:
6511 case ApplyCommand:
6512 case HalfSizeCommand:
6513 case OriginalSizeCommand:
6514 case DoubleSizeCommand:
6515 case ResizeCommand:
6516 case TrimCommand:
6517 case CropCommand:
6518 case ChopCommand:
6519 case FlipCommand:
6520 case FlopCommand:
6521 case RotateRightCommand:
6522 case RotateLeftCommand:
6523 case RotateCommand:
6524 case ShearCommand:
6525 case RollCommand:
6526 case NegateCommand:
6527 case ContrastStretchCommand:
6528 case SigmoidalContrastCommand:
6529 case NormalizeCommand:
6530 case EqualizeCommand:
6531 case HueCommand:
6532 case SaturationCommand:
6533 case BrightnessCommand:
6534 case GammaCommand:
6535 case SpiffCommand:
6536 case DullCommand:
6537 case GrayscaleCommand:
6538 case MapCommand:
6539 case QuantizeCommand:
6540 case DespeckleCommand:
6541 case EmbossCommand:
6542 case ReduceNoiseCommand:
6543 case AddNoiseCommand:
6544 case SharpenCommand:
6545 case BlurCommand:
6546 case ThresholdCommand:
6547 case EdgeDetectCommand:
6548 case SpreadCommand:
6549 case ShadeCommand:
6550 case RaiseCommand:
6551 case SegmentCommand:
6552 case SolarizeCommand:
6553 case SepiaToneCommand:
6554 case SwirlCommand:
6555 case ImplodeCommand:
6556 case VignetteCommand:
6557 case WaveCommand:
6558 case OilPaintCommand:
6559 case CharcoalDrawCommand:
6560 case AnnotateCommand:
6561 case AddBorderCommand:
6562 case AddFrameCommand:
6563 case CompositeCommand:
6564 case CommentCommand:
6565 case LaunchCommand:
6566 case RegionofInterestCommand:
6567 case SaveToUndoBufferCommand:
6568 case RedoCommand:
6569 {
6570 Image
6571 *previous_image;
6572
cristybb503372010-05-27 20:51:26 +00006573 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006574 bytes;
6575
cristy101ab702011-10-13 13:06:32 +00006576 bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
cristy3ed852e2009-09-05 21:47:34 +00006577 if (undo_image != (Image *) NULL)
6578 {
6579 /*
cristya9a86bb2011-01-13 01:11:00 +00006580 Ensure the undo cache has enough memory available.
cristy3ed852e2009-09-05 21:47:34 +00006581 */
6582 previous_image=undo_image;
6583 while (previous_image != (Image *) NULL)
6584 {
6585 bytes+=previous_image->list->columns*previous_image->list->rows*
cristy101ab702011-10-13 13:06:32 +00006586 sizeof(PixelInfo);
cristybb503372010-05-27 20:51:26 +00006587 if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
cristy3ed852e2009-09-05 21:47:34 +00006588 {
6589 previous_image=GetPreviousImageInList(previous_image);
6590 continue;
6591 }
6592 bytes-=previous_image->list->columns*previous_image->list->rows*
cristy101ab702011-10-13 13:06:32 +00006593 sizeof(PixelInfo);
cristy3ed852e2009-09-05 21:47:34 +00006594 if (previous_image == undo_image)
6595 undo_image=NewImageList();
6596 else
6597 previous_image->next->previous=NewImageList();
6598 break;
6599 }
6600 while (previous_image != (Image *) NULL)
6601 {
6602 /*
6603 Delete any excess memory from undo cache.
6604 */
6605 cache_image=previous_image;
6606 previous_image=GetPreviousImageInList(previous_image);
6607 cache_image->list=DestroyImage(cache_image->list);
6608 cache_image=DestroyImage(cache_image);
6609 }
6610 }
cristybb503372010-05-27 20:51:26 +00006611 if (bytes > (ssize_t) (resource_info->undo_cache << 20))
cristy3ed852e2009-09-05 21:47:34 +00006612 break;
6613 /*
6614 Save image before transformations are applied.
6615 */
cristy9950d572011-10-01 18:22:35 +00006616 cache_image=AcquireImage((ImageInfo *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +00006617 if (cache_image == (Image *) NULL)
6618 break;
6619 XSetCursorState(display,windows,MagickTrue);
6620 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00006621 cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00006622 XSetCursorState(display,windows,MagickFalse);
6623 if (cache_image->list == (Image *) NULL)
6624 {
6625 cache_image=DestroyImage(cache_image);
6626 break;
6627 }
cristybb503372010-05-27 20:51:26 +00006628 cache_image->columns=(size_t) windows->image.ximage->width;
6629 cache_image->rows=(size_t) windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +00006630 cache_image->geometry=windows->image.crop_geometry;
6631 if (windows->image.crop_geometry != (char *) NULL)
6632 {
6633 cache_image->geometry=AcquireString((char *) NULL);
6634 (void) CopyMagickString(cache_image->geometry,
6635 windows->image.crop_geometry,MaxTextExtent);
6636 }
6637 if (undo_image == (Image *) NULL)
6638 {
6639 undo_image=cache_image;
6640 break;
6641 }
6642 undo_image->next=cache_image;
6643 undo_image->next->previous=undo_image;
6644 undo_image=undo_image->next;
6645 break;
6646 }
6647 default:
6648 break;
6649 }
6650 if (command == RedoCommand)
6651 {
6652 /*
6653 Redo the last image transformation.
6654 */
6655 if (redo_image == (Image *) NULL)
6656 {
6657 (void) XBell(display,0);
6658 return;
6659 }
6660 windows->image.window_changes.width=(int) redo_image->columns;
6661 windows->image.window_changes.height=(int) redo_image->rows;
6662 if (windows->image.crop_geometry != (char *) NULL)
6663 windows->image.crop_geometry=(char *)
6664 RelinquishMagickMemory(windows->image.crop_geometry);
6665 windows->image.crop_geometry=redo_image->geometry;
6666 *image=DestroyImage(*image);
6667 *image=redo_image;
6668 redo_image=NewImageList();
6669 if (windows->image.orphan != MagickFalse)
6670 return;
cristy6710d842011-10-20 23:23:00 +00006671 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00006672 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006673 return;
6674 }
6675 if (command != InfoCommand)
6676 return;
6677 /*
6678 Display image info.
6679 */
6680 XSetCursorState(display,windows,MagickTrue);
6681 XCheckRefreshWindows(display,windows);
cristy6710d842011-10-20 23:23:00 +00006682 XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006683 XSetCursorState(display,windows,MagickFalse);
6684}
6685
6686/*
6687%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6688% %
6689% %
6690% %
6691+ X I m a g e W i n d o w C o m m a n d %
6692% %
6693% %
6694% %
6695%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6696%
6697% XImageWindowCommand() makes a transform to the image or Image window as
6698% specified by a user menu button or keyboard command.
6699%
cristy051718b2011-08-28 22:49:25 +00006700% The format of the XImageWindowCommand method is:
cristy3ed852e2009-09-05 21:47:34 +00006701%
6702% CommandType XImageWindowCommand(Display *display,
6703% XResourceInfo *resource_info,XWindows *windows,
cristy051718b2011-08-28 22:49:25 +00006704% const MagickStatusType state,KeySym key_symbol,Image **image,
6705% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00006706%
6707% A description of each parameter follows:
6708%
6709% o nexus: Method XImageWindowCommand returns an image when the
6710% user chooses 'Open Image' from the command menu. Otherwise a null
6711% image is returned.
6712%
6713% o display: Specifies a connection to an X server; returned from
6714% XOpenDisplay.
6715%
6716% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6717%
6718% o windows: Specifies a pointer to a XWindows structure.
6719%
6720% o state: key mask.
6721%
6722% o key_symbol: Specifies a command to perform.
6723%
cristy051718b2011-08-28 22:49:25 +00006724% o image: the image; XImageWIndowCommand may transform the image and
6725% return a new image pointer.
6726%
6727% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00006728%
6729*/
6730static CommandType XImageWindowCommand(Display *display,
6731 XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
cristy051718b2011-08-28 22:49:25 +00006732 KeySym key_symbol,Image **image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00006733{
6734 static char
6735 delta[MaxTextExtent] = "";
6736
6737 static const char
6738 Digits[] = "01234567890";
6739
6740 static KeySym
6741 last_symbol = XK_0;
6742
6743 if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6744 {
6745 if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6746 {
6747 *delta='\0';
6748 resource_info->quantum=1;
6749 }
6750 last_symbol=key_symbol;
6751 delta[strlen(delta)+1]='\0';
6752 delta[strlen(delta)]=Digits[key_symbol-XK_0];
cristyf2f27272009-12-17 14:48:46 +00006753 resource_info->quantum=StringToLong(delta);
cristy3ed852e2009-09-05 21:47:34 +00006754 return(NullCommand);
6755 }
6756 last_symbol=key_symbol;
6757 if (resource_info->immutable)
6758 {
6759 /*
6760 Virtual image window has a restricted command set.
6761 */
6762 switch (key_symbol)
6763 {
6764 case XK_question:
6765 return(InfoCommand);
6766 case XK_p:
6767 case XK_Print:
6768 return(PrintCommand);
6769 case XK_space:
6770 return(NextCommand);
6771 case XK_q:
6772 case XK_Escape:
6773 return(QuitCommand);
6774 default:
6775 break;
6776 }
6777 return(NullCommand);
6778 }
6779 switch ((int) key_symbol)
6780 {
6781 case XK_o:
6782 {
6783 if ((state & ControlMask) == 0)
6784 break;
6785 return(OpenCommand);
6786 }
6787 case XK_space:
6788 return(NextCommand);
6789 case XK_BackSpace:
6790 return(FormerCommand);
6791 case XK_s:
6792 {
6793 if ((state & Mod1Mask) != 0)
6794 return(SwirlCommand);
6795 if ((state & ControlMask) == 0)
6796 return(ShearCommand);
6797 return(SaveCommand);
6798 }
6799 case XK_p:
6800 case XK_Print:
6801 {
6802 if ((state & Mod1Mask) != 0)
6803 return(OilPaintCommand);
6804 if ((state & Mod4Mask) != 0)
6805 return(ColorCommand);
6806 if ((state & ControlMask) == 0)
6807 return(NullCommand);
6808 return(PrintCommand);
6809 }
6810 case XK_d:
6811 {
6812 if ((state & Mod4Mask) != 0)
6813 return(DrawCommand);
6814 if ((state & ControlMask) == 0)
6815 return(NullCommand);
6816 return(DeleteCommand);
6817 }
6818 case XK_Select:
6819 {
6820 if ((state & ControlMask) == 0)
6821 return(NullCommand);
6822 return(SelectCommand);
6823 }
6824 case XK_n:
6825 {
6826 if ((state & ControlMask) == 0)
6827 return(NullCommand);
6828 return(NewCommand);
6829 }
6830 case XK_q:
6831 case XK_Escape:
6832 return(QuitCommand);
6833 case XK_z:
6834 case XK_Undo:
6835 {
6836 if ((state & ControlMask) == 0)
6837 return(NullCommand);
6838 return(UndoCommand);
6839 }
6840 case XK_r:
6841 case XK_Redo:
6842 {
6843 if ((state & ControlMask) == 0)
6844 return(RollCommand);
6845 return(RedoCommand);
6846 }
6847 case XK_x:
6848 {
6849 if ((state & ControlMask) == 0)
6850 return(NullCommand);
6851 return(CutCommand);
6852 }
6853 case XK_c:
6854 {
6855 if ((state & Mod1Mask) != 0)
6856 return(CharcoalDrawCommand);
6857 if ((state & ControlMask) == 0)
6858 return(CropCommand);
6859 return(CopyCommand);
6860 }
6861 case XK_v:
6862 case XK_Insert:
6863 {
6864 if ((state & Mod4Mask) != 0)
6865 return(CompositeCommand);
6866 if ((state & ControlMask) == 0)
6867 return(FlipCommand);
6868 return(PasteCommand);
6869 }
6870 case XK_less:
6871 return(HalfSizeCommand);
6872 case XK_minus:
6873 return(OriginalSizeCommand);
6874 case XK_greater:
6875 return(DoubleSizeCommand);
6876 case XK_percent:
6877 return(ResizeCommand);
6878 case XK_at:
6879 return(RefreshCommand);
6880 case XK_bracketleft:
6881 return(ChopCommand);
6882 case XK_h:
6883 return(FlopCommand);
6884 case XK_slash:
6885 return(RotateRightCommand);
6886 case XK_backslash:
6887 return(RotateLeftCommand);
6888 case XK_asterisk:
6889 return(RotateCommand);
6890 case XK_t:
6891 return(TrimCommand);
6892 case XK_H:
6893 return(HueCommand);
6894 case XK_S:
6895 return(SaturationCommand);
6896 case XK_L:
6897 return(BrightnessCommand);
6898 case XK_G:
6899 return(GammaCommand);
6900 case XK_C:
6901 return(SpiffCommand);
6902 case XK_Z:
6903 return(DullCommand);
6904 case XK_N:
6905 return(NormalizeCommand);
6906 case XK_equal:
6907 return(EqualizeCommand);
6908 case XK_asciitilde:
6909 return(NegateCommand);
6910 case XK_period:
6911 return(GrayscaleCommand);
6912 case XK_numbersign:
6913 return(QuantizeCommand);
6914 case XK_F2:
6915 return(DespeckleCommand);
6916 case XK_F3:
6917 return(EmbossCommand);
6918 case XK_F4:
6919 return(ReduceNoiseCommand);
6920 case XK_F5:
6921 return(AddNoiseCommand);
6922 case XK_F6:
6923 return(SharpenCommand);
6924 case XK_F7:
6925 return(BlurCommand);
6926 case XK_F8:
6927 return(ThresholdCommand);
6928 case XK_F9:
6929 return(EdgeDetectCommand);
6930 case XK_F10:
6931 return(SpreadCommand);
6932 case XK_F11:
6933 return(ShadeCommand);
6934 case XK_F12:
6935 return(RaiseCommand);
6936 case XK_F13:
6937 return(SegmentCommand);
6938 case XK_i:
6939 {
6940 if ((state & Mod1Mask) == 0)
6941 return(NullCommand);
6942 return(ImplodeCommand);
6943 }
6944 case XK_w:
6945 {
6946 if ((state & Mod1Mask) == 0)
6947 return(NullCommand);
6948 return(WaveCommand);
6949 }
6950 case XK_m:
6951 {
6952 if ((state & Mod4Mask) == 0)
6953 return(NullCommand);
6954 return(MatteCommand);
6955 }
6956 case XK_b:
6957 {
6958 if ((state & Mod4Mask) == 0)
6959 return(NullCommand);
6960 return(AddBorderCommand);
6961 }
6962 case XK_f:
6963 {
6964 if ((state & Mod4Mask) == 0)
6965 return(NullCommand);
6966 return(AddFrameCommand);
6967 }
6968 case XK_exclam:
6969 {
6970 if ((state & Mod4Mask) == 0)
6971 return(NullCommand);
6972 return(CommentCommand);
6973 }
6974 case XK_a:
6975 {
6976 if ((state & Mod1Mask) != 0)
6977 return(ApplyCommand);
6978 if ((state & Mod4Mask) != 0)
6979 return(AnnotateCommand);
6980 if ((state & ControlMask) == 0)
6981 return(NullCommand);
6982 return(RegionofInterestCommand);
6983 }
6984 case XK_question:
6985 return(InfoCommand);
6986 case XK_plus:
6987 return(ZoomCommand);
6988 case XK_P:
6989 {
6990 if ((state & ShiftMask) == 0)
6991 return(NullCommand);
6992 return(ShowPreviewCommand);
6993 }
6994 case XK_Execute:
6995 return(LaunchCommand);
6996 case XK_F1:
6997 return(HelpCommand);
6998 case XK_Find:
6999 return(BrowseDocumentationCommand);
7000 case XK_Menu:
7001 {
7002 (void) XMapRaised(display,windows->command.id);
7003 return(NullCommand);
7004 }
7005 case XK_Next:
7006 case XK_Prior:
7007 case XK_Home:
7008 case XK_KP_Home:
7009 {
7010 XTranslateImage(display,windows,*image,key_symbol);
7011 return(NullCommand);
7012 }
7013 case XK_Up:
7014 case XK_KP_Up:
7015 case XK_Down:
7016 case XK_KP_Down:
7017 case XK_Left:
7018 case XK_KP_Left:
7019 case XK_Right:
7020 case XK_KP_Right:
7021 {
7022 if ((state & Mod1Mask) != 0)
7023 {
7024 RectangleInfo
7025 crop_info;
7026
7027 /*
7028 Trim one pixel from edge of image.
7029 */
7030 crop_info.x=0;
7031 crop_info.y=0;
cristybb503372010-05-27 20:51:26 +00007032 crop_info.width=(size_t) windows->image.ximage->width;
7033 crop_info.height=(size_t) windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +00007034 if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7035 {
7036 if (resource_info->quantum >= (int) crop_info.height)
7037 resource_info->quantum=(int) crop_info.height-1;
7038 crop_info.height-=resource_info->quantum;
7039 }
7040 if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7041 {
7042 if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7043 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7044 crop_info.y+=resource_info->quantum;
7045 crop_info.height-=resource_info->quantum;
7046 }
7047 if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7048 {
7049 if (resource_info->quantum >= (int) crop_info.width)
7050 resource_info->quantum=(int) crop_info.width-1;
7051 crop_info.width-=resource_info->quantum;
7052 }
7053 if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7054 {
7055 if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7056 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7057 crop_info.x+=resource_info->quantum;
7058 crop_info.width-=resource_info->quantum;
7059 }
7060 if ((int) (windows->image.x+windows->image.width) >
7061 (int) crop_info.width)
7062 windows->image.x=(int) (crop_info.width-windows->image.width);
7063 if ((int) (windows->image.y+windows->image.height) >
7064 (int) crop_info.height)
7065 windows->image.y=(int) (crop_info.height-windows->image.height);
7066 XSetCropGeometry(display,windows,&crop_info,*image);
7067 windows->image.window_changes.width=(int) crop_info.width;
7068 windows->image.window_changes.height=(int) crop_info.height;
7069 (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
cristy051718b2011-08-28 22:49:25 +00007070 (void) XConfigureImage(display,resource_info,windows,*image,
7071 exception);
cristy3ed852e2009-09-05 21:47:34 +00007072 return(NullCommand);
7073 }
7074 XTranslateImage(display,windows,*image,key_symbol);
7075 return(NullCommand);
7076 }
7077 default:
7078 return(NullCommand);
7079 }
7080 return(NullCommand);
7081}
7082
7083/*
7084%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7085% %
7086% %
7087% %
7088+ X M a g i c k C o m m a n d %
7089% %
7090% %
7091% %
7092%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7093%
7094% XMagickCommand() makes a transform to the image or Image window as
7095% specified by a user menu button or keyboard command.
7096%
7097% The format of the XMagickCommand method is:
7098%
7099% Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +00007100% XWindows *windows,const CommandType command,Image **image,
7101% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007102%
7103% A description of each parameter follows:
7104%
cristy3ed852e2009-09-05 21:47:34 +00007105% o display: Specifies a connection to an X server; returned from
7106% XOpenDisplay.
7107%
7108% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7109%
7110% o windows: Specifies a pointer to a XWindows structure.
7111%
7112% o command: Specifies a command to perform.
7113%
cristy051718b2011-08-28 22:49:25 +00007114% o image: the image; XMagickCommand may transform the image and return a
7115% new image pointer.
7116%
7117% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007118%
7119*/
7120static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +00007121 XWindows *windows,const CommandType command,Image **image,
7122 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007123{
7124 char
7125 filename[MaxTextExtent],
7126 geometry[MaxTextExtent],
7127 modulate_factors[MaxTextExtent];
7128
7129 GeometryInfo
7130 geometry_info;
7131
7132 Image
7133 *nexus;
7134
7135 ImageInfo
7136 *image_info;
7137
7138 int
7139 x,
7140 y;
7141
7142 MagickStatusType
7143 flags,
7144 status;
7145
7146 QuantizeInfo
7147 quantize_info;
7148
7149 RectangleInfo
7150 page_geometry;
7151
7152 register int
7153 i;
7154
7155 static char
7156 color[MaxTextExtent] = "gray";
7157
7158 unsigned int
7159 height,
7160 width;
7161
7162 /*
7163 Process user command.
7164 */
7165 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007166 XImageCache(display,resource_info,windows,command,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007167 nexus=NewImageList();
7168 windows->image.window_changes.width=windows->image.ximage->width;
7169 windows->image.window_changes.height=windows->image.ximage->height;
7170 image_info=CloneImageInfo(resource_info->image_info);
7171 SetGeometryInfo(&geometry_info);
7172 GetQuantizeInfo(&quantize_info);
7173 switch (command)
7174 {
7175 case OpenCommand:
7176 {
7177 /*
7178 Load image.
7179 */
7180 nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7181 break;
7182 }
7183 case NextCommand:
7184 {
7185 /*
7186 Display next image.
7187 */
7188 for (i=0; i < resource_info->quantum; i++)
7189 XClientMessage(display,windows->image.id,windows->im_protocols,
7190 windows->im_next_image,CurrentTime);
7191 break;
7192 }
7193 case FormerCommand:
7194 {
7195 /*
7196 Display former image.
7197 */
7198 for (i=0; i < resource_info->quantum; i++)
7199 XClientMessage(display,windows->image.id,windows->im_protocols,
7200 windows->im_former_image,CurrentTime);
7201 break;
7202 }
7203 case SelectCommand:
7204 {
7205 int
7206 status;
7207
7208 /*
7209 Select image.
7210 */
7211 status=chdir(resource_info->home_directory);
7212 if (status == -1)
cristy051718b2011-08-28 22:49:25 +00007213 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7214 "UnableToOpenFile","%s",resource_info->home_directory);
cristy3ed852e2009-09-05 21:47:34 +00007215 nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7216 break;
7217 }
7218 case SaveCommand:
7219 {
7220 /*
7221 Save image.
7222 */
cristy051718b2011-08-28 22:49:25 +00007223 status=XSaveImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007224 if (status == MagickFalse)
7225 {
cristyc663dbd2011-09-16 19:43:14 +00007226 char
7227 message[MaxTextExtent];
7228
7229 (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7230 exception->reason != (char *) NULL ? exception->reason : "",
7231 exception->description != (char *) NULL ? exception->description :
7232 "");
7233 XNoticeWidget(display,windows,"Unable to save file:",message);
cristy3ed852e2009-09-05 21:47:34 +00007234 break;
7235 }
7236 break;
7237 }
7238 case PrintCommand:
7239 {
7240 /*
7241 Print image.
7242 */
cristy051718b2011-08-28 22:49:25 +00007243 status=XPrintImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007244 if (status == MagickFalse)
7245 {
cristyc663dbd2011-09-16 19:43:14 +00007246 char
7247 message[MaxTextExtent];
7248
7249 (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7250 exception->reason != (char *) NULL ? exception->reason : "",
7251 exception->description != (char *) NULL ? exception->description :
7252 "");
7253 XNoticeWidget(display,windows,"Unable to print file:",message);
cristy3ed852e2009-09-05 21:47:34 +00007254 break;
7255 }
7256 break;
7257 }
7258 case DeleteCommand:
7259 {
7260 static char
7261 filename[MaxTextExtent] = "\0";
7262
7263 /*
7264 Delete image file.
7265 */
7266 XFileBrowserWidget(display,windows,"Delete",filename);
7267 if (*filename == '\0')
7268 break;
cristy18c6c272011-09-23 14:40:37 +00007269 status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00007270 if (status != MagickFalse)
7271 XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7272 break;
7273 }
7274 case NewCommand:
7275 {
7276 int
7277 status;
7278
7279 static char
7280 color[MaxTextExtent] = "gray",
7281 geometry[MaxTextExtent] = "640x480";
7282
7283 static const char
7284 *format = "gradient";
7285
7286 /*
7287 Query user for canvas geometry.
7288 */
7289 status=XDialogWidget(display,windows,"New","Enter image geometry:",
7290 geometry);
7291 if (*geometry == '\0')
7292 break;
7293 if (status == 0)
7294 format="xc";
7295 XColorBrowserWidget(display,windows,"Select",color);
7296 if (*color == '\0')
7297 break;
7298 /*
7299 Create canvas.
7300 */
cristyb51dff52011-05-19 16:55:47 +00007301 (void) FormatLocaleString(image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00007302 "%s:%s",format,color);
7303 (void) CloneString(&image_info->size,geometry);
cristy051718b2011-08-28 22:49:25 +00007304 nexus=ReadImage(image_info,exception);
7305 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00007306 XClientMessage(display,windows->image.id,windows->im_protocols,
7307 windows->im_next_image,CurrentTime);
7308 break;
7309 }
7310 case VisualDirectoryCommand:
7311 {
7312 /*
7313 Visual Image directory.
7314 */
cristy051718b2011-08-28 22:49:25 +00007315 nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
cristy3ed852e2009-09-05 21:47:34 +00007316 break;
7317 }
7318 case QuitCommand:
7319 {
7320 /*
7321 exit program.
7322 */
7323 if (resource_info->confirm_exit == MagickFalse)
7324 XClientMessage(display,windows->image.id,windows->im_protocols,
7325 windows->im_exit,CurrentTime);
7326 else
7327 {
7328 int
7329 status;
7330
7331 /*
7332 Confirm program exit.
7333 */
7334 status=XConfirmWidget(display,windows,"Do you really want to exit",
7335 resource_info->client_name);
7336 if (status > 0)
7337 XClientMessage(display,windows->image.id,windows->im_protocols,
7338 windows->im_exit,CurrentTime);
7339 }
7340 break;
7341 }
7342 case CutCommand:
7343 {
7344 /*
7345 Cut image.
7346 */
cristy051718b2011-08-28 22:49:25 +00007347 (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00007348 break;
7349 }
7350 case CopyCommand:
7351 {
7352 /*
7353 Copy image.
7354 */
cristy051718b2011-08-28 22:49:25 +00007355 (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7356 exception);
cristy3ed852e2009-09-05 21:47:34 +00007357 break;
7358 }
7359 case PasteCommand:
7360 {
7361 /*
7362 Paste image.
7363 */
cristy051718b2011-08-28 22:49:25 +00007364 status=XPasteImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007365 if (status == MagickFalse)
7366 {
7367 XNoticeWidget(display,windows,"Unable to paste X image",
7368 (*image)->filename);
7369 break;
7370 }
7371 break;
7372 }
7373 case HalfSizeCommand:
7374 {
7375 /*
7376 Half image size.
7377 */
7378 windows->image.window_changes.width=windows->image.ximage->width/2;
7379 windows->image.window_changes.height=windows->image.ximage->height/2;
cristy051718b2011-08-28 22:49:25 +00007380 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007381 break;
7382 }
7383 case OriginalSizeCommand:
7384 {
7385 /*
7386 Original image size.
7387 */
7388 windows->image.window_changes.width=(int) (*image)->columns;
7389 windows->image.window_changes.height=(int) (*image)->rows;
cristy051718b2011-08-28 22:49:25 +00007390 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007391 break;
7392 }
7393 case DoubleSizeCommand:
7394 {
7395 /*
7396 Double the image size.
7397 */
7398 windows->image.window_changes.width=windows->image.ximage->width << 1;
7399 windows->image.window_changes.height=windows->image.ximage->height << 1;
cristy051718b2011-08-28 22:49:25 +00007400 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007401 break;
7402 }
7403 case ResizeCommand:
7404 {
7405 int
7406 status;
7407
cristybb503372010-05-27 20:51:26 +00007408 size_t
cristy3ed852e2009-09-05 21:47:34 +00007409 height,
7410 width;
7411
cristy9d314ff2011-03-09 01:30:28 +00007412 ssize_t
7413 x,
7414 y;
7415
cristy3ed852e2009-09-05 21:47:34 +00007416 /*
7417 Resize image.
7418 */
cristybb503372010-05-27 20:51:26 +00007419 width=(size_t) windows->image.ximage->width;
7420 height=(size_t) windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +00007421 x=0;
7422 y=0;
cristyb51dff52011-05-19 16:55:47 +00007423 (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
cristye8c25f92010-06-03 00:53:06 +00007424 (double) width,(double) height);
cristy3ed852e2009-09-05 21:47:34 +00007425 status=XDialogWidget(display,windows,"Resize",
7426 "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7427 if (*geometry == '\0')
7428 break;
7429 if (status == 0)
7430 (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7431 (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7432 windows->image.window_changes.width=(int) width;
7433 windows->image.window_changes.height=(int) height;
cristy051718b2011-08-28 22:49:25 +00007434 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007435 break;
7436 }
7437 case ApplyCommand:
7438 {
7439 char
7440 image_geometry[MaxTextExtent];
7441
7442 if ((windows->image.crop_geometry == (char *) NULL) &&
7443 ((int) (*image)->columns == windows->image.ximage->width) &&
7444 ((int) (*image)->rows == windows->image.ximage->height))
7445 break;
7446 /*
7447 Apply size transforms to image.
7448 */
7449 XSetCursorState(display,windows,MagickTrue);
7450 XCheckRefreshWindows(display,windows);
7451 /*
7452 Crop and/or scale displayed image.
7453 */
cristyb51dff52011-05-19 16:55:47 +00007454 (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
cristy3ed852e2009-09-05 21:47:34 +00007455 windows->image.ximage->width,windows->image.ximage->height);
cristye941a752011-10-15 01:52:48 +00007456 (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7457 exception);
cristy3ed852e2009-09-05 21:47:34 +00007458 if (windows->image.crop_geometry != (char *) NULL)
cristye941a752011-10-15 01:52:48 +00007459 windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7460 windows->image.crop_geometry);
cristy3ed852e2009-09-05 21:47:34 +00007461 windows->image.x=0;
7462 windows->image.y=0;
cristy6710d842011-10-20 23:23:00 +00007463 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007464 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007465 break;
7466 }
7467 case RefreshCommand:
7468 {
cristy051718b2011-08-28 22:49:25 +00007469 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007470 break;
7471 }
7472 case RestoreCommand:
7473 {
7474 /*
7475 Restore Image window to its original size.
7476 */
7477 if ((windows->image.width == (unsigned int) (*image)->columns) &&
7478 (windows->image.height == (unsigned int) (*image)->rows) &&
7479 (windows->image.crop_geometry == (char *) NULL))
7480 {
7481 (void) XBell(display,0);
7482 break;
7483 }
7484 windows->image.window_changes.width=(int) (*image)->columns;
7485 windows->image.window_changes.height=(int) (*image)->rows;
7486 if (windows->image.crop_geometry != (char *) NULL)
7487 {
7488 windows->image.crop_geometry=(char *)
7489 RelinquishMagickMemory(windows->image.crop_geometry);
7490 windows->image.crop_geometry=(char *) NULL;
7491 windows->image.x=0;
7492 windows->image.y=0;
7493 }
cristy6710d842011-10-20 23:23:00 +00007494 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007495 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007496 break;
7497 }
7498 case CropCommand:
7499 {
7500 /*
7501 Crop image.
7502 */
cristy051718b2011-08-28 22:49:25 +00007503 (void) XCropImage(display,resource_info,windows,*image,CropMode,
7504 exception);
cristy3ed852e2009-09-05 21:47:34 +00007505 break;
7506 }
7507 case ChopCommand:
7508 {
7509 /*
7510 Chop image.
7511 */
cristy051718b2011-08-28 22:49:25 +00007512 status=XChopImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007513 if (status == MagickFalse)
7514 {
7515 XNoticeWidget(display,windows,"Unable to cut X image",
7516 (*image)->filename);
7517 break;
7518 }
7519 break;
7520 }
7521 case FlopCommand:
7522 {
7523 Image
7524 *flop_image;
7525
7526 /*
7527 Flop image scanlines.
7528 */
7529 XSetCursorState(display,windows,MagickTrue);
7530 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007531 flop_image=FlopImage(*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007532 if (flop_image != (Image *) NULL)
7533 {
7534 *image=DestroyImage(*image);
7535 *image=flop_image;
7536 }
cristy051718b2011-08-28 22:49:25 +00007537 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00007538 XSetCursorState(display,windows,MagickFalse);
7539 if (windows->image.crop_geometry != (char *) NULL)
7540 {
7541 /*
7542 Flop crop geometry.
7543 */
7544 width=(unsigned int) (*image)->columns;
7545 height=(unsigned int) (*image)->rows;
7546 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7547 &width,&height);
cristyb51dff52011-05-19 16:55:47 +00007548 (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00007549 "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7550 }
7551 if (windows->image.orphan != MagickFalse)
7552 break;
cristy051718b2011-08-28 22:49:25 +00007553 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007554 break;
7555 }
7556 case FlipCommand:
7557 {
7558 Image
7559 *flip_image;
7560
7561 /*
7562 Flip image scanlines.
7563 */
7564 XSetCursorState(display,windows,MagickTrue);
7565 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007566 flip_image=FlipImage(*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007567 if (flip_image != (Image *) NULL)
7568 {
7569 *image=DestroyImage(*image);
7570 *image=flip_image;
7571 }
cristy051718b2011-08-28 22:49:25 +00007572 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00007573 XSetCursorState(display,windows,MagickFalse);
7574 if (windows->image.crop_geometry != (char *) NULL)
7575 {
7576 /*
7577 Flip crop geometry.
7578 */
7579 width=(unsigned int) (*image)->columns;
7580 height=(unsigned int) (*image)->rows;
7581 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7582 &width,&height);
cristyb51dff52011-05-19 16:55:47 +00007583 (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00007584 "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7585 }
7586 if (windows->image.orphan != MagickFalse)
7587 break;
cristy051718b2011-08-28 22:49:25 +00007588 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007589 break;
7590 }
7591 case RotateRightCommand:
7592 {
7593 /*
7594 Rotate image 90 degrees clockwise.
7595 */
cristy051718b2011-08-28 22:49:25 +00007596 status=XRotateImage(display,resource_info,windows,90.0,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007597 if (status == MagickFalse)
7598 {
7599 XNoticeWidget(display,windows,"Unable to rotate X image",
7600 (*image)->filename);
7601 break;
7602 }
7603 break;
7604 }
7605 case RotateLeftCommand:
7606 {
7607 /*
7608 Rotate image 90 degrees counter-clockwise.
7609 */
cristy051718b2011-08-28 22:49:25 +00007610 status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007611 if (status == MagickFalse)
7612 {
7613 XNoticeWidget(display,windows,"Unable to rotate X image",
7614 (*image)->filename);
7615 break;
7616 }
7617 break;
7618 }
7619 case RotateCommand:
7620 {
7621 /*
7622 Rotate image.
7623 */
cristy051718b2011-08-28 22:49:25 +00007624 status=XRotateImage(display,resource_info,windows,0.0,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007625 if (status == MagickFalse)
7626 {
7627 XNoticeWidget(display,windows,"Unable to rotate X image",
7628 (*image)->filename);
7629 break;
7630 }
7631 break;
7632 }
7633 case ShearCommand:
7634 {
7635 Image
7636 *shear_image;
7637
7638 static char
7639 geometry[MaxTextExtent] = "45.0x45.0";
7640
7641 /*
7642 Query user for shear color and geometry.
7643 */
7644 XColorBrowserWidget(display,windows,"Select",color);
7645 if (*color == '\0')
7646 break;
7647 (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7648 geometry);
7649 if (*geometry == '\0')
7650 break;
7651 /*
7652 Shear image.
7653 */
cristy051718b2011-08-28 22:49:25 +00007654 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7655 exception);
cristy3ed852e2009-09-05 21:47:34 +00007656 XSetCursorState(display,windows,MagickTrue);
7657 XCheckRefreshWindows(display,windows);
cristy9950d572011-10-01 18:22:35 +00007658 (void) QueryColorCompliance(color,AllCompliance,
7659 &(*image)->background_color,exception);
cristy3ed852e2009-09-05 21:47:34 +00007660 flags=ParseGeometry(geometry,&geometry_info);
7661 if ((flags & SigmaValue) == 0)
7662 geometry_info.sigma=geometry_info.rho;
7663 shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
cristy051718b2011-08-28 22:49:25 +00007664 exception);
cristy3ed852e2009-09-05 21:47:34 +00007665 if (shear_image != (Image *) NULL)
7666 {
7667 *image=DestroyImage(*image);
7668 *image=shear_image;
7669 }
cristy051718b2011-08-28 22:49:25 +00007670 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00007671 XSetCursorState(display,windows,MagickFalse);
7672 if (windows->image.orphan != MagickFalse)
7673 break;
7674 windows->image.window_changes.width=(int) (*image)->columns;
7675 windows->image.window_changes.height=(int) (*image)->rows;
cristy6710d842011-10-20 23:23:00 +00007676 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007677 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007678 break;
7679 }
7680 case RollCommand:
7681 {
7682 Image
7683 *roll_image;
7684
7685 static char
7686 geometry[MaxTextExtent] = "+2+2";
7687
7688 /*
7689 Query user for the roll geometry.
7690 */
7691 (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7692 geometry);
7693 if (*geometry == '\0')
7694 break;
7695 /*
7696 Roll image.
7697 */
cristy051718b2011-08-28 22:49:25 +00007698 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7699 exception);
cristy3ed852e2009-09-05 21:47:34 +00007700 XSetCursorState(display,windows,MagickTrue);
7701 XCheckRefreshWindows(display,windows);
7702 (void) ParsePageGeometry(*image,geometry,&page_geometry,
cristy051718b2011-08-28 22:49:25 +00007703 exception);
cristy3ed852e2009-09-05 21:47:34 +00007704 roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
cristy051718b2011-08-28 22:49:25 +00007705 exception);
cristy3ed852e2009-09-05 21:47:34 +00007706 if (roll_image != (Image *) NULL)
7707 {
7708 *image=DestroyImage(*image);
7709 *image=roll_image;
7710 }
cristy051718b2011-08-28 22:49:25 +00007711 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00007712 XSetCursorState(display,windows,MagickFalse);
7713 if (windows->image.orphan != MagickFalse)
7714 break;
7715 windows->image.window_changes.width=(int) (*image)->columns;
7716 windows->image.window_changes.height=(int) (*image)->rows;
cristy6710d842011-10-20 23:23:00 +00007717 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007718 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007719 break;
7720 }
7721 case TrimCommand:
7722 {
7723 static char
7724 fuzz[MaxTextExtent];
7725
7726 /*
7727 Query user for the fuzz factor.
7728 */
cristyb51dff52011-05-19 16:55:47 +00007729 (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
cristy8cd5b312010-01-07 01:10:24 +00007730 (*image)->fuzz/(QuantumRange+1.0));
cristy3ed852e2009-09-05 21:47:34 +00007731 (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7732 if (*fuzz == '\0')
7733 break;
cristydbdd0e32011-11-04 23:29:40 +00007734 (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
cristy3ed852e2009-09-05 21:47:34 +00007735 /*
7736 Trim image.
7737 */
cristy051718b2011-08-28 22:49:25 +00007738 status=XTrimImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007739 if (status == MagickFalse)
7740 {
7741 XNoticeWidget(display,windows,"Unable to trim X image",
7742 (*image)->filename);
7743 break;
7744 }
7745 break;
7746 }
7747 case HueCommand:
7748 {
7749 static char
7750 hue_percent[MaxTextExtent] = "110";
7751
7752 /*
7753 Query user for percent hue change.
7754 */
7755 (void) XDialogWidget(display,windows,"Apply",
7756 "Enter percent change in image hue (0-200):",hue_percent);
7757 if (*hue_percent == '\0')
7758 break;
7759 /*
7760 Vary the image hue.
7761 */
7762 XSetCursorState(display,windows,MagickTrue);
7763 XCheckRefreshWindows(display,windows);
7764 (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7765 (void) ConcatenateMagickString(modulate_factors,hue_percent,
7766 MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +00007767 (void) ModulateImage(*image,modulate_factors,exception);
cristy3ed852e2009-09-05 21:47:34 +00007768 XSetCursorState(display,windows,MagickFalse);
7769 if (windows->image.orphan != MagickFalse)
7770 break;
cristy6710d842011-10-20 23:23:00 +00007771 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007772 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007773 break;
7774 }
7775 case SaturationCommand:
7776 {
7777 static char
7778 saturation_percent[MaxTextExtent] = "110";
7779
7780 /*
7781 Query user for percent saturation change.
7782 */
7783 (void) XDialogWidget(display,windows,"Apply",
7784 "Enter percent change in color saturation (0-200):",saturation_percent);
7785 if (*saturation_percent == '\0')
7786 break;
7787 /*
7788 Vary color saturation.
7789 */
7790 XSetCursorState(display,windows,MagickTrue);
7791 XCheckRefreshWindows(display,windows);
7792 (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7793 (void) ConcatenateMagickString(modulate_factors,saturation_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;
cristy6710d842011-10-20 23:23:00 +00007799 XConfigureImageColormap(display,resource_info,windows,*image,exception);
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 BrightnessCommand:
7804 {
7805 static char
7806 brightness_percent[MaxTextExtent] = "110";
7807
7808 /*
7809 Query user for percent brightness change.
7810 */
7811 (void) XDialogWidget(display,windows,"Apply",
7812 "Enter percent change in color brightness (0-200):",brightness_percent);
7813 if (*brightness_percent == '\0')
7814 break;
7815 /*
7816 Vary the color brightness.
7817 */
7818 XSetCursorState(display,windows,MagickTrue);
7819 XCheckRefreshWindows(display,windows);
7820 (void) CopyMagickString(modulate_factors,brightness_percent,
7821 MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +00007822 (void) ModulateImage(*image,modulate_factors,exception);
cristy3ed852e2009-09-05 21:47:34 +00007823 XSetCursorState(display,windows,MagickFalse);
7824 if (windows->image.orphan != MagickFalse)
7825 break;
cristy6710d842011-10-20 23:23:00 +00007826 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007827 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007828 break;
7829 }
7830 case GammaCommand:
7831 {
7832 static char
7833 factor[MaxTextExtent] = "1.6";
7834
7835 /*
7836 Query user for gamma value.
7837 */
7838 (void) XDialogWidget(display,windows,"Gamma",
cristy50fbc382011-07-07 02:19:17 +00007839 "Enter gamma value (e.g. 1.2):",factor);
cristy3ed852e2009-09-05 21:47:34 +00007840 if (*factor == '\0')
7841 break;
7842 /*
7843 Gamma correct image.
7844 */
7845 XSetCursorState(display,windows,MagickTrue);
7846 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007847 (void) GammaImage(*image,atof(factor),exception);
cristy3ed852e2009-09-05 21:47:34 +00007848 XSetCursorState(display,windows,MagickFalse);
7849 if (windows->image.orphan != MagickFalse)
7850 break;
cristy6710d842011-10-20 23:23:00 +00007851 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007852 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007853 break;
7854 }
7855 case SpiffCommand:
7856 {
7857 /*
7858 Sharpen the image contrast.
7859 */
7860 XSetCursorState(display,windows,MagickTrue);
7861 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007862 (void) ContrastImage(*image,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00007863 XSetCursorState(display,windows,MagickFalse);
7864 if (windows->image.orphan != MagickFalse)
7865 break;
cristy6710d842011-10-20 23:23:00 +00007866 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007867 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007868 break;
7869 }
7870 case DullCommand:
7871 {
7872 /*
7873 Dull the image contrast.
7874 */
7875 XSetCursorState(display,windows,MagickTrue);
7876 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007877 (void) ContrastImage(*image,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00007878 XSetCursorState(display,windows,MagickFalse);
7879 if (windows->image.orphan != MagickFalse)
7880 break;
cristy6710d842011-10-20 23:23:00 +00007881 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007882 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007883 break;
7884 }
7885 case ContrastStretchCommand:
7886 {
7887 double
7888 black_point,
7889 white_point;
7890
7891 static char
7892 levels[MaxTextExtent] = "1%";
7893
7894 /*
7895 Query user for gamma value.
7896 */
7897 (void) XDialogWidget(display,windows,"Contrast Stretch",
7898 "Enter black and white points:",levels);
7899 if (*levels == '\0')
7900 break;
7901 /*
7902 Contrast stretch image.
7903 */
7904 XSetCursorState(display,windows,MagickTrue);
7905 XCheckRefreshWindows(display,windows);
7906 flags=ParseGeometry(levels,&geometry_info);
7907 black_point=geometry_info.rho;
7908 white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7909 if ((flags & PercentValue) != 0)
7910 {
7911 black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7912 white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7913 }
7914 white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
cristye23ec9d2011-08-16 18:15:40 +00007915 (void) ContrastStretchImage(*image,black_point,white_point,
cristy051718b2011-08-28 22:49:25 +00007916 exception);
cristy3ed852e2009-09-05 21:47:34 +00007917 XSetCursorState(display,windows,MagickFalse);
7918 if (windows->image.orphan != MagickFalse)
7919 break;
cristy6710d842011-10-20 23:23:00 +00007920 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007921 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007922 break;
7923 }
7924 case SigmoidalContrastCommand:
7925 {
cristy9ee60942011-07-06 14:54:38 +00007926 GeometryInfo
7927 geometry_info;
7928
7929 MagickStatusType
7930 flags;
7931
cristy3ed852e2009-09-05 21:47:34 +00007932 static char
7933 levels[MaxTextExtent] = "3x50%";
7934
7935 /*
7936 Query user for gamma value.
7937 */
7938 (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7939 "Enter contrast and midpoint:",levels);
7940 if (*levels == '\0')
7941 break;
7942 /*
7943 Contrast stretch image.
7944 */
7945 XSetCursorState(display,windows,MagickTrue);
7946 XCheckRefreshWindows(display,windows);
cristy9ee60942011-07-06 14:54:38 +00007947 flags=ParseGeometry(levels,&geometry_info);
7948 if ((flags & SigmaValue) == 0)
7949 geometry_info.sigma=1.0*QuantumRange/2.0;
7950 if ((flags & PercentValue) != 0)
7951 geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7952 (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
cristy051718b2011-08-28 22:49:25 +00007953 geometry_info.sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +00007954 XSetCursorState(display,windows,MagickFalse);
7955 if (windows->image.orphan != MagickFalse)
7956 break;
cristy6710d842011-10-20 23:23:00 +00007957 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007958 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007959 break;
7960 }
7961 case NormalizeCommand:
7962 {
7963 /*
7964 Perform histogram normalization on the image.
7965 */
7966 XSetCursorState(display,windows,MagickTrue);
7967 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007968 (void) NormalizeImage(*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007969 XSetCursorState(display,windows,MagickFalse);
7970 if (windows->image.orphan != MagickFalse)
7971 break;
cristy6710d842011-10-20 23:23:00 +00007972 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007973 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007974 break;
7975 }
7976 case EqualizeCommand:
7977 {
7978 /*
7979 Perform histogram equalization on the image.
7980 */
7981 XSetCursorState(display,windows,MagickTrue);
7982 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007983 (void) EqualizeImage(*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007984 XSetCursorState(display,windows,MagickFalse);
7985 if (windows->image.orphan != MagickFalse)
7986 break;
cristy6710d842011-10-20 23:23:00 +00007987 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007988 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007989 break;
7990 }
7991 case NegateCommand:
7992 {
7993 /*
7994 Negate colors in image.
7995 */
7996 XSetCursorState(display,windows,MagickTrue);
7997 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007998 (void) NegateImage(*image,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00007999 XSetCursorState(display,windows,MagickFalse);
8000 if (windows->image.orphan != MagickFalse)
8001 break;
cristy6710d842011-10-20 23:23:00 +00008002 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008003 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008004 break;
8005 }
8006 case GrayscaleCommand:
8007 {
8008 /*
8009 Convert image to grayscale.
8010 */
8011 XSetCursorState(display,windows,MagickTrue);
8012 XCheckRefreshWindows(display,windows);
8013 (void) SetImageType(*image,(*image)->matte == MagickFalse ?
cristy018f07f2011-09-04 21:15:19 +00008014 GrayscaleType : GrayscaleMatteType,exception);
cristy3ed852e2009-09-05 21:47:34 +00008015 XSetCursorState(display,windows,MagickFalse);
8016 if (windows->image.orphan != MagickFalse)
8017 break;
cristy6710d842011-10-20 23:23:00 +00008018 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008019 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008020 break;
8021 }
8022 case MapCommand:
8023 {
8024 Image
8025 *affinity_image;
8026
8027 static char
8028 filename[MaxTextExtent] = "\0";
8029
8030 /*
8031 Request image file name from user.
8032 */
8033 XFileBrowserWidget(display,windows,"Map",filename);
8034 if (*filename == '\0')
8035 break;
8036 /*
8037 Map image.
8038 */
8039 XSetCursorState(display,windows,MagickTrue);
8040 XCheckRefreshWindows(display,windows);
8041 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +00008042 affinity_image=ReadImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00008043 if (affinity_image != (Image *) NULL)
8044 {
cristy018f07f2011-09-04 21:15:19 +00008045 (void) RemapImage(&quantize_info,*image,affinity_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008046 affinity_image=DestroyImage(affinity_image);
8047 }
cristy051718b2011-08-28 22:49:25 +00008048 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008049 XSetCursorState(display,windows,MagickFalse);
8050 if (windows->image.orphan != MagickFalse)
8051 break;
cristy6710d842011-10-20 23:23:00 +00008052 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008053 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008054 break;
8055 }
8056 case QuantizeCommand:
8057 {
8058 int
8059 status;
8060
8061 static char
8062 colors[MaxTextExtent] = "256";
8063
8064 /*
8065 Query user for maximum number of colors.
8066 */
8067 status=XDialogWidget(display,windows,"Quantize",
8068 "Maximum number of colors:",colors);
8069 if (*colors == '\0')
8070 break;
8071 /*
8072 Color reduce the image.
8073 */
8074 XSetCursorState(display,windows,MagickTrue);
8075 XCheckRefreshWindows(display,windows);
cristye27293e2009-12-18 02:53:20 +00008076 quantize_info.number_colors=StringToUnsignedLong(colors);
cristy3ed852e2009-09-05 21:47:34 +00008077 quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
cristy018f07f2011-09-04 21:15:19 +00008078 (void) QuantizeImage(&quantize_info,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008079 XSetCursorState(display,windows,MagickFalse);
8080 if (windows->image.orphan != MagickFalse)
8081 break;
cristy6710d842011-10-20 23:23:00 +00008082 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008083 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008084 break;
8085 }
8086 case DespeckleCommand:
8087 {
8088 Image
8089 *despeckle_image;
8090
8091 /*
8092 Despeckle image.
8093 */
8094 XSetCursorState(display,windows,MagickTrue);
8095 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00008096 despeckle_image=DespeckleImage(*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008097 if (despeckle_image != (Image *) NULL)
8098 {
8099 *image=DestroyImage(*image);
8100 *image=despeckle_image;
8101 }
cristy051718b2011-08-28 22:49:25 +00008102 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008103 XSetCursorState(display,windows,MagickFalse);
8104 if (windows->image.orphan != MagickFalse)
8105 break;
cristy6710d842011-10-20 23:23:00 +00008106 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008107 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008108 break;
8109 }
8110 case EmbossCommand:
8111 {
8112 Image
8113 *emboss_image;
8114
8115 static char
8116 radius[MaxTextExtent] = "0.0x1.0";
8117
8118 /*
8119 Query user for emboss radius.
8120 */
8121 (void) XDialogWidget(display,windows,"Emboss",
8122 "Enter the emboss radius and standard deviation:",radius);
8123 if (*radius == '\0')
8124 break;
8125 /*
8126 Reduce noise in the image.
8127 */
8128 XSetCursorState(display,windows,MagickTrue);
8129 XCheckRefreshWindows(display,windows);
8130 flags=ParseGeometry(radius,&geometry_info);
8131 if ((flags & SigmaValue) == 0)
8132 geometry_info.sigma=1.0;
8133 emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
cristy051718b2011-08-28 22:49:25 +00008134 exception);
cristy3ed852e2009-09-05 21:47:34 +00008135 if (emboss_image != (Image *) NULL)
8136 {
8137 *image=DestroyImage(*image);
8138 *image=emboss_image;
8139 }
cristy051718b2011-08-28 22:49:25 +00008140 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008141 XSetCursorState(display,windows,MagickFalse);
8142 if (windows->image.orphan != MagickFalse)
8143 break;
cristy6710d842011-10-20 23:23:00 +00008144 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008145 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008146 break;
8147 }
8148 case ReduceNoiseCommand:
8149 {
8150 Image
8151 *noise_image;
8152
8153 static char
8154 radius[MaxTextExtent] = "0";
8155
8156 /*
8157 Query user for noise radius.
8158 */
8159 (void) XDialogWidget(display,windows,"Reduce Noise",
8160 "Enter the noise radius:",radius);
8161 if (*radius == '\0')
8162 break;
8163 /*
8164 Reduce noise in the image.
8165 */
8166 XSetCursorState(display,windows,MagickTrue);
8167 XCheckRefreshWindows(display,windows);
8168 flags=ParseGeometry(radius,&geometry_info);
cristy95c38342011-03-18 22:39:51 +00008169 noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
cristy051718b2011-08-28 22:49:25 +00008170 geometry_info.rho,(size_t) geometry_info.rho,exception);
cristy3ed852e2009-09-05 21:47:34 +00008171 if (noise_image != (Image *) NULL)
8172 {
8173 *image=DestroyImage(*image);
8174 *image=noise_image;
8175 }
cristy051718b2011-08-28 22:49:25 +00008176 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008177 XSetCursorState(display,windows,MagickFalse);
8178 if (windows->image.orphan != MagickFalse)
8179 break;
cristy6710d842011-10-20 23:23:00 +00008180 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008181 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008182 break;
8183 }
8184 case AddNoiseCommand:
8185 {
8186 char
8187 **noises;
8188
8189 Image
8190 *noise_image;
8191
8192 static char
8193 noise_type[MaxTextExtent] = "Gaussian";
8194
8195 /*
8196 Add noise to the image.
8197 */
cristy042ee782011-04-22 18:48:30 +00008198 noises=GetCommandOptions(MagickNoiseOptions);
cristy3ed852e2009-09-05 21:47:34 +00008199 if (noises == (char **) NULL)
8200 break;
8201 XListBrowserWidget(display,windows,&windows->widget,
8202 (const char **) noises,"Add Noise",
8203 "Select a type of noise to add to your image:",noise_type);
8204 noises=DestroyStringList(noises);
8205 if (*noise_type == '\0')
8206 break;
8207 XSetCursorState(display,windows,MagickTrue);
8208 XCheckRefreshWindows(display,windows);
cristy042ee782011-04-22 18:48:30 +00008209 noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
cristy9ed1f812011-10-08 02:00:08 +00008210 MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
cristy3ed852e2009-09-05 21:47:34 +00008211 if (noise_image != (Image *) NULL)
8212 {
8213 *image=DestroyImage(*image);
8214 *image=noise_image;
8215 }
cristy051718b2011-08-28 22:49:25 +00008216 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008217 XSetCursorState(display,windows,MagickFalse);
8218 if (windows->image.orphan != MagickFalse)
8219 break;
cristy6710d842011-10-20 23:23:00 +00008220 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008221 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008222 break;
8223 }
8224 case SharpenCommand:
8225 {
8226 Image
8227 *sharp_image;
8228
8229 static char
8230 radius[MaxTextExtent] = "0.0x1.0";
8231
8232 /*
8233 Query user for sharpen radius.
8234 */
8235 (void) XDialogWidget(display,windows,"Sharpen",
8236 "Enter the sharpen radius and standard deviation:",radius);
8237 if (*radius == '\0')
8238 break;
8239 /*
8240 Sharpen image scanlines.
8241 */
8242 XSetCursorState(display,windows,MagickTrue);
8243 XCheckRefreshWindows(display,windows);
8244 flags=ParseGeometry(radius,&geometry_info);
8245 sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
cristy05c0c9a2011-09-05 23:16:13 +00008246 geometry_info.xi,exception);
cristy3ed852e2009-09-05 21:47:34 +00008247 if (sharp_image != (Image *) NULL)
8248 {
8249 *image=DestroyImage(*image);
8250 *image=sharp_image;
8251 }
cristy051718b2011-08-28 22:49:25 +00008252 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008253 XSetCursorState(display,windows,MagickFalse);
8254 if (windows->image.orphan != MagickFalse)
8255 break;
cristy6710d842011-10-20 23:23:00 +00008256 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008257 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008258 break;
8259 }
8260 case BlurCommand:
8261 {
8262 Image
8263 *blur_image;
8264
8265 static char
8266 radius[MaxTextExtent] = "0.0x1.0";
8267
8268 /*
8269 Query user for blur radius.
8270 */
8271 (void) XDialogWidget(display,windows,"Blur",
8272 "Enter the blur radius and standard deviation:",radius);
8273 if (*radius == '\0')
8274 break;
8275 /*
8276 Blur an image.
8277 */
8278 XSetCursorState(display,windows,MagickTrue);
8279 XCheckRefreshWindows(display,windows);
8280 flags=ParseGeometry(radius,&geometry_info);
8281 blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
cristy05c0c9a2011-09-05 23:16:13 +00008282 geometry_info.xi,exception);
cristy3ed852e2009-09-05 21:47:34 +00008283 if (blur_image != (Image *) NULL)
8284 {
8285 *image=DestroyImage(*image);
8286 *image=blur_image;
8287 }
cristy051718b2011-08-28 22:49:25 +00008288 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008289 XSetCursorState(display,windows,MagickFalse);
8290 if (windows->image.orphan != MagickFalse)
8291 break;
cristy6710d842011-10-20 23:23:00 +00008292 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008293 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008294 break;
8295 }
8296 case ThresholdCommand:
8297 {
8298 double
8299 threshold;
8300
8301 static char
8302 factor[MaxTextExtent] = "128";
8303
8304 /*
8305 Query user for threshold value.
8306 */
8307 (void) XDialogWidget(display,windows,"Threshold",
8308 "Enter threshold value:",factor);
8309 if (*factor == '\0')
8310 break;
8311 /*
8312 Gamma correct image.
8313 */
8314 XSetCursorState(display,windows,MagickTrue);
8315 XCheckRefreshWindows(display,windows);
cristy9b34e302011-11-05 02:15:45 +00008316 threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
cristye941a752011-10-15 01:52:48 +00008317 (void) BilevelImage(*image,threshold,exception);
cristy3ed852e2009-09-05 21:47:34 +00008318 XSetCursorState(display,windows,MagickFalse);
8319 if (windows->image.orphan != MagickFalse)
8320 break;
cristy6710d842011-10-20 23:23:00 +00008321 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008322 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008323 break;
8324 }
8325 case EdgeDetectCommand:
8326 {
8327 Image
8328 *edge_image;
8329
8330 static char
8331 radius[MaxTextExtent] = "0";
8332
8333 /*
8334 Query user for edge factor.
8335 */
8336 (void) XDialogWidget(display,windows,"Detect Edges",
8337 "Enter the edge detect radius:",radius);
8338 if (*radius == '\0')
8339 break;
8340 /*
8341 Detect edge in image.
8342 */
8343 XSetCursorState(display,windows,MagickTrue);
8344 XCheckRefreshWindows(display,windows);
8345 flags=ParseGeometry(radius,&geometry_info);
cristy8ae632d2011-09-05 17:29:53 +00008346 edge_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8347 exception);
cristy3ed852e2009-09-05 21:47:34 +00008348 if (edge_image != (Image *) NULL)
8349 {
8350 *image=DestroyImage(*image);
8351 *image=edge_image;
8352 }
cristy051718b2011-08-28 22:49:25 +00008353 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008354 XSetCursorState(display,windows,MagickFalse);
8355 if (windows->image.orphan != MagickFalse)
8356 break;
cristy6710d842011-10-20 23:23:00 +00008357 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008358 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008359 break;
8360 }
8361 case SpreadCommand:
8362 {
8363 Image
8364 *spread_image;
8365
8366 static char
8367 amount[MaxTextExtent] = "2";
8368
8369 /*
8370 Query user for spread amount.
8371 */
8372 (void) XDialogWidget(display,windows,"Spread",
8373 "Enter the displacement amount:",amount);
8374 if (*amount == '\0')
8375 break;
8376 /*
8377 Displace image pixels by a random amount.
8378 */
8379 XSetCursorState(display,windows,MagickTrue);
8380 XCheckRefreshWindows(display,windows);
8381 flags=ParseGeometry(amount,&geometry_info);
cristy8ae632d2011-09-05 17:29:53 +00008382 spread_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8383 exception);
cristy3ed852e2009-09-05 21:47:34 +00008384 if (spread_image != (Image *) NULL)
8385 {
8386 *image=DestroyImage(*image);
8387 *image=spread_image;
8388 }
cristy051718b2011-08-28 22:49:25 +00008389 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008390 XSetCursorState(display,windows,MagickFalse);
8391 if (windows->image.orphan != MagickFalse)
8392 break;
cristy6710d842011-10-20 23:23:00 +00008393 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008394 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008395 break;
8396 }
8397 case ShadeCommand:
8398 {
8399 Image
8400 *shade_image;
8401
8402 int
8403 status;
8404
8405 static char
8406 geometry[MaxTextExtent] = "30x30";
8407
8408 /*
8409 Query user for the shade geometry.
8410 */
8411 status=XDialogWidget(display,windows,"Shade",
8412 "Enter the azimuth and elevation of the light source:",geometry);
8413 if (*geometry == '\0')
8414 break;
8415 /*
8416 Shade image pixels.
8417 */
8418 XSetCursorState(display,windows,MagickTrue);
8419 XCheckRefreshWindows(display,windows);
8420 flags=ParseGeometry(geometry,&geometry_info);
8421 if ((flags & SigmaValue) == 0)
8422 geometry_info.sigma=1.0;
8423 shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
cristy051718b2011-08-28 22:49:25 +00008424 geometry_info.rho,geometry_info.sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +00008425 if (shade_image != (Image *) NULL)
8426 {
8427 *image=DestroyImage(*image);
8428 *image=shade_image;
8429 }
cristy051718b2011-08-28 22:49:25 +00008430 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008431 XSetCursorState(display,windows,MagickFalse);
8432 if (windows->image.orphan != MagickFalse)
8433 break;
cristy6710d842011-10-20 23:23:00 +00008434 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008435 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008436 break;
8437 }
8438 case RaiseCommand:
8439 {
8440 static char
8441 bevel_width[MaxTextExtent] = "10";
8442
8443 /*
8444 Query user for bevel width.
8445 */
8446 (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8447 if (*bevel_width == '\0')
8448 break;
8449 /*
8450 Raise an image.
8451 */
cristy051718b2011-08-28 22:49:25 +00008452 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8453 exception);
cristy3ed852e2009-09-05 21:47:34 +00008454 XSetCursorState(display,windows,MagickTrue);
8455 XCheckRefreshWindows(display,windows);
8456 (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
cristy051718b2011-08-28 22:49:25 +00008457 exception);
8458 (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00008459 XSetCursorState(display,windows,MagickFalse);
8460 if (windows->image.orphan != MagickFalse)
8461 break;
cristy6710d842011-10-20 23:23:00 +00008462 XConfigureImageColormap(display,resource_info,windows,*image,exception);
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 SegmentCommand:
8467 {
8468 static char
8469 threshold[MaxTextExtent] = "1.0x1.5";
8470
8471 /*
8472 Query user for smoothing threshold.
8473 */
8474 (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8475 threshold);
8476 if (*threshold == '\0')
8477 break;
8478 /*
8479 Segment an image.
8480 */
8481 XSetCursorState(display,windows,MagickTrue);
8482 XCheckRefreshWindows(display,windows);
8483 flags=ParseGeometry(threshold,&geometry_info);
8484 if ((flags & SigmaValue) == 0)
8485 geometry_info.sigma=1.0;
8486 (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
cristy018f07f2011-09-04 21:15:19 +00008487 geometry_info.sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +00008488 XSetCursorState(display,windows,MagickFalse);
8489 if (windows->image.orphan != MagickFalse)
8490 break;
cristy6710d842011-10-20 23:23:00 +00008491 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008492 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008493 break;
8494 }
8495 case SepiaToneCommand:
8496 {
8497 double
8498 threshold;
8499
8500 Image
8501 *sepia_image;
8502
8503 static char
8504 factor[MaxTextExtent] = "80%";
8505
8506 /*
8507 Query user for sepia-tone factor.
8508 */
8509 (void) XDialogWidget(display,windows,"Sepia Tone",
8510 "Enter the sepia tone factor (0 - 99.9%):",factor);
8511 if (*factor == '\0')
8512 break;
8513 /*
8514 Sepia tone image pixels.
8515 */
8516 XSetCursorState(display,windows,MagickTrue);
8517 XCheckRefreshWindows(display,windows);
cristy9b34e302011-11-05 02:15:45 +00008518 threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
cristy051718b2011-08-28 22:49:25 +00008519 sepia_image=SepiaToneImage(*image,threshold,exception);
cristy3ed852e2009-09-05 21:47:34 +00008520 if (sepia_image != (Image *) NULL)
8521 {
8522 *image=DestroyImage(*image);
8523 *image=sepia_image;
8524 }
cristy051718b2011-08-28 22:49:25 +00008525 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008526 XSetCursorState(display,windows,MagickFalse);
8527 if (windows->image.orphan != MagickFalse)
8528 break;
cristy6710d842011-10-20 23:23:00 +00008529 XConfigureImageColormap(display,resource_info,windows,*image,exception);
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 SolarizeCommand:
8534 {
8535 double
8536 threshold;
8537
8538 static char
8539 factor[MaxTextExtent] = "60%";
8540
8541 /*
8542 Query user for solarize factor.
8543 */
8544 (void) XDialogWidget(display,windows,"Solarize",
8545 "Enter the solarize factor (0 - 99.9%):",factor);
8546 if (*factor == '\0')
8547 break;
8548 /*
8549 Solarize image pixels.
8550 */
8551 XSetCursorState(display,windows,MagickTrue);
8552 XCheckRefreshWindows(display,windows);
cristy9b34e302011-11-05 02:15:45 +00008553 threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
cristy5cbc0162011-08-29 00:36:28 +00008554 (void) SolarizeImage(*image,threshold,exception);
cristy3ed852e2009-09-05 21:47:34 +00008555 XSetCursorState(display,windows,MagickFalse);
8556 if (windows->image.orphan != MagickFalse)
8557 break;
cristy6710d842011-10-20 23:23:00 +00008558 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008559 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008560 break;
8561 }
8562 case SwirlCommand:
8563 {
8564 Image
8565 *swirl_image;
8566
8567 static char
8568 degrees[MaxTextExtent] = "60";
8569
8570 /*
8571 Query user for swirl angle.
8572 */
8573 (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8574 degrees);
8575 if (*degrees == '\0')
8576 break;
8577 /*
8578 Swirl image pixels about the center.
8579 */
8580 XSetCursorState(display,windows,MagickTrue);
8581 XCheckRefreshWindows(display,windows);
8582 flags=ParseGeometry(degrees,&geometry_info);
cristy76f512e2011-09-12 01:26:56 +00008583 swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8584 exception);
cristy3ed852e2009-09-05 21:47:34 +00008585 if (swirl_image != (Image *) NULL)
8586 {
8587 *image=DestroyImage(*image);
8588 *image=swirl_image;
8589 }
cristy051718b2011-08-28 22:49:25 +00008590 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008591 XSetCursorState(display,windows,MagickFalse);
8592 if (windows->image.orphan != MagickFalse)
8593 break;
cristy6710d842011-10-20 23:23:00 +00008594 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008595 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008596 break;
8597 }
8598 case ImplodeCommand:
8599 {
8600 Image
8601 *implode_image;
8602
8603 static char
8604 factor[MaxTextExtent] = "0.3";
8605
8606 /*
8607 Query user for implode factor.
8608 */
8609 (void) XDialogWidget(display,windows,"Implode",
8610 "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8611 if (*factor == '\0')
8612 break;
8613 /*
8614 Implode image pixels about the center.
8615 */
8616 XSetCursorState(display,windows,MagickTrue);
8617 XCheckRefreshWindows(display,windows);
8618 flags=ParseGeometry(factor,&geometry_info);
cristy76f512e2011-09-12 01:26:56 +00008619 implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8620 exception);
cristy3ed852e2009-09-05 21:47:34 +00008621 if (implode_image != (Image *) NULL)
8622 {
8623 *image=DestroyImage(*image);
8624 *image=implode_image;
8625 }
cristy051718b2011-08-28 22:49:25 +00008626 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008627 XSetCursorState(display,windows,MagickFalse);
8628 if (windows->image.orphan != MagickFalse)
8629 break;
cristy6710d842011-10-20 23:23:00 +00008630 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008631 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008632 break;
8633 }
8634 case VignetteCommand:
8635 {
8636 Image
8637 *vignette_image;
8638
8639 static char
8640 geometry[MaxTextExtent] = "0x20";
8641
8642 /*
8643 Query user for the vignette geometry.
8644 */
8645 (void) XDialogWidget(display,windows,"Vignette",
8646 "Enter the radius, sigma, and x and y offsets:",geometry);
8647 if (*geometry == '\0')
8648 break;
8649 /*
8650 Soften the edges of the image in vignette style
8651 */
8652 XSetCursorState(display,windows,MagickTrue);
8653 XCheckRefreshWindows(display,windows);
8654 flags=ParseGeometry(geometry,&geometry_info);
8655 if ((flags & SigmaValue) == 0)
8656 geometry_info.sigma=1.0;
8657 if ((flags & XiValue) == 0)
8658 geometry_info.xi=0.1*(*image)->columns;
8659 if ((flags & PsiValue) == 0)
8660 geometry_info.psi=0.1*(*image)->rows;
8661 vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
cristy78ec1a92011-12-09 09:25:34 +00008662 0.0,(ssize_t) ceil(geometry_info.xi-0.5),(ssize_t)
8663 ceil(geometry_info.psi-0.5),exception);
cristy3ed852e2009-09-05 21:47:34 +00008664 if (vignette_image != (Image *) NULL)
8665 {
8666 *image=DestroyImage(*image);
8667 *image=vignette_image;
8668 }
cristy051718b2011-08-28 22:49:25 +00008669 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008670 XSetCursorState(display,windows,MagickFalse);
8671 if (windows->image.orphan != MagickFalse)
8672 break;
cristy6710d842011-10-20 23:23:00 +00008673 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008674 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008675 break;
8676 }
8677 case WaveCommand:
8678 {
8679 Image
8680 *wave_image;
8681
8682 static char
8683 geometry[MaxTextExtent] = "25x150";
8684
8685 /*
8686 Query user for the wave geometry.
8687 */
8688 (void) XDialogWidget(display,windows,"Wave",
8689 "Enter the amplitude and length of the wave:",geometry);
8690 if (*geometry == '\0')
8691 break;
8692 /*
cristycee97112010-05-28 00:44:52 +00008693 Alter an image along a sine wave.
cristy3ed852e2009-09-05 21:47:34 +00008694 */
8695 XSetCursorState(display,windows,MagickTrue);
8696 XCheckRefreshWindows(display,windows);
8697 flags=ParseGeometry(geometry,&geometry_info);
8698 if ((flags & SigmaValue) == 0)
8699 geometry_info.sigma=1.0;
8700 wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
cristy5c4e2582011-09-11 19:21:03 +00008701 (*image)->interpolate,exception);
cristy3ed852e2009-09-05 21:47:34 +00008702 if (wave_image != (Image *) NULL)
8703 {
8704 *image=DestroyImage(*image);
8705 *image=wave_image;
8706 }
cristy051718b2011-08-28 22:49:25 +00008707 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008708 XSetCursorState(display,windows,MagickFalse);
8709 if (windows->image.orphan != MagickFalse)
8710 break;
cristy6710d842011-10-20 23:23:00 +00008711 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008712 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008713 break;
8714 }
8715 case OilPaintCommand:
8716 {
8717 Image
8718 *paint_image;
8719
8720 static char
8721 radius[MaxTextExtent] = "0";
8722
8723 /*
8724 Query user for circular neighborhood radius.
8725 */
8726 (void) XDialogWidget(display,windows,"Oil Paint",
8727 "Enter the mask radius:",radius);
8728 if (*radius == '\0')
8729 break;
8730 /*
8731 OilPaint image scanlines.
8732 */
8733 XSetCursorState(display,windows,MagickTrue);
8734 XCheckRefreshWindows(display,windows);
8735 flags=ParseGeometry(radius,&geometry_info);
cristy14973ba2011-08-27 23:48:07 +00008736 paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
cristy051718b2011-08-28 22:49:25 +00008737 exception);
cristy3ed852e2009-09-05 21:47:34 +00008738 if (paint_image != (Image *) NULL)
8739 {
8740 *image=DestroyImage(*image);
8741 *image=paint_image;
8742 }
cristy051718b2011-08-28 22:49:25 +00008743 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008744 XSetCursorState(display,windows,MagickFalse);
8745 if (windows->image.orphan != MagickFalse)
8746 break;
cristy6710d842011-10-20 23:23:00 +00008747 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008748 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008749 break;
8750 }
8751 case CharcoalDrawCommand:
8752 {
8753 Image
8754 *charcoal_image;
8755
8756 static char
8757 radius[MaxTextExtent] = "0x1";
8758
8759 /*
8760 Query user for charcoal radius.
8761 */
8762 (void) XDialogWidget(display,windows,"Charcoal Draw",
8763 "Enter the charcoal radius and sigma:",radius);
8764 if (*radius == '\0')
8765 break;
8766 /*
8767 Charcoal the image.
8768 */
cristy051718b2011-08-28 22:49:25 +00008769 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8770 exception);
cristy3ed852e2009-09-05 21:47:34 +00008771 XSetCursorState(display,windows,MagickTrue);
8772 XCheckRefreshWindows(display,windows);
8773 flags=ParseGeometry(radius,&geometry_info);
8774 if ((flags & SigmaValue) == 0)
8775 geometry_info.sigma=geometry_info.rho;
8776 charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
cristy05c0c9a2011-09-05 23:16:13 +00008777 geometry_info.xi,exception);
cristy3ed852e2009-09-05 21:47:34 +00008778 if (charcoal_image != (Image *) NULL)
8779 {
8780 *image=DestroyImage(*image);
8781 *image=charcoal_image;
8782 }
cristy051718b2011-08-28 22:49:25 +00008783 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008784 XSetCursorState(display,windows,MagickFalse);
8785 if (windows->image.orphan != MagickFalse)
8786 break;
cristy6710d842011-10-20 23:23:00 +00008787 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008788 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008789 break;
8790 }
8791 case AnnotateCommand:
8792 {
8793 /*
8794 Annotate the image with text.
8795 */
cristy051718b2011-08-28 22:49:25 +00008796 status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008797 if (status == MagickFalse)
8798 {
8799 XNoticeWidget(display,windows,"Unable to annotate X image",
8800 (*image)->filename);
8801 break;
8802 }
8803 break;
8804 }
8805 case DrawCommand:
8806 {
8807 /*
8808 Draw image.
8809 */
cristy051718b2011-08-28 22:49:25 +00008810 status=XDrawEditImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008811 if (status == MagickFalse)
8812 {
8813 XNoticeWidget(display,windows,"Unable to draw on the X image",
8814 (*image)->filename);
8815 break;
8816 }
8817 break;
8818 }
8819 case ColorCommand:
8820 {
8821 /*
8822 Color edit.
8823 */
cristy051718b2011-08-28 22:49:25 +00008824 status=XColorEditImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008825 if (status == MagickFalse)
8826 {
8827 XNoticeWidget(display,windows,"Unable to pixel edit X image",
8828 (*image)->filename);
8829 break;
8830 }
8831 break;
8832 }
8833 case MatteCommand:
8834 {
8835 /*
8836 Matte edit.
8837 */
cristy051718b2011-08-28 22:49:25 +00008838 status=XMatteEditImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008839 if (status == MagickFalse)
8840 {
8841 XNoticeWidget(display,windows,"Unable to matte edit X image",
8842 (*image)->filename);
8843 break;
8844 }
8845 break;
8846 }
8847 case CompositeCommand:
8848 {
8849 /*
8850 Composite image.
8851 */
cristy051718b2011-08-28 22:49:25 +00008852 status=XCompositeImage(display,resource_info,windows,*image,
8853 exception);
cristy3ed852e2009-09-05 21:47:34 +00008854 if (status == MagickFalse)
8855 {
8856 XNoticeWidget(display,windows,"Unable to composite X image",
8857 (*image)->filename);
8858 break;
8859 }
8860 break;
8861 }
8862 case AddBorderCommand:
8863 {
8864 Image
8865 *border_image;
8866
8867 static char
8868 geometry[MaxTextExtent] = "6x6";
8869
8870 /*
8871 Query user for border color and geometry.
8872 */
8873 XColorBrowserWidget(display,windows,"Select",color);
8874 if (*color == '\0')
8875 break;
8876 (void) XDialogWidget(display,windows,"Add Border",
8877 "Enter border geometry:",geometry);
8878 if (*geometry == '\0')
8879 break;
8880 /*
8881 Add a border to the image.
8882 */
cristy051718b2011-08-28 22:49:25 +00008883 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8884 exception);
cristy3ed852e2009-09-05 21:47:34 +00008885 XSetCursorState(display,windows,MagickTrue);
8886 XCheckRefreshWindows(display,windows);
cristy9950d572011-10-01 18:22:35 +00008887 (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
cristy051718b2011-08-28 22:49:25 +00008888 exception);
cristy3ed852e2009-09-05 21:47:34 +00008889 (void) ParsePageGeometry(*image,geometry,&page_geometry,
cristy051718b2011-08-28 22:49:25 +00008890 exception);
cristy633f0c62011-09-15 13:27:36 +00008891 border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8892 exception);
cristy3ed852e2009-09-05 21:47:34 +00008893 if (border_image != (Image *) NULL)
8894 {
8895 *image=DestroyImage(*image);
8896 *image=border_image;
8897 }
cristy051718b2011-08-28 22:49:25 +00008898 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008899 XSetCursorState(display,windows,MagickFalse);
8900 if (windows->image.orphan != MagickFalse)
8901 break;
8902 windows->image.window_changes.width=(int) (*image)->columns;
8903 windows->image.window_changes.height=(int) (*image)->rows;
cristy6710d842011-10-20 23:23:00 +00008904 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008905 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008906 break;
8907 }
8908 case AddFrameCommand:
8909 {
8910 FrameInfo
8911 frame_info;
8912
8913 Image
8914 *frame_image;
8915
8916 static char
8917 geometry[MaxTextExtent] = "6x6";
8918
8919 /*
8920 Query user for frame color and geometry.
8921 */
8922 XColorBrowserWidget(display,windows,"Select",color);
8923 if (*color == '\0')
8924 break;
8925 (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8926 geometry);
8927 if (*geometry == '\0')
8928 break;
8929 /*
8930 Surround image with an ornamental border.
8931 */
cristy051718b2011-08-28 22:49:25 +00008932 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8933 exception);
cristy3ed852e2009-09-05 21:47:34 +00008934 XSetCursorState(display,windows,MagickTrue);
8935 XCheckRefreshWindows(display,windows);
cristy9950d572011-10-01 18:22:35 +00008936 (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
cristy051718b2011-08-28 22:49:25 +00008937 exception);
cristy3ed852e2009-09-05 21:47:34 +00008938 (void) ParsePageGeometry(*image,geometry,&page_geometry,
cristy051718b2011-08-28 22:49:25 +00008939 exception);
cristy3ed852e2009-09-05 21:47:34 +00008940 frame_info.width=page_geometry.width;
8941 frame_info.height=page_geometry.height;
8942 frame_info.outer_bevel=page_geometry.x;
8943 frame_info.inner_bevel=page_geometry.y;
cristybb503372010-05-27 20:51:26 +00008944 frame_info.x=(ssize_t) frame_info.width;
8945 frame_info.y=(ssize_t) frame_info.height;
cristy3ed852e2009-09-05 21:47:34 +00008946 frame_info.width=(*image)->columns+2*frame_info.width;
8947 frame_info.height=(*image)->rows+2*frame_info.height;
cristy633f0c62011-09-15 13:27:36 +00008948 frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
cristy3ed852e2009-09-05 21:47:34 +00008949 if (frame_image != (Image *) NULL)
8950 {
8951 *image=DestroyImage(*image);
8952 *image=frame_image;
8953 }
cristy051718b2011-08-28 22:49:25 +00008954 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008955 XSetCursorState(display,windows,MagickFalse);
8956 if (windows->image.orphan != MagickFalse)
8957 break;
8958 windows->image.window_changes.width=(int) (*image)->columns;
8959 windows->image.window_changes.height=(int) (*image)->rows;
cristy6710d842011-10-20 23:23:00 +00008960 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008961 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008962 break;
8963 }
8964 case CommentCommand:
8965 {
8966 const char
8967 *value;
8968
8969 FILE
8970 *file;
8971
8972 int
8973 unique_file;
8974
8975 /*
8976 Edit image comment.
8977 */
8978 unique_file=AcquireUniqueFileResource(image_info->filename);
8979 if (unique_file == -1)
8980 XNoticeWidget(display,windows,"Unable to edit image comment",
8981 image_info->filename);
cristyd15e6592011-10-15 00:13:06 +00008982 value=GetImageProperty(*image,"comment",exception);
cristy3ed852e2009-09-05 21:47:34 +00008983 if (value == (char *) NULL)
8984 unique_file=close(unique_file)-1;
8985 else
8986 {
8987 register const char
8988 *p;
8989
8990 file=fdopen(unique_file,"w");
8991 if (file == (FILE *) NULL)
8992 {
8993 XNoticeWidget(display,windows,"Unable to edit image comment",
8994 image_info->filename);
8995 break;
8996 }
8997 for (p=value; *p != '\0'; p++)
8998 (void) fputc((int) *p,file);
8999 (void) fputc('\n',file);
9000 (void) fclose(file);
9001 }
9002 XSetCursorState(display,windows,MagickTrue);
9003 XCheckRefreshWindows(display,windows);
9004 status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
cristy051718b2011-08-28 22:49:25 +00009005 exception);
cristy3ed852e2009-09-05 21:47:34 +00009006 if (status == MagickFalse)
9007 XNoticeWidget(display,windows,"Unable to edit image comment",
9008 (char *) NULL);
9009 else
9010 {
9011 char
9012 *comment;
9013
cristy051718b2011-08-28 22:49:25 +00009014 comment=FileToString(image_info->filename,~0UL,exception);
cristy3ed852e2009-09-05 21:47:34 +00009015 if (comment != (char *) NULL)
9016 {
cristyd15e6592011-10-15 00:13:06 +00009017 (void) SetImageProperty(*image,"comment",comment,exception);
cristy3ed852e2009-09-05 21:47:34 +00009018 (*image)->taint=MagickTrue;
9019 }
9020 }
9021 (void) RelinquishUniqueFileResource(image_info->filename);
9022 XSetCursorState(display,windows,MagickFalse);
9023 break;
9024 }
9025 case LaunchCommand:
9026 {
9027 /*
9028 Launch program.
9029 */
9030 XSetCursorState(display,windows,MagickTrue);
9031 XCheckRefreshWindows(display,windows);
9032 (void) AcquireUniqueFilename(filename);
cristyb51dff52011-05-19 16:55:47 +00009033 (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
cristy3ed852e2009-09-05 21:47:34 +00009034 filename);
cristy051718b2011-08-28 22:49:25 +00009035 status=WriteImage(image_info,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009036 if (status == MagickFalse)
9037 XNoticeWidget(display,windows,"Unable to launch image editor",
9038 (char *) NULL);
9039 else
9040 {
cristy051718b2011-08-28 22:49:25 +00009041 nexus=ReadImage(resource_info->image_info,exception);
9042 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00009043 XClientMessage(display,windows->image.id,windows->im_protocols,
9044 windows->im_next_image,CurrentTime);
9045 }
9046 (void) RelinquishUniqueFileResource(filename);
9047 XSetCursorState(display,windows,MagickFalse);
9048 break;
9049 }
9050 case RegionofInterestCommand:
9051 {
9052 /*
9053 Apply an image processing technique to a region of interest.
9054 */
cristy051718b2011-08-28 22:49:25 +00009055 (void) XROIImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009056 break;
9057 }
9058 case InfoCommand:
9059 break;
9060 case ZoomCommand:
9061 {
9062 /*
9063 Zoom image.
9064 */
9065 if (windows->magnify.mapped != MagickFalse)
9066 (void) XRaiseWindow(display,windows->magnify.id);
9067 else
9068 {
9069 /*
9070 Make magnify image.
9071 */
9072 XSetCursorState(display,windows,MagickTrue);
9073 (void) XMapRaised(display,windows->magnify.id);
9074 XSetCursorState(display,windows,MagickFalse);
9075 }
9076 break;
9077 }
9078 case ShowPreviewCommand:
9079 {
9080 char
9081 **previews;
9082
9083 Image
9084 *preview_image;
9085
9086 static char
9087 preview_type[MaxTextExtent] = "Gamma";
9088
9089 /*
9090 Select preview type from menu.
9091 */
cristy042ee782011-04-22 18:48:30 +00009092 previews=GetCommandOptions(MagickPreviewOptions);
cristy3ed852e2009-09-05 21:47:34 +00009093 if (previews == (char **) NULL)
9094 break;
9095 XListBrowserWidget(display,windows,&windows->widget,
9096 (const char **) previews,"Preview",
9097 "Select an enhancement, effect, or F/X:",preview_type);
9098 previews=DestroyStringList(previews);
9099 if (*preview_type == '\0')
9100 break;
9101 /*
9102 Show image preview.
9103 */
9104 XSetCursorState(display,windows,MagickTrue);
9105 XCheckRefreshWindows(display,windows);
9106 image_info->preview_type=(PreviewType)
cristy042ee782011-04-22 18:48:30 +00009107 ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
cristybb503372010-05-27 20:51:26 +00009108 image_info->group=(ssize_t) windows->image.id;
cristy3ed852e2009-09-05 21:47:34 +00009109 (void) DeleteImageProperty(*image,"label");
cristyd15e6592011-10-15 00:13:06 +00009110 (void) SetImageProperty(*image,"label","Preview",exception);
cristy3ed852e2009-09-05 21:47:34 +00009111 (void) AcquireUniqueFilename(filename);
cristyb51dff52011-05-19 16:55:47 +00009112 (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
cristy3ed852e2009-09-05 21:47:34 +00009113 filename);
cristy051718b2011-08-28 22:49:25 +00009114 status=WriteImage(image_info,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009115 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +00009116 preview_image=ReadImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00009117 (void) RelinquishUniqueFileResource(filename);
9118 if (preview_image == (Image *) NULL)
9119 break;
cristyb51dff52011-05-19 16:55:47 +00009120 (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
cristy3ed852e2009-09-05 21:47:34 +00009121 filename);
cristy051718b2011-08-28 22:49:25 +00009122 status=WriteImage(image_info,preview_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009123 preview_image=DestroyImage(preview_image);
9124 if (status == MagickFalse)
9125 XNoticeWidget(display,windows,"Unable to show image preview",
9126 (*image)->filename);
9127 XDelay(display,1500);
9128 XSetCursorState(display,windows,MagickFalse);
9129 break;
9130 }
9131 case ShowHistogramCommand:
9132 {
9133 Image
9134 *histogram_image;
9135
9136 /*
9137 Show image histogram.
9138 */
9139 XSetCursorState(display,windows,MagickTrue);
9140 XCheckRefreshWindows(display,windows);
cristybb503372010-05-27 20:51:26 +00009141 image_info->group=(ssize_t) windows->image.id;
cristy3ed852e2009-09-05 21:47:34 +00009142 (void) DeleteImageProperty(*image,"label");
cristyd15e6592011-10-15 00:13:06 +00009143 (void) SetImageProperty(*image,"label","Histogram",exception);
cristy3ed852e2009-09-05 21:47:34 +00009144 (void) AcquireUniqueFilename(filename);
cristyb51dff52011-05-19 16:55:47 +00009145 (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
cristy3ed852e2009-09-05 21:47:34 +00009146 filename);
cristy051718b2011-08-28 22:49:25 +00009147 status=WriteImage(image_info,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009148 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +00009149 histogram_image=ReadImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00009150 (void) RelinquishUniqueFileResource(filename);
9151 if (histogram_image == (Image *) NULL)
9152 break;
cristyb51dff52011-05-19 16:55:47 +00009153 (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00009154 "show:%s",filename);
cristy051718b2011-08-28 22:49:25 +00009155 status=WriteImage(image_info,histogram_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009156 histogram_image=DestroyImage(histogram_image);
9157 if (status == MagickFalse)
9158 XNoticeWidget(display,windows,"Unable to show histogram",
9159 (*image)->filename);
9160 XDelay(display,1500);
9161 XSetCursorState(display,windows,MagickFalse);
9162 break;
9163 }
9164 case ShowMatteCommand:
9165 {
9166 Image
9167 *matte_image;
9168
9169 if ((*image)->matte == MagickFalse)
9170 {
9171 XNoticeWidget(display,windows,
9172 "Image does not have any matte information",(*image)->filename);
9173 break;
9174 }
9175 /*
9176 Show image matte.
9177 */
9178 XSetCursorState(display,windows,MagickTrue);
9179 XCheckRefreshWindows(display,windows);
cristybb503372010-05-27 20:51:26 +00009180 image_info->group=(ssize_t) windows->image.id;
cristy3ed852e2009-09-05 21:47:34 +00009181 (void) DeleteImageProperty(*image,"label");
cristyd15e6592011-10-15 00:13:06 +00009182 (void) SetImageProperty(*image,"label","Matte",exception);
cristy3ed852e2009-09-05 21:47:34 +00009183 (void) AcquireUniqueFilename(filename);
cristyb51dff52011-05-19 16:55:47 +00009184 (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
cristy3ed852e2009-09-05 21:47:34 +00009185 filename);
cristy051718b2011-08-28 22:49:25 +00009186 status=WriteImage(image_info,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009187 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +00009188 matte_image=ReadImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00009189 (void) RelinquishUniqueFileResource(filename);
9190 if (matte_image == (Image *) NULL)
9191 break;
cristyb51dff52011-05-19 16:55:47 +00009192 (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
cristy3ed852e2009-09-05 21:47:34 +00009193 filename);
cristy051718b2011-08-28 22:49:25 +00009194 status=WriteImage(image_info,matte_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009195 matte_image=DestroyImage(matte_image);
9196 if (status == MagickFalse)
9197 XNoticeWidget(display,windows,"Unable to show matte",
9198 (*image)->filename);
9199 XDelay(display,1500);
9200 XSetCursorState(display,windows,MagickFalse);
9201 break;
9202 }
9203 case BackgroundCommand:
9204 {
9205 /*
9206 Background image.
9207 */
cristy051718b2011-08-28 22:49:25 +00009208 status=XBackgroundImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009209 if (status == MagickFalse)
9210 break;
cristy051718b2011-08-28 22:49:25 +00009211 nexus=CloneImage(*image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00009212 if (nexus != (Image *) NULL)
9213 XClientMessage(display,windows->image.id,windows->im_protocols,
9214 windows->im_next_image,CurrentTime);
9215 break;
9216 }
9217 case SlideShowCommand:
9218 {
9219 static char
9220 delay[MaxTextExtent] = "5";
9221
9222 /*
9223 Display next image after pausing.
9224 */
9225 (void) XDialogWidget(display,windows,"Slide Show",
9226 "Pause how many 1/100ths of a second between images:",delay);
9227 if (*delay == '\0')
9228 break;
cristye27293e2009-12-18 02:53:20 +00009229 resource_info->delay=StringToUnsignedLong(delay);
cristy3ed852e2009-09-05 21:47:34 +00009230 XClientMessage(display,windows->image.id,windows->im_protocols,
9231 windows->im_next_image,CurrentTime);
9232 break;
9233 }
9234 case PreferencesCommand:
9235 {
9236 /*
9237 Set user preferences.
9238 */
9239 status=XPreferencesWidget(display,resource_info,windows);
9240 if (status == MagickFalse)
9241 break;
cristy051718b2011-08-28 22:49:25 +00009242 nexus=CloneImage(*image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00009243 if (nexus != (Image *) NULL)
9244 XClientMessage(display,windows->image.id,windows->im_protocols,
9245 windows->im_next_image,CurrentTime);
9246 break;
9247 }
9248 case HelpCommand:
9249 {
9250 /*
9251 User requested help.
9252 */
9253 XTextViewWidget(display,resource_info,windows,MagickFalse,
9254 "Help Viewer - Display",DisplayHelp);
9255 break;
9256 }
9257 case BrowseDocumentationCommand:
9258 {
9259 Atom
9260 mozilla_atom;
9261
9262 Window
9263 mozilla_window,
9264 root_window;
9265
9266 /*
9267 Browse the ImageMagick documentation.
9268 */
9269 root_window=XRootWindow(display,XDefaultScreen(display));
9270 mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9271 mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9272 if (mozilla_window != (Window) NULL)
9273 {
9274 char
9275 command[MaxTextExtent],
9276 *url;
9277
9278 /*
9279 Display documentation using Netscape remote control.
9280 */
9281 url=GetMagickHomeURL();
cristyb51dff52011-05-19 16:55:47 +00009282 (void) FormatLocaleString(command,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00009283 "openurl(%s,new-tab)",url);
9284 url=DestroyString(url);
9285 mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9286 (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9287 8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9288 XSetCursorState(display,windows,MagickFalse);
9289 break;
9290 }
9291 XSetCursorState(display,windows,MagickTrue);
9292 XCheckRefreshWindows(display,windows);
9293 status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
cristy051718b2011-08-28 22:49:25 +00009294 exception);
cristy3ed852e2009-09-05 21:47:34 +00009295 if (status == MagickFalse)
9296 XNoticeWidget(display,windows,"Unable to browse documentation",
9297 (char *) NULL);
9298 XDelay(display,1500);
9299 XSetCursorState(display,windows,MagickFalse);
9300 break;
9301 }
9302 case VersionCommand:
9303 {
cristybb503372010-05-27 20:51:26 +00009304 XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
cristy3ed852e2009-09-05 21:47:34 +00009305 GetMagickCopyright());
9306 break;
9307 }
9308 case SaveToUndoBufferCommand:
9309 break;
9310 default:
9311 {
9312 (void) XBell(display,0);
9313 break;
9314 }
9315 }
9316 image_info=DestroyImageInfo(image_info);
9317 return(nexus);
9318}
9319
9320/*
9321%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9322% %
9323% %
9324% %
9325+ X M a g n i f y I m a g e %
9326% %
9327% %
9328% %
9329%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9330%
9331% XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9332% The magnified portion is displayed in a separate window.
9333%
9334% The format of the XMagnifyImage method is:
9335%
cristy6710d842011-10-20 23:23:00 +00009336% void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9337% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00009338%
9339% A description of each parameter follows:
9340%
9341% o display: Specifies a connection to an X server; returned from
9342% XOpenDisplay.
9343%
9344% o windows: Specifies a pointer to a XWindows structure.
9345%
9346% o event: Specifies a pointer to a XEvent structure. If it is NULL,
9347% the entire image is refreshed.
9348%
cristy6710d842011-10-20 23:23:00 +00009349% o exception: return any errors or warnings in this structure.
9350%
cristy3ed852e2009-09-05 21:47:34 +00009351*/
cristy6710d842011-10-20 23:23:00 +00009352static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9353 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00009354{
9355 char
9356 text[MaxTextExtent];
9357
9358 register int
9359 x,
9360 y;
9361
cristybb503372010-05-27 20:51:26 +00009362 size_t
cristy3ed852e2009-09-05 21:47:34 +00009363 state;
9364
9365 /*
9366 Update magnified image until the mouse button is released.
9367 */
9368 (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9369 state=DefaultState;
9370 x=event->xbutton.x;
9371 y=event->xbutton.y;
cristy49e2d862010-11-12 02:50:30 +00009372 windows->magnify.x=(int) windows->image.x+x;
9373 windows->magnify.y=(int) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +00009374 do
9375 {
9376 /*
9377 Map and unmap Info widget as text cursor crosses its boundaries.
9378 */
9379 if (windows->info.mapped != MagickFalse)
9380 {
9381 if ((x < (int) (windows->info.x+windows->info.width)) &&
9382 (y < (int) (windows->info.y+windows->info.height)))
9383 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9384 }
9385 else
9386 if ((x > (int) (windows->info.x+windows->info.width)) ||
9387 (y > (int) (windows->info.y+windows->info.height)))
9388 (void) XMapWindow(display,windows->info.id);
9389 if (windows->info.mapped != MagickFalse)
9390 {
9391 /*
9392 Display pointer position.
9393 */
cristyb51dff52011-05-19 16:55:47 +00009394 (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
cristy3ed852e2009-09-05 21:47:34 +00009395 windows->magnify.x,windows->magnify.y);
9396 XInfoWidget(display,windows,text);
9397 }
9398 /*
9399 Wait for next event.
9400 */
cristy6710d842011-10-20 23:23:00 +00009401 XScreenEvent(display,windows,event,exception);
cristy3ed852e2009-09-05 21:47:34 +00009402 switch (event->type)
9403 {
9404 case ButtonPress:
9405 break;
9406 case ButtonRelease:
9407 {
9408 /*
9409 User has finished magnifying image.
9410 */
9411 x=event->xbutton.x;
9412 y=event->xbutton.y;
9413 state|=ExitState;
9414 break;
9415 }
9416 case Expose:
9417 break;
9418 case MotionNotify:
9419 {
9420 x=event->xmotion.x;
9421 y=event->xmotion.y;
9422 break;
9423 }
9424 default:
9425 break;
9426 }
9427 /*
9428 Check boundary conditions.
9429 */
9430 if (x < 0)
9431 x=0;
9432 else
9433 if (x >= (int) windows->image.width)
9434 x=(int) windows->image.width-1;
9435 if (y < 0)
9436 y=0;
9437 else
9438 if (y >= (int) windows->image.height)
9439 y=(int) windows->image.height-1;
9440 } while ((state & ExitState) == 0);
9441 /*
9442 Display magnified image.
9443 */
9444 XSetCursorState(display,windows,MagickFalse);
9445}
9446
9447/*
9448%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9449% %
9450% %
9451% %
9452+ X M a g n i f y W i n d o w C o m m a n d %
9453% %
9454% %
9455% %
9456%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9457%
9458% XMagnifyWindowCommand() moves the image within an Magnify window by one
9459% pixel as specified by the key symbol.
9460%
9461% The format of the XMagnifyWindowCommand method is:
9462%
9463% void XMagnifyWindowCommand(Display *display,XWindows *windows,
cristy6710d842011-10-20 23:23:00 +00009464% const MagickStatusType state,const KeySym key_symbol,
9465% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00009466%
9467% A description of each parameter follows:
9468%
9469% o display: Specifies a connection to an X server; returned from
9470% XOpenDisplay.
9471%
9472% o windows: Specifies a pointer to a XWindows structure.
9473%
9474% o state: key mask.
9475%
9476% o key_symbol: Specifies a KeySym which indicates which side of the image
9477% to trim.
9478%
cristy6710d842011-10-20 23:23:00 +00009479% o exception: return any errors or warnings in this structure.
9480%
cristy3ed852e2009-09-05 21:47:34 +00009481*/
9482static void XMagnifyWindowCommand(Display *display,XWindows *windows,
cristy6710d842011-10-20 23:23:00 +00009483 const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00009484{
9485 unsigned int
9486 quantum;
9487
9488 /*
9489 User specified a magnify factor or position.
9490 */
9491 quantum=1;
9492 if ((state & Mod1Mask) != 0)
9493 quantum=10;
9494 switch ((int) key_symbol)
9495 {
9496 case QuitCommand:
9497 {
9498 (void) XWithdrawWindow(display,windows->magnify.id,
9499 windows->magnify.screen);
9500 break;
9501 }
9502 case XK_Home:
9503 case XK_KP_Home:
9504 {
9505 windows->magnify.x=(int) windows->image.width/2;
9506 windows->magnify.y=(int) windows->image.height/2;
9507 break;
9508 }
9509 case XK_Left:
9510 case XK_KP_Left:
9511 {
9512 if (windows->magnify.x > 0)
9513 windows->magnify.x-=quantum;
9514 break;
9515 }
9516 case XK_Up:
9517 case XK_KP_Up:
9518 {
9519 if (windows->magnify.y > 0)
9520 windows->magnify.y-=quantum;
9521 break;
9522 }
9523 case XK_Right:
9524 case XK_KP_Right:
9525 {
9526 if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9527 windows->magnify.x+=quantum;
9528 break;
9529 }
9530 case XK_Down:
9531 case XK_KP_Down:
9532 {
9533 if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9534 windows->magnify.y+=quantum;
9535 break;
9536 }
9537 case XK_0:
9538 case XK_1:
9539 case XK_2:
9540 case XK_3:
9541 case XK_4:
9542 case XK_5:
9543 case XK_6:
9544 case XK_7:
9545 case XK_8:
9546 case XK_9:
9547 {
9548 windows->magnify.data=(key_symbol-XK_0);
9549 break;
9550 }
9551 case XK_KP_0:
9552 case XK_KP_1:
9553 case XK_KP_2:
9554 case XK_KP_3:
9555 case XK_KP_4:
9556 case XK_KP_5:
9557 case XK_KP_6:
9558 case XK_KP_7:
9559 case XK_KP_8:
9560 case XK_KP_9:
9561 {
9562 windows->magnify.data=(key_symbol-XK_KP_0);
9563 break;
9564 }
9565 default:
9566 break;
9567 }
cristy6710d842011-10-20 23:23:00 +00009568 XMakeMagnifyImage(display,windows,exception);
cristy3ed852e2009-09-05 21:47:34 +00009569}
9570
9571/*
9572%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9573% %
9574% %
9575% %
9576+ X M a k e P a n I m a g e %
9577% %
9578% %
9579% %
9580%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9581%
9582% XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9583% icon window.
9584%
9585% The format of the XMakePanImage method is:
9586%
9587% void XMakePanImage(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +00009588% XWindows *windows,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00009589%
9590% A description of each parameter follows:
9591%
9592% o display: Specifies a connection to an X server; returned from
9593% XOpenDisplay.
9594%
9595% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9596%
9597% o windows: Specifies a pointer to a XWindows structure.
9598%
9599% o image: the image.
9600%
cristy051718b2011-08-28 22:49:25 +00009601% o exception: return any errors or warnings in this structure.
9602%
cristy3ed852e2009-09-05 21:47:34 +00009603*/
9604static void XMakePanImage(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +00009605 XWindows *windows,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00009606{
9607 MagickStatusType
9608 status;
9609
9610 /*
9611 Create and display image for panning icon.
9612 */
9613 XSetCursorState(display,windows,MagickTrue);
9614 XCheckRefreshWindows(display,windows);
cristy49e2d862010-11-12 02:50:30 +00009615 windows->pan.x=(int) windows->image.x;
9616 windows->pan.y=(int) windows->image.y;
cristy3ed852e2009-09-05 21:47:34 +00009617 status=XMakeImage(display,resource_info,&windows->pan,image,
cristy051718b2011-08-28 22:49:25 +00009618 windows->pan.width,windows->pan.height,exception);
cristy3ed852e2009-09-05 21:47:34 +00009619 if (status == MagickFalse)
cristy051718b2011-08-28 22:49:25 +00009620 ThrowXWindowFatalException(ResourceLimitError,
9621 "MemoryAllocationFailed",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00009622 (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9623 windows->pan.pixmap);
9624 (void) XClearWindow(display,windows->pan.id);
9625 XDrawPanRectangle(display,windows);
9626 XSetCursorState(display,windows,MagickFalse);
9627}
9628
9629/*
9630%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9631% %
9632% %
9633% %
9634+ X M a t t a E d i t I m a g e %
9635% %
9636% %
9637% %
9638%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9639%
9640% XMatteEditImage() allows the user to interactively change the Matte channel
9641% of an image. If the image is PseudoClass it is promoted to DirectClass
9642% before the matte information is stored.
9643%
9644% The format of the XMatteEditImage method is:
9645%
9646% MagickBooleanType XMatteEditImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00009647% XResourceInfo *resource_info,XWindows *windows,Image **image,
9648% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00009649%
9650% A description of each parameter follows:
9651%
9652% o display: Specifies a connection to an X server; returned from
9653% XOpenDisplay.
9654%
9655% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9656%
9657% o windows: Specifies a pointer to a XWindows structure.
9658%
9659% o image: the image; returned from ReadImage.
9660%
cristy051718b2011-08-28 22:49:25 +00009661% o exception: return any errors or warnings in this structure.
9662%
cristy3ed852e2009-09-05 21:47:34 +00009663*/
9664static MagickBooleanType XMatteEditImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00009665 XResourceInfo *resource_info,XWindows *windows,Image **image,
9666 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00009667{
9668 static char
9669 matte[MaxTextExtent] = "0";
9670
9671 static const char
9672 *MatteEditMenu[] =
9673 {
9674 "Method",
9675 "Border Color",
9676 "Fuzz",
9677 "Matte Value",
9678 "Undo",
9679 "Help",
9680 "Dismiss",
9681 (char *) NULL
9682 };
9683
9684 static const ModeType
9685 MatteEditCommands[] =
9686 {
9687 MatteEditMethod,
9688 MatteEditBorderCommand,
9689 MatteEditFuzzCommand,
9690 MatteEditValueCommand,
9691 MatteEditUndoCommand,
9692 MatteEditHelpCommand,
9693 MatteEditDismissCommand
9694 };
9695
9696 static PaintMethod
9697 method = PointMethod;
9698
9699 static XColor
9700 border_color = { 0, 0, 0, 0, 0, 0 };
9701
9702 char
9703 command[MaxTextExtent],
9704 text[MaxTextExtent];
9705
9706 Cursor
9707 cursor;
9708
9709 int
9710 entry,
9711 id,
9712 x,
9713 x_offset,
9714 y,
9715 y_offset;
9716
9717 register int
9718 i;
9719
cristy4c08aed2011-07-01 19:47:50 +00009720 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00009721 *q;
9722
9723 unsigned int
9724 height,
9725 width;
9726
cristybb503372010-05-27 20:51:26 +00009727 size_t
cristy3ed852e2009-09-05 21:47:34 +00009728 state;
9729
9730 XEvent
9731 event;
9732
9733 /*
9734 Map Command widget.
9735 */
9736 (void) CloneString(&windows->command.name,"Matte Edit");
9737 windows->command.data=4;
9738 (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9739 (void) XMapRaised(display,windows->command.id);
9740 XClientMessage(display,windows->image.id,windows->im_protocols,
9741 windows->im_update_widget,CurrentTime);
9742 /*
9743 Make cursor.
9744 */
9745 cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9746 resource_info->background_color,resource_info->foreground_color);
9747 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9748 /*
9749 Track pointer until button 1 is pressed.
9750 */
9751 XQueryPosition(display,windows->image.id,&x,&y);
9752 (void) XSelectInput(display,windows->image.id,
9753 windows->image.attributes.event_mask | PointerMotionMask);
9754 state=DefaultState;
9755 do
9756 {
9757 if (windows->info.mapped != MagickFalse)
9758 {
9759 /*
9760 Display pointer position.
9761 */
cristyb51dff52011-05-19 16:55:47 +00009762 (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
cristy3ed852e2009-09-05 21:47:34 +00009763 x+windows->image.x,y+windows->image.y);
9764 XInfoWidget(display,windows,text);
9765 }
9766 /*
9767 Wait for next event.
9768 */
cristy6710d842011-10-20 23:23:00 +00009769 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +00009770 if (event.xany.window == windows->command.id)
9771 {
9772 /*
9773 Select a command from the Command widget.
9774 */
9775 id=XCommandWidget(display,windows,MatteEditMenu,&event);
9776 if (id < 0)
9777 {
9778 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9779 continue;
9780 }
9781 switch (MatteEditCommands[id])
9782 {
9783 case MatteEditMethod:
9784 {
9785 char
9786 **methods;
9787
9788 /*
9789 Select a method from the pop-up menu.
9790 */
cristy042ee782011-04-22 18:48:30 +00009791 methods=GetCommandOptions(MagickMethodOptions);
cristy3ed852e2009-09-05 21:47:34 +00009792 if (methods == (char **) NULL)
9793 break;
9794 entry=XMenuWidget(display,windows,MatteEditMenu[id],
9795 (const char **) methods,command);
9796 if (entry >= 0)
cristy042ee782011-04-22 18:48:30 +00009797 method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
cristy3ed852e2009-09-05 21:47:34 +00009798 MagickFalse,methods[entry]);
9799 methods=DestroyStringList(methods);
9800 break;
9801 }
9802 case MatteEditBorderCommand:
9803 {
9804 const char
9805 *ColorMenu[MaxNumberPens];
9806
9807 int
9808 pen_number;
9809
9810 /*
9811 Initialize menu selections.
9812 */
9813 for (i=0; i < (int) (MaxNumberPens-2); i++)
9814 ColorMenu[i]=resource_info->pen_colors[i];
9815 ColorMenu[MaxNumberPens-2]="Browser...";
9816 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9817 /*
9818 Select a pen color from the pop-up menu.
9819 */
9820 pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9821 (const char **) ColorMenu,command);
9822 if (pen_number < 0)
9823 break;
9824 if (pen_number == (MaxNumberPens-2))
9825 {
9826 static char
9827 color_name[MaxTextExtent] = "gray";
9828
9829 /*
9830 Select a pen color from a dialog.
9831 */
9832 resource_info->pen_colors[pen_number]=color_name;
9833 XColorBrowserWidget(display,windows,"Select",color_name);
9834 if (*color_name == '\0')
9835 break;
9836 }
9837 /*
9838 Set border color.
9839 */
9840 (void) XParseColor(display,windows->map_info->colormap,
9841 resource_info->pen_colors[pen_number],&border_color);
9842 break;
9843 }
9844 case MatteEditFuzzCommand:
9845 {
9846 static char
9847 fuzz[MaxTextExtent];
9848
9849 static const char
9850 *FuzzMenu[] =
9851 {
9852 "0%",
9853 "2%",
9854 "5%",
9855 "10%",
9856 "15%",
9857 "Dialog...",
9858 (char *) NULL,
9859 };
9860
9861 /*
9862 Select a command from the pop-up menu.
9863 */
9864 entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9865 command);
9866 if (entry < 0)
9867 break;
9868 if (entry != 5)
9869 {
cristydbdd0e32011-11-04 23:29:40 +00009870 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
cristy4c08aed2011-07-01 19:47:50 +00009871 QuantumRange+1.0);
cristy3ed852e2009-09-05 21:47:34 +00009872 break;
9873 }
9874 (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9875 (void) XDialogWidget(display,windows,"Ok",
9876 "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9877 if (*fuzz == '\0')
9878 break;
9879 (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
cristy9b34e302011-11-05 02:15:45 +00009880 (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9881 1.0);
cristy3ed852e2009-09-05 21:47:34 +00009882 break;
9883 }
9884 case MatteEditValueCommand:
9885 {
9886 static char
9887 message[MaxTextExtent];
9888
9889 static const char
9890 *MatteMenu[] =
9891 {
9892 "Opaque",
9893 "Transparent",
9894 "Dialog...",
9895 (char *) NULL,
9896 };
9897
9898 /*
9899 Select a command from the pop-up menu.
9900 */
9901 entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9902 command);
9903 if (entry < 0)
9904 break;
9905 if (entry != 2)
9906 {
cristyb51dff52011-05-19 16:55:47 +00009907 (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
cristy4c08aed2011-07-01 19:47:50 +00009908 OpaqueAlpha);
cristy3ed852e2009-09-05 21:47:34 +00009909 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
cristyb51dff52011-05-19 16:55:47 +00009910 (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
cristy4c08aed2011-07-01 19:47:50 +00009911 (Quantum) TransparentAlpha);
cristy3ed852e2009-09-05 21:47:34 +00009912 break;
9913 }
cristyb51dff52011-05-19 16:55:47 +00009914 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00009915 "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9916 QuantumRange);
9917 (void) XDialogWidget(display,windows,"Matte",message,matte);
9918 if (*matte == '\0')
9919 break;
9920 break;
9921 }
9922 case MatteEditUndoCommand:
9923 {
9924 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
cristy051718b2011-08-28 22:49:25 +00009925 image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009926 break;
9927 }
9928 case MatteEditHelpCommand:
9929 {
9930 XTextViewWidget(display,resource_info,windows,MagickFalse,
9931 "Help Viewer - Matte Edit",ImageMatteEditHelp);
9932 break;
9933 }
9934 case MatteEditDismissCommand:
9935 {
9936 /*
9937 Prematurely exit.
9938 */
9939 state|=EscapeState;
9940 state|=ExitState;
9941 break;
9942 }
9943 default:
9944 break;
9945 }
9946 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9947 continue;
9948 }
9949 switch (event.type)
9950 {
9951 case ButtonPress:
9952 {
9953 if (event.xbutton.button != Button1)
9954 break;
9955 if ((event.xbutton.window != windows->image.id) &&
9956 (event.xbutton.window != windows->magnify.id))
9957 break;
9958 /*
9959 Update matte data.
9960 */
9961 x=event.xbutton.x;
9962 y=event.xbutton.y;
9963 (void) XMagickCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +00009964 SaveToUndoBufferCommand,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009965 state|=UpdateConfigurationState;
9966 break;
9967 }
9968 case ButtonRelease:
9969 {
9970 if (event.xbutton.button != Button1)
9971 break;
9972 if ((event.xbutton.window != windows->image.id) &&
9973 (event.xbutton.window != windows->magnify.id))
9974 break;
9975 /*
9976 Update colormap information.
9977 */
9978 x=event.xbutton.x;
9979 y=event.xbutton.y;
cristy6710d842011-10-20 23:23:00 +00009980 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00009981 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009982 XInfoWidget(display,windows,text);
9983 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9984 state&=(~UpdateConfigurationState);
9985 break;
9986 }
9987 case Expose:
9988 break;
9989 case KeyPress:
9990 {
9991 char
9992 command[MaxTextExtent];
9993
9994 KeySym
9995 key_symbol;
9996
9997 if (event.xkey.window == windows->magnify.id)
9998 {
9999 Window
10000 window;
10001
10002 window=windows->magnify.id;
10003 while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
10004 }
10005 if (event.xkey.window != windows->image.id)
10006 break;
10007 /*
10008 Respond to a user key press.
10009 */
10010 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
10011 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10012 switch ((int) key_symbol)
10013 {
10014 case XK_Escape:
10015 case XK_F20:
10016 {
10017 /*
10018 Prematurely exit.
10019 */
10020 state|=ExitState;
10021 break;
10022 }
10023 case XK_F1:
10024 case XK_Help:
10025 {
10026 XTextViewWidget(display,resource_info,windows,MagickFalse,
10027 "Help Viewer - Matte Edit",ImageMatteEditHelp);
10028 break;
10029 }
10030 default:
10031 {
10032 (void) XBell(display,0);
10033 break;
10034 }
10035 }
10036 break;
10037 }
10038 case MotionNotify:
10039 {
10040 /*
10041 Map and unmap Info widget as cursor crosses its boundaries.
10042 */
10043 x=event.xmotion.x;
10044 y=event.xmotion.y;
10045 if (windows->info.mapped != MagickFalse)
10046 {
10047 if ((x < (int) (windows->info.x+windows->info.width)) &&
10048 (y < (int) (windows->info.y+windows->info.height)))
10049 (void) XWithdrawWindow(display,windows->info.id,
10050 windows->info.screen);
10051 }
10052 else
10053 if ((x > (int) (windows->info.x+windows->info.width)) ||
10054 (y > (int) (windows->info.y+windows->info.height)))
10055 (void) XMapWindow(display,windows->info.id);
10056 break;
10057 }
10058 default:
10059 break;
10060 }
10061 if (event.xany.window == windows->magnify.id)
10062 {
10063 x=windows->magnify.x-windows->image.x;
10064 y=windows->magnify.y-windows->image.y;
10065 }
10066 x_offset=x;
10067 y_offset=y;
10068 if ((state & UpdateConfigurationState) != 0)
10069 {
cristy49e2d862010-11-12 02:50:30 +000010070 CacheView
10071 *image_view;
10072
cristy3ed852e2009-09-05 21:47:34 +000010073 int
10074 x,
10075 y;
10076
10077 /*
10078 Matte edit is relative to image configuration.
10079 */
10080 (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10081 MagickTrue);
10082 XPutPixel(windows->image.ximage,x_offset,y_offset,
10083 windows->pixel_info->background_color.pixel);
10084 width=(unsigned int) (*image)->columns;
10085 height=(unsigned int) (*image)->rows;
10086 x=0;
10087 y=0;
10088 if (windows->image.crop_geometry != (char *) NULL)
cristy4c08aed2011-07-01 19:47:50 +000010089 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10090 &height);
10091 x_offset=(int) (width*(windows->image.x+x_offset)/
10092 windows->image.ximage->width+x);
10093 y_offset=(int) (height*(windows->image.y+y_offset)/
10094 windows->image.ximage->height+y);
cristy3ed852e2009-09-05 21:47:34 +000010095 if ((x_offset < 0) || (y_offset < 0))
10096 continue;
10097 if ((x_offset >= (int) (*image)->columns) ||
10098 (y_offset >= (int) (*image)->rows))
10099 continue;
cristy574cc262011-08-05 01:23:58 +000010100 if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010101 return(MagickFalse);
10102 (*image)->matte=MagickTrue;
cristy49e2d862010-11-12 02:50:30 +000010103 image_view=AcquireCacheView(*image);
cristy3ed852e2009-09-05 21:47:34 +000010104 switch (method)
10105 {
10106 case PointMethod:
10107 default:
10108 {
10109 /*
10110 Update matte information using point algorithm.
10111 */
cristy49e2d862010-11-12 02:50:30 +000010112 q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10113 (ssize_t) y_offset,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +000010114 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010115 break;
cristy4c08aed2011-07-01 19:47:50 +000010116 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
cristy49e2d862010-11-12 02:50:30 +000010117 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +000010118 break;
10119 }
10120 case ReplaceMethod:
10121 {
cristy101ab702011-10-13 13:06:32 +000010122 PixelInfo
cristy4c08aed2011-07-01 19:47:50 +000010123 pixel,
cristy3ed852e2009-09-05 21:47:34 +000010124 target;
10125
cristy2ed42f62011-10-02 19:49:57 +000010126 Quantum
cristy5f95f4f2011-10-23 01:01:01 +000010127 virtual_pixel[CompositePixelChannel];
cristy2ed42f62011-10-02 19:49:57 +000010128
cristy3ed852e2009-09-05 21:47:34 +000010129 /*
10130 Update matte information using replace algorithm.
10131 */
cristy49e2d862010-11-12 02:50:30 +000010132 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
cristy2ed42f62011-10-02 19:49:57 +000010133 (ssize_t) y_offset,virtual_pixel,exception);
10134 target.red=virtual_pixel[RedPixelChannel];
10135 target.green=virtual_pixel[GreenPixelChannel];
10136 target.blue=virtual_pixel[BluePixelChannel];
10137 target.alpha=virtual_pixel[AlphaPixelChannel];
cristy49e2d862010-11-12 02:50:30 +000010138 for (y=0; y < (int) (*image)->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010139 {
cristy49e2d862010-11-12 02:50:30 +000010140 q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
cristy051718b2011-08-28 22:49:25 +000010141 (*image)->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +000010142 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010143 break;
10144 for (x=0; x < (int) (*image)->columns; x++)
10145 {
cristy101ab702011-10-13 13:06:32 +000010146 GetPixelInfoPixel(*image,q,&pixel);
10147 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
cristy4c08aed2011-07-01 19:47:50 +000010148 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
cristyed231572011-07-14 02:18:59 +000010149 q+=GetPixelChannels(*image);
cristy3ed852e2009-09-05 21:47:34 +000010150 }
cristy49e2d862010-11-12 02:50:30 +000010151 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010152 break;
10153 }
10154 break;
10155 }
10156 case FloodfillMethod:
10157 case FillToBorderMethod:
10158 {
cristybd5a96c2011-08-21 00:04:26 +000010159 ChannelType
10160 channel_mask;
10161
cristy3ed852e2009-09-05 21:47:34 +000010162 DrawInfo
10163 *draw_info;
10164
cristy4c08aed2011-07-01 19:47:50 +000010165 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +000010166 target;
10167
10168 /*
10169 Update matte information using floodfill algorithm.
10170 */
cristy52010022011-10-21 18:07:37 +000010171 (void) GetOneVirtualMagickPixel(*image,
10172 GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10173 y_offset,&target,exception);
cristy3ed852e2009-09-05 21:47:34 +000010174 if (method == FillToBorderMethod)
10175 {
cristy4c08aed2011-07-01 19:47:50 +000010176 target.red=(MagickRealType) ScaleShortToQuantum(
10177 border_color.red);
10178 target.green=(MagickRealType) ScaleShortToQuantum(
10179 border_color.green);
10180 target.blue=(MagickRealType) ScaleShortToQuantum(
10181 border_color.blue);
cristy3ed852e2009-09-05 21:47:34 +000010182 }
10183 draw_info=CloneDrawInfo(resource_info->image_info,
10184 (DrawInfo *) NULL);
cristydbdd0e32011-11-04 23:29:40 +000010185 draw_info->fill.alpha=ClampToQuantum(StringToDouble(matte,
cristy0df696d2011-05-18 19:55:22 +000010186 (char **) NULL));
cristybd5a96c2011-08-21 00:04:26 +000010187 channel_mask=SetPixelChannelMask(*image,AlphaChannel);
cristyd42d9952011-07-08 14:21:50 +000010188 (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10189 x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
cristy189e84c2011-08-27 18:08:53 +000010190 MagickFalse : MagickTrue,exception);
cristye2a912b2011-12-05 20:02:07 +000010191 (void) SetPixelChannelMapMask(*image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +000010192 draw_info=DestroyDrawInfo(draw_info);
10193 break;
10194 }
10195 case ResetMethod:
10196 {
10197 /*
10198 Update matte information using reset algorithm.
10199 */
cristy574cc262011-08-05 01:23:58 +000010200 if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010201 return(MagickFalse);
cristy49e2d862010-11-12 02:50:30 +000010202 for (y=0; y < (int) (*image)->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010203 {
cristy49e2d862010-11-12 02:50:30 +000010204 q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10205 (*image)->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +000010206 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010207 break;
10208 for (x=0; x < (int) (*image)->columns; x++)
10209 {
cristy4c08aed2011-07-01 19:47:50 +000010210 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
cristyed231572011-07-14 02:18:59 +000010211 q+=GetPixelChannels(*image);
cristy3ed852e2009-09-05 21:47:34 +000010212 }
cristy49e2d862010-11-12 02:50:30 +000010213 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010214 break;
10215 }
cristy4c08aed2011-07-01 19:47:50 +000010216 if (StringToLong(matte) == (long) OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +000010217 (*image)->matte=MagickFalse;
10218 break;
10219 }
10220 }
cristy49e2d862010-11-12 02:50:30 +000010221 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +000010222 state&=(~UpdateConfigurationState);
10223 }
10224 } while ((state & ExitState) == 0);
10225 (void) XSelectInput(display,windows->image.id,
10226 windows->image.attributes.event_mask);
10227 XSetCursorState(display,windows,MagickFalse);
10228 (void) XFreeCursor(display,cursor);
10229 return(MagickTrue);
10230}
10231
10232/*
10233%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10234% %
10235% %
10236% %
10237+ X O p e n I m a g e %
10238% %
10239% %
10240% %
10241%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10242%
10243% XOpenImage() loads an image from a file.
10244%
10245% The format of the XOpenImage method is:
10246%
10247% Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10248% XWindows *windows,const unsigned int command)
10249%
10250% A description of each parameter follows:
10251%
10252% o display: Specifies a connection to an X server; returned from
10253% XOpenDisplay.
10254%
10255% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10256%
10257% o windows: Specifies a pointer to a XWindows structure.
10258%
10259% o command: A value other than zero indicates that the file is selected
10260% from the command line argument list.
10261%
10262*/
10263static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10264 XWindows *windows,const MagickBooleanType command)
10265{
10266 const MagickInfo
10267 *magick_info;
10268
10269 ExceptionInfo
10270 *exception;
10271
10272 Image
10273 *nexus;
10274
10275 ImageInfo
10276 *image_info;
10277
10278 static char
10279 filename[MaxTextExtent] = "\0";
10280
10281 /*
10282 Request file name from user.
10283 */
10284 if (command == MagickFalse)
10285 XFileBrowserWidget(display,windows,"Open",filename);
10286 else
10287 {
10288 char
10289 **filelist,
10290 **files;
10291
10292 int
10293 count,
10294 status;
10295
10296 register int
10297 i,
10298 j;
10299
10300 /*
10301 Select next image from the command line.
10302 */
10303 status=XGetCommand(display,windows->image.id,&files,&count);
10304 if (status == 0)
10305 {
10306 ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10307 return((Image *) NULL);
10308 }
10309 filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10310 if (filelist == (char **) NULL)
10311 {
10312 ThrowXWindowFatalException(ResourceLimitError,
10313 "MemoryAllocationFailed","...");
10314 (void) XFreeStringList(files);
10315 return((Image *) NULL);
10316 }
10317 j=0;
10318 for (i=1; i < count; i++)
10319 if (*files[i] != '-')
10320 filelist[j++]=files[i];
10321 filelist[j]=(char *) NULL;
10322 XListBrowserWidget(display,windows,&windows->widget,
10323 (const char **) filelist,"Load","Select Image to Load:",filename);
10324 filelist=(char **) RelinquishMagickMemory(filelist);
10325 (void) XFreeStringList(files);
10326 }
10327 if (*filename == '\0')
10328 return((Image *) NULL);
10329 image_info=CloneImageInfo(resource_info->image_info);
10330 (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10331 (void *) NULL);
10332 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10333 exception=AcquireExceptionInfo();
cristyd965a422010-03-03 17:47:35 +000010334 (void) SetImageInfo(image_info,0,exception);
cristy3ed852e2009-09-05 21:47:34 +000010335 if (LocaleCompare(image_info->magick,"X") == 0)
10336 {
10337 char
10338 seconds[MaxTextExtent];
10339
10340 /*
10341 User may want to delay the X server screen grab.
10342 */
10343 (void) CopyMagickString(seconds,"0",MaxTextExtent);
10344 (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10345 seconds);
10346 if (*seconds == '\0')
10347 return((Image *) NULL);
cristybb503372010-05-27 20:51:26 +000010348 XDelay(display,(size_t) (1000*StringToLong(seconds)));
cristy3ed852e2009-09-05 21:47:34 +000010349 }
10350 magick_info=GetMagickInfo(image_info->magick,exception);
10351 if ((magick_info != (const MagickInfo *) NULL) &&
10352 (magick_info->raw != MagickFalse))
10353 {
10354 char
10355 geometry[MaxTextExtent];
10356
10357 /*
10358 Request image size from the user.
10359 */
10360 (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10361 if (image_info->size != (char *) NULL)
10362 (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10363 (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10364 geometry);
10365 (void) CloneString(&image_info->size,geometry);
10366 }
10367 /*
10368 Load the image.
10369 */
10370 XSetCursorState(display,windows,MagickTrue);
10371 XCheckRefreshWindows(display,windows);
10372 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10373 nexus=ReadImage(image_info,exception);
10374 CatchException(exception);
10375 XSetCursorState(display,windows,MagickFalse);
10376 if (nexus != (Image *) NULL)
10377 XClientMessage(display,windows->image.id,windows->im_protocols,
10378 windows->im_next_image,CurrentTime);
10379 else
10380 {
10381 char
10382 *text,
10383 **textlist;
10384
10385 /*
10386 Unknown image format.
10387 */
10388 text=FileToString(filename,~0,exception);
10389 if (text == (char *) NULL)
10390 return((Image *) NULL);
10391 textlist=StringToList(text);
10392 if (textlist != (char **) NULL)
10393 {
10394 char
10395 title[MaxTextExtent];
10396
10397 register int
10398 i;
10399
cristyb51dff52011-05-19 16:55:47 +000010400 (void) FormatLocaleString(title,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000010401 "Unknown format: %s",filename);
10402 XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10403 (const char **) textlist);
10404 for (i=0; textlist[i] != (char *) NULL; i++)
10405 textlist[i]=DestroyString(textlist[i]);
10406 textlist=(char **) RelinquishMagickMemory(textlist);
10407 }
10408 text=DestroyString(text);
10409 }
10410 exception=DestroyExceptionInfo(exception);
10411 image_info=DestroyImageInfo(image_info);
10412 return(nexus);
10413}
10414
10415/*
10416%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10417% %
10418% %
10419% %
10420+ X P a n I m a g e %
10421% %
10422% %
10423% %
10424%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10425%
10426% XPanImage() pans the image until the mouse button is released.
10427%
10428% The format of the XPanImage method is:
10429%
cristy6710d842011-10-20 23:23:00 +000010430% void XPanImage(Display *display,XWindows *windows,XEvent *event,
10431% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010432%
10433% A description of each parameter follows:
10434%
10435% o display: Specifies a connection to an X server; returned from
10436% XOpenDisplay.
10437%
10438% o windows: Specifies a pointer to a XWindows structure.
10439%
10440% o event: Specifies a pointer to a XEvent structure. If it is NULL,
10441% the entire image is refreshed.
10442%
cristy6710d842011-10-20 23:23:00 +000010443% o exception: return any errors or warnings in this structure.
10444%
cristy3ed852e2009-09-05 21:47:34 +000010445*/
cristy6710d842011-10-20 23:23:00 +000010446static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10447 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010448{
10449 char
10450 text[MaxTextExtent];
10451
10452 Cursor
10453 cursor;
10454
10455 MagickRealType
10456 x_factor,
10457 y_factor;
10458
10459 RectangleInfo
10460 pan_info;
10461
cristybb503372010-05-27 20:51:26 +000010462 size_t
cristy3ed852e2009-09-05 21:47:34 +000010463 state;
10464
10465 /*
10466 Define cursor.
10467 */
10468 if ((windows->image.ximage->width > (int) windows->image.width) &&
10469 (windows->image.ximage->height > (int) windows->image.height))
10470 cursor=XCreateFontCursor(display,XC_fleur);
10471 else
10472 if (windows->image.ximage->width > (int) windows->image.width)
10473 cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10474 else
10475 if (windows->image.ximage->height > (int) windows->image.height)
10476 cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10477 else
10478 cursor=XCreateFontCursor(display,XC_arrow);
10479 (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10480 /*
10481 Pan image as pointer moves until the mouse button is released.
10482 */
10483 x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10484 y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10485 pan_info.width=windows->pan.width*windows->image.width/
10486 windows->image.ximage->width;
10487 pan_info.height=windows->pan.height*windows->image.height/
10488 windows->image.ximage->height;
10489 pan_info.x=0;
10490 pan_info.y=0;
10491 state=UpdateConfigurationState;
10492 do
10493 {
10494 switch (event->type)
10495 {
10496 case ButtonPress:
10497 {
10498 /*
10499 User choose an initial pan location.
10500 */
cristy49e2d862010-11-12 02:50:30 +000010501 pan_info.x=(ssize_t) event->xbutton.x;
10502 pan_info.y=(ssize_t) event->xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000010503 state|=UpdateConfigurationState;
10504 break;
10505 }
10506 case ButtonRelease:
10507 {
10508 /*
10509 User has finished panning the image.
10510 */
cristy49e2d862010-11-12 02:50:30 +000010511 pan_info.x=(ssize_t) event->xbutton.x;
10512 pan_info.y=(ssize_t) event->xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000010513 state|=UpdateConfigurationState | ExitState;
10514 break;
10515 }
10516 case MotionNotify:
10517 {
cristy49e2d862010-11-12 02:50:30 +000010518 pan_info.x=(ssize_t) event->xmotion.x;
10519 pan_info.y=(ssize_t) event->xmotion.y;
cristy3ed852e2009-09-05 21:47:34 +000010520 state|=UpdateConfigurationState;
10521 }
10522 default:
10523 break;
10524 }
10525 if ((state & UpdateConfigurationState) != 0)
10526 {
10527 /*
10528 Check boundary conditions.
10529 */
cristy49e2d862010-11-12 02:50:30 +000010530 if (pan_info.x < (ssize_t) (pan_info.width/2))
cristy3ed852e2009-09-05 21:47:34 +000010531 pan_info.x=0;
10532 else
cristy49e2d862010-11-12 02:50:30 +000010533 pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
cristy3ed852e2009-09-05 21:47:34 +000010534 if (pan_info.x < 0)
10535 pan_info.x=0;
10536 else
10537 if ((int) (pan_info.x+windows->image.width) >
10538 windows->image.ximage->width)
cristybb503372010-05-27 20:51:26 +000010539 pan_info.x=(ssize_t)
cristy3ed852e2009-09-05 21:47:34 +000010540 (windows->image.ximage->width-windows->image.width);
cristybb503372010-05-27 20:51:26 +000010541 if (pan_info.y < (ssize_t) (pan_info.height/2))
cristy3ed852e2009-09-05 21:47:34 +000010542 pan_info.y=0;
10543 else
cristybb503372010-05-27 20:51:26 +000010544 pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
cristy3ed852e2009-09-05 21:47:34 +000010545 if (pan_info.y < 0)
10546 pan_info.y=0;
10547 else
10548 if ((int) (pan_info.y+windows->image.height) >
10549 windows->image.ximage->height)
cristybb503372010-05-27 20:51:26 +000010550 pan_info.y=(ssize_t)
cristy3ed852e2009-09-05 21:47:34 +000010551 (windows->image.ximage->height-windows->image.height);
10552 if ((windows->image.x != (int) pan_info.x) ||
10553 (windows->image.y != (int) pan_info.y))
10554 {
10555 /*
10556 Display image pan offset.
10557 */
10558 windows->image.x=(int) pan_info.x;
10559 windows->image.y=(int) pan_info.y;
cristyb51dff52011-05-19 16:55:47 +000010560 (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
cristy3ed852e2009-09-05 21:47:34 +000010561 windows->image.width,windows->image.height,windows->image.x,
10562 windows->image.y);
10563 XInfoWidget(display,windows,text);
10564 /*
10565 Refresh Image window.
10566 */
10567 XDrawPanRectangle(display,windows);
10568 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10569 }
10570 state&=(~UpdateConfigurationState);
10571 }
10572 /*
10573 Wait for next event.
10574 */
10575 if ((state & ExitState) == 0)
cristy6710d842011-10-20 23:23:00 +000010576 XScreenEvent(display,windows,event,exception);
cristy3ed852e2009-09-05 21:47:34 +000010577 } while ((state & ExitState) == 0);
10578 /*
10579 Restore cursor.
10580 */
10581 (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10582 (void) XFreeCursor(display,cursor);
10583 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10584}
10585
10586/*
10587%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10588% %
10589% %
10590% %
10591+ X P a s t e I m a g e %
10592% %
10593% %
10594% %
10595%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10596%
10597% XPasteImage() pastes an image previously saved with XCropImage in the X
10598% window image at a location the user chooses with the pointer.
10599%
10600% The format of the XPasteImage method is:
10601%
10602% MagickBooleanType XPasteImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000010603% XResourceInfo *resource_info,XWindows *windows,Image *image,
10604% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010605%
10606% A description of each parameter follows:
10607%
10608% o display: Specifies a connection to an X server; returned from
10609% XOpenDisplay.
10610%
10611% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10612%
10613% o windows: Specifies a pointer to a XWindows structure.
10614%
10615% o image: the image; returned from ReadImage.
10616%
cristy051718b2011-08-28 22:49:25 +000010617% o exception: return any errors or warnings in this structure.
10618%
cristy3ed852e2009-09-05 21:47:34 +000010619*/
10620static MagickBooleanType XPasteImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000010621 XResourceInfo *resource_info,XWindows *windows,Image *image,
10622 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010623{
10624 static const char
10625 *PasteMenu[] =
10626 {
10627 "Operator",
10628 "Help",
10629 "Dismiss",
10630 (char *) NULL
10631 };
10632
10633 static const ModeType
10634 PasteCommands[] =
10635 {
10636 PasteOperatorsCommand,
10637 PasteHelpCommand,
10638 PasteDismissCommand
10639 };
10640
10641 static CompositeOperator
10642 compose = CopyCompositeOp;
10643
10644 char
10645 text[MaxTextExtent];
10646
10647 Cursor
10648 cursor;
10649
10650 Image
10651 *paste_image;
10652
10653 int
10654 entry,
10655 id,
10656 x,
10657 y;
10658
10659 MagickRealType
10660 scale_factor;
10661
10662 RectangleInfo
10663 highlight_info,
10664 paste_info;
10665
10666 unsigned int
10667 height,
10668 width;
10669
cristybb503372010-05-27 20:51:26 +000010670 size_t
cristy3ed852e2009-09-05 21:47:34 +000010671 state;
10672
10673 XEvent
10674 event;
10675
10676 /*
10677 Copy image.
10678 */
10679 if (resource_info->copy_image == (Image *) NULL)
10680 return(MagickFalse);
cristy051718b2011-08-28 22:49:25 +000010681 paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000010682 /*
10683 Map Command widget.
10684 */
10685 (void) CloneString(&windows->command.name,"Paste");
10686 windows->command.data=1;
10687 (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10688 (void) XMapRaised(display,windows->command.id);
10689 XClientMessage(display,windows->image.id,windows->im_protocols,
10690 windows->im_update_widget,CurrentTime);
10691 /*
10692 Track pointer until button 1 is pressed.
10693 */
10694 XSetCursorState(display,windows,MagickFalse);
10695 XQueryPosition(display,windows->image.id,&x,&y);
10696 (void) XSelectInput(display,windows->image.id,
10697 windows->image.attributes.event_mask | PointerMotionMask);
cristy49e2d862010-11-12 02:50:30 +000010698 paste_info.x=(ssize_t) windows->image.x+x;
10699 paste_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +000010700 paste_info.width=0;
10701 paste_info.height=0;
10702 cursor=XCreateFontCursor(display,XC_ul_angle);
10703 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10704 state=DefaultState;
10705 do
10706 {
10707 if (windows->info.mapped != MagickFalse)
10708 {
10709 /*
10710 Display pointer position.
10711 */
cristyb51dff52011-05-19 16:55:47 +000010712 (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
cristyf2faecf2010-05-28 19:19:36 +000010713 (long) paste_info.x,(long) paste_info.y);
cristy3ed852e2009-09-05 21:47:34 +000010714 XInfoWidget(display,windows,text);
10715 }
10716 highlight_info=paste_info;
10717 highlight_info.x=paste_info.x-windows->image.x;
10718 highlight_info.y=paste_info.y-windows->image.y;
10719 XHighlightRectangle(display,windows->image.id,
10720 windows->image.highlight_context,&highlight_info);
10721 /*
10722 Wait for next event.
10723 */
cristy6710d842011-10-20 23:23:00 +000010724 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +000010725 XHighlightRectangle(display,windows->image.id,
10726 windows->image.highlight_context,&highlight_info);
10727 if (event.xany.window == windows->command.id)
10728 {
10729 /*
10730 Select a command from the Command widget.
10731 */
10732 id=XCommandWidget(display,windows,PasteMenu,&event);
10733 if (id < 0)
10734 continue;
10735 switch (PasteCommands[id])
10736 {
10737 case PasteOperatorsCommand:
10738 {
10739 char
10740 command[MaxTextExtent],
10741 **operators;
10742
10743 /*
10744 Select a command from the pop-up menu.
10745 */
cristy042ee782011-04-22 18:48:30 +000010746 operators=GetCommandOptions(MagickComposeOptions);
cristy3ed852e2009-09-05 21:47:34 +000010747 if (operators == (char **) NULL)
10748 break;
10749 entry=XMenuWidget(display,windows,PasteMenu[id],
10750 (const char **) operators,command);
10751 if (entry >= 0)
cristy042ee782011-04-22 18:48:30 +000010752 compose=(CompositeOperator) ParseCommandOption(
cristy3ed852e2009-09-05 21:47:34 +000010753 MagickComposeOptions,MagickFalse,operators[entry]);
10754 operators=DestroyStringList(operators);
10755 break;
10756 }
10757 case PasteHelpCommand:
10758 {
10759 XTextViewWidget(display,resource_info,windows,MagickFalse,
10760 "Help Viewer - Image Composite",ImagePasteHelp);
10761 break;
10762 }
10763 case PasteDismissCommand:
10764 {
10765 /*
10766 Prematurely exit.
10767 */
10768 state|=EscapeState;
10769 state|=ExitState;
10770 break;
10771 }
10772 default:
10773 break;
10774 }
10775 continue;
10776 }
10777 switch (event.type)
10778 {
10779 case ButtonPress:
10780 {
10781 if (image->debug != MagickFalse)
10782 (void) LogMagickEvent(X11Event,GetMagickModule(),
10783 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10784 event.xbutton.button,event.xbutton.x,event.xbutton.y);
10785 if (event.xbutton.button != Button1)
10786 break;
10787 if (event.xbutton.window != windows->image.id)
10788 break;
10789 /*
10790 Paste rectangle is relative to image configuration.
10791 */
10792 width=(unsigned int) image->columns;
10793 height=(unsigned int) image->rows;
10794 x=0;
10795 y=0;
10796 if (windows->image.crop_geometry != (char *) NULL)
10797 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10798 &width,&height);
10799 scale_factor=(MagickRealType) windows->image.ximage->width/width;
10800 paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10801 scale_factor=(MagickRealType) windows->image.ximage->height/height;
10802 paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10803 (void) XCheckDefineCursor(display,windows->image.id,cursor);
cristy49e2d862010-11-12 02:50:30 +000010804 paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10805 paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000010806 break;
10807 }
10808 case ButtonRelease:
10809 {
10810 if (image->debug != MagickFalse)
10811 (void) LogMagickEvent(X11Event,GetMagickModule(),
10812 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10813 event.xbutton.button,event.xbutton.x,event.xbutton.y);
10814 if (event.xbutton.button != Button1)
10815 break;
10816 if (event.xbutton.window != windows->image.id)
10817 break;
10818 if ((paste_info.width != 0) && (paste_info.height != 0))
10819 {
10820 /*
10821 User has selected the location of the paste image.
10822 */
cristy49e2d862010-11-12 02:50:30 +000010823 paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10824 paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000010825 state|=ExitState;
10826 }
10827 break;
10828 }
10829 case Expose:
10830 break;
10831 case KeyPress:
10832 {
10833 char
10834 command[MaxTextExtent];
10835
10836 KeySym
10837 key_symbol;
10838
10839 int
10840 length;
10841
10842 if (event.xkey.window != windows->image.id)
10843 break;
10844 /*
10845 Respond to a user key press.
10846 */
10847 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10848 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10849 *(command+length)='\0';
10850 if (image->debug != MagickFalse)
10851 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010852 "Key press: 0x%lx (%s)",(long) key_symbol,command);
cristy3ed852e2009-09-05 21:47:34 +000010853 switch ((int) key_symbol)
10854 {
10855 case XK_Escape:
10856 case XK_F20:
10857 {
10858 /*
10859 Prematurely exit.
10860 */
10861 paste_image=DestroyImage(paste_image);
10862 state|=EscapeState;
10863 state|=ExitState;
10864 break;
10865 }
10866 case XK_F1:
10867 case XK_Help:
10868 {
10869 (void) XSetFunction(display,windows->image.highlight_context,
10870 GXcopy);
10871 XTextViewWidget(display,resource_info,windows,MagickFalse,
10872 "Help Viewer - Image Composite",ImagePasteHelp);
10873 (void) XSetFunction(display,windows->image.highlight_context,
10874 GXinvert);
10875 break;
10876 }
10877 default:
10878 {
10879 (void) XBell(display,0);
10880 break;
10881 }
10882 }
10883 break;
10884 }
10885 case MotionNotify:
10886 {
10887 /*
10888 Map and unmap Info widget as text cursor crosses its boundaries.
10889 */
10890 x=event.xmotion.x;
10891 y=event.xmotion.y;
10892 if (windows->info.mapped != MagickFalse)
10893 {
10894 if ((x < (int) (windows->info.x+windows->info.width)) &&
10895 (y < (int) (windows->info.y+windows->info.height)))
10896 (void) XWithdrawWindow(display,windows->info.id,
10897 windows->info.screen);
10898 }
10899 else
10900 if ((x > (int) (windows->info.x+windows->info.width)) ||
10901 (y > (int) (windows->info.y+windows->info.height)))
10902 (void) XMapWindow(display,windows->info.id);
cristy49e2d862010-11-12 02:50:30 +000010903 paste_info.x=(ssize_t) windows->image.x+x;
10904 paste_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +000010905 break;
10906 }
10907 default:
10908 {
10909 if (image->debug != MagickFalse)
10910 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10911 event.type);
10912 break;
10913 }
10914 }
10915 } while ((state & ExitState) == 0);
10916 (void) XSelectInput(display,windows->image.id,
10917 windows->image.attributes.event_mask);
10918 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10919 XSetCursorState(display,windows,MagickFalse);
10920 (void) XFreeCursor(display,cursor);
10921 if ((state & EscapeState) != 0)
10922 return(MagickTrue);
10923 /*
10924 Image pasting is relative to image configuration.
10925 */
10926 XSetCursorState(display,windows,MagickTrue);
10927 XCheckRefreshWindows(display,windows);
10928 width=(unsigned int) image->columns;
10929 height=(unsigned int) image->rows;
10930 x=0;
10931 y=0;
10932 if (windows->image.crop_geometry != (char *) NULL)
10933 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10934 scale_factor=(MagickRealType) width/windows->image.ximage->width;
10935 paste_info.x+=x;
cristy49e2d862010-11-12 02:50:30 +000010936 paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +000010937 paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10938 scale_factor=(MagickRealType) height/windows->image.ximage->height;
10939 paste_info.y+=y;
cristy49e2d862010-11-12 02:50:30 +000010940 paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
cristy3ed852e2009-09-05 21:47:34 +000010941 paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10942 /*
10943 Paste image with X Image window.
10944 */
cristye941a752011-10-15 01:52:48 +000010945 (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y,
10946 exception);
cristy3ed852e2009-09-05 21:47:34 +000010947 paste_image=DestroyImage(paste_image);
10948 XSetCursorState(display,windows,MagickFalse);
10949 /*
10950 Update image colormap.
10951 */
cristy6710d842011-10-20 23:23:00 +000010952 XConfigureImageColormap(display,resource_info,windows,image,exception);
cristy051718b2011-08-28 22:49:25 +000010953 (void) XConfigureImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000010954 return(MagickTrue);
10955}
10956
10957/*
10958%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10959% %
10960% %
10961% %
10962+ X P r i n t I m a g e %
10963% %
10964% %
10965% %
10966%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10967%
10968% XPrintImage() prints an image to a Postscript printer.
10969%
10970% The format of the XPrintImage method is:
10971%
10972% MagickBooleanType XPrintImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000010973% XResourceInfo *resource_info,XWindows *windows,Image *image,
10974% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010975%
10976% A description of each parameter follows:
10977%
10978% o display: Specifies a connection to an X server; returned from
10979% XOpenDisplay.
10980%
10981% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10982%
10983% o windows: Specifies a pointer to a XWindows structure.
10984%
10985% o image: the image.
10986%
cristy051718b2011-08-28 22:49:25 +000010987% o exception: return any errors or warnings in this structure.
10988%
cristy3ed852e2009-09-05 21:47:34 +000010989*/
10990static MagickBooleanType XPrintImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000010991 XResourceInfo *resource_info,XWindows *windows,Image *image,
10992 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010993{
10994 char
10995 filename[MaxTextExtent],
10996 geometry[MaxTextExtent];
10997
10998 Image
10999 *print_image;
11000
11001 ImageInfo
11002 *image_info;
11003
11004 MagickStatusType
11005 status;
11006
11007 /*
11008 Request Postscript page geometry from user.
11009 */
11010 image_info=CloneImageInfo(resource_info->image_info);
cristyb51dff52011-05-19 16:55:47 +000011011 (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
cristy3ed852e2009-09-05 21:47:34 +000011012 if (image_info->page != (char *) NULL)
11013 (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
11014 XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
11015 "Select Postscript Page Geometry:",geometry);
11016 if (*geometry == '\0')
11017 return(MagickTrue);
11018 image_info->page=GetPageGeometry(geometry);
11019 /*
11020 Apply image transforms.
11021 */
11022 XSetCursorState(display,windows,MagickTrue);
11023 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +000011024 print_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000011025 if (print_image == (Image *) NULL)
11026 return(MagickFalse);
cristyb51dff52011-05-19 16:55:47 +000011027 (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
cristy3ed852e2009-09-05 21:47:34 +000011028 windows->image.ximage->width,windows->image.ximage->height);
cristye941a752011-10-15 01:52:48 +000011029 (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11030 exception);
cristy3ed852e2009-09-05 21:47:34 +000011031 /*
11032 Print image.
11033 */
11034 (void) AcquireUniqueFilename(filename);
cristyb51dff52011-05-19 16:55:47 +000011035 (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
cristy3ed852e2009-09-05 21:47:34 +000011036 filename);
cristy051718b2011-08-28 22:49:25 +000011037 status=WriteImage(image_info,print_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011038 (void) RelinquishUniqueFileResource(filename);
11039 print_image=DestroyImage(print_image);
11040 image_info=DestroyImageInfo(image_info);
11041 XSetCursorState(display,windows,MagickFalse);
11042 return(status != 0 ? MagickTrue : MagickFalse);
11043}
11044
11045/*
11046%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11047% %
11048% %
11049% %
11050+ X R O I I m a g e %
11051% %
11052% %
11053% %
11054%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11055%
11056% XROIImage() applies an image processing technique to a region of interest.
11057%
11058% The format of the XROIImage method is:
11059%
11060% MagickBooleanType XROIImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000011061% XResourceInfo *resource_info,XWindows *windows,Image **image,
11062% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011063%
11064% A description of each parameter follows:
11065%
11066% o display: Specifies a connection to an X server; returned from
11067% XOpenDisplay.
11068%
11069% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11070%
11071% o windows: Specifies a pointer to a XWindows structure.
11072%
11073% o image: the image; returned from ReadImage.
11074%
cristy051718b2011-08-28 22:49:25 +000011075% o exception: return any errors or warnings in this structure.
11076%
cristy3ed852e2009-09-05 21:47:34 +000011077*/
11078static MagickBooleanType XROIImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000011079 XResourceInfo *resource_info,XWindows *windows,Image **image,
11080 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011081{
11082#define ApplyMenus 7
11083
11084 static const char
11085 *ROIMenu[] =
11086 {
11087 "Help",
11088 "Dismiss",
11089 (char *) NULL
11090 },
11091 *ApplyMenu[] =
11092 {
11093 "File",
11094 "Edit",
11095 "Transform",
11096 "Enhance",
11097 "Effects",
11098 "F/X",
11099 "Miscellany",
11100 "Help",
11101 "Dismiss",
11102 (char *) NULL
11103 },
11104 *FileMenu[] =
11105 {
11106 "Save...",
11107 "Print...",
11108 (char *) NULL
11109 },
11110 *EditMenu[] =
11111 {
11112 "Undo",
11113 "Redo",
11114 (char *) NULL
11115 },
11116 *TransformMenu[] =
11117 {
11118 "Flop",
11119 "Flip",
11120 "Rotate Right",
11121 "Rotate Left",
11122 (char *) NULL
11123 },
11124 *EnhanceMenu[] =
11125 {
11126 "Hue...",
11127 "Saturation...",
11128 "Brightness...",
11129 "Gamma...",
11130 "Spiff",
11131 "Dull",
11132 "Contrast Stretch...",
11133 "Sigmoidal Contrast...",
11134 "Normalize",
11135 "Equalize",
11136 "Negate",
11137 "Grayscale",
11138 "Map...",
11139 "Quantize...",
11140 (char *) NULL
11141 },
11142 *EffectsMenu[] =
11143 {
11144 "Despeckle",
11145 "Emboss",
11146 "Reduce Noise",
11147 "Add Noise",
11148 "Sharpen...",
11149 "Blur...",
11150 "Threshold...",
11151 "Edge Detect...",
11152 "Spread...",
11153 "Shade...",
11154 "Raise...",
11155 "Segment...",
11156 (char *) NULL
11157 },
11158 *FXMenu[] =
11159 {
11160 "Solarize...",
11161 "Sepia Tone...",
11162 "Swirl...",
11163 "Implode...",
11164 "Vignette...",
11165 "Wave...",
11166 "Oil Paint...",
11167 "Charcoal Draw...",
11168 (char *) NULL
11169 },
11170 *MiscellanyMenu[] =
11171 {
11172 "Image Info",
11173 "Zoom Image",
11174 "Show Preview...",
11175 "Show Histogram",
11176 "Show Matte",
11177 (char *) NULL
11178 };
11179
11180 static const char
11181 **Menus[ApplyMenus] =
11182 {
11183 FileMenu,
11184 EditMenu,
11185 TransformMenu,
11186 EnhanceMenu,
11187 EffectsMenu,
11188 FXMenu,
11189 MiscellanyMenu
11190 };
11191
11192 static const CommandType
11193 ApplyCommands[] =
11194 {
11195 NullCommand,
11196 NullCommand,
11197 NullCommand,
11198 NullCommand,
11199 NullCommand,
11200 NullCommand,
11201 NullCommand,
11202 HelpCommand,
11203 QuitCommand
11204 },
11205 FileCommands[] =
11206 {
11207 SaveCommand,
11208 PrintCommand
11209 },
11210 EditCommands[] =
11211 {
11212 UndoCommand,
11213 RedoCommand
11214 },
11215 TransformCommands[] =
11216 {
11217 FlopCommand,
11218 FlipCommand,
11219 RotateRightCommand,
11220 RotateLeftCommand
11221 },
11222 EnhanceCommands[] =
11223 {
11224 HueCommand,
11225 SaturationCommand,
11226 BrightnessCommand,
11227 GammaCommand,
11228 SpiffCommand,
11229 DullCommand,
11230 ContrastStretchCommand,
11231 SigmoidalContrastCommand,
11232 NormalizeCommand,
11233 EqualizeCommand,
11234 NegateCommand,
11235 GrayscaleCommand,
11236 MapCommand,
11237 QuantizeCommand
11238 },
11239 EffectsCommands[] =
11240 {
11241 DespeckleCommand,
11242 EmbossCommand,
11243 ReduceNoiseCommand,
11244 AddNoiseCommand,
11245 SharpenCommand,
11246 BlurCommand,
11247 EdgeDetectCommand,
11248 SpreadCommand,
11249 ShadeCommand,
11250 RaiseCommand,
11251 SegmentCommand
11252 },
11253 FXCommands[] =
11254 {
11255 SolarizeCommand,
11256 SepiaToneCommand,
11257 SwirlCommand,
11258 ImplodeCommand,
11259 VignetteCommand,
11260 WaveCommand,
11261 OilPaintCommand,
11262 CharcoalDrawCommand
11263 },
11264 MiscellanyCommands[] =
11265 {
11266 InfoCommand,
11267 ZoomCommand,
11268 ShowPreviewCommand,
11269 ShowHistogramCommand,
11270 ShowMatteCommand
11271 },
11272 ROICommands[] =
11273 {
11274 ROIHelpCommand,
11275 ROIDismissCommand
11276 };
11277
11278 static const CommandType
11279 *Commands[ApplyMenus] =
11280 {
11281 FileCommands,
11282 EditCommands,
11283 TransformCommands,
11284 EnhanceCommands,
11285 EffectsCommands,
11286 FXCommands,
11287 MiscellanyCommands
11288 };
11289
11290 char
11291 command[MaxTextExtent],
11292 text[MaxTextExtent];
11293
11294 CommandType
11295 command_type;
11296
11297 Cursor
11298 cursor;
11299
11300 Image
11301 *roi_image;
11302
11303 int
11304 entry,
11305 id,
11306 x,
11307 y;
11308
11309 MagickRealType
11310 scale_factor;
11311
11312 MagickProgressMonitor
11313 progress_monitor;
11314
11315 RectangleInfo
11316 crop_info,
11317 highlight_info,
11318 roi_info;
11319
11320 unsigned int
11321 height,
11322 width;
11323
cristybb503372010-05-27 20:51:26 +000011324 size_t
cristy3ed852e2009-09-05 21:47:34 +000011325 state;
11326
11327 XEvent
11328 event;
11329
11330 /*
11331 Map Command widget.
11332 */
11333 (void) CloneString(&windows->command.name,"ROI");
11334 windows->command.data=0;
11335 (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11336 (void) XMapRaised(display,windows->command.id);
11337 XClientMessage(display,windows->image.id,windows->im_protocols,
11338 windows->im_update_widget,CurrentTime);
11339 /*
11340 Track pointer until button 1 is pressed.
11341 */
11342 XQueryPosition(display,windows->image.id,&x,&y);
11343 (void) XSelectInput(display,windows->image.id,
11344 windows->image.attributes.event_mask | PointerMotionMask);
cristy49e2d862010-11-12 02:50:30 +000011345 roi_info.x=(ssize_t) windows->image.x+x;
11346 roi_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +000011347 roi_info.width=0;
11348 roi_info.height=0;
11349 cursor=XCreateFontCursor(display,XC_fleur);
11350 state=DefaultState;
11351 do
11352 {
11353 if (windows->info.mapped != MagickFalse)
11354 {
11355 /*
11356 Display pointer position.
11357 */
cristyb51dff52011-05-19 16:55:47 +000011358 (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
cristyf2faecf2010-05-28 19:19:36 +000011359 (long) roi_info.x,(long) roi_info.y);
cristy3ed852e2009-09-05 21:47:34 +000011360 XInfoWidget(display,windows,text);
11361 }
11362 /*
11363 Wait for next event.
11364 */
cristy6710d842011-10-20 23:23:00 +000011365 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +000011366 if (event.xany.window == windows->command.id)
11367 {
11368 /*
11369 Select a command from the Command widget.
11370 */
11371 id=XCommandWidget(display,windows,ROIMenu,&event);
11372 if (id < 0)
11373 continue;
11374 switch (ROICommands[id])
11375 {
11376 case ROIHelpCommand:
11377 {
11378 XTextViewWidget(display,resource_info,windows,MagickFalse,
11379 "Help Viewer - Region of Interest",ImageROIHelp);
11380 break;
11381 }
11382 case ROIDismissCommand:
11383 {
11384 /*
11385 Prematurely exit.
11386 */
11387 state|=EscapeState;
11388 state|=ExitState;
11389 break;
11390 }
11391 default:
11392 break;
11393 }
11394 continue;
11395 }
11396 switch (event.type)
11397 {
11398 case ButtonPress:
11399 {
11400 if (event.xbutton.button != Button1)
11401 break;
11402 if (event.xbutton.window != windows->image.id)
11403 break;
11404 /*
11405 Note first corner of region of interest rectangle-- exit loop.
11406 */
11407 (void) XCheckDefineCursor(display,windows->image.id,cursor);
cristy49e2d862010-11-12 02:50:30 +000011408 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11409 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000011410 state|=ExitState;
11411 break;
11412 }
11413 case ButtonRelease:
11414 break;
11415 case Expose:
11416 break;
11417 case KeyPress:
11418 {
11419 KeySym
11420 key_symbol;
11421
11422 if (event.xkey.window != windows->image.id)
11423 break;
11424 /*
11425 Respond to a user key press.
11426 */
11427 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11428 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11429 switch ((int) key_symbol)
11430 {
11431 case XK_Escape:
11432 case XK_F20:
11433 {
11434 /*
11435 Prematurely exit.
11436 */
11437 state|=EscapeState;
11438 state|=ExitState;
11439 break;
11440 }
11441 case XK_F1:
11442 case XK_Help:
11443 {
11444 XTextViewWidget(display,resource_info,windows,MagickFalse,
11445 "Help Viewer - Region of Interest",ImageROIHelp);
11446 break;
11447 }
11448 default:
11449 {
11450 (void) XBell(display,0);
11451 break;
11452 }
11453 }
11454 break;
11455 }
11456 case MotionNotify:
11457 {
11458 /*
11459 Map and unmap Info widget as text cursor crosses its boundaries.
11460 */
11461 x=event.xmotion.x;
11462 y=event.xmotion.y;
11463 if (windows->info.mapped != MagickFalse)
11464 {
11465 if ((x < (int) (windows->info.x+windows->info.width)) &&
11466 (y < (int) (windows->info.y+windows->info.height)))
11467 (void) XWithdrawWindow(display,windows->info.id,
11468 windows->info.screen);
11469 }
11470 else
11471 if ((x > (int) (windows->info.x+windows->info.width)) ||
11472 (y > (int) (windows->info.y+windows->info.height)))
11473 (void) XMapWindow(display,windows->info.id);
cristy49e2d862010-11-12 02:50:30 +000011474 roi_info.x=(ssize_t) windows->image.x+x;
11475 roi_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +000011476 break;
11477 }
11478 default:
11479 break;
11480 }
11481 } while ((state & ExitState) == 0);
11482 (void) XSelectInput(display,windows->image.id,
11483 windows->image.attributes.event_mask);
11484 if ((state & EscapeState) != 0)
11485 {
11486 /*
11487 User want to exit without region of interest.
11488 */
11489 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11490 (void) XFreeCursor(display,cursor);
11491 return(MagickTrue);
11492 }
11493 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11494 do
11495 {
11496 /*
11497 Size rectangle as pointer moves until the mouse button is released.
11498 */
11499 x=(int) roi_info.x;
11500 y=(int) roi_info.y;
11501 roi_info.width=0;
11502 roi_info.height=0;
11503 state=DefaultState;
11504 do
11505 {
11506 highlight_info=roi_info;
11507 highlight_info.x=roi_info.x-windows->image.x;
11508 highlight_info.y=roi_info.y-windows->image.y;
11509 if ((highlight_info.width > 3) && (highlight_info.height > 3))
11510 {
11511 /*
11512 Display info and draw region of interest rectangle.
11513 */
11514 if (windows->info.mapped == MagickFalse)
11515 (void) XMapWindow(display,windows->info.id);
cristyb51dff52011-05-19 16:55:47 +000011516 (void) FormatLocaleString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +000011517 " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +000011518 roi_info.height,(double) roi_info.x,(double) roi_info.y);
cristy3ed852e2009-09-05 21:47:34 +000011519 XInfoWidget(display,windows,text);
11520 XHighlightRectangle(display,windows->image.id,
11521 windows->image.highlight_context,&highlight_info);
11522 }
11523 else
11524 if (windows->info.mapped != MagickFalse)
11525 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11526 /*
11527 Wait for next event.
11528 */
cristy6710d842011-10-20 23:23:00 +000011529 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +000011530 if ((highlight_info.width > 3) && (highlight_info.height > 3))
11531 XHighlightRectangle(display,windows->image.id,
11532 windows->image.highlight_context,&highlight_info);
11533 switch (event.type)
11534 {
11535 case ButtonPress:
11536 {
cristy49e2d862010-11-12 02:50:30 +000011537 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11538 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000011539 break;
11540 }
11541 case ButtonRelease:
11542 {
11543 /*
11544 User has committed to region of interest rectangle.
11545 */
cristy49e2d862010-11-12 02:50:30 +000011546 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11547 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000011548 XSetCursorState(display,windows,MagickFalse);
11549 state|=ExitState;
11550 if (LocaleCompare(windows->command.name,"Apply") == 0)
11551 break;
11552 (void) CloneString(&windows->command.name,"Apply");
11553 windows->command.data=ApplyMenus;
11554 (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11555 break;
11556 }
11557 case Expose:
11558 break;
11559 case MotionNotify:
11560 {
cristy49e2d862010-11-12 02:50:30 +000011561 roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11562 roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
cristy3ed852e2009-09-05 21:47:34 +000011563 }
11564 default:
11565 break;
11566 }
11567 if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11568 ((state & ExitState) != 0))
11569 {
11570 /*
11571 Check boundary conditions.
11572 */
11573 if (roi_info.x < 0)
11574 roi_info.x=0;
11575 else
cristy49e2d862010-11-12 02:50:30 +000011576 if (roi_info.x > (ssize_t) windows->image.ximage->width)
11577 roi_info.x=(ssize_t) windows->image.ximage->width;
cristy3ed852e2009-09-05 21:47:34 +000011578 if ((int) roi_info.x < x)
11579 roi_info.width=(unsigned int) (x-roi_info.x);
11580 else
11581 {
11582 roi_info.width=(unsigned int) (roi_info.x-x);
cristy49e2d862010-11-12 02:50:30 +000011583 roi_info.x=(ssize_t) x;
cristy3ed852e2009-09-05 21:47:34 +000011584 }
11585 if (roi_info.y < 0)
11586 roi_info.y=0;
11587 else
cristy49e2d862010-11-12 02:50:30 +000011588 if (roi_info.y > (ssize_t) windows->image.ximage->height)
11589 roi_info.y=(ssize_t) windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +000011590 if ((int) roi_info.y < y)
11591 roi_info.height=(unsigned int) (y-roi_info.y);
11592 else
11593 {
11594 roi_info.height=(unsigned int) (roi_info.y-y);
cristy49e2d862010-11-12 02:50:30 +000011595 roi_info.y=(ssize_t) y;
cristy3ed852e2009-09-05 21:47:34 +000011596 }
11597 }
11598 } while ((state & ExitState) == 0);
11599 /*
11600 Wait for user to grab a corner of the rectangle or press return.
11601 */
11602 state=DefaultState;
11603 command_type=NullCommand;
11604 (void) XMapWindow(display,windows->info.id);
11605 do
11606 {
11607 if (windows->info.mapped != MagickFalse)
11608 {
11609 /*
11610 Display pointer position.
11611 */
cristyb51dff52011-05-19 16:55:47 +000011612 (void) FormatLocaleString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +000011613 " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +000011614 roi_info.height,(double) roi_info.x,(double) roi_info.y);
cristy3ed852e2009-09-05 21:47:34 +000011615 XInfoWidget(display,windows,text);
11616 }
11617 highlight_info=roi_info;
11618 highlight_info.x=roi_info.x-windows->image.x;
11619 highlight_info.y=roi_info.y-windows->image.y;
11620 if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11621 {
11622 state|=EscapeState;
11623 state|=ExitState;
11624 break;
11625 }
11626 if ((state & UpdateRegionState) != 0)
11627 {
11628 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11629 switch (command_type)
11630 {
11631 case UndoCommand:
11632 case RedoCommand:
11633 {
11634 (void) XMagickCommand(display,resource_info,windows,command_type,
cristy051718b2011-08-28 22:49:25 +000011635 image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011636 break;
11637 }
11638 default:
11639 {
11640 /*
11641 Region of interest is relative to image configuration.
11642 */
11643 progress_monitor=SetImageProgressMonitor(*image,
11644 (MagickProgressMonitor) NULL,(*image)->client_data);
11645 crop_info=roi_info;
11646 width=(unsigned int) (*image)->columns;
11647 height=(unsigned int) (*image)->rows;
11648 x=0;
11649 y=0;
11650 if (windows->image.crop_geometry != (char *) NULL)
11651 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11652 &width,&height);
11653 scale_factor=(MagickRealType) width/windows->image.ximage->width;
11654 crop_info.x+=x;
cristy49e2d862010-11-12 02:50:30 +000011655 crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +000011656 crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11657 scale_factor=(MagickRealType)
11658 height/windows->image.ximage->height;
11659 crop_info.y+=y;
cristy49e2d862010-11-12 02:50:30 +000011660 crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +000011661 crop_info.height=(unsigned int)
11662 (scale_factor*crop_info.height+0.5);
cristy051718b2011-08-28 22:49:25 +000011663 roi_image=CropImage(*image,&crop_info,exception);
cristy3ed852e2009-09-05 21:47:34 +000011664 (void) SetImageProgressMonitor(*image,progress_monitor,
11665 (*image)->client_data);
11666 if (roi_image == (Image *) NULL)
11667 continue;
11668 /*
11669 Apply image processing technique to the region of interest.
11670 */
11671 windows->image.orphan=MagickTrue;
11672 (void) XMagickCommand(display,resource_info,windows,command_type,
cristy051718b2011-08-28 22:49:25 +000011673 &roi_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011674 progress_monitor=SetImageProgressMonitor(*image,
11675 (MagickProgressMonitor) NULL,(*image)->client_data);
11676 (void) XMagickCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000011677 SaveToUndoBufferCommand,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011678 windows->image.orphan=MagickFalse;
11679 (void) CompositeImage(*image,CopyCompositeOp,roi_image,
cristye941a752011-10-15 01:52:48 +000011680 crop_info.x,crop_info.y,exception);
cristy3ed852e2009-09-05 21:47:34 +000011681 roi_image=DestroyImage(roi_image);
11682 (void) SetImageProgressMonitor(*image,progress_monitor,
11683 (*image)->client_data);
11684 break;
11685 }
11686 }
11687 if (command_type != InfoCommand)
11688 {
cristy6710d842011-10-20 23:23:00 +000011689 XConfigureImageColormap(display,resource_info,windows,*image,
11690 exception);
11691 (void) XConfigureImage(display,resource_info,windows,*image,
11692 exception);
cristy3ed852e2009-09-05 21:47:34 +000011693 }
11694 XCheckRefreshWindows(display,windows);
11695 XInfoWidget(display,windows,text);
11696 (void) XSetFunction(display,windows->image.highlight_context,
11697 GXinvert);
11698 state&=(~UpdateRegionState);
11699 }
11700 XHighlightRectangle(display,windows->image.id,
11701 windows->image.highlight_context,&highlight_info);
cristy6710d842011-10-20 23:23:00 +000011702 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +000011703 if (event.xany.window == windows->command.id)
11704 {
11705 /*
11706 Select a command from the Command widget.
11707 */
11708 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11709 command_type=NullCommand;
11710 id=XCommandWidget(display,windows,ApplyMenu,&event);
11711 if (id >= 0)
11712 {
11713 (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11714 command_type=ApplyCommands[id];
11715 if (id < ApplyMenus)
11716 {
11717 /*
11718 Select a command from a pop-up menu.
11719 */
11720 entry=XMenuWidget(display,windows,ApplyMenu[id],
11721 (const char **) Menus[id],command);
11722 if (entry >= 0)
11723 {
11724 (void) CopyMagickString(command,Menus[id][entry],
11725 MaxTextExtent);
11726 command_type=Commands[id][entry];
11727 }
11728 }
11729 }
11730 (void) XSetFunction(display,windows->image.highlight_context,
11731 GXinvert);
11732 XHighlightRectangle(display,windows->image.id,
11733 windows->image.highlight_context,&highlight_info);
11734 if (command_type == HelpCommand)
11735 {
11736 (void) XSetFunction(display,windows->image.highlight_context,
11737 GXcopy);
11738 XTextViewWidget(display,resource_info,windows,MagickFalse,
11739 "Help Viewer - Region of Interest",ImageROIHelp);
11740 (void) XSetFunction(display,windows->image.highlight_context,
11741 GXinvert);
11742 continue;
11743 }
11744 if (command_type == QuitCommand)
11745 {
11746 /*
11747 exit.
11748 */
11749 state|=EscapeState;
11750 state|=ExitState;
11751 continue;
11752 }
11753 if (command_type != NullCommand)
11754 state|=UpdateRegionState;
11755 continue;
11756 }
11757 XHighlightRectangle(display,windows->image.id,
11758 windows->image.highlight_context,&highlight_info);
11759 switch (event.type)
11760 {
11761 case ButtonPress:
11762 {
11763 x=windows->image.x;
11764 y=windows->image.y;
11765 if (event.xbutton.button != Button1)
11766 break;
11767 if (event.xbutton.window != windows->image.id)
11768 break;
11769 x=windows->image.x+event.xbutton.x;
11770 y=windows->image.y+event.xbutton.y;
11771 if ((x < (int) (roi_info.x+RoiDelta)) &&
11772 (x > (int) (roi_info.x-RoiDelta)) &&
11773 (y < (int) (roi_info.y+RoiDelta)) &&
11774 (y > (int) (roi_info.y-RoiDelta)))
11775 {
cristybb503372010-05-27 20:51:26 +000011776 roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11777 roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
cristy3ed852e2009-09-05 21:47:34 +000011778 state|=UpdateConfigurationState;
11779 break;
11780 }
11781 if ((x < (int) (roi_info.x+RoiDelta)) &&
11782 (x > (int) (roi_info.x-RoiDelta)) &&
11783 (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11784 (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11785 {
cristybb503372010-05-27 20:51:26 +000011786 roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
cristy3ed852e2009-09-05 21:47:34 +000011787 state|=UpdateConfigurationState;
11788 break;
11789 }
11790 if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11791 (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11792 (y < (int) (roi_info.y+RoiDelta)) &&
11793 (y > (int) (roi_info.y-RoiDelta)))
11794 {
cristybb503372010-05-27 20:51:26 +000011795 roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
cristy3ed852e2009-09-05 21:47:34 +000011796 state|=UpdateConfigurationState;
11797 break;
11798 }
11799 if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11800 (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11801 (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11802 (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11803 {
11804 state|=UpdateConfigurationState;
11805 break;
11806 }
11807 }
11808 case ButtonRelease:
11809 {
11810 if (event.xbutton.window == windows->pan.id)
11811 if ((highlight_info.x != crop_info.x-windows->image.x) ||
11812 (highlight_info.y != crop_info.y-windows->image.y))
11813 XHighlightRectangle(display,windows->image.id,
11814 windows->image.highlight_context,&highlight_info);
11815 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11816 event.xbutton.time);
11817 break;
11818 }
11819 case Expose:
11820 {
11821 if (event.xexpose.window == windows->image.id)
11822 if (event.xexpose.count == 0)
11823 {
11824 event.xexpose.x=(int) highlight_info.x;
11825 event.xexpose.y=(int) highlight_info.y;
11826 event.xexpose.width=(int) highlight_info.width;
11827 event.xexpose.height=(int) highlight_info.height;
11828 XRefreshWindow(display,&windows->image,&event);
11829 }
11830 if (event.xexpose.window == windows->info.id)
11831 if (event.xexpose.count == 0)
11832 XInfoWidget(display,windows,text);
11833 break;
11834 }
11835 case KeyPress:
11836 {
11837 KeySym
11838 key_symbol;
11839
11840 if (event.xkey.window != windows->image.id)
11841 break;
11842 /*
11843 Respond to a user key press.
11844 */
11845 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11846 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11847 switch ((int) key_symbol)
11848 {
11849 case XK_Shift_L:
11850 case XK_Shift_R:
11851 break;
11852 case XK_Escape:
11853 case XK_F20:
11854 state|=EscapeState;
11855 case XK_Return:
11856 {
11857 state|=ExitState;
11858 break;
11859 }
11860 case XK_Home:
11861 case XK_KP_Home:
11862 {
cristybb503372010-05-27 20:51:26 +000011863 roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
cristy49e2d862010-11-12 02:50:30 +000011864 roi_info.y=(ssize_t) (windows->image.height/2L-
11865 roi_info.height/2L);
cristy3ed852e2009-09-05 21:47:34 +000011866 break;
11867 }
11868 case XK_Left:
11869 case XK_KP_Left:
11870 {
11871 roi_info.x--;
11872 break;
11873 }
11874 case XK_Up:
11875 case XK_KP_Up:
11876 case XK_Next:
11877 {
11878 roi_info.y--;
11879 break;
11880 }
11881 case XK_Right:
11882 case XK_KP_Right:
11883 {
11884 roi_info.x++;
11885 break;
11886 }
11887 case XK_Prior:
11888 case XK_Down:
11889 case XK_KP_Down:
11890 {
11891 roi_info.y++;
11892 break;
11893 }
11894 case XK_F1:
11895 case XK_Help:
11896 {
11897 (void) XSetFunction(display,windows->image.highlight_context,
11898 GXcopy);
11899 XTextViewWidget(display,resource_info,windows,MagickFalse,
11900 "Help Viewer - Region of Interest",ImageROIHelp);
11901 (void) XSetFunction(display,windows->image.highlight_context,
11902 GXinvert);
11903 break;
11904 }
11905 default:
11906 {
11907 command_type=XImageWindowCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000011908 event.xkey.state,key_symbol,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011909 if (command_type != NullCommand)
11910 state|=UpdateRegionState;
11911 break;
11912 }
11913 }
11914 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11915 event.xkey.time);
11916 break;
11917 }
11918 case KeyRelease:
11919 break;
11920 case MotionNotify:
11921 {
11922 if (event.xbutton.window != windows->image.id)
11923 break;
11924 /*
11925 Map and unmap Info widget as text cursor crosses its boundaries.
11926 */
11927 x=event.xmotion.x;
11928 y=event.xmotion.y;
11929 if (windows->info.mapped != MagickFalse)
11930 {
11931 if ((x < (int) (windows->info.x+windows->info.width)) &&
11932 (y < (int) (windows->info.y+windows->info.height)))
11933 (void) XWithdrawWindow(display,windows->info.id,
11934 windows->info.screen);
11935 }
11936 else
11937 if ((x > (int) (windows->info.x+windows->info.width)) ||
11938 (y > (int) (windows->info.y+windows->info.height)))
11939 (void) XMapWindow(display,windows->info.id);
cristy49e2d862010-11-12 02:50:30 +000011940 roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11941 roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
cristy3ed852e2009-09-05 21:47:34 +000011942 break;
11943 }
11944 case SelectionRequest:
11945 {
11946 XSelectionEvent
11947 notify;
11948
11949 XSelectionRequestEvent
11950 *request;
11951
11952 /*
11953 Set primary selection.
11954 */
cristyb51dff52011-05-19 16:55:47 +000011955 (void) FormatLocaleString(text,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +000011956 "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +000011957 roi_info.height,(double) roi_info.x,(double) roi_info.y);
cristy3ed852e2009-09-05 21:47:34 +000011958 request=(&(event.xselectionrequest));
11959 (void) XChangeProperty(request->display,request->requestor,
11960 request->property,request->target,8,PropModeReplace,
11961 (unsigned char *) text,(int) strlen(text));
11962 notify.type=SelectionNotify;
11963 notify.display=request->display;
11964 notify.requestor=request->requestor;
11965 notify.selection=request->selection;
11966 notify.target=request->target;
11967 notify.time=request->time;
11968 if (request->property == None)
11969 notify.property=request->target;
11970 else
11971 notify.property=request->property;
11972 (void) XSendEvent(request->display,request->requestor,False,0,
11973 (XEvent *) &notify);
11974 }
11975 default:
11976 break;
11977 }
11978 if ((state & UpdateConfigurationState) != 0)
11979 {
11980 (void) XPutBackEvent(display,&event);
11981 (void) XCheckDefineCursor(display,windows->image.id,cursor);
11982 break;
11983 }
11984 } while ((state & ExitState) == 0);
11985 } while ((state & ExitState) == 0);
11986 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11987 XSetCursorState(display,windows,MagickFalse);
11988 if ((state & EscapeState) != 0)
11989 return(MagickTrue);
11990 return(MagickTrue);
11991}
11992
11993/*
11994%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11995% %
11996% %
11997% %
11998+ X R o t a t e I m a g e %
11999% %
12000% %
12001% %
12002%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12003%
12004% XRotateImage() rotates the X image. If the degrees parameter if zero, the
12005% rotation angle is computed from the slope of a line drawn by the user.
12006%
12007% The format of the XRotateImage method is:
12008%
12009% MagickBooleanType XRotateImage(Display *display,
12010% XResourceInfo *resource_info,XWindows *windows,double degrees,
cristy051718b2011-08-28 22:49:25 +000012011% Image **image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012012%
12013% A description of each parameter follows:
12014%
12015% o display: Specifies a connection to an X server; returned from
12016% XOpenDisplay.
12017%
12018% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12019%
12020% o windows: Specifies a pointer to a XWindows structure.
12021%
12022% o degrees: Specifies the number of degrees to rotate the image.
12023%
12024% o image: the image.
12025%
cristy051718b2011-08-28 22:49:25 +000012026% o exception: return any errors or warnings in this structure.
12027%
cristy3ed852e2009-09-05 21:47:34 +000012028*/
12029static MagickBooleanType XRotateImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000012030 XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12031 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012032{
12033 static const char
12034 *RotateMenu[] =
12035 {
12036 "Pixel Color",
12037 "Direction",
12038 "Help",
12039 "Dismiss",
12040 (char *) NULL
12041 };
12042
12043 static ModeType
12044 direction = HorizontalRotateCommand;
12045
12046 static const ModeType
12047 DirectionCommands[] =
12048 {
12049 HorizontalRotateCommand,
12050 VerticalRotateCommand
12051 },
12052 RotateCommands[] =
12053 {
12054 RotateColorCommand,
12055 RotateDirectionCommand,
12056 RotateHelpCommand,
12057 RotateDismissCommand
12058 };
12059
12060 static unsigned int
12061 pen_id = 0;
12062
12063 char
12064 command[MaxTextExtent],
12065 text[MaxTextExtent];
12066
12067 Image
12068 *rotate_image;
12069
12070 int
12071 id,
12072 x,
12073 y;
12074
12075 MagickRealType
12076 normalized_degrees;
12077
12078 register int
12079 i;
12080
12081 unsigned int
12082 height,
12083 rotations,
12084 width;
12085
12086 if (degrees == 0.0)
12087 {
12088 unsigned int
12089 distance;
12090
cristybb503372010-05-27 20:51:26 +000012091 size_t
cristy3ed852e2009-09-05 21:47:34 +000012092 state;
12093
12094 XEvent
12095 event;
12096
12097 XSegment
12098 rotate_info;
12099
12100 /*
12101 Map Command widget.
12102 */
12103 (void) CloneString(&windows->command.name,"Rotate");
12104 windows->command.data=2;
12105 (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12106 (void) XMapRaised(display,windows->command.id);
12107 XClientMessage(display,windows->image.id,windows->im_protocols,
12108 windows->im_update_widget,CurrentTime);
12109 /*
12110 Wait for first button press.
12111 */
12112 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12113 XQueryPosition(display,windows->image.id,&x,&y);
12114 rotate_info.x1=x;
12115 rotate_info.y1=y;
12116 rotate_info.x2=x;
12117 rotate_info.y2=y;
12118 state=DefaultState;
12119 do
12120 {
12121 XHighlightLine(display,windows->image.id,
12122 windows->image.highlight_context,&rotate_info);
12123 /*
12124 Wait for next event.
12125 */
cristy6710d842011-10-20 23:23:00 +000012126 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +000012127 XHighlightLine(display,windows->image.id,
12128 windows->image.highlight_context,&rotate_info);
12129 if (event.xany.window == windows->command.id)
12130 {
12131 /*
12132 Select a command from the Command widget.
12133 */
12134 id=XCommandWidget(display,windows,RotateMenu,&event);
12135 if (id < 0)
12136 continue;
12137 (void) XSetFunction(display,windows->image.highlight_context,
12138 GXcopy);
12139 switch (RotateCommands[id])
12140 {
12141 case RotateColorCommand:
12142 {
12143 const char
12144 *ColorMenu[MaxNumberPens];
12145
12146 int
12147 pen_number;
12148
12149 XColor
12150 color;
12151
12152 /*
12153 Initialize menu selections.
12154 */
12155 for (i=0; i < (int) (MaxNumberPens-2); i++)
12156 ColorMenu[i]=resource_info->pen_colors[i];
12157 ColorMenu[MaxNumberPens-2]="Browser...";
12158 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12159 /*
12160 Select a pen color from the pop-up menu.
12161 */
12162 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12163 (const char **) ColorMenu,command);
12164 if (pen_number < 0)
12165 break;
12166 if (pen_number == (MaxNumberPens-2))
12167 {
12168 static char
12169 color_name[MaxTextExtent] = "gray";
12170
12171 /*
12172 Select a pen color from a dialog.
12173 */
12174 resource_info->pen_colors[pen_number]=color_name;
12175 XColorBrowserWidget(display,windows,"Select",color_name);
12176 if (*color_name == '\0')
12177 break;
12178 }
12179 /*
12180 Set pen color.
12181 */
12182 (void) XParseColor(display,windows->map_info->colormap,
12183 resource_info->pen_colors[pen_number],&color);
12184 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12185 (unsigned int) MaxColors,&color);
12186 windows->pixel_info->pen_colors[pen_number]=color;
12187 pen_id=(unsigned int) pen_number;
12188 break;
12189 }
12190 case RotateDirectionCommand:
12191 {
12192 static const char
12193 *Directions[] =
12194 {
12195 "horizontal",
12196 "vertical",
12197 (char *) NULL,
12198 };
12199
12200 /*
12201 Select a command from the pop-up menu.
12202 */
12203 id=XMenuWidget(display,windows,RotateMenu[id],
12204 Directions,command);
12205 if (id >= 0)
12206 direction=DirectionCommands[id];
12207 break;
12208 }
12209 case RotateHelpCommand:
12210 {
12211 XTextViewWidget(display,resource_info,windows,MagickFalse,
12212 "Help Viewer - Image Rotation",ImageRotateHelp);
12213 break;
12214 }
12215 case RotateDismissCommand:
12216 {
12217 /*
12218 Prematurely exit.
12219 */
12220 state|=EscapeState;
12221 state|=ExitState;
12222 break;
12223 }
12224 default:
12225 break;
12226 }
12227 (void) XSetFunction(display,windows->image.highlight_context,
12228 GXinvert);
12229 continue;
12230 }
12231 switch (event.type)
12232 {
12233 case ButtonPress:
12234 {
12235 if (event.xbutton.button != Button1)
12236 break;
12237 if (event.xbutton.window != windows->image.id)
12238 break;
12239 /*
12240 exit loop.
12241 */
12242 (void) XSetFunction(display,windows->image.highlight_context,
12243 GXcopy);
12244 rotate_info.x1=event.xbutton.x;
12245 rotate_info.y1=event.xbutton.y;
12246 state|=ExitState;
12247 break;
12248 }
12249 case ButtonRelease:
12250 break;
12251 case Expose:
12252 break;
12253 case KeyPress:
12254 {
12255 char
12256 command[MaxTextExtent];
12257
12258 KeySym
12259 key_symbol;
12260
12261 if (event.xkey.window != windows->image.id)
12262 break;
12263 /*
12264 Respond to a user key press.
12265 */
12266 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12267 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12268 switch ((int) key_symbol)
12269 {
12270 case XK_Escape:
12271 case XK_F20:
12272 {
12273 /*
12274 Prematurely exit.
12275 */
12276 state|=EscapeState;
12277 state|=ExitState;
12278 break;
12279 }
12280 case XK_F1:
12281 case XK_Help:
12282 {
12283 (void) XSetFunction(display,windows->image.highlight_context,
12284 GXcopy);
12285 XTextViewWidget(display,resource_info,windows,MagickFalse,
12286 "Help Viewer - Image Rotation",ImageRotateHelp);
12287 (void) XSetFunction(display,windows->image.highlight_context,
12288 GXinvert);
12289 break;
12290 }
12291 default:
12292 {
12293 (void) XBell(display,0);
12294 break;
12295 }
12296 }
12297 break;
12298 }
12299 case MotionNotify:
12300 {
12301 rotate_info.x1=event.xmotion.x;
12302 rotate_info.y1=event.xmotion.y;
12303 }
12304 }
12305 rotate_info.x2=rotate_info.x1;
12306 rotate_info.y2=rotate_info.y1;
12307 if (direction == HorizontalRotateCommand)
12308 rotate_info.x2+=32;
12309 else
12310 rotate_info.y2-=32;
12311 } while ((state & ExitState) == 0);
12312 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12313 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12314 if ((state & EscapeState) != 0)
12315 return(MagickTrue);
12316 /*
12317 Draw line as pointer moves until the mouse button is released.
12318 */
12319 distance=0;
12320 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12321 state=DefaultState;
12322 do
12323 {
12324 if (distance > 9)
12325 {
12326 /*
12327 Display info and draw rotation line.
12328 */
12329 if (windows->info.mapped == MagickFalse)
12330 (void) XMapWindow(display,windows->info.id);
cristyb51dff52011-05-19 16:55:47 +000012331 (void) FormatLocaleString(text,MaxTextExtent," %g",
cristy3ed852e2009-09-05 21:47:34 +000012332 direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12333 XInfoWidget(display,windows,text);
12334 XHighlightLine(display,windows->image.id,
12335 windows->image.highlight_context,&rotate_info);
12336 }
12337 else
12338 if (windows->info.mapped != MagickFalse)
12339 (void) XWithdrawWindow(display,windows->info.id,
12340 windows->info.screen);
12341 /*
12342 Wait for next event.
12343 */
cristy6710d842011-10-20 23:23:00 +000012344 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +000012345 if (distance > 9)
12346 XHighlightLine(display,windows->image.id,
12347 windows->image.highlight_context,&rotate_info);
12348 switch (event.type)
12349 {
12350 case ButtonPress:
12351 break;
12352 case ButtonRelease:
12353 {
12354 /*
12355 User has committed to rotation line.
12356 */
12357 rotate_info.x2=event.xbutton.x;
12358 rotate_info.y2=event.xbutton.y;
12359 state|=ExitState;
12360 break;
12361 }
12362 case Expose:
12363 break;
12364 case MotionNotify:
12365 {
12366 rotate_info.x2=event.xmotion.x;
12367 rotate_info.y2=event.xmotion.y;
12368 }
12369 default:
12370 break;
12371 }
12372 /*
12373 Check boundary conditions.
12374 */
12375 if (rotate_info.x2 < 0)
12376 rotate_info.x2=0;
12377 else
12378 if (rotate_info.x2 > (int) windows->image.width)
12379 rotate_info.x2=(short) windows->image.width;
12380 if (rotate_info.y2 < 0)
12381 rotate_info.y2=0;
12382 else
12383 if (rotate_info.y2 > (int) windows->image.height)
12384 rotate_info.y2=(short) windows->image.height;
12385 /*
12386 Compute rotation angle from the slope of the line.
12387 */
12388 degrees=0.0;
12389 distance=(unsigned int)
12390 ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12391 ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12392 if (distance > 9)
12393 degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12394 rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12395 } while ((state & ExitState) == 0);
12396 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12397 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12398 if (distance <= 9)
12399 return(MagickTrue);
12400 }
12401 if (direction == VerticalRotateCommand)
12402 degrees-=90.0;
12403 if (degrees == 0.0)
12404 return(MagickTrue);
12405 /*
12406 Rotate image.
12407 */
12408 normalized_degrees=degrees;
12409 while (normalized_degrees < -45.0)
12410 normalized_degrees+=360.0;
12411 for (rotations=0; normalized_degrees > 45.0; rotations++)
12412 normalized_degrees-=90.0;
12413 if (normalized_degrees != 0.0)
cristy051718b2011-08-28 22:49:25 +000012414 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12415 exception);
cristy3ed852e2009-09-05 21:47:34 +000012416 XSetCursorState(display,windows,MagickTrue);
12417 XCheckRefreshWindows(display,windows);
12418 (*image)->background_color.red=ScaleShortToQuantum(
12419 windows->pixel_info->pen_colors[pen_id].red);
12420 (*image)->background_color.green=ScaleShortToQuantum(
12421 windows->pixel_info->pen_colors[pen_id].green);
12422 (*image)->background_color.blue=ScaleShortToQuantum(
12423 windows->pixel_info->pen_colors[pen_id].blue);
cristy051718b2011-08-28 22:49:25 +000012424 rotate_image=RotateImage(*image,degrees,exception);
cristy3ed852e2009-09-05 21:47:34 +000012425 XSetCursorState(display,windows,MagickFalse);
12426 if (rotate_image == (Image *) NULL)
12427 return(MagickFalse);
12428 *image=DestroyImage(*image);
12429 *image=rotate_image;
12430 if (windows->image.crop_geometry != (char *) NULL)
12431 {
12432 /*
12433 Rotate crop geometry.
12434 */
12435 width=(unsigned int) (*image)->columns;
12436 height=(unsigned int) (*image)->rows;
12437 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12438 switch (rotations % 4)
12439 {
12440 default:
12441 case 0:
12442 break;
12443 case 1:
12444 {
12445 /*
12446 Rotate 90 degrees.
12447 */
cristyb51dff52011-05-19 16:55:47 +000012448 (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000012449 "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12450 (int) height-y,x);
12451 break;
12452 }
12453 case 2:
12454 {
12455 /*
12456 Rotate 180 degrees.
12457 */
cristyb51dff52011-05-19 16:55:47 +000012458 (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000012459 "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12460 break;
12461 }
12462 case 3:
12463 {
12464 /*
12465 Rotate 270 degrees.
12466 */
cristyb51dff52011-05-19 16:55:47 +000012467 (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000012468 "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12469 break;
12470 }
12471 }
12472 }
12473 if (windows->image.orphan != MagickFalse)
12474 return(MagickTrue);
12475 if (normalized_degrees != 0.0)
12476 {
12477 /*
12478 Update image colormap.
12479 */
12480 windows->image.window_changes.width=(int) (*image)->columns;
12481 windows->image.window_changes.height=(int) (*image)->rows;
12482 if (windows->image.crop_geometry != (char *) NULL)
12483 {
12484 /*
12485 Obtain dimensions of image from crop geometry.
12486 */
12487 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12488 &width,&height);
12489 windows->image.window_changes.width=(int) width;
12490 windows->image.window_changes.height=(int) height;
12491 }
cristy6710d842011-10-20 23:23:00 +000012492 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012493 }
12494 else
12495 if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12496 {
12497 windows->image.window_changes.width=windows->image.ximage->height;
12498 windows->image.window_changes.height=windows->image.ximage->width;
12499 }
12500 /*
12501 Update image configuration.
12502 */
cristy051718b2011-08-28 22:49:25 +000012503 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012504 return(MagickTrue);
12505}
12506
12507/*
12508%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12509% %
12510% %
12511% %
12512+ X S a v e I m a g e %
12513% %
12514% %
12515% %
12516%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12517%
12518% XSaveImage() saves an image to a file.
12519%
12520% The format of the XSaveImage method is:
12521%
12522% MagickBooleanType XSaveImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000012523% XResourceInfo *resource_info,XWindows *windows,Image *image,
12524% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012525%
12526% A description of each parameter follows:
12527%
12528% o display: Specifies a connection to an X server; returned from
12529% XOpenDisplay.
12530%
12531% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12532%
12533% o windows: Specifies a pointer to a XWindows structure.
12534%
12535% o image: the image.
12536%
cristy051718b2011-08-28 22:49:25 +000012537% o exception: return any errors or warnings in this structure.
12538%
cristy3ed852e2009-09-05 21:47:34 +000012539*/
12540static MagickBooleanType XSaveImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000012541 XResourceInfo *resource_info,XWindows *windows,Image *image,
12542 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012543{
12544 char
12545 filename[MaxTextExtent],
12546 geometry[MaxTextExtent];
12547
12548 Image
12549 *save_image;
12550
12551 ImageInfo
12552 *image_info;
12553
12554 MagickStatusType
12555 status;
12556
12557 /*
12558 Request file name from user.
12559 */
12560 if (resource_info->write_filename != (char *) NULL)
12561 (void) CopyMagickString(filename,resource_info->write_filename,
12562 MaxTextExtent);
12563 else
12564 {
12565 char
12566 path[MaxTextExtent];
12567
12568 int
12569 status;
12570
12571 GetPathComponent(image->filename,HeadPath,path);
12572 GetPathComponent(image->filename,TailPath,filename);
cristy0da1d642011-08-29 16:53:16 +000012573 if (*path != '\0')
12574 {
12575 status=chdir(path);
12576 if (status == -1)
12577 (void) ThrowMagickException(exception,GetMagickModule(),
12578 FileOpenError,"UnableToOpenFile","%s",path);
12579 }
cristy3ed852e2009-09-05 21:47:34 +000012580 }
12581 XFileBrowserWidget(display,windows,"Save",filename);
12582 if (*filename == '\0')
12583 return(MagickTrue);
12584 if (IsPathAccessible(filename) != MagickFalse)
12585 {
12586 int
12587 status;
12588
12589 /*
12590 File exists-- seek user's permission before overwriting.
12591 */
12592 status=XConfirmWidget(display,windows,"Overwrite",filename);
12593 if (status <= 0)
12594 return(MagickTrue);
12595 }
12596 image_info=CloneImageInfo(resource_info->image_info);
12597 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +000012598 (void) SetImageInfo(image_info,1,exception);
cristy3ed852e2009-09-05 21:47:34 +000012599 if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12600 (LocaleCompare(image_info->magick,"JPG") == 0))
12601 {
12602 char
12603 quality[MaxTextExtent];
12604
12605 int
12606 status;
12607
12608 /*
12609 Request JPEG quality from user.
12610 */
cristyb51dff52011-05-19 16:55:47 +000012611 (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +000012612 image->quality);
cristy3ed852e2009-09-05 21:47:34 +000012613 status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12614 quality);
12615 if (*quality == '\0')
12616 return(MagickTrue);
cristye27293e2009-12-18 02:53:20 +000012617 image->quality=StringToUnsignedLong(quality);
cristy3ed852e2009-09-05 21:47:34 +000012618 image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12619 }
12620 if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12621 (LocaleCompare(image_info->magick,"PDF") == 0) ||
12622 (LocaleCompare(image_info->magick,"PS") == 0) ||
12623 (LocaleCompare(image_info->magick,"PS2") == 0))
12624 {
12625 char
12626 geometry[MaxTextExtent];
12627
12628 /*
12629 Request page geometry from user.
12630 */
cristyb93d9e72009-09-12 20:02:21 +000012631 (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +000012632 if (LocaleCompare(image_info->magick,"PDF") == 0)
cristyb93d9e72009-09-12 20:02:21 +000012633 (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +000012634 if (image_info->page != (char *) NULL)
12635 (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12636 XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12637 "Select page geometry:",geometry);
12638 if (*geometry != '\0')
12639 image_info->page=GetPageGeometry(geometry);
12640 }
12641 /*
12642 Apply image transforms.
12643 */
12644 XSetCursorState(display,windows,MagickTrue);
12645 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +000012646 save_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000012647 if (save_image == (Image *) NULL)
12648 return(MagickFalse);
cristyb51dff52011-05-19 16:55:47 +000012649 (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
cristy3ed852e2009-09-05 21:47:34 +000012650 windows->image.ximage->width,windows->image.ximage->height);
cristye941a752011-10-15 01:52:48 +000012651 (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12652 exception);
cristy3ed852e2009-09-05 21:47:34 +000012653 /*
12654 Write image.
12655 */
12656 (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +000012657 status=WriteImage(image_info,save_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012658 if (status != MagickFalse)
12659 image->taint=MagickFalse;
12660 save_image=DestroyImage(save_image);
12661 image_info=DestroyImageInfo(image_info);
12662 XSetCursorState(display,windows,MagickFalse);
12663 return(status != 0 ? MagickTrue : MagickFalse);
12664}
12665
12666/*
12667%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12668% %
12669% %
12670% %
12671+ X S c r e e n E v e n t %
12672% %
12673% %
12674% %
12675%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12676%
12677% XScreenEvent() handles global events associated with the Pan and Magnify
12678% windows.
12679%
12680% The format of the XScreenEvent function is:
12681%
cristy6710d842011-10-20 23:23:00 +000012682% void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12683% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012684%
12685% A description of each parameter follows:
12686%
12687% o display: Specifies a pointer to the Display structure; returned from
12688% XOpenDisplay.
12689%
12690% o windows: Specifies a pointer to a XWindows structure.
12691%
12692% o event: Specifies a pointer to a X11 XEvent structure.
12693%
cristy6710d842011-10-20 23:23:00 +000012694% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +000012695%
12696*/
12697
12698#if defined(__cplusplus) || defined(c_plusplus)
12699extern "C" {
12700#endif
12701
12702static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12703{
12704 register XWindows
12705 *windows;
12706
12707 windows=(XWindows *) data;
12708 if ((event->type == ClientMessage) &&
12709 (event->xclient.window == windows->image.id))
12710 return(MagickFalse);
12711 return(MagickTrue);
12712}
12713
12714#if defined(__cplusplus) || defined(c_plusplus)
12715}
12716#endif
12717
cristy6710d842011-10-20 23:23:00 +000012718static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12719 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012720{
12721 register int
12722 x,
12723 y;
12724
12725 (void) XIfEvent(display,event,XPredicate,(char *) windows);
12726 if (event->xany.window == windows->command.id)
12727 return;
12728 switch (event->type)
12729 {
12730 case ButtonPress:
12731 case ButtonRelease:
12732 {
12733 if ((event->xbutton.button == Button3) &&
12734 (event->xbutton.state & Mod1Mask))
12735 {
12736 /*
12737 Convert Alt-Button3 to Button2.
12738 */
12739 event->xbutton.button=Button2;
12740 event->xbutton.state&=(~Mod1Mask);
12741 }
12742 if (event->xbutton.window == windows->backdrop.id)
12743 {
12744 (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12745 event->xbutton.time);
12746 break;
12747 }
12748 if (event->xbutton.window == windows->pan.id)
12749 {
cristy6710d842011-10-20 23:23:00 +000012750 XPanImage(display,windows,event,exception);
cristy3ed852e2009-09-05 21:47:34 +000012751 break;
12752 }
12753 if (event->xbutton.window == windows->image.id)
12754 if (event->xbutton.button == Button2)
12755 {
12756 /*
12757 Update magnified image.
12758 */
12759 x=event->xbutton.x;
12760 y=event->xbutton.y;
12761 if (x < 0)
12762 x=0;
12763 else
12764 if (x >= (int) windows->image.width)
12765 x=(int) (windows->image.width-1);
cristy49e2d862010-11-12 02:50:30 +000012766 windows->magnify.x=(int) windows->image.x+x;
cristy3ed852e2009-09-05 21:47:34 +000012767 if (y < 0)
12768 y=0;
12769 else
12770 if (y >= (int) windows->image.height)
12771 y=(int) (windows->image.height-1);
12772 windows->magnify.y=windows->image.y+y;
12773 if (windows->magnify.mapped == MagickFalse)
12774 (void) XMapRaised(display,windows->magnify.id);
cristy6710d842011-10-20 23:23:00 +000012775 XMakeMagnifyImage(display,windows,exception);
cristy3ed852e2009-09-05 21:47:34 +000012776 if (event->type == ButtonRelease)
12777 (void) XWithdrawWindow(display,windows->info.id,
12778 windows->info.screen);
12779 break;
12780 }
12781 break;
12782 }
12783 case ClientMessage:
12784 {
12785 /*
12786 If client window delete message, exit.
12787 */
12788 if (event->xclient.message_type != windows->wm_protocols)
12789 break;
cristyecd0ab52010-05-30 14:59:20 +000012790 if (*event->xclient.data.l != (long) windows->wm_delete_window)
cristy3ed852e2009-09-05 21:47:34 +000012791 break;
12792 if (event->xclient.window == windows->magnify.id)
12793 {
12794 (void) XWithdrawWindow(display,windows->magnify.id,
12795 windows->magnify.screen);
12796 break;
12797 }
12798 break;
12799 }
12800 case ConfigureNotify:
12801 {
12802 if (event->xconfigure.window == windows->magnify.id)
12803 {
12804 unsigned int
12805 magnify;
12806
12807 /*
12808 Magnify window has a new configuration.
12809 */
12810 windows->magnify.width=(unsigned int) event->xconfigure.width;
12811 windows->magnify.height=(unsigned int) event->xconfigure.height;
12812 if (windows->magnify.mapped == MagickFalse)
12813 break;
12814 magnify=1;
12815 while ((int) magnify <= event->xconfigure.width)
12816 magnify<<=1;
12817 while ((int) magnify <= event->xconfigure.height)
12818 magnify<<=1;
12819 magnify>>=1;
12820 if (((int) magnify != event->xconfigure.width) ||
12821 ((int) magnify != event->xconfigure.height))
12822 {
12823 XWindowChanges
12824 window_changes;
12825
12826 window_changes.width=(int) magnify;
12827 window_changes.height=(int) magnify;
12828 (void) XReconfigureWMWindow(display,windows->magnify.id,
12829 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12830 &window_changes);
12831 break;
12832 }
cristy6710d842011-10-20 23:23:00 +000012833 XMakeMagnifyImage(display,windows,exception);
cristy3ed852e2009-09-05 21:47:34 +000012834 break;
12835 }
12836 break;
12837 }
12838 case Expose:
12839 {
12840 if (event->xexpose.window == windows->image.id)
12841 {
12842 XRefreshWindow(display,&windows->image,event);
12843 break;
12844 }
12845 if (event->xexpose.window == windows->pan.id)
12846 if (event->xexpose.count == 0)
12847 {
12848 XDrawPanRectangle(display,windows);
12849 break;
12850 }
12851 if (event->xexpose.window == windows->magnify.id)
12852 if (event->xexpose.count == 0)
12853 {
cristy6710d842011-10-20 23:23:00 +000012854 XMakeMagnifyImage(display,windows,exception);
cristy3ed852e2009-09-05 21:47:34 +000012855 break;
12856 }
12857 break;
12858 }
12859 case KeyPress:
12860 {
12861 char
12862 command[MaxTextExtent];
12863
12864 KeySym
12865 key_symbol;
12866
12867 if (event->xkey.window != windows->magnify.id)
12868 break;
12869 /*
12870 Respond to a user key press.
12871 */
12872 (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12873 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
cristy6710d842011-10-20 23:23:00 +000012874 XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12875 exception);
cristy3ed852e2009-09-05 21:47:34 +000012876 break;
12877 }
12878 case MapNotify:
12879 {
12880 if (event->xmap.window == windows->magnify.id)
12881 {
12882 windows->magnify.mapped=MagickTrue;
12883 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12884 break;
12885 }
12886 if (event->xmap.window == windows->info.id)
12887 {
12888 windows->info.mapped=MagickTrue;
12889 break;
12890 }
12891 break;
12892 }
12893 case MotionNotify:
12894 {
12895 while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12896 if (event->xmotion.window == windows->image.id)
12897 if (windows->magnify.mapped != MagickFalse)
12898 {
12899 /*
12900 Update magnified image.
12901 */
12902 x=event->xmotion.x;
12903 y=event->xmotion.y;
12904 if (x < 0)
12905 x=0;
12906 else
12907 if (x >= (int) windows->image.width)
12908 x=(int) (windows->image.width-1);
cristy49e2d862010-11-12 02:50:30 +000012909 windows->magnify.x=(int) windows->image.x+x;
cristy3ed852e2009-09-05 21:47:34 +000012910 if (y < 0)
12911 y=0;
12912 else
12913 if (y >= (int) windows->image.height)
12914 y=(int) (windows->image.height-1);
12915 windows->magnify.y=windows->image.y+y;
cristy6710d842011-10-20 23:23:00 +000012916 XMakeMagnifyImage(display,windows,exception);
cristy3ed852e2009-09-05 21:47:34 +000012917 }
12918 break;
12919 }
12920 case UnmapNotify:
12921 {
12922 if (event->xunmap.window == windows->magnify.id)
12923 {
12924 windows->magnify.mapped=MagickFalse;
12925 break;
12926 }
12927 if (event->xunmap.window == windows->info.id)
12928 {
12929 windows->info.mapped=MagickFalse;
12930 break;
12931 }
12932 break;
12933 }
12934 default:
12935 break;
12936 }
12937}
12938
12939/*
12940%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12941% %
12942% %
12943% %
12944+ X S e t C r o p G e o m e t r y %
12945% %
12946% %
12947% %
12948%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12949%
12950% XSetCropGeometry() accepts a cropping geometry relative to the Image window
12951% and translates it to a cropping geometry relative to the image.
12952%
12953% The format of the XSetCropGeometry method is:
12954%
12955% void XSetCropGeometry(Display *display,XWindows *windows,
12956% RectangleInfo *crop_info,Image *image)
12957%
12958% A description of each parameter follows:
12959%
12960% o display: Specifies a connection to an X server; returned from
12961% XOpenDisplay.
12962%
12963% o windows: Specifies a pointer to a XWindows structure.
12964%
12965% o crop_info: A pointer to a RectangleInfo that defines a region of the
12966% Image window to crop.
12967%
12968% o image: the image.
12969%
12970*/
12971static void XSetCropGeometry(Display *display,XWindows *windows,
12972 RectangleInfo *crop_info,Image *image)
12973{
12974 char
12975 text[MaxTextExtent];
12976
12977 int
12978 x,
12979 y;
12980
12981 MagickRealType
12982 scale_factor;
12983
12984 unsigned int
12985 height,
12986 width;
12987
12988 if (windows->info.mapped != MagickFalse)
12989 {
12990 /*
12991 Display info on cropping rectangle.
12992 */
cristyb51dff52011-05-19 16:55:47 +000012993 (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
cristye8c25f92010-06-03 00:53:06 +000012994 (double) crop_info->width,(double) crop_info->height,(double)
12995 crop_info->x,(double) crop_info->y);
cristy3ed852e2009-09-05 21:47:34 +000012996 XInfoWidget(display,windows,text);
12997 }
12998 /*
12999 Cropping geometry is relative to any previous crop geometry.
13000 */
13001 x=0;
13002 y=0;
13003 width=(unsigned int) image->columns;
13004 height=(unsigned int) image->rows;
13005 if (windows->image.crop_geometry != (char *) NULL)
13006 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13007 else
13008 windows->image.crop_geometry=AcquireString((char *) NULL);
13009 /*
13010 Define the crop geometry string from the cropping rectangle.
13011 */
13012 scale_factor=(MagickRealType) width/windows->image.ximage->width;
13013 if (crop_info->x > 0)
13014 x+=(int) (scale_factor*crop_info->x+0.5);
13015 width=(unsigned int) (scale_factor*crop_info->width+0.5);
13016 if (width == 0)
13017 width=1;
13018 scale_factor=(MagickRealType) height/windows->image.ximage->height;
13019 if (crop_info->y > 0)
13020 y+=(int) (scale_factor*crop_info->y+0.5);
13021 height=(unsigned int) (scale_factor*crop_info->height+0.5);
13022 if (height == 0)
13023 height=1;
cristyb51dff52011-05-19 16:55:47 +000013024 (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000013025 "%ux%u%+d%+d",width,height,x,y);
13026}
13027
13028/*
13029%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13030% %
13031% %
13032% %
13033+ X T i l e I m a g e %
13034% %
13035% %
13036% %
13037%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13038%
13039% XTileImage() loads or deletes a selected tile from a visual image directory.
13040% The load or delete command is chosen from a menu.
13041%
13042% The format of the XTileImage method is:
13043%
13044% Image *XTileImage(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +000013045% XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000013046%
13047% A description of each parameter follows:
13048%
13049% o tile_image: XTileImage reads or deletes the tile image
13050% and returns it. A null image is returned if an error occurs.
13051%
13052% o display: Specifies a connection to an X server; returned from
13053% XOpenDisplay.
13054%
13055% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13056%
13057% o windows: Specifies a pointer to a XWindows structure.
13058%
13059% o image: the image; returned from ReadImage.
13060%
13061% o event: Specifies a pointer to a XEvent structure. If it is NULL,
13062% the entire image is refreshed.
13063%
cristy051718b2011-08-28 22:49:25 +000013064% o exception: return any errors or warnings in this structure.
13065%
cristy3ed852e2009-09-05 21:47:34 +000013066*/
13067static Image *XTileImage(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +000013068 XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000013069{
13070 static const char
13071 *VerbMenu[] =
13072 {
13073 "Load",
13074 "Next",
13075 "Former",
13076 "Delete",
13077 "Update",
13078 (char *) NULL,
13079 };
13080
13081 static const ModeType
13082 TileCommands[] =
13083 {
13084 TileLoadCommand,
13085 TileNextCommand,
13086 TileFormerCommand,
13087 TileDeleteCommand,
13088 TileUpdateCommand
13089 };
13090
13091 char
13092 command[MaxTextExtent],
13093 filename[MaxTextExtent];
13094
13095 Image
13096 *tile_image;
13097
13098 int
13099 id,
13100 status,
13101 tile,
13102 x,
13103 y;
13104
13105 MagickRealType
13106 scale_factor;
13107
13108 register char
13109 *p,
13110 *q;
13111
13112 register int
13113 i;
13114
13115 unsigned int
13116 height,
13117 width;
13118
13119 /*
13120 Tile image is relative to montage image configuration.
13121 */
13122 x=0;
13123 y=0;
13124 width=(unsigned int) image->columns;
13125 height=(unsigned int) image->rows;
13126 if (windows->image.crop_geometry != (char *) NULL)
13127 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13128 scale_factor=(MagickRealType) width/windows->image.ximage->width;
13129 event->xbutton.x+=windows->image.x;
13130 event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13131 scale_factor=(MagickRealType) height/windows->image.ximage->height;
13132 event->xbutton.y+=windows->image.y;
13133 event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13134 /*
13135 Determine size and location of each tile in the visual image directory.
13136 */
13137 width=(unsigned int) image->columns;
13138 height=(unsigned int) image->rows;
13139 x=0;
13140 y=0;
13141 (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13142 tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13143 (event->xbutton.x-x)/width;
13144 if (tile < 0)
13145 {
13146 /*
13147 Button press is outside any tile.
13148 */
13149 (void) XBell(display,0);
13150 return((Image *) NULL);
13151 }
13152 /*
13153 Determine file name from the tile directory.
13154 */
13155 p=image->directory;
13156 for (i=tile; (i != 0) && (*p != '\0'); )
13157 {
13158 if (*p == '\n')
13159 i--;
13160 p++;
13161 }
13162 if (*p == '\0')
13163 {
13164 /*
13165 Button press is outside any tile.
13166 */
13167 (void) XBell(display,0);
13168 return((Image *) NULL);
13169 }
13170 /*
13171 Select a command from the pop-up menu.
13172 */
13173 id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13174 if (id < 0)
13175 return((Image *) NULL);
13176 q=p;
13177 while ((*q != '\n') && (*q != '\0'))
13178 q++;
13179 (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13180 /*
13181 Perform command for the selected tile.
13182 */
13183 XSetCursorState(display,windows,MagickTrue);
13184 XCheckRefreshWindows(display,windows);
13185 tile_image=NewImageList();
13186 switch (TileCommands[id])
13187 {
13188 case TileLoadCommand:
13189 {
13190 /*
13191 Load tile image.
13192 */
13193 XCheckRefreshWindows(display,windows);
13194 (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13195 MaxTextExtent);
13196 (void) CopyMagickString(resource_info->image_info->filename,filename,
13197 MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +000013198 tile_image=ReadImage(resource_info->image_info,exception);
13199 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +000013200 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13201 break;
13202 }
13203 case TileNextCommand:
13204 {
13205 /*
13206 Display next image.
13207 */
13208 XClientMessage(display,windows->image.id,windows->im_protocols,
13209 windows->im_next_image,CurrentTime);
13210 break;
13211 }
13212 case TileFormerCommand:
13213 {
13214 /*
13215 Display former image.
13216 */
13217 XClientMessage(display,windows->image.id,windows->im_protocols,
13218 windows->im_former_image,CurrentTime);
13219 break;
13220 }
13221 case TileDeleteCommand:
13222 {
13223 /*
13224 Delete tile image.
13225 */
13226 if (IsPathAccessible(filename) == MagickFalse)
13227 {
13228 XNoticeWidget(display,windows,"Image file does not exist:",filename);
13229 break;
13230 }
13231 status=XConfirmWidget(display,windows,"Really delete tile",filename);
13232 if (status <= 0)
13233 break;
cristy18c6c272011-09-23 14:40:37 +000013234 status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +000013235 if (status != MagickFalse)
13236 {
13237 XNoticeWidget(display,windows,"Unable to delete image file:",
13238 filename);
13239 break;
13240 }
13241 }
13242 case TileUpdateCommand:
13243 {
cristy3ed852e2009-09-05 21:47:34 +000013244 int
13245 x_offset,
13246 y_offset;
13247
cristy101ab702011-10-13 13:06:32 +000013248 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +000013249 pixel;
13250
cristy2ed42f62011-10-02 19:49:57 +000013251 Quantum
cristy5f95f4f2011-10-23 01:01:01 +000013252 virtual_pixel[CompositePixelChannel];
cristy2ed42f62011-10-02 19:49:57 +000013253
cristy3ed852e2009-09-05 21:47:34 +000013254 register int
13255 j;
13256
cristy4c08aed2011-07-01 19:47:50 +000013257 register Quantum
cristy3ed852e2009-09-05 21:47:34 +000013258 *s;
13259
13260 /*
13261 Ensure all the images exist.
13262 */
13263 tile=0;
cristy101ab702011-10-13 13:06:32 +000013264 GetPixelInfo(image,&pixel);
cristy3ed852e2009-09-05 21:47:34 +000013265 for (p=image->directory; *p != '\0'; p++)
13266 {
cristyf7c25522010-11-15 01:25:14 +000013267 CacheView
13268 *image_view;
13269
cristy3ed852e2009-09-05 21:47:34 +000013270 q=p;
13271 while ((*q != '\n') && (*q != '\0'))
13272 q++;
13273 (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13274 p=q;
13275 if (IsPathAccessible(filename) != MagickFalse)
13276 {
13277 tile++;
13278 continue;
13279 }
13280 /*
13281 Overwrite tile with background color.
13282 */
13283 x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13284 y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
cristy49e2d862010-11-12 02:50:30 +000013285 image_view=AcquireCacheView(image);
cristy2ed42f62011-10-02 19:49:57 +000013286 (void) GetOneCacheViewVirtualPixel(image_view,0,0,virtual_pixel,
13287 exception);
13288 pixel.red=virtual_pixel[RedPixelChannel];
13289 pixel.green=virtual_pixel[GreenPixelChannel];
13290 pixel.blue=virtual_pixel[BluePixelChannel];
13291 pixel.alpha=virtual_pixel[AlphaPixelChannel];
cristy3ed852e2009-09-05 21:47:34 +000013292 for (i=0; i < (int) height; i++)
13293 {
cristy49e2d862010-11-12 02:50:30 +000013294 s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13295 y_offset+i,width,1,exception);
cristy4c08aed2011-07-01 19:47:50 +000013296 if (s == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000013297 break;
13298 for (j=0; j < (int) width; j++)
cristy4c08aed2011-07-01 19:47:50 +000013299 {
cristy803640d2011-11-17 02:11:32 +000013300 SetPixelInfoPixel(image,&pixel,s);
cristyed231572011-07-14 02:18:59 +000013301 s+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +000013302 }
cristy49e2d862010-11-12 02:50:30 +000013303 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000013304 break;
13305 }
cristyca1628f2010-11-15 01:17:49 +000013306 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +000013307 tile++;
13308 }
13309 windows->image.window_changes.width=(int) image->columns;
13310 windows->image.window_changes.height=(int) image->rows;
cristy6710d842011-10-20 23:23:00 +000013311 XConfigureImageColormap(display,resource_info,windows,image,exception);
cristy051718b2011-08-28 22:49:25 +000013312 (void) XConfigureImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013313 break;
13314 }
13315 default:
13316 break;
13317 }
13318 XSetCursorState(display,windows,MagickFalse);
13319 return(tile_image);
13320}
13321
13322/*
13323%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13324% %
13325% %
13326% %
13327+ X T r a n s l a t e I m a g e %
13328% %
13329% %
13330% %
13331%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13332%
13333% XTranslateImage() translates the image within an Image window by one pixel
13334% as specified by the key symbol. If the image has a `montage string the
13335% translation is respect to the width and height contained within the string.
13336%
13337% The format of the XTranslateImage method is:
13338%
13339% void XTranslateImage(Display *display,XWindows *windows,
13340% Image *image,const KeySym key_symbol)
13341%
13342% A description of each parameter follows:
13343%
13344% o display: Specifies a connection to an X server; returned from
13345% XOpenDisplay.
13346%
13347% o windows: Specifies a pointer to a XWindows structure.
13348%
13349% o image: the image.
13350%
13351% o key_symbol: Specifies a KeySym which indicates which side of the image
13352% to trim.
13353%
13354*/
13355static void XTranslateImage(Display *display,XWindows *windows,
13356 Image *image,const KeySym key_symbol)
13357{
13358 char
13359 text[MaxTextExtent];
13360
13361 int
13362 x,
13363 y;
13364
13365 unsigned int
13366 x_offset,
13367 y_offset;
13368
13369 /*
13370 User specified a pan position offset.
13371 */
13372 x_offset=windows->image.width;
13373 y_offset=windows->image.height;
13374 if (image->montage != (char *) NULL)
13375 (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13376 switch ((int) key_symbol)
13377 {
13378 case XK_Home:
13379 case XK_KP_Home:
13380 {
13381 windows->image.x=(int) windows->image.width/2;
13382 windows->image.y=(int) windows->image.height/2;
13383 break;
13384 }
13385 case XK_Left:
13386 case XK_KP_Left:
13387 {
13388 windows->image.x-=x_offset;
13389 break;
13390 }
13391 case XK_Next:
13392 case XK_Up:
13393 case XK_KP_Up:
13394 {
13395 windows->image.y-=y_offset;
13396 break;
13397 }
13398 case XK_Right:
13399 case XK_KP_Right:
13400 {
13401 windows->image.x+=x_offset;
13402 break;
13403 }
13404 case XK_Prior:
13405 case XK_Down:
13406 case XK_KP_Down:
13407 {
13408 windows->image.y+=y_offset;
13409 break;
13410 }
13411 default:
13412 return;
13413 }
13414 /*
13415 Check boundary conditions.
13416 */
13417 if (windows->image.x < 0)
13418 windows->image.x=0;
13419 else
13420 if ((int) (windows->image.x+windows->image.width) >
13421 windows->image.ximage->width)
cristy49e2d862010-11-12 02:50:30 +000013422 windows->image.x=(int) windows->image.ximage->width-windows->image.width;
cristy3ed852e2009-09-05 21:47:34 +000013423 if (windows->image.y < 0)
13424 windows->image.y=0;
13425 else
13426 if ((int) (windows->image.y+windows->image.height) >
13427 windows->image.ximage->height)
cristy49e2d862010-11-12 02:50:30 +000013428 windows->image.y=(int) windows->image.ximage->height-windows->image.height;
cristy3ed852e2009-09-05 21:47:34 +000013429 /*
13430 Refresh Image window.
13431 */
cristyb51dff52011-05-19 16:55:47 +000013432 (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
cristy3ed852e2009-09-05 21:47:34 +000013433 windows->image.width,windows->image.height,windows->image.x,
13434 windows->image.y);
13435 XInfoWidget(display,windows,text);
13436 XCheckRefreshWindows(display,windows);
13437 XDrawPanRectangle(display,windows);
13438 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13439 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13440}
13441
13442/*
13443%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13444% %
13445% %
13446% %
13447+ X T r i m I m a g e %
13448% %
13449% %
13450% %
13451%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13452%
13453% XTrimImage() trims the edges from the Image window.
13454%
13455% The format of the XTrimImage method is:
13456%
13457% MagickBooleanType XTrimImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000013458% XResourceInfo *resource_info,XWindows *windows,Image *image,
13459% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000013460%
13461% A description of each parameter follows:
13462%
13463% o display: Specifies a connection to an X server; returned from
13464% XOpenDisplay.
13465%
13466% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13467%
13468% o windows: Specifies a pointer to a XWindows structure.
13469%
13470% o image: the image.
13471%
cristy051718b2011-08-28 22:49:25 +000013472% o exception: return any errors or warnings in this structure.
13473%
cristy3ed852e2009-09-05 21:47:34 +000013474*/
13475static MagickBooleanType XTrimImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000013476 XResourceInfo *resource_info,XWindows *windows,Image *image,
13477 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000013478{
13479 RectangleInfo
13480 trim_info;
13481
13482 register int
13483 x,
13484 y;
13485
cristybb503372010-05-27 20:51:26 +000013486 size_t
cristy3ed852e2009-09-05 21:47:34 +000013487 background,
13488 pixel;
13489
13490 /*
13491 Trim edges from image.
13492 */
13493 XSetCursorState(display,windows,MagickTrue);
13494 XCheckRefreshWindows(display,windows);
13495 /*
13496 Crop the left edge.
13497 */
13498 background=XGetPixel(windows->image.ximage,0,0);
cristybb503372010-05-27 20:51:26 +000013499 trim_info.width=(size_t) windows->image.ximage->width;
cristy3ed852e2009-09-05 21:47:34 +000013500 for (x=0; x < windows->image.ximage->width; x++)
13501 {
13502 for (y=0; y < windows->image.ximage->height; y++)
13503 {
13504 pixel=XGetPixel(windows->image.ximage,x,y);
13505 if (pixel != background)
13506 break;
13507 }
13508 if (y < windows->image.ximage->height)
13509 break;
13510 }
cristy49e2d862010-11-12 02:50:30 +000013511 trim_info.x=(ssize_t) x;
13512 if (trim_info.x == (ssize_t) windows->image.ximage->width)
cristy3ed852e2009-09-05 21:47:34 +000013513 {
13514 XSetCursorState(display,windows,MagickFalse);
13515 return(MagickFalse);
13516 }
13517 /*
13518 Crop the right edge.
13519 */
13520 background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13521 for (x=windows->image.ximage->width-1; x != 0; x--)
13522 {
13523 for (y=0; y < windows->image.ximage->height; y++)
13524 {
13525 pixel=XGetPixel(windows->image.ximage,x,y);
13526 if (pixel != background)
13527 break;
13528 }
13529 if (y < windows->image.ximage->height)
13530 break;
13531 }
cristybb503372010-05-27 20:51:26 +000013532 trim_info.width=(size_t) (x-trim_info.x+1);
cristy3ed852e2009-09-05 21:47:34 +000013533 /*
13534 Crop the top edge.
13535 */
13536 background=XGetPixel(windows->image.ximage,0,0);
cristybb503372010-05-27 20:51:26 +000013537 trim_info.height=(size_t) windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +000013538 for (y=0; y < windows->image.ximage->height; y++)
13539 {
13540 for (x=0; x < windows->image.ximage->width; x++)
13541 {
13542 pixel=XGetPixel(windows->image.ximage,x,y);
13543 if (pixel != background)
13544 break;
13545 }
13546 if (x < windows->image.ximage->width)
13547 break;
13548 }
cristy49e2d862010-11-12 02:50:30 +000013549 trim_info.y=(ssize_t) y;
cristy3ed852e2009-09-05 21:47:34 +000013550 /*
13551 Crop the bottom edge.
13552 */
13553 background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13554 for (y=windows->image.ximage->height-1; y != 0; y--)
13555 {
13556 for (x=0; x < windows->image.ximage->width; x++)
13557 {
13558 pixel=XGetPixel(windows->image.ximage,x,y);
13559 if (pixel != background)
13560 break;
13561 }
13562 if (x < windows->image.ximage->width)
13563 break;
13564 }
cristybb503372010-05-27 20:51:26 +000013565 trim_info.height=(size_t) y-trim_info.y+1;
cristy3ed852e2009-09-05 21:47:34 +000013566 if (((unsigned int) trim_info.width != windows->image.width) ||
13567 ((unsigned int) trim_info.height != windows->image.height))
13568 {
13569 /*
13570 Reconfigure Image window as defined by the trimming rectangle.
13571 */
13572 XSetCropGeometry(display,windows,&trim_info,image);
13573 windows->image.window_changes.width=(int) trim_info.width;
13574 windows->image.window_changes.height=(int) trim_info.height;
cristy051718b2011-08-28 22:49:25 +000013575 (void) XConfigureImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013576 }
13577 XSetCursorState(display,windows,MagickFalse);
13578 return(MagickTrue);
13579}
13580
13581/*
13582%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13583% %
13584% %
13585% %
13586+ X V i s u a l D i r e c t o r y I m a g e %
13587% %
13588% %
13589% %
13590%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13591%
13592% XVisualDirectoryImage() creates a Visual Image Directory.
13593%
13594% The format of the XVisualDirectoryImage method is:
13595%
13596% Image *XVisualDirectoryImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000013597% XResourceInfo *resource_info,XWindows *windows,
13598% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000013599%
13600% A description of each parameter follows:
13601%
cristy3ed852e2009-09-05 21:47:34 +000013602% o display: Specifies a connection to an X server; returned from
13603% XOpenDisplay.
13604%
13605% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13606%
13607% o windows: Specifies a pointer to a XWindows structure.
13608%
cristy051718b2011-08-28 22:49:25 +000013609% o exception: return any errors or warnings in this structure.
13610%
cristy3ed852e2009-09-05 21:47:34 +000013611*/
13612static Image *XVisualDirectoryImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000013613 XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000013614{
13615#define TileImageTag "Scale/Image"
13616#define XClientName "montage"
13617
13618 char
13619 **filelist;
13620
cristy3ed852e2009-09-05 21:47:34 +000013621 Image
13622 *images,
13623 *montage_image,
13624 *next_image,
13625 *thumbnail_image;
13626
13627 ImageInfo
13628 *read_info;
13629
13630 int
13631 number_files;
13632
13633 MagickBooleanType
13634 backdrop;
13635
13636 MagickStatusType
13637 status;
13638
13639 MontageInfo
13640 *montage_info;
13641
13642 RectangleInfo
13643 geometry;
13644
13645 register int
13646 i;
13647
13648 static char
13649 filename[MaxTextExtent] = "\0",
13650 filenames[MaxTextExtent] = "*";
13651
13652 XResourceInfo
13653 background_resources;
13654
13655 /*
13656 Request file name from user.
13657 */
13658 XFileBrowserWidget(display,windows,"Directory",filenames);
13659 if (*filenames == '\0')
13660 return((Image *) NULL);
13661 /*
13662 Expand the filenames.
13663 */
cristy73bd4a52010-10-05 11:24:23 +000013664 filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
cristy3ed852e2009-09-05 21:47:34 +000013665 if (filelist == (char **) NULL)
13666 {
13667 ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13668 filenames);
13669 return((Image *) NULL);
13670 }
13671 number_files=1;
13672 filelist[0]=filenames;
13673 status=ExpandFilenames(&number_files,&filelist);
13674 if ((status == MagickFalse) || (number_files == 0))
13675 {
13676 if (number_files == 0)
13677 ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13678 else
13679 ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13680 filenames);
13681 return((Image *) NULL);
13682 }
13683 /*
13684 Set image background resources.
13685 */
13686 background_resources=(*resource_info);
13687 background_resources.window_id=AcquireString("");
cristyb51dff52011-05-19 16:55:47 +000013688 (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000013689 "0x%lx",windows->image.id);
13690 background_resources.backdrop=MagickTrue;
13691 /*
13692 Read each image and convert them to a tile.
13693 */
13694 backdrop=(windows->visual_info->klass == TrueColor) ||
13695 (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13696 read_info=CloneImageInfo(resource_info->image_info);
cristy9ce61202010-11-24 00:38:37 +000013697 (void) SetImageOption(read_info,"jpeg:size","120x120");
13698 (void) CloneString(&read_info->size,DefaultTileGeometry);
cristy3ed852e2009-09-05 21:47:34 +000013699 (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13700 (void *) NULL);
13701 images=NewImageList();
cristy3ed852e2009-09-05 21:47:34 +000013702 XSetCursorState(display,windows,MagickTrue);
13703 XCheckRefreshWindows(display,windows);
cristy49e2d862010-11-12 02:50:30 +000013704 for (i=0; i < (int) number_files; i++)
cristy3ed852e2009-09-05 21:47:34 +000013705 {
13706 (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13707 filelist[i]=DestroyString(filelist[i]);
13708 *read_info->magick='\0';
cristy3ed852e2009-09-05 21:47:34 +000013709 next_image=ReadImage(read_info,exception);
13710 CatchException(exception);
13711 if (next_image != (Image *) NULL)
13712 {
13713 (void) DeleteImageProperty(next_image,"label");
cristy77619442010-11-24 00:27:23 +000013714 (void) SetImageProperty(next_image,"label",InterpretImageProperties(
cristyd15e6592011-10-15 00:13:06 +000013715 read_info,next_image,DefaultTileLabel,exception),exception);
cristy3ed852e2009-09-05 21:47:34 +000013716 (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13717 exception);
13718 thumbnail_image=ThumbnailImage(next_image,geometry.width,
13719 geometry.height,exception);
13720 if (thumbnail_image != (Image *) NULL)
13721 {
13722 next_image=DestroyImage(next_image);
13723 next_image=thumbnail_image;
13724 }
13725 if (backdrop)
13726 {
13727 (void) XDisplayBackgroundImage(display,&background_resources,
cristy051718b2011-08-28 22:49:25 +000013728 next_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013729 XSetCursorState(display,windows,MagickTrue);
13730 }
13731 AppendImageToList(&images,next_image);
13732 if (images->progress_monitor != (MagickProgressMonitor) NULL)
13733 {
13734 MagickBooleanType
13735 proceed;
13736
13737 proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13738 (MagickSizeType) number_files);
13739 if (proceed == MagickFalse)
13740 break;
13741 }
13742 }
13743 }
cristy3ed852e2009-09-05 21:47:34 +000013744 filelist=(char **) RelinquishMagickMemory(filelist);
cristy3ed852e2009-09-05 21:47:34 +000013745 if (images == (Image *) NULL)
13746 {
cristy8d52fca2010-11-24 00:45:05 +000013747 read_info=DestroyImageInfo(read_info);
cristy3ed852e2009-09-05 21:47:34 +000013748 XSetCursorState(display,windows,MagickFalse);
13749 ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13750 return((Image *) NULL);
13751 }
13752 /*
13753 Create the Visual Image Directory.
13754 */
cristy8d52fca2010-11-24 00:45:05 +000013755 montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13756 montage_info->pointsize=10;
cristy3ed852e2009-09-05 21:47:34 +000013757 if (resource_info->font != (char *) NULL)
13758 (void) CloneString(&montage_info->font,resource_info->font);
13759 (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
cristy8d52fca2010-11-24 00:45:05 +000013760 montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
cristy051718b2011-08-28 22:49:25 +000013761 images),exception);
cristy3ed852e2009-09-05 21:47:34 +000013762 images=DestroyImageList(images);
cristy8d52fca2010-11-24 00:45:05 +000013763 montage_info=DestroyMontageInfo(montage_info);
13764 read_info=DestroyImageInfo(read_info);
cristy3ed852e2009-09-05 21:47:34 +000013765 XSetCursorState(display,windows,MagickFalse);
13766 if (montage_image == (Image *) NULL)
13767 return(montage_image);
13768 XClientMessage(display,windows->image.id,windows->im_protocols,
13769 windows->im_next_image,CurrentTime);
13770 return(montage_image);
13771}
13772
13773/*
13774%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13775% %
13776% %
13777% %
13778% X D i s p l a y B a c k g r o u n d I m a g e %
13779% %
13780% %
13781% %
13782%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13783%
13784% XDisplayBackgroundImage() displays an image in the background of a window.
13785%
13786% The format of the XDisplayBackgroundImage method is:
13787%
13788% MagickBooleanType XDisplayBackgroundImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000013789% XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000013790%
13791% A description of each parameter follows:
13792%
13793% o display: Specifies a connection to an X server; returned from
13794% XOpenDisplay.
13795%
13796% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13797%
13798% o image: the image.
13799%
cristy051718b2011-08-28 22:49:25 +000013800% o exception: return any errors or warnings in this structure.
13801%
cristy3ed852e2009-09-05 21:47:34 +000013802*/
13803MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000013804 XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000013805{
13806 char
13807 geometry[MaxTextExtent],
13808 visual_type[MaxTextExtent];
13809
13810 int
13811 height,
13812 status,
13813 width;
13814
13815 RectangleInfo
13816 geometry_info;
13817
13818 static XPixelInfo
13819 pixel;
13820
13821 static XStandardColormap
13822 *map_info;
13823
13824 static XVisualInfo
13825 *visual_info = (XVisualInfo *) NULL;
13826
13827 static XWindowInfo
13828 window_info;
13829
cristybb503372010-05-27 20:51:26 +000013830 size_t
cristy3ed852e2009-09-05 21:47:34 +000013831 delay;
13832
13833 Window
13834 root_window;
13835
13836 XGCValues
13837 context_values;
13838
13839 XResourceInfo
13840 resources;
13841
13842 XWindowAttributes
13843 window_attributes;
13844
13845 /*
13846 Determine target window.
13847 */
13848 assert(image != (Image *) NULL);
13849 assert(image->signature == MagickSignature);
13850 if (image->debug != MagickFalse)
13851 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13852 resources=(*resource_info);
13853 window_info.id=(Window) NULL;
13854 root_window=XRootWindow(display,XDefaultScreen(display));
13855 if (LocaleCompare(resources.window_id,"root") == 0)
13856 window_info.id=root_window;
13857 else
13858 {
13859 if (isdigit((unsigned char) *resources.window_id) != 0)
13860 window_info.id=XWindowByID(display,root_window,
13861 (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13862 if (window_info.id == (Window) NULL)
13863 window_info.id=XWindowByName(display,root_window,resources.window_id);
13864 }
13865 if (window_info.id == (Window) NULL)
13866 {
13867 ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13868 resources.window_id);
13869 return(MagickFalse);
13870 }
13871 /*
13872 Determine window visual id.
13873 */
13874 window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13875 window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13876 (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13877 status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13878 if (status != 0)
cristyb51dff52011-05-19 16:55:47 +000013879 (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
cristy3ed852e2009-09-05 21:47:34 +000013880 XVisualIDFromVisual(window_attributes.visual));
13881 if (visual_info == (XVisualInfo *) NULL)
13882 {
13883 /*
13884 Allocate standard colormap.
13885 */
13886 map_info=XAllocStandardColormap();
13887 if (map_info == (XStandardColormap *) NULL)
13888 ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13889 image->filename);
13890 map_info->colormap=(Colormap) NULL;
cristyf2faecf2010-05-28 19:19:36 +000013891 pixel.pixels=(unsigned long *) NULL;
cristy3ed852e2009-09-05 21:47:34 +000013892 /*
13893 Initialize visual info.
13894 */
13895 resources.map_type=(char *) NULL;
13896 resources.visual_type=visual_type;
13897 visual_info=XBestVisualInfo(display,map_info,&resources);
13898 if (visual_info == (XVisualInfo *) NULL)
13899 ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13900 resources.visual_type);
13901 /*
13902 Initialize window info.
13903 */
13904 window_info.ximage=(XImage *) NULL;
13905 window_info.matte_image=(XImage *) NULL;
13906 window_info.pixmap=(Pixmap) NULL;
13907 window_info.matte_pixmap=(Pixmap) NULL;
13908 }
13909 /*
13910 Free previous root colors.
13911 */
13912 if (window_info.id == root_window)
13913 (void) XDestroyWindowColors(display,root_window);
13914 /*
13915 Initialize Standard Colormap.
13916 */
13917 resources.colormap=SharedColormap;
cristy6710d842011-10-20 23:23:00 +000013918 XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13919 exception);
cristy3ed852e2009-09-05 21:47:34 +000013920 /*
13921 Graphic context superclass.
13922 */
13923 context_values.background=pixel.background_color.pixel;
13924 context_values.foreground=pixel.foreground_color.pixel;
13925 pixel.annotate_context=XCreateGC(display,window_info.id,
cristybb503372010-05-27 20:51:26 +000013926 (size_t) (GCBackground | GCForeground),&context_values);
cristy3ed852e2009-09-05 21:47:34 +000013927 if (pixel.annotate_context == (GC) NULL)
13928 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13929 image->filename);
13930 /*
13931 Initialize Image window attributes.
13932 */
13933 window_info.name=AcquireString("\0");
13934 window_info.icon_name=AcquireString("\0");
13935 XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13936 &resources,&window_info);
13937 /*
13938 Create the X image.
13939 */
13940 window_info.width=(unsigned int) image->columns;
13941 window_info.height=(unsigned int) image->rows;
13942 if ((image->columns != window_info.width) ||
13943 (image->rows != window_info.height))
13944 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13945 image->filename);
cristyb51dff52011-05-19 16:55:47 +000013946 (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
cristy3ed852e2009-09-05 21:47:34 +000013947 window_attributes.width,window_attributes.height);
13948 geometry_info.width=window_info.width;
13949 geometry_info.height=window_info.height;
cristyecd0ab52010-05-30 14:59:20 +000013950 geometry_info.x=(ssize_t) window_info.x;
13951 geometry_info.y=(ssize_t) window_info.y;
cristy3ed852e2009-09-05 21:47:34 +000013952 (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13953 &geometry_info.width,&geometry_info.height);
13954 window_info.width=(unsigned int) geometry_info.width;
13955 window_info.height=(unsigned int) geometry_info.height;
13956 window_info.x=(int) geometry_info.x;
13957 window_info.y=(int) geometry_info.y;
13958 status=XMakeImage(display,&resources,&window_info,image,window_info.width,
cristy051718b2011-08-28 22:49:25 +000013959 window_info.height,exception);
cristy3ed852e2009-09-05 21:47:34 +000013960 if (status == MagickFalse)
13961 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13962 image->filename);
13963 window_info.x=0;
13964 window_info.y=0;
13965 if (image->debug != MagickFalse)
13966 {
13967 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000013968 "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13969 (double) image->columns,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000013970 if (image->colors != 0)
cristye8c25f92010-06-03 00:53:06 +000013971 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13972 image->colors);
cristy3ed852e2009-09-05 21:47:34 +000013973 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13974 }
13975 /*
13976 Adjust image dimensions as specified by backdrop or geometry options.
13977 */
13978 width=(int) window_info.width;
13979 height=(int) window_info.height;
13980 if (resources.backdrop != MagickFalse)
13981 {
13982 /*
13983 Center image on window.
13984 */
13985 window_info.x=(window_attributes.width/2)-
13986 (window_info.ximage->width/2);
13987 window_info.y=(window_attributes.height/2)-
13988 (window_info.ximage->height/2);
13989 width=window_attributes.width;
13990 height=window_attributes.height;
13991 }
13992 if ((resources.image_geometry != (char *) NULL) &&
13993 (*resources.image_geometry != '\0'))
13994 {
13995 char
13996 default_geometry[MaxTextExtent];
13997
13998 int
13999 flags,
14000 gravity;
14001
14002 XSizeHints
14003 *size_hints;
14004
14005 /*
14006 User specified geometry.
14007 */
14008 size_hints=XAllocSizeHints();
14009 if (size_hints == (XSizeHints *) NULL)
14010 ThrowXWindowFatalException(ResourceLimitFatalError,
14011 "MemoryAllocationFailed",image->filename);
14012 size_hints->flags=0L;
cristyb51dff52011-05-19 16:55:47 +000014013 (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
cristy3ed852e2009-09-05 21:47:34 +000014014 width,height);
14015 flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
14016 default_geometry,window_info.border_width,size_hints,&window_info.x,
14017 &window_info.y,&width,&height,&gravity);
14018 if (flags & (XValue | YValue))
14019 {
14020 width=window_attributes.width;
14021 height=window_attributes.height;
14022 }
14023 (void) XFree((void *) size_hints);
14024 }
14025 /*
14026 Create the X pixmap.
14027 */
14028 window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14029 (unsigned int) height,window_info.depth);
14030 if (window_info.pixmap == (Pixmap) NULL)
14031 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14032 image->filename);
14033 /*
14034 Display pixmap on the window.
14035 */
14036 if (((unsigned int) width > window_info.width) ||
14037 ((unsigned int) height > window_info.height))
14038 (void) XFillRectangle(display,window_info.pixmap,
14039 window_info.annotate_context,0,0,(unsigned int) width,
14040 (unsigned int) height);
14041 (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14042 window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14043 window_info.width,(unsigned int) window_info.height);
14044 (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14045 (void) XClearWindow(display,window_info.id);
14046 delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14047 XDelay(display,delay == 0UL ? 10UL : delay);
14048 (void) XSync(display,MagickFalse);
14049 return(window_info.id == root_window ? MagickTrue : MagickFalse);
14050}
14051
14052/*
14053%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14054% %
14055% %
14056% %
14057+ X D i s p l a y I m a g e %
14058% %
14059% %
14060% %
14061%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14062%
14063% XDisplayImage() displays an image via X11. A new image is created and
14064% returned if the user interactively transforms the displayed image.
14065%
14066% The format of the XDisplayImage method is:
14067%
14068% Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +000014069% char **argv,int argc,Image **image,size_t *state,
14070% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000014071%
14072% A description of each parameter follows:
14073%
14074% o nexus: Method XDisplayImage returns an image when the
14075% user chooses 'Open Image' from the command menu or picks a tile
14076% from the image directory. Otherwise a null image is returned.
14077%
14078% o display: Specifies a connection to an X server; returned from
14079% XOpenDisplay.
14080%
14081% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14082%
14083% o argv: Specifies the application's argument list.
14084%
14085% o argc: Specifies the number of arguments.
14086%
14087% o image: Specifies an address to an address of an Image structure;
14088%
cristy051718b2011-08-28 22:49:25 +000014089% o exception: return any errors or warnings in this structure.
14090%
cristy3ed852e2009-09-05 21:47:34 +000014091*/
14092MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +000014093 char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000014094{
14095#define MagnifySize 256 /* must be a power of 2 */
14096#define MagickMenus 10
14097#define MagickTitle "Commands"
14098
14099 static const char
14100 *CommandMenu[] =
14101 {
14102 "File",
14103 "Edit",
14104 "View",
14105 "Transform",
14106 "Enhance",
14107 "Effects",
14108 "F/X",
14109 "Image Edit",
14110 "Miscellany",
14111 "Help",
14112 (char *) NULL
14113 },
14114 *FileMenu[] =
14115 {
14116 "Open...",
14117 "Next",
14118 "Former",
14119 "Select...",
14120 "Save...",
14121 "Print...",
14122 "Delete...",
14123 "New...",
14124 "Visual Directory...",
14125 "Quit",
14126 (char *) NULL
14127 },
14128 *EditMenu[] =
14129 {
14130 "Undo",
14131 "Redo",
14132 "Cut",
14133 "Copy",
14134 "Paste",
14135 (char *) NULL
14136 },
14137 *ViewMenu[] =
14138 {
14139 "Half Size",
14140 "Original Size",
14141 "Double Size",
14142 "Resize...",
14143 "Apply",
14144 "Refresh",
14145 "Restore",
14146 (char *) NULL
14147 },
14148 *TransformMenu[] =
14149 {
14150 "Crop",
14151 "Chop",
14152 "Flop",
14153 "Flip",
14154 "Rotate Right",
14155 "Rotate Left",
14156 "Rotate...",
14157 "Shear...",
14158 "Roll...",
14159 "Trim Edges",
14160 (char *) NULL
14161 },
14162 *EnhanceMenu[] =
14163 {
14164 "Hue...",
14165 "Saturation...",
14166 "Brightness...",
14167 "Gamma...",
14168 "Spiff",
14169 "Dull",
14170 "Contrast Stretch...",
14171 "Sigmoidal Contrast...",
14172 "Normalize",
14173 "Equalize",
14174 "Negate",
14175 "Grayscale",
14176 "Map...",
14177 "Quantize...",
14178 (char *) NULL
14179 },
14180 *EffectsMenu[] =
14181 {
14182 "Despeckle",
14183 "Emboss",
14184 "Reduce Noise",
14185 "Add Noise...",
14186 "Sharpen...",
14187 "Blur...",
14188 "Threshold...",
14189 "Edge Detect...",
14190 "Spread...",
14191 "Shade...",
14192 "Raise...",
14193 "Segment...",
14194 (char *) NULL
14195 },
14196 *FXMenu[] =
14197 {
14198 "Solarize...",
14199 "Sepia Tone...",
14200 "Swirl...",
14201 "Implode...",
14202 "Vignette...",
14203 "Wave...",
14204 "Oil Paint...",
14205 "Charcoal Draw...",
14206 (char *) NULL
14207 },
14208 *ImageEditMenu[] =
14209 {
14210 "Annotate...",
14211 "Draw...",
14212 "Color...",
14213 "Matte...",
14214 "Composite...",
14215 "Add Border...",
14216 "Add Frame...",
14217 "Comment...",
14218 "Launch...",
14219 "Region of Interest...",
14220 (char *) NULL
14221 },
14222 *MiscellanyMenu[] =
14223 {
14224 "Image Info",
14225 "Zoom Image",
14226 "Show Preview...",
14227 "Show Histogram",
14228 "Show Matte",
14229 "Background...",
14230 "Slide Show...",
14231 "Preferences...",
14232 (char *) NULL
14233 },
14234 *HelpMenu[] =
14235 {
14236 "Overview",
14237 "Browse Documentation",
14238 "About Display",
14239 (char *) NULL
14240 },
14241 *ShortCutsMenu[] =
14242 {
14243 "Next",
14244 "Former",
14245 "Open...",
14246 "Save...",
14247 "Print...",
14248 "Undo",
14249 "Restore",
14250 "Image Info",
14251 "Quit",
14252 (char *) NULL
14253 },
14254 *VirtualMenu[] =
14255 {
14256 "Image Info",
14257 "Print",
14258 "Next",
14259 "Quit",
14260 (char *) NULL
14261 };
14262
14263 static const char
14264 **Menus[MagickMenus] =
14265 {
14266 FileMenu,
14267 EditMenu,
14268 ViewMenu,
14269 TransformMenu,
14270 EnhanceMenu,
14271 EffectsMenu,
14272 FXMenu,
14273 ImageEditMenu,
14274 MiscellanyMenu,
14275 HelpMenu
14276 };
14277
14278 static CommandType
14279 CommandMenus[] =
14280 {
14281 NullCommand,
14282 NullCommand,
14283 NullCommand,
14284 NullCommand,
14285 NullCommand,
14286 NullCommand,
14287 NullCommand,
14288 NullCommand,
14289 NullCommand,
14290 NullCommand,
14291 },
14292 FileCommands[] =
14293 {
14294 OpenCommand,
14295 NextCommand,
14296 FormerCommand,
14297 SelectCommand,
14298 SaveCommand,
14299 PrintCommand,
14300 DeleteCommand,
14301 NewCommand,
14302 VisualDirectoryCommand,
14303 QuitCommand
14304 },
14305 EditCommands[] =
14306 {
14307 UndoCommand,
14308 RedoCommand,
14309 CutCommand,
14310 CopyCommand,
14311 PasteCommand
14312 },
14313 ViewCommands[] =
14314 {
14315 HalfSizeCommand,
14316 OriginalSizeCommand,
14317 DoubleSizeCommand,
14318 ResizeCommand,
14319 ApplyCommand,
14320 RefreshCommand,
14321 RestoreCommand
14322 },
14323 TransformCommands[] =
14324 {
14325 CropCommand,
14326 ChopCommand,
14327 FlopCommand,
14328 FlipCommand,
14329 RotateRightCommand,
14330 RotateLeftCommand,
14331 RotateCommand,
14332 ShearCommand,
14333 RollCommand,
14334 TrimCommand
14335 },
14336 EnhanceCommands[] =
14337 {
14338 HueCommand,
14339 SaturationCommand,
14340 BrightnessCommand,
14341 GammaCommand,
14342 SpiffCommand,
14343 DullCommand,
14344 ContrastStretchCommand,
14345 SigmoidalContrastCommand,
14346 NormalizeCommand,
14347 EqualizeCommand,
14348 NegateCommand,
14349 GrayscaleCommand,
14350 MapCommand,
14351 QuantizeCommand
14352 },
14353 EffectsCommands[] =
14354 {
14355 DespeckleCommand,
14356 EmbossCommand,
14357 ReduceNoiseCommand,
14358 AddNoiseCommand,
14359 SharpenCommand,
14360 BlurCommand,
14361 ThresholdCommand,
14362 EdgeDetectCommand,
14363 SpreadCommand,
14364 ShadeCommand,
14365 RaiseCommand,
14366 SegmentCommand
14367 },
14368 FXCommands[] =
14369 {
14370 SolarizeCommand,
14371 SepiaToneCommand,
14372 SwirlCommand,
14373 ImplodeCommand,
14374 VignetteCommand,
14375 WaveCommand,
14376 OilPaintCommand,
14377 CharcoalDrawCommand
14378 },
14379 ImageEditCommands[] =
14380 {
14381 AnnotateCommand,
14382 DrawCommand,
14383 ColorCommand,
14384 MatteCommand,
14385 CompositeCommand,
14386 AddBorderCommand,
14387 AddFrameCommand,
14388 CommentCommand,
14389 LaunchCommand,
14390 RegionofInterestCommand
14391 },
14392 MiscellanyCommands[] =
14393 {
14394 InfoCommand,
14395 ZoomCommand,
14396 ShowPreviewCommand,
14397 ShowHistogramCommand,
14398 ShowMatteCommand,
14399 BackgroundCommand,
14400 SlideShowCommand,
14401 PreferencesCommand
14402 },
14403 HelpCommands[] =
14404 {
14405 HelpCommand,
14406 BrowseDocumentationCommand,
14407 VersionCommand
14408 },
14409 ShortCutsCommands[] =
14410 {
14411 NextCommand,
14412 FormerCommand,
14413 OpenCommand,
14414 SaveCommand,
14415 PrintCommand,
14416 UndoCommand,
14417 RestoreCommand,
14418 InfoCommand,
14419 QuitCommand
14420 },
14421 VirtualCommands[] =
14422 {
14423 InfoCommand,
14424 PrintCommand,
14425 NextCommand,
14426 QuitCommand
14427 };
14428
14429 static CommandType
14430 *Commands[MagickMenus] =
14431 {
14432 FileCommands,
14433 EditCommands,
14434 ViewCommands,
14435 TransformCommands,
14436 EnhanceCommands,
14437 EffectsCommands,
14438 FXCommands,
14439 ImageEditCommands,
14440 MiscellanyCommands,
14441 HelpCommands
14442 };
14443
14444 char
14445 command[MaxTextExtent],
cristy00976d82011-02-20 20:31:28 +000014446 *directory,
cristy3ed852e2009-09-05 21:47:34 +000014447 geometry[MaxTextExtent],
14448 resource_name[MaxTextExtent];
14449
14450 CommandType
14451 command_type;
14452
14453 Image
14454 *display_image,
14455 *nexus;
14456
14457 int
14458 entry,
14459 id;
14460
14461 KeySym
14462 key_symbol;
14463
14464 MagickStatusType
14465 context_mask,
14466 status;
14467
14468 RectangleInfo
14469 geometry_info;
14470
14471 register int
14472 i;
14473
14474 static char
14475 working_directory[MaxTextExtent];
14476
14477 static XPoint
14478 vid_info;
14479
14480 static XWindowInfo
14481 *magick_windows[MaxXWindows];
14482
14483 static unsigned int
14484 number_windows;
14485
14486 struct stat
14487 attributes;
14488
14489 time_t
14490 timer,
14491 timestamp,
14492 update_time;
14493
14494 unsigned int
14495 height,
14496 width;
14497
cristybb503372010-05-27 20:51:26 +000014498 size_t
cristy3ed852e2009-09-05 21:47:34 +000014499 delay;
14500
14501 WarningHandler
14502 warning_handler;
14503
14504 Window
14505 root_window;
14506
14507 XClassHint
14508 *class_hints;
14509
14510 XEvent
14511 event;
14512
14513 XFontStruct
14514 *font_info;
14515
14516 XGCValues
14517 context_values;
14518
14519 XPixelInfo
14520 *icon_pixel,
14521 *pixel;
14522
14523 XResourceInfo
14524 *icon_resources;
14525
14526 XStandardColormap
14527 *icon_map,
14528 *map_info;
14529
14530 XVisualInfo
14531 *icon_visual,
14532 *visual_info;
14533
14534 XWindowChanges
14535 window_changes;
14536
14537 XWindows
14538 *windows;
14539
14540 XWMHints
14541 *manager_hints;
14542
14543 assert(image != (Image **) NULL);
14544 assert((*image)->signature == MagickSignature);
14545 if ((*image)->debug != MagickFalse)
14546 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14547 display_image=(*image);
14548 warning_handler=(WarningHandler) NULL;
14549 windows=XSetWindows((XWindows *) ~0);
14550 if (windows != (XWindows *) NULL)
14551 {
14552 int
14553 status;
14554
14555 status=chdir(working_directory);
14556 if (status == -1)
cristy051718b2011-08-28 22:49:25 +000014557 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14558 "UnableToOpenFile","%s",working_directory);
cristy3ed852e2009-09-05 21:47:34 +000014559 warning_handler=resource_info->display_warnings ?
14560 SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14561 warning_handler=resource_info->display_warnings ?
14562 SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14563 }
14564 else
14565 {
14566 /*
14567 Allocate windows structure.
14568 */
14569 resource_info->colors=display_image->colors;
14570 windows=XSetWindows(XInitializeWindows(display,resource_info));
14571 if (windows == (XWindows *) NULL)
14572 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14573 (*image)->filename);
14574 /*
14575 Initialize window id's.
14576 */
14577 number_windows=0;
14578 magick_windows[number_windows++]=(&windows->icon);
14579 magick_windows[number_windows++]=(&windows->backdrop);
14580 magick_windows[number_windows++]=(&windows->image);
14581 magick_windows[number_windows++]=(&windows->info);
14582 magick_windows[number_windows++]=(&windows->command);
14583 magick_windows[number_windows++]=(&windows->widget);
14584 magick_windows[number_windows++]=(&windows->popup);
14585 magick_windows[number_windows++]=(&windows->magnify);
14586 magick_windows[number_windows++]=(&windows->pan);
14587 for (i=0; i < (int) number_windows; i++)
14588 magick_windows[i]->id=(Window) NULL;
14589 vid_info.x=0;
14590 vid_info.y=0;
14591 }
14592 /*
14593 Initialize font info.
14594 */
14595 if (windows->font_info != (XFontStruct *) NULL)
14596 (void) XFreeFont(display,windows->font_info);
14597 windows->font_info=XBestFont(display,resource_info,MagickFalse);
14598 if (windows->font_info == (XFontStruct *) NULL)
14599 ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14600 resource_info->font);
14601 /*
14602 Initialize Standard Colormap.
14603 */
14604 map_info=windows->map_info;
14605 icon_map=windows->icon_map;
14606 visual_info=windows->visual_info;
14607 icon_visual=windows->icon_visual;
14608 pixel=windows->pixel_info;
14609 icon_pixel=windows->icon_pixel;
14610 font_info=windows->font_info;
14611 icon_resources=windows->icon_resources;
14612 class_hints=windows->class_hints;
14613 manager_hints=windows->manager_hints;
14614 root_window=XRootWindow(display,visual_info->screen);
14615 nexus=NewImageList();
14616 if (display_image->debug != MagickFalse)
14617 {
14618 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000014619 "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14620 (double) display_image->scene,(double) display_image->columns,
14621 (double) display_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000014622 if (display_image->colors != 0)
cristye8c25f92010-06-03 00:53:06 +000014623 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14624 display_image->colors);
cristy3ed852e2009-09-05 21:47:34 +000014625 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14626 display_image->magick);
14627 }
14628 XMakeStandardColormap(display,visual_info,resource_info,display_image,
cristy6710d842011-10-20 23:23:00 +000014629 map_info,pixel,exception);
cristy3ed852e2009-09-05 21:47:34 +000014630 display_image->taint=MagickFalse;
14631 /*
14632 Initialize graphic context.
14633 */
14634 windows->context.id=(Window) NULL;
14635 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14636 resource_info,&windows->context);
14637 (void) CloneString(&class_hints->res_name,resource_info->client_name);
14638 (void) CloneString(&class_hints->res_class,resource_info->client_name);
14639 class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14640 manager_hints->flags=InputHint | StateHint;
14641 manager_hints->input=MagickFalse;
14642 manager_hints->initial_state=WithdrawnState;
14643 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14644 &windows->context);
14645 if (display_image->debug != MagickFalse)
14646 (void) LogMagickEvent(X11Event,GetMagickModule(),
14647 "Window id: 0x%lx (context)",windows->context.id);
14648 context_values.background=pixel->background_color.pixel;
14649 context_values.font=font_info->fid;
14650 context_values.foreground=pixel->foreground_color.pixel;
14651 context_values.graphics_exposures=MagickFalse;
14652 context_mask=(MagickStatusType)
14653 (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14654 if (pixel->annotate_context != (GC) NULL)
14655 (void) XFreeGC(display,pixel->annotate_context);
14656 pixel->annotate_context=XCreateGC(display,windows->context.id,
14657 context_mask,&context_values);
14658 if (pixel->annotate_context == (GC) NULL)
14659 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14660 display_image->filename);
14661 context_values.background=pixel->depth_color.pixel;
14662 if (pixel->widget_context != (GC) NULL)
14663 (void) XFreeGC(display,pixel->widget_context);
14664 pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14665 &context_values);
14666 if (pixel->widget_context == (GC) NULL)
14667 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14668 display_image->filename);
14669 context_values.background=pixel->foreground_color.pixel;
14670 context_values.foreground=pixel->background_color.pixel;
14671 context_values.plane_mask=context_values.background ^
14672 context_values.foreground;
14673 if (pixel->highlight_context != (GC) NULL)
14674 (void) XFreeGC(display,pixel->highlight_context);
14675 pixel->highlight_context=XCreateGC(display,windows->context.id,
cristybb503372010-05-27 20:51:26 +000014676 (size_t) (context_mask | GCPlaneMask),&context_values);
cristy3ed852e2009-09-05 21:47:34 +000014677 if (pixel->highlight_context == (GC) NULL)
14678 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14679 display_image->filename);
14680 (void) XDestroyWindow(display,windows->context.id);
14681 /*
14682 Initialize icon window.
14683 */
14684 XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14685 icon_resources,&windows->icon);
14686 windows->icon.geometry=resource_info->icon_geometry;
14687 XBestIconSize(display,&windows->icon,display_image);
14688 windows->icon.attributes.colormap=XDefaultColormap(display,
14689 icon_visual->screen);
14690 windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14691 manager_hints->flags=InputHint | StateHint;
14692 manager_hints->input=MagickFalse;
14693 manager_hints->initial_state=IconicState;
14694 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14695 &windows->icon);
14696 if (display_image->debug != MagickFalse)
14697 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14698 windows->icon.id);
14699 /*
14700 Initialize graphic context for icon window.
14701 */
14702 if (icon_pixel->annotate_context != (GC) NULL)
14703 (void) XFreeGC(display,icon_pixel->annotate_context);
14704 context_values.background=icon_pixel->background_color.pixel;
14705 context_values.foreground=icon_pixel->foreground_color.pixel;
14706 icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
cristybb503372010-05-27 20:51:26 +000014707 (size_t) (GCBackground | GCForeground),&context_values);
cristy3ed852e2009-09-05 21:47:34 +000014708 if (icon_pixel->annotate_context == (GC) NULL)
14709 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14710 display_image->filename);
14711 windows->icon.annotate_context=icon_pixel->annotate_context;
14712 /*
14713 Initialize Image window.
14714 */
14715 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14716 &windows->image);
14717 windows->image.shape=MagickTrue; /* non-rectangular shape hint */
14718 if (resource_info->use_shared_memory == MagickFalse)
14719 windows->image.shared_memory=MagickFalse;
14720 if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14721 {
14722 char
14723 *title;
14724
14725 title=InterpretImageProperties(resource_info->image_info,display_image,
cristy018f07f2011-09-04 21:15:19 +000014726 resource_info->title,exception);
cristy3ed852e2009-09-05 21:47:34 +000014727 (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14728 (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14729 title=DestroyString(title);
14730 }
14731 else
14732 {
14733 char
14734 filename[MaxTextExtent];
14735
14736 /*
14737 Window name is the base of the filename.
14738 */
14739 GetPathComponent(display_image->magick_filename,TailPath,filename);
cristy9a48b172011-09-20 17:41:05 +000014740 if (display_image->scene == 0)
cristyb51dff52011-05-19 16:55:47 +000014741 (void) FormatLocaleString(windows->image.name,MaxTextExtent,
cristy40a08ad2010-02-09 02:27:44 +000014742 "%s: %s",MagickPackageName,filename);
cristy3ed852e2009-09-05 21:47:34 +000014743 else
cristyb51dff52011-05-19 16:55:47 +000014744 (void) FormatLocaleString(windows->image.name,MaxTextExtent,
cristy04d55062011-03-15 18:58:50 +000014745 "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14746 (double) display_image->scene,(double) GetImageListLength(
14747 display_image));
cristy3ed852e2009-09-05 21:47:34 +000014748 (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14749 }
14750 if (resource_info->immutable)
14751 windows->image.immutable=MagickTrue;
14752 windows->image.use_pixmap=resource_info->use_pixmap;
14753 windows->image.geometry=resource_info->image_geometry;
cristyb51dff52011-05-19 16:55:47 +000014754 (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
cristy3ed852e2009-09-05 21:47:34 +000014755 XDisplayWidth(display,visual_info->screen),
14756 XDisplayHeight(display,visual_info->screen));
14757 geometry_info.width=display_image->columns;
14758 geometry_info.height=display_image->rows;
14759 geometry_info.x=0;
14760 geometry_info.y=0;
14761 (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14762 &geometry_info.width,&geometry_info.height);
14763 windows->image.width=(unsigned int) geometry_info.width;
14764 windows->image.height=(unsigned int) geometry_info.height;
14765 windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14766 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14767 KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14768 PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14769 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14770 resource_info,&windows->backdrop);
14771 if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14772 {
14773 /*
14774 Initialize backdrop window.
14775 */
14776 windows->backdrop.x=0;
14777 windows->backdrop.y=0;
cristy40a08ad2010-02-09 02:27:44 +000014778 (void) CloneString(&windows->backdrop.name,"Backdrop");
cristybb503372010-05-27 20:51:26 +000014779 windows->backdrop.flags=(size_t) (USSize | USPosition);
cristy3ed852e2009-09-05 21:47:34 +000014780 windows->backdrop.width=(unsigned int)
14781 XDisplayWidth(display,visual_info->screen);
14782 windows->backdrop.height=(unsigned int)
14783 XDisplayHeight(display,visual_info->screen);
14784 windows->backdrop.border_width=0;
14785 windows->backdrop.immutable=MagickTrue;
14786 windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14787 ButtonReleaseMask;
14788 windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14789 StructureNotifyMask;
14790 manager_hints->flags=IconWindowHint | InputHint | StateHint;
14791 manager_hints->icon_window=windows->icon.id;
14792 manager_hints->input=MagickTrue;
14793 manager_hints->initial_state=resource_info->iconic ? IconicState :
14794 NormalState;
14795 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14796 &windows->backdrop);
14797 if (display_image->debug != MagickFalse)
14798 (void) LogMagickEvent(X11Event,GetMagickModule(),
14799 "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14800 (void) XMapWindow(display,windows->backdrop.id);
14801 (void) XClearWindow(display,windows->backdrop.id);
14802 if (windows->image.id != (Window) NULL)
14803 {
14804 (void) XDestroyWindow(display,windows->image.id);
14805 windows->image.id=(Window) NULL;
14806 }
14807 /*
14808 Position image in the center the backdrop.
14809 */
14810 windows->image.flags|=USPosition;
14811 windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14812 (windows->image.width/2);
14813 windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14814 (windows->image.height/2);
14815 }
14816 manager_hints->flags=IconWindowHint | InputHint | StateHint;
14817 manager_hints->icon_window=windows->icon.id;
14818 manager_hints->input=MagickTrue;
14819 manager_hints->initial_state=resource_info->iconic ? IconicState :
14820 NormalState;
14821 if (windows->group_leader.id != (Window) NULL)
14822 {
14823 /*
14824 Follow the leader.
14825 */
14826 manager_hints->flags|=WindowGroupHint;
14827 manager_hints->window_group=windows->group_leader.id;
14828 (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14829 if (display_image->debug != MagickFalse)
14830 (void) LogMagickEvent(X11Event,GetMagickModule(),
14831 "Window id: 0x%lx (group leader)",windows->group_leader.id);
14832 }
14833 XMakeWindow(display,
14834 (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14835 argv,argc,class_hints,manager_hints,&windows->image);
14836 (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14837 XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14838 if (windows->group_leader.id != (Window) NULL)
14839 (void) XSetTransientForHint(display,windows->image.id,
14840 windows->group_leader.id);
14841 if (display_image->debug != MagickFalse)
14842 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14843 windows->image.id);
14844 /*
14845 Initialize Info widget.
14846 */
14847 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14848 &windows->info);
14849 (void) CloneString(&windows->info.name,"Info");
14850 (void) CloneString(&windows->info.icon_name,"Info");
14851 windows->info.border_width=1;
14852 windows->info.x=2;
14853 windows->info.y=2;
14854 windows->info.flags|=PPosition;
14855 windows->info.attributes.win_gravity=UnmapGravity;
14856 windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14857 StructureNotifyMask;
14858 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14859 manager_hints->input=MagickFalse;
14860 manager_hints->initial_state=NormalState;
14861 manager_hints->window_group=windows->image.id;
14862 XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14863 &windows->info);
14864 windows->info.highlight_stipple=XCreateBitmapFromData(display,
14865 windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14866 windows->info.shadow_stipple=XCreateBitmapFromData(display,
14867 windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14868 (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14869 if (windows->image.mapped != MagickFalse)
14870 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14871 if (display_image->debug != MagickFalse)
14872 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14873 windows->info.id);
14874 /*
14875 Initialize Command widget.
14876 */
14877 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14878 resource_info,&windows->command);
14879 windows->command.data=MagickMenus;
14880 (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
cristyb51dff52011-05-19 16:55:47 +000014881 (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
cristy3ed852e2009-09-05 21:47:34 +000014882 resource_info->client_name);
14883 windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14884 resource_name,"geometry",(char *) NULL);
14885 (void) CloneString(&windows->command.name,MagickTitle);
14886 windows->command.border_width=0;
14887 windows->command.flags|=PPosition;
14888 windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14889 ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14890 OwnerGrabButtonMask | StructureNotifyMask;
14891 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14892 manager_hints->input=MagickTrue;
14893 manager_hints->initial_state=NormalState;
14894 manager_hints->window_group=windows->image.id;
14895 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14896 &windows->command);
14897 windows->command.highlight_stipple=XCreateBitmapFromData(display,
14898 windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14899 HighlightHeight);
14900 windows->command.shadow_stipple=XCreateBitmapFromData(display,
14901 windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14902 (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14903 if (windows->command.mapped != MagickFalse)
14904 (void) XMapRaised(display,windows->command.id);
14905 if (display_image->debug != MagickFalse)
14906 (void) LogMagickEvent(X11Event,GetMagickModule(),
14907 "Window id: 0x%lx (command)",windows->command.id);
14908 /*
14909 Initialize Widget window.
14910 */
14911 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14912 resource_info,&windows->widget);
cristyb51dff52011-05-19 16:55:47 +000014913 (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
cristy3ed852e2009-09-05 21:47:34 +000014914 resource_info->client_name);
14915 windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14916 resource_name,"geometry",(char *) NULL);
14917 windows->widget.border_width=0;
14918 windows->widget.flags|=PPosition;
14919 windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14920 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14921 KeyReleaseMask | LeaveWindowMask | 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->widget);
14929 windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14930 windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14931 windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14932 windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14933 (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14934 if (display_image->debug != MagickFalse)
14935 (void) LogMagickEvent(X11Event,GetMagickModule(),
14936 "Window id: 0x%lx (widget)",windows->widget.id);
14937 /*
14938 Initialize popup window.
14939 */
14940 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14941 resource_info,&windows->popup);
14942 windows->popup.border_width=0;
14943 windows->popup.flags|=PPosition;
14944 windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14945 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14946 KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14947 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14948 manager_hints->input=MagickTrue;
14949 manager_hints->initial_state=NormalState;
14950 manager_hints->window_group=windows->image.id;
14951 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14952 &windows->popup);
14953 windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14954 windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14955 windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14956 windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14957 (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14958 if (display_image->debug != MagickFalse)
14959 (void) LogMagickEvent(X11Event,GetMagickModule(),
14960 "Window id: 0x%lx (pop up)",windows->popup.id);
14961 /*
14962 Initialize Magnify window and cursor.
14963 */
14964 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14965 resource_info,&windows->magnify);
14966 if (resource_info->use_shared_memory == MagickFalse)
14967 windows->magnify.shared_memory=MagickFalse;
cristyb51dff52011-05-19 16:55:47 +000014968 (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
cristy3ed852e2009-09-05 21:47:34 +000014969 resource_info->client_name);
14970 windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14971 resource_name,"geometry",(char *) NULL);
cristyb51dff52011-05-19 16:55:47 +000014972 (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
cristy3ed852e2009-09-05 21:47:34 +000014973 resource_info->magnify);
14974 if (windows->magnify.cursor != (Cursor) NULL)
14975 (void) XFreeCursor(display,windows->magnify.cursor);
14976 windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14977 map_info->colormap,resource_info->background_color,
14978 resource_info->foreground_color);
14979 if (windows->magnify.cursor == (Cursor) NULL)
14980 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14981 display_image->filename);
14982 windows->magnify.width=MagnifySize;
14983 windows->magnify.height=MagnifySize;
14984 windows->magnify.flags|=PPosition;
14985 windows->magnify.min_width=MagnifySize;
14986 windows->magnify.min_height=MagnifySize;
14987 windows->magnify.width_inc=MagnifySize;
14988 windows->magnify.height_inc=MagnifySize;
14989 windows->magnify.data=resource_info->magnify;
14990 windows->magnify.attributes.cursor=windows->magnify.cursor;
14991 windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14992 ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14993 StructureNotifyMask;
14994 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14995 manager_hints->input=MagickTrue;
14996 manager_hints->initial_state=NormalState;
14997 manager_hints->window_group=windows->image.id;
14998 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14999 &windows->magnify);
15000 if (display_image->debug != MagickFalse)
15001 (void) LogMagickEvent(X11Event,GetMagickModule(),
15002 "Window id: 0x%lx (magnify)",windows->magnify.id);
15003 (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
15004 /*
15005 Initialize panning window.
15006 */
15007 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
15008 resource_info,&windows->pan);
15009 (void) CloneString(&windows->pan.name,"Pan Icon");
15010 windows->pan.width=windows->icon.width;
15011 windows->pan.height=windows->icon.height;
cristyb51dff52011-05-19 16:55:47 +000015012 (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
cristy3ed852e2009-09-05 21:47:34 +000015013 resource_info->client_name);
15014 windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
15015 resource_name,"geometry",(char *) NULL);
15016 (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15017 &windows->pan.width,&windows->pan.height);
15018 windows->pan.flags|=PPosition;
15019 windows->pan.immutable=MagickTrue;
15020 windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15021 ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15022 StructureNotifyMask;
15023 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15024 manager_hints->input=MagickFalse;
15025 manager_hints->initial_state=NormalState;
15026 manager_hints->window_group=windows->image.id;
15027 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15028 &windows->pan);
15029 if (display_image->debug != MagickFalse)
15030 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15031 windows->pan.id);
15032 (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15033 if (windows->info.mapped != MagickFalse)
15034 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15035 if ((windows->image.mapped == MagickFalse) ||
15036 (windows->backdrop.id != (Window) NULL))
15037 (void) XMapWindow(display,windows->image.id);
15038 /*
15039 Set our progress monitor and warning handlers.
15040 */
15041 if (warning_handler == (WarningHandler) NULL)
15042 {
15043 warning_handler=resource_info->display_warnings ?
15044 SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15045 warning_handler=resource_info->display_warnings ?
15046 SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15047 }
15048 /*
15049 Initialize Image and Magnify X images.
15050 */
15051 windows->image.x=0;
15052 windows->image.y=0;
15053 windows->magnify.shape=MagickFalse;
15054 width=(unsigned int) display_image->columns;
15055 height=(unsigned int) display_image->rows;
15056 if ((display_image->columns != width) || (display_image->rows != height))
15057 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15058 display_image->filename);
15059 status=XMakeImage(display,resource_info,&windows->image,display_image,
cristy051718b2011-08-28 22:49:25 +000015060 width,height,exception);
cristy3ed852e2009-09-05 21:47:34 +000015061 if (status == MagickFalse)
15062 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15063 display_image->filename);
15064 status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
cristy051718b2011-08-28 22:49:25 +000015065 windows->magnify.width,windows->magnify.height,exception);
cristy3ed852e2009-09-05 21:47:34 +000015066 if (status == MagickFalse)
15067 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15068 display_image->filename);
15069 if (windows->magnify.mapped != MagickFalse)
15070 (void) XMapRaised(display,windows->magnify.id);
15071 if (windows->pan.mapped != MagickFalse)
15072 (void) XMapRaised(display,windows->pan.id);
15073 windows->image.window_changes.width=(int) display_image->columns;
15074 windows->image.window_changes.height=(int) display_image->rows;
cristy051718b2011-08-28 22:49:25 +000015075 (void) XConfigureImage(display,resource_info,windows,display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000015076 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15077 (void) XSync(display,MagickFalse);
15078 /*
15079 Respond to events.
15080 */
15081 delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15082 timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15083 update_time=0;
15084 if (resource_info->update != MagickFalse)
15085 {
15086 MagickBooleanType
15087 status;
15088
15089 /*
15090 Determine when file data was last modified.
15091 */
15092 status=GetPathAttributes(display_image->filename,&attributes);
15093 if (status != MagickFalse)
15094 update_time=attributes.st_mtime;
15095 }
15096 *state&=(~FormerImageState);
15097 *state&=(~MontageImageState);
15098 *state&=(~NextImageState);
15099 do
15100 {
15101 /*
15102 Handle a window event.
15103 */
15104 if (windows->image.mapped != MagickFalse)
15105 if ((display_image->delay != 0) || (resource_info->update != 0))
15106 {
15107 if (timer < time((time_t *) NULL))
15108 {
15109 if (resource_info->update == MagickFalse)
15110 *state|=NextImageState | ExitState;
15111 else
15112 {
15113 MagickBooleanType
15114 status;
15115
15116 /*
15117 Determine if image file was modified.
15118 */
15119 status=GetPathAttributes(display_image->filename,&attributes);
15120 if (status != MagickFalse)
15121 if (update_time != attributes.st_mtime)
15122 {
15123 /*
15124 Redisplay image.
15125 */
cristyb51dff52011-05-19 16:55:47 +000015126 (void) FormatLocaleString(
cristy3ed852e2009-09-05 21:47:34 +000015127 resource_info->image_info->filename,MaxTextExtent,
15128 "%s:%s",display_image->magick,
15129 display_image->filename);
cristy947cb4c2011-10-20 18:41:46 +000015130 nexus=ReadImage(resource_info->image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +000015131 if (nexus != (Image *) NULL)
15132 {
15133 nexus=DestroyImage(nexus);
15134 *state|=NextImageState | ExitState;
15135 }
15136 }
15137 delay=display_image->delay/MagickMax(
15138 display_image->ticks_per_second,1L);
15139 timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15140 }
15141 }
15142 if (XEventsQueued(display,QueuedAfterFlush) == 0)
15143 {
15144 /*
15145 Do not block if delay > 0.
15146 */
15147 XDelay(display,SuspendTime << 2);
15148 continue;
15149 }
15150 }
15151 timestamp=time((time_t *) NULL);
15152 (void) XNextEvent(display,&event);
15153 if (windows->image.stasis == MagickFalse)
15154 windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15155 MagickTrue : MagickFalse;
15156 if (windows->magnify.stasis == MagickFalse)
15157 windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15158 MagickTrue : MagickFalse;
15159 if (event.xany.window == windows->command.id)
15160 {
15161 /*
15162 Select a command from the Command widget.
15163 */
15164 id=XCommandWidget(display,windows,CommandMenu,&event);
15165 if (id < 0)
15166 continue;
15167 (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15168 command_type=CommandMenus[id];
15169 if (id < MagickMenus)
15170 {
15171 /*
15172 Select a command from a pop-up menu.
15173 */
15174 entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15175 command);
15176 if (entry < 0)
15177 continue;
15178 (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15179 command_type=Commands[id][entry];
15180 }
15181 if (command_type != NullCommand)
15182 nexus=XMagickCommand(display,resource_info,windows,command_type,
cristy051718b2011-08-28 22:49:25 +000015183 &display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000015184 continue;
15185 }
15186 switch (event.type)
15187 {
15188 case ButtonPress:
15189 {
15190 if (display_image->debug != MagickFalse)
15191 (void) LogMagickEvent(X11Event,GetMagickModule(),
15192 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15193 event.xbutton.button,event.xbutton.x,event.xbutton.y);
15194 if ((event.xbutton.button == Button3) &&
15195 (event.xbutton.state & Mod1Mask))
15196 {
15197 /*
15198 Convert Alt-Button3 to Button2.
15199 */
15200 event.xbutton.button=Button2;
15201 event.xbutton.state&=(~Mod1Mask);
15202 }
15203 if (event.xbutton.window == windows->backdrop.id)
15204 {
15205 (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15206 event.xbutton.time);
15207 break;
15208 }
15209 if (event.xbutton.window == windows->image.id)
15210 {
15211 switch (event.xbutton.button)
15212 {
15213 case Button1:
15214 {
15215 if (resource_info->immutable)
15216 {
15217 /*
15218 Select a command from the Virtual menu.
15219 */
15220 entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15221 command);
15222 if (entry >= 0)
15223 nexus=XMagickCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000015224 VirtualCommands[entry],&display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000015225 break;
15226 }
15227 /*
15228 Map/unmap Command widget.
15229 */
15230 if (windows->command.mapped != MagickFalse)
15231 (void) XWithdrawWindow(display,windows->command.id,
15232 windows->command.screen);
15233 else
15234 {
15235 (void) XCommandWidget(display,windows,CommandMenu,
15236 (XEvent *) NULL);
15237 (void) XMapRaised(display,windows->command.id);
15238 }
15239 break;
15240 }
15241 case Button2:
15242 {
15243 /*
15244 User pressed the image magnify button.
15245 */
15246 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
cristy051718b2011-08-28 22:49:25 +000015247 &display_image,exception);
cristy6710d842011-10-20 23:23:00 +000015248 XMagnifyImage(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +000015249 break;
15250 }
15251 case Button3:
15252 {
15253 if (resource_info->immutable)
15254 {
15255 /*
15256 Select a command from the Virtual menu.
15257 */
15258 entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15259 command);
15260 if (entry >= 0)
15261 nexus=XMagickCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000015262 VirtualCommands[entry],&display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000015263 break;
15264 }
15265 if (display_image->montage != (char *) NULL)
15266 {
15267 /*
15268 Open or delete a tile from a visual image directory.
15269 */
15270 nexus=XTileImage(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000015271 display_image,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +000015272 if (nexus != (Image *) NULL)
15273 *state|=MontageImageState | NextImageState | ExitState;
cristy49e2d862010-11-12 02:50:30 +000015274 vid_info.x=(short int) windows->image.x;
15275 vid_info.y=(short int) windows->image.y;
cristy3ed852e2009-09-05 21:47:34 +000015276 break;
15277 }
15278 /*
15279 Select a command from the Short Cuts menu.
15280 */
15281 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15282 command);
15283 if (entry >= 0)
15284 nexus=XMagickCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000015285 ShortCutsCommands[entry],&display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000015286 break;
15287 }
15288 case Button4:
15289 {
15290 /*
15291 Wheel up.
15292 */
15293 XTranslateImage(display,windows,*image,XK_Up);
15294 break;
15295 }
15296 case Button5:
15297 {
15298 /*
15299 Wheel down.
15300 */
15301 XTranslateImage(display,windows,*image,XK_Down);
15302 break;
15303 }
15304 default:
15305 break;
15306 }
15307 break;
15308 }
15309 if (event.xbutton.window == windows->magnify.id)
15310 {
15311 int
15312 factor;
15313
15314 static const char
15315 *MagnifyMenu[] =
15316 {
15317 "2",
15318 "4",
15319 "5",
15320 "6",
15321 "7",
15322 "8",
15323 "9",
15324 "3",
15325 (char *) NULL,
15326 };
15327
15328 static KeySym
15329 MagnifyCommands[] =
15330 {
15331 XK_2,
15332 XK_4,
15333 XK_5,
15334 XK_6,
15335 XK_7,
15336 XK_8,
15337 XK_9,
15338 XK_3
15339 };
15340
15341 /*
15342 Select a magnify factor from the pop-up menu.
15343 */
15344 factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15345 if (factor >= 0)
cristy6710d842011-10-20 23:23:00 +000015346 XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15347 exception);
cristy3ed852e2009-09-05 21:47:34 +000015348 break;
15349 }
15350 if (event.xbutton.window == windows->pan.id)
15351 {
15352 switch (event.xbutton.button)
15353 {
15354 case Button4:
15355 {
15356 /*
15357 Wheel up.
15358 */
15359 XTranslateImage(display,windows,*image,XK_Up);
15360 break;
15361 }
15362 case Button5:
15363 {
15364 /*
15365 Wheel down.
15366 */
15367 XTranslateImage(display,windows,*image,XK_Down);
15368 break;
15369 }
15370 default:
15371 {
cristy6710d842011-10-20 23:23:00 +000015372 XPanImage(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +000015373 break;
15374 }
15375 }
15376 break;
15377 }
15378 delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15379 1L);
15380 timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15381 break;
15382 }
15383 case ButtonRelease:
15384 {
15385 if (display_image->debug != MagickFalse)
15386 (void) LogMagickEvent(X11Event,GetMagickModule(),
15387 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15388 event.xbutton.button,event.xbutton.x,event.xbutton.y);
15389 break;
15390 }
15391 case ClientMessage:
15392 {
15393 if (display_image->debug != MagickFalse)
15394 (void) LogMagickEvent(X11Event,GetMagickModule(),
15395 "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
cristyf2faecf2010-05-28 19:19:36 +000015396 event.xclient.message_type,event.xclient.format,(unsigned long)
cristy3ed852e2009-09-05 21:47:34 +000015397 event.xclient.data.l[0]);
15398 if (event.xclient.message_type == windows->im_protocols)
15399 {
cristyecd0ab52010-05-30 14:59:20 +000015400 if (*event.xclient.data.l == (long) windows->im_update_widget)
cristy3ed852e2009-09-05 21:47:34 +000015401 {
15402 (void) CloneString(&windows->command.name,MagickTitle);
15403 windows->command.data=MagickMenus;
15404 (void) XCommandWidget(display,windows,CommandMenu,
15405 (XEvent *) NULL);
15406 break;
15407 }
cristyecd0ab52010-05-30 14:59:20 +000015408 if (*event.xclient.data.l == (long) windows->im_update_colormap)
cristy3ed852e2009-09-05 21:47:34 +000015409 {
15410 /*
15411 Update graphic context and window colormap.
15412 */
15413 for (i=0; i < (int) number_windows; i++)
15414 {
15415 if (magick_windows[i]->id == windows->icon.id)
15416 continue;
15417 context_values.background=pixel->background_color.pixel;
15418 context_values.foreground=pixel->foreground_color.pixel;
15419 (void) XChangeGC(display,magick_windows[i]->annotate_context,
15420 context_mask,&context_values);
15421 (void) XChangeGC(display,magick_windows[i]->widget_context,
15422 context_mask,&context_values);
15423 context_values.background=pixel->foreground_color.pixel;
15424 context_values.foreground=pixel->background_color.pixel;
15425 context_values.plane_mask=context_values.background ^
15426 context_values.foreground;
15427 (void) XChangeGC(display,magick_windows[i]->highlight_context,
cristybb503372010-05-27 20:51:26 +000015428 (size_t) (context_mask | GCPlaneMask),
cristy3ed852e2009-09-05 21:47:34 +000015429 &context_values);
15430 magick_windows[i]->attributes.background_pixel=
15431 pixel->background_color.pixel;
15432 magick_windows[i]->attributes.border_pixel=
15433 pixel->border_color.pixel;
15434 magick_windows[i]->attributes.colormap=map_info->colormap;
15435 (void) XChangeWindowAttributes(display,magick_windows[i]->id,
cristy49e2d862010-11-12 02:50:30 +000015436 (unsigned long) magick_windows[i]->mask,
15437 &magick_windows[i]->attributes);
cristy3ed852e2009-09-05 21:47:34 +000015438 }
15439 if (windows->pan.mapped != MagickFalse)
15440 {
15441 (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15442 windows->pan.pixmap);
15443 (void) XClearWindow(display,windows->pan.id);
15444 XDrawPanRectangle(display,windows);
15445 }
15446 if (windows->backdrop.id != (Window) NULL)
15447 (void) XInstallColormap(display,map_info->colormap);
15448 break;
15449 }
cristyecd0ab52010-05-30 14:59:20 +000015450 if (*event.xclient.data.l == (long) windows->im_former_image)
cristy3ed852e2009-09-05 21:47:34 +000015451 {
15452 *state|=FormerImageState | ExitState;
15453 break;
15454 }
cristyecd0ab52010-05-30 14:59:20 +000015455 if (*event.xclient.data.l == (long) windows->im_next_image)
cristy3ed852e2009-09-05 21:47:34 +000015456 {
15457 *state|=NextImageState | ExitState;
15458 break;
15459 }
cristyecd0ab52010-05-30 14:59:20 +000015460 if (*event.xclient.data.l == (long) windows->im_retain_colors)
cristy3ed852e2009-09-05 21:47:34 +000015461 {
15462 *state|=RetainColorsState;
15463 break;
15464 }
cristyecd0ab52010-05-30 14:59:20 +000015465 if (*event.xclient.data.l == (long) windows->im_exit)
cristy3ed852e2009-09-05 21:47:34 +000015466 {
15467 *state|=ExitState;
15468 break;
15469 }
15470 break;
15471 }
15472 if (event.xclient.message_type == windows->dnd_protocols)
15473 {
15474 Atom
15475 selection,
15476 type;
15477
15478 int
15479 format,
15480 status;
15481
15482 unsigned char
15483 *data;
15484
cristyf2faecf2010-05-28 19:19:36 +000015485 unsigned long
cristy3ed852e2009-09-05 21:47:34 +000015486 after,
15487 length;
15488
15489 /*
15490 Display image named by the Drag-and-Drop selection.
15491 */
15492 if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15493 break;
15494 selection=XInternAtom(display,"DndSelection",MagickFalse);
cristyecd0ab52010-05-30 14:59:20 +000015495 status=XGetWindowProperty(display,root_window,selection,0L,(long)
cristy3ed852e2009-09-05 21:47:34 +000015496 MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15497 &length,&after,&data);
15498 if ((status != Success) || (length == 0))
15499 break;
15500 if (*event.xclient.data.l == 2)
15501 {
15502 /*
15503 Offix DND.
15504 */
15505 (void) CopyMagickString(resource_info->image_info->filename,
15506 (char *) data,MaxTextExtent);
15507 }
15508 else
15509 {
15510 /*
15511 XDND.
15512 */
15513 if (strncmp((char *) data, "file:", 5) != 0)
15514 {
15515 (void) XFree((void *) data);
15516 break;
15517 }
15518 (void) CopyMagickString(resource_info->image_info->filename,
15519 ((char *) data)+5,MaxTextExtent);
15520 }
cristy947cb4c2011-10-20 18:41:46 +000015521 nexus=ReadImage(resource_info->image_info,exception);
15522 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +000015523 if (nexus != (Image *) NULL)
15524 *state|=NextImageState | ExitState;
15525 (void) XFree((void *) data);
15526 break;
15527 }
15528 /*
15529 If client window delete message, exit.
15530 */
15531 if (event.xclient.message_type != windows->wm_protocols)
15532 break;
cristyecd0ab52010-05-30 14:59:20 +000015533 if (*event.xclient.data.l != (long) windows->wm_delete_window)
cristy3ed852e2009-09-05 21:47:34 +000015534 break;
15535 (void) XWithdrawWindow(display,event.xclient.window,
15536 visual_info->screen);
15537 if (event.xclient.window == windows->image.id)
15538 {
15539 *state|=ExitState;
15540 break;
15541 }
15542 if (event.xclient.window == windows->pan.id)
15543 {
15544 /*
15545 Restore original image size when pan window is deleted.
15546 */
15547 windows->image.window_changes.width=windows->image.ximage->width;
15548 windows->image.window_changes.height=windows->image.ximage->height;
15549 (void) XConfigureImage(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000015550 display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000015551 }
15552 break;
15553 }
15554 case ConfigureNotify:
15555 {
15556 if (display_image->debug != MagickFalse)
15557 (void) LogMagickEvent(X11Event,GetMagickModule(),
15558 "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15559 event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15560 event.xconfigure.y,event.xconfigure.send_event);
15561 if (event.xconfigure.window == windows->image.id)
15562 {
15563 /*
15564 Image window has a new configuration.
15565 */
15566 if (event.xconfigure.send_event != 0)
15567 {
15568 XWindowChanges
15569 window_changes;
15570
15571 /*
15572 Position the transient windows relative of the Image window.
15573 */
15574 if (windows->command.geometry == (char *) NULL)
15575 if (windows->command.mapped == MagickFalse)
15576 {
15577 windows->command.x=event.xconfigure.x-
15578 windows->command.width-25;
15579 windows->command.y=event.xconfigure.y;
15580 XConstrainWindowPosition(display,&windows->command);
15581 window_changes.x=windows->command.x;
15582 window_changes.y=windows->command.y;
15583 (void) XReconfigureWMWindow(display,windows->command.id,
15584 windows->command.screen,(unsigned int) (CWX | CWY),
15585 &window_changes);
15586 }
15587 if (windows->widget.geometry == (char *) NULL)
15588 if (windows->widget.mapped == MagickFalse)
15589 {
15590 windows->widget.x=event.xconfigure.x+
15591 event.xconfigure.width/10;
15592 windows->widget.y=event.xconfigure.y+
15593 event.xconfigure.height/10;
15594 XConstrainWindowPosition(display,&windows->widget);
15595 window_changes.x=windows->widget.x;
15596 window_changes.y=windows->widget.y;
15597 (void) XReconfigureWMWindow(display,windows->widget.id,
15598 windows->widget.screen,(unsigned int) (CWX | CWY),
15599 &window_changes);
15600 }
15601 if (windows->magnify.geometry == (char *) NULL)
15602 if (windows->magnify.mapped == MagickFalse)
15603 {
15604 windows->magnify.x=event.xconfigure.x+
15605 event.xconfigure.width+25;
15606 windows->magnify.y=event.xconfigure.y;
15607 XConstrainWindowPosition(display,&windows->magnify);
15608 window_changes.x=windows->magnify.x;
15609 window_changes.y=windows->magnify.y;
15610 (void) XReconfigureWMWindow(display,windows->magnify.id,
15611 windows->magnify.screen,(unsigned int) (CWX | CWY),
15612 &window_changes);
15613 }
15614 if (windows->pan.geometry == (char *) NULL)
15615 if (windows->pan.mapped == MagickFalse)
15616 {
15617 windows->pan.x=event.xconfigure.x+
15618 event.xconfigure.width+25;
15619 windows->pan.y=event.xconfigure.y+
15620 windows->magnify.height+50;
15621 XConstrainWindowPosition(display,&windows->pan);
15622 window_changes.x=windows->pan.x;
15623 window_changes.y=windows->pan.y;
15624 (void) XReconfigureWMWindow(display,windows->pan.id,
15625 windows->pan.screen,(unsigned int) (CWX | CWY),
15626 &window_changes);
15627 }
15628 }
cristyecd0ab52010-05-30 14:59:20 +000015629 if ((event.xconfigure.width == (int) windows->image.width) &&
15630 (event.xconfigure.height == (int) windows->image.height))
cristy3ed852e2009-09-05 21:47:34 +000015631 break;
15632 windows->image.width=(unsigned int) event.xconfigure.width;
15633 windows->image.height=(unsigned int) event.xconfigure.height;
15634 windows->image.x=0;
15635 windows->image.y=0;
15636 if (display_image->montage != (char *) NULL)
15637 {
15638 windows->image.x=vid_info.x;
15639 windows->image.y=vid_info.y;
15640 }
cristy34b9f452010-01-06 20:04:29 +000015641 if ((windows->image.mapped != MagickFalse) &&
15642 (windows->image.stasis != MagickFalse))
15643 {
15644 /*
15645 Update image window configuration.
15646 */
15647 windows->image.window_changes.width=event.xconfigure.width;
15648 windows->image.window_changes.height=event.xconfigure.height;
15649 (void) XConfigureImage(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000015650 display_image,exception);
cristy34b9f452010-01-06 20:04:29 +000015651 }
cristy3ed852e2009-09-05 21:47:34 +000015652 /*
15653 Update pan window configuration.
15654 */
15655 if ((event.xconfigure.width < windows->image.ximage->width) ||
15656 (event.xconfigure.height < windows->image.ximage->height))
15657 {
15658 (void) XMapRaised(display,windows->pan.id);
15659 XDrawPanRectangle(display,windows);
15660 }
15661 else
15662 if (windows->pan.mapped != MagickFalse)
15663 (void) XWithdrawWindow(display,windows->pan.id,
15664 windows->pan.screen);
15665 break;
15666 }
15667 if (event.xconfigure.window == windows->magnify.id)
15668 {
15669 unsigned int
15670 magnify;
15671
15672 /*
15673 Magnify window has a new configuration.
15674 */
15675 windows->magnify.width=(unsigned int) event.xconfigure.width;
15676 windows->magnify.height=(unsigned int) event.xconfigure.height;
15677 if (windows->magnify.mapped == MagickFalse)
15678 break;
15679 magnify=1;
15680 while ((int) magnify <= event.xconfigure.width)
15681 magnify<<=1;
15682 while ((int) magnify <= event.xconfigure.height)
15683 magnify<<=1;
15684 magnify>>=1;
15685 if (((int) magnify != event.xconfigure.width) ||
15686 ((int) magnify != event.xconfigure.height))
15687 {
15688 window_changes.width=(int) magnify;
15689 window_changes.height=(int) magnify;
15690 (void) XReconfigureWMWindow(display,windows->magnify.id,
15691 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15692 &window_changes);
15693 break;
15694 }
15695 if ((windows->magnify.mapped != MagickFalse) &&
15696 (windows->magnify.stasis != MagickFalse))
15697 {
15698 status=XMakeImage(display,resource_info,&windows->magnify,
cristy051718b2011-08-28 22:49:25 +000015699 display_image,windows->magnify.width,windows->magnify.height,
15700 exception);
cristy6710d842011-10-20 23:23:00 +000015701 XMakeMagnifyImage(display,windows,exception);
cristy3ed852e2009-09-05 21:47:34 +000015702 }
15703 break;
15704 }
15705 if ((windows->magnify.mapped != MagickFalse) &&
15706 (event.xconfigure.window == windows->pan.id))
15707 {
15708 /*
15709 Pan icon window has a new configuration.
15710 */
15711 if (event.xconfigure.send_event != 0)
15712 {
15713 windows->pan.x=event.xconfigure.x;
15714 windows->pan.y=event.xconfigure.y;
15715 }
15716 windows->pan.width=(unsigned int) event.xconfigure.width;
15717 windows->pan.height=(unsigned int) event.xconfigure.height;
15718 break;
15719 }
15720 if (event.xconfigure.window == windows->icon.id)
15721 {
15722 /*
15723 Icon window has a new configuration.
15724 */
15725 windows->icon.width=(unsigned int) event.xconfigure.width;
15726 windows->icon.height=(unsigned int) event.xconfigure.height;
15727 break;
15728 }
15729 break;
15730 }
15731 case DestroyNotify:
15732 {
15733 /*
15734 Group leader has exited.
15735 */
15736 if (display_image->debug != MagickFalse)
15737 (void) LogMagickEvent(X11Event,GetMagickModule(),
15738 "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15739 if (event.xdestroywindow.window == windows->group_leader.id)
15740 {
15741 *state|=ExitState;
15742 break;
15743 }
15744 break;
15745 }
15746 case EnterNotify:
15747 {
15748 /*
15749 Selectively install colormap.
15750 */
15751 if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15752 if (event.xcrossing.mode != NotifyUngrab)
15753 XInstallColormap(display,map_info->colormap);
15754 break;
15755 }
15756 case Expose:
15757 {
15758 if (display_image->debug != MagickFalse)
15759 (void) LogMagickEvent(X11Event,GetMagickModule(),
15760 "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15761 event.xexpose.width,event.xexpose.height,event.xexpose.x,
15762 event.xexpose.y);
15763 /*
15764 Refresh windows that are now exposed.
15765 */
cristy6bee4042010-01-30 15:27:14 +000015766 if ((event.xexpose.window == windows->image.id) &&
15767 (windows->image.mapped != MagickFalse))
15768 {
15769 XRefreshWindow(display,&windows->image,&event);
15770 delay=display_image->delay/MagickMax(
15771 display_image->ticks_per_second,1L);
15772 timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15773 break;
15774 }
15775 if ((event.xexpose.window == windows->magnify.id) &&
15776 (windows->magnify.mapped != MagickFalse))
15777 {
cristy6710d842011-10-20 23:23:00 +000015778 XMakeMagnifyImage(display,windows,exception);
cristy6bee4042010-01-30 15:27:14 +000015779 break;
15780 }
cristy3ed852e2009-09-05 21:47:34 +000015781 if (event.xexpose.window == windows->pan.id)
cristy6bee4042010-01-30 15:27:14 +000015782 {
15783 XDrawPanRectangle(display,windows);
15784 break;
15785 }
cristy3ed852e2009-09-05 21:47:34 +000015786 if (event.xexpose.window == windows->icon.id)
cristy6bee4042010-01-30 15:27:14 +000015787 {
15788 XRefreshWindow(display,&windows->icon,&event);
15789 break;
15790 }
cristy3ed852e2009-09-05 21:47:34 +000015791 break;
15792 }
15793 case KeyPress:
15794 {
15795 int
15796 length;
15797
15798 /*
15799 Respond to a user key press.
15800 */
15801 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15802 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15803 *(command+length)='\0';
15804 if (display_image->debug != MagickFalse)
15805 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000015806 "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
cristy3ed852e2009-09-05 21:47:34 +000015807 key_symbol,command);
15808 if (event.xkey.window == windows->image.id)
15809 {
15810 command_type=XImageWindowCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000015811 event.xkey.state,key_symbol,&display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000015812 if (command_type != NullCommand)
15813 nexus=XMagickCommand(display,resource_info,windows,command_type,
cristy051718b2011-08-28 22:49:25 +000015814 &display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000015815 }
15816 if (event.xkey.window == windows->magnify.id)
cristy6710d842011-10-20 23:23:00 +000015817 XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15818 exception);
cristy3ed852e2009-09-05 21:47:34 +000015819 if (event.xkey.window == windows->pan.id)
15820 {
15821 if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15822 (void) XWithdrawWindow(display,windows->pan.id,
15823 windows->pan.screen);
15824 else
15825 if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15826 XTextViewWidget(display,resource_info,windows,MagickFalse,
15827 "Help Viewer - Image Pan",ImagePanHelp);
15828 else
15829 XTranslateImage(display,windows,*image,key_symbol);
15830 }
15831 delay=display_image->delay/MagickMax(
15832 display_image->ticks_per_second,1L);
15833 timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15834 break;
15835 }
15836 case KeyRelease:
15837 {
15838 /*
15839 Respond to a user key release.
15840 */
15841 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15842 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15843 if (display_image->debug != MagickFalse)
15844 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000015845 "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
cristy3ed852e2009-09-05 21:47:34 +000015846 break;
15847 }
15848 case LeaveNotify:
15849 {
15850 /*
15851 Selectively uninstall colormap.
15852 */
15853 if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15854 if (event.xcrossing.mode != NotifyUngrab)
15855 XUninstallColormap(display,map_info->colormap);
15856 break;
15857 }
15858 case MapNotify:
15859 {
15860 if (display_image->debug != MagickFalse)
15861 (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15862 event.xmap.window);
15863 if (event.xmap.window == windows->backdrop.id)
15864 {
15865 (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15866 CurrentTime);
15867 windows->backdrop.mapped=MagickTrue;
15868 break;
15869 }
15870 if (event.xmap.window == windows->image.id)
15871 {
15872 if (windows->backdrop.id != (Window) NULL)
15873 (void) XInstallColormap(display,map_info->colormap);
15874 if (LocaleCompare(display_image->magick,"LOGO") == 0)
15875 {
15876 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15877 nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15878 }
15879 if (((int) windows->image.width < windows->image.ximage->width) ||
15880 ((int) windows->image.height < windows->image.ximage->height))
15881 (void) XMapRaised(display,windows->pan.id);
15882 windows->image.mapped=MagickTrue;
15883 break;
15884 }
15885 if (event.xmap.window == windows->magnify.id)
15886 {
cristy6710d842011-10-20 23:23:00 +000015887 XMakeMagnifyImage(display,windows,exception);
cristy3ed852e2009-09-05 21:47:34 +000015888 windows->magnify.mapped=MagickTrue;
15889 (void) XWithdrawWindow(display,windows->info.id,
15890 windows->info.screen);
15891 break;
15892 }
15893 if (event.xmap.window == windows->pan.id)
15894 {
cristy051718b2011-08-28 22:49:25 +000015895 XMakePanImage(display,resource_info,windows,display_image,
15896 exception);
cristy3ed852e2009-09-05 21:47:34 +000015897 windows->pan.mapped=MagickTrue;
15898 break;
15899 }
15900 if (event.xmap.window == windows->info.id)
15901 {
15902 windows->info.mapped=MagickTrue;
15903 break;
15904 }
15905 if (event.xmap.window == windows->icon.id)
15906 {
15907 MagickBooleanType
15908 taint;
15909
15910 /*
15911 Create an icon image.
15912 */
15913 taint=display_image->taint;
15914 XMakeStandardColormap(display,icon_visual,icon_resources,
cristy6710d842011-10-20 23:23:00 +000015915 display_image,icon_map,icon_pixel,exception);
cristy3ed852e2009-09-05 21:47:34 +000015916 (void) XMakeImage(display,icon_resources,&windows->icon,
cristy051718b2011-08-28 22:49:25 +000015917 display_image,windows->icon.width,windows->icon.height,
15918 exception);
cristy3ed852e2009-09-05 21:47:34 +000015919 display_image->taint=taint;
15920 (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15921 windows->icon.pixmap);
15922 (void) XClearWindow(display,windows->icon.id);
15923 (void) XWithdrawWindow(display,windows->info.id,
15924 windows->info.screen);
15925 windows->icon.mapped=MagickTrue;
15926 break;
15927 }
15928 if (event.xmap.window == windows->command.id)
15929 {
15930 windows->command.mapped=MagickTrue;
15931 break;
15932 }
15933 if (event.xmap.window == windows->popup.id)
15934 {
15935 windows->popup.mapped=MagickTrue;
15936 break;
15937 }
15938 if (event.xmap.window == windows->widget.id)
15939 {
15940 windows->widget.mapped=MagickTrue;
15941 break;
15942 }
15943 break;
15944 }
15945 case MappingNotify:
15946 {
15947 (void) XRefreshKeyboardMapping(&event.xmapping);
15948 break;
15949 }
15950 case NoExpose:
15951 break;
15952 case PropertyNotify:
15953 {
15954 Atom
15955 type;
15956
15957 int
15958 format,
15959 status;
15960
15961 unsigned char
15962 *data;
15963
cristyf2faecf2010-05-28 19:19:36 +000015964 unsigned long
cristy3ed852e2009-09-05 21:47:34 +000015965 after,
15966 length;
15967
15968 if (display_image->debug != MagickFalse)
15969 (void) LogMagickEvent(X11Event,GetMagickModule(),
15970 "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15971 event.xproperty.atom,event.xproperty.state);
15972 if (event.xproperty.atom != windows->im_remote_command)
15973 break;
15974 /*
15975 Display image named by the remote command protocol.
15976 */
15977 status=XGetWindowProperty(display,event.xproperty.window,
cristyecd0ab52010-05-30 14:59:20 +000015978 event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
cristy3ed852e2009-09-05 21:47:34 +000015979 AnyPropertyType,&type,&format,&length,&after,&data);
15980 if ((status != Success) || (length == 0))
15981 break;
15982 if (LocaleCompare((char *) data,"-quit") == 0)
15983 {
15984 XClientMessage(display,windows->image.id,windows->im_protocols,
15985 windows->im_exit,CurrentTime);
15986 (void) XFree((void *) data);
15987 break;
15988 }
15989 (void) CopyMagickString(resource_info->image_info->filename,
15990 (char *) data,MaxTextExtent);
15991 (void) XFree((void *) data);
cristy947cb4c2011-10-20 18:41:46 +000015992 nexus=ReadImage(resource_info->image_info,exception);
15993 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +000015994 if (nexus != (Image *) NULL)
15995 *state|=NextImageState | ExitState;
15996 break;
15997 }
15998 case ReparentNotify:
15999 {
16000 if (display_image->debug != MagickFalse)
16001 (void) LogMagickEvent(X11Event,GetMagickModule(),
16002 "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
16003 event.xreparent.window);
16004 break;
16005 }
16006 case UnmapNotify:
16007 {
16008 if (display_image->debug != MagickFalse)
16009 (void) LogMagickEvent(X11Event,GetMagickModule(),
16010 "Unmap Notify: 0x%lx",event.xunmap.window);
16011 if (event.xunmap.window == windows->backdrop.id)
16012 {
16013 windows->backdrop.mapped=MagickFalse;
16014 break;
16015 }
16016 if (event.xunmap.window == windows->image.id)
16017 {
16018 windows->image.mapped=MagickFalse;
16019 break;
16020 }
16021 if (event.xunmap.window == windows->magnify.id)
16022 {
16023 windows->magnify.mapped=MagickFalse;
16024 break;
16025 }
16026 if (event.xunmap.window == windows->pan.id)
16027 {
16028 windows->pan.mapped=MagickFalse;
16029 break;
16030 }
16031 if (event.xunmap.window == windows->info.id)
16032 {
16033 windows->info.mapped=MagickFalse;
16034 break;
16035 }
16036 if (event.xunmap.window == windows->icon.id)
16037 {
16038 if (map_info->colormap == icon_map->colormap)
16039 XConfigureImageColormap(display,resource_info,windows,
cristy6710d842011-10-20 23:23:00 +000016040 display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000016041 (void) XFreeStandardColormap(display,icon_visual,icon_map,
16042 icon_pixel);
16043 windows->icon.mapped=MagickFalse;
16044 break;
16045 }
16046 if (event.xunmap.window == windows->command.id)
16047 {
16048 windows->command.mapped=MagickFalse;
16049 break;
16050 }
16051 if (event.xunmap.window == windows->popup.id)
16052 {
16053 if (windows->backdrop.id != (Window) NULL)
16054 (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16055 CurrentTime);
16056 windows->popup.mapped=MagickFalse;
16057 break;
16058 }
16059 if (event.xunmap.window == windows->widget.id)
16060 {
16061 if (windows->backdrop.id != (Window) NULL)
16062 (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16063 CurrentTime);
16064 windows->widget.mapped=MagickFalse;
16065 break;
16066 }
16067 break;
16068 }
16069 default:
16070 {
16071 if (display_image->debug != MagickFalse)
16072 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16073 event.type);
16074 break;
16075 }
16076 }
16077 } while (!(*state & ExitState));
16078 if ((*state & ExitState) == 0)
16079 (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
cristy051718b2011-08-28 22:49:25 +000016080 &display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000016081 else
16082 if (resource_info->confirm_edit != MagickFalse)
16083 {
16084 /*
16085 Query user if image has changed.
16086 */
16087 if ((resource_info->immutable == MagickFalse) &&
16088 (display_image->taint != MagickFalse))
16089 {
16090 int
16091 status;
16092
16093 status=XConfirmWidget(display,windows,"Your image changed.",
16094 "Do you want to save it");
16095 if (status == 0)
16096 *state&=(~ExitState);
16097 else
16098 if (status > 0)
16099 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
cristy051718b2011-08-28 22:49:25 +000016100 &display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000016101 }
16102 }
16103 if ((windows->visual_info->klass == GrayScale) ||
16104 (windows->visual_info->klass == PseudoColor) ||
16105 (windows->visual_info->klass == DirectColor))
16106 {
16107 /*
16108 Withdraw pan and Magnify window.
16109 */
16110 if (windows->info.mapped != MagickFalse)
16111 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16112 if (windows->magnify.mapped != MagickFalse)
16113 (void) XWithdrawWindow(display,windows->magnify.id,
16114 windows->magnify.screen);
16115 if (windows->command.mapped != MagickFalse)
16116 (void) XWithdrawWindow(display,windows->command.id,
16117 windows->command.screen);
16118 }
16119 if (windows->pan.mapped != MagickFalse)
16120 (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16121 if (resource_info->backdrop == MagickFalse)
16122 if (windows->backdrop.mapped)
16123 {
16124 (void) XWithdrawWindow(display,windows->backdrop.id,
16125 windows->backdrop.screen);
16126 (void) XDestroyWindow(display,windows->backdrop.id);
16127 windows->backdrop.id=(Window) NULL;
16128 (void) XWithdrawWindow(display,windows->image.id,
16129 windows->image.screen);
16130 (void) XDestroyWindow(display,windows->image.id);
16131 windows->image.id=(Window) NULL;
16132 }
16133 XSetCursorState(display,windows,MagickTrue);
16134 XCheckRefreshWindows(display,windows);
16135 if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16136 *state&=(~ExitState);
16137 if (*state & ExitState)
16138 {
16139 /*
16140 Free Standard Colormap.
16141 */
16142 (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16143 if (resource_info->map_type == (char *) NULL)
16144 (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16145 /*
16146 Free X resources.
16147 */
16148 if (resource_info->copy_image != (Image *) NULL)
16149 {
16150 resource_info->copy_image=DestroyImage(resource_info->copy_image);
16151 resource_info->copy_image=NewImageList();
16152 }
16153 DestroyXResources();
16154 }
16155 (void) XSync(display,MagickFalse);
16156 /*
16157 Restore our progress monitor and warning handlers.
16158 */
16159 (void) SetErrorHandler(warning_handler);
16160 (void) SetWarningHandler(warning_handler);
16161 /*
16162 Change to home directory.
16163 */
cristy00976d82011-02-20 20:31:28 +000016164 directory=getcwd(working_directory,MaxTextExtent);
16165 (void) directory;
cristy3ed852e2009-09-05 21:47:34 +000016166 {
16167 int
16168 status;
16169
16170 status=chdir(resource_info->home_directory);
16171 if (status == -1)
cristy947cb4c2011-10-20 18:41:46 +000016172 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16173 "UnableToOpenFile","%s",resource_info->home_directory);
cristy3ed852e2009-09-05 21:47:34 +000016174 }
16175 *image=display_image;
16176 return(nexus);
16177}
16178#else
16179
16180/*
16181%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16182% %
16183% %
16184% %
16185+ D i s p l a y I m a g e s %
16186% %
16187% %
16188% %
16189%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16190%
16191% DisplayImages() displays an image sequence to any X window screen. It
16192% returns a value other than 0 if successful. Check the exception member
16193% of image to determine the reason for any failure.
16194%
16195% The format of the DisplayImages method is:
16196%
16197% MagickBooleanType DisplayImages(const ImageInfo *image_info,
cristy051718b2011-08-28 22:49:25 +000016198% Image *images,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000016199%
16200% A description of each parameter follows:
16201%
16202% o image_info: the image info.
16203%
16204% o image: the image.
16205%
cristy051718b2011-08-28 22:49:25 +000016206% o exception: return any errors or warnings in this structure.
16207%
cristy3ed852e2009-09-05 21:47:34 +000016208*/
16209MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
cristy051718b2011-08-28 22:49:25 +000016210 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000016211{
16212 assert(image_info != (const ImageInfo *) NULL);
16213 assert(image_info->signature == MagickSignature);
16214 assert(image != (Image *) NULL);
16215 assert(image->signature == MagickSignature);
16216 if (image->debug != MagickFalse)
16217 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy051718b2011-08-28 22:49:25 +000016218 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16219 "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image->filename);
cristy3ed852e2009-09-05 21:47:34 +000016220 return(MagickFalse);
16221}
16222
16223/*
16224%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16225% %
16226% %
16227% %
16228+ R e m o t e D i s p l a y C o m m a n d %
16229% %
16230% %
16231% %
16232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16233%
16234% RemoteDisplayCommand() encourages a remote display program to display the
16235% specified image filename.
16236%
16237% The format of the RemoteDisplayCommand method is:
16238%
16239% MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16240% const char *window,const char *filename,ExceptionInfo *exception)
16241%
16242% A description of each parameter follows:
16243%
16244% o image_info: the image info.
16245%
16246% o window: Specifies the name or id of an X window.
16247%
16248% o filename: the name of the image filename to display.
16249%
16250% o exception: return any errors or warnings in this structure.
16251%
16252*/
16253MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16254 const char *window,const char *filename,ExceptionInfo *exception)
16255{
16256 assert(image_info != (const ImageInfo *) NULL);
16257 assert(image_info->signature == MagickSignature);
16258 assert(filename != (char *) NULL);
16259 (void) window;
16260 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16261 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16262 "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16263 return(MagickFalse);
16264}
16265#endif