MagickCore 6.9.13
Loading...
Searching...
No Matches
display.c
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% DDDD IIIII SSSSS PPPP L AAA Y Y %
7% D D I SS P P L A A Y Y %
8% D D I SSS PPPP L AAAAA Y %
9% D D I SS P L A A Y %
10% DDDD IIIII SSSSS P LLLLL A A Y %
11% %
12% %
13% MagickCore Methods to Interactively Display and Edit an Image %
14% %
15% Software Design %
16% Cristy %
17% July 1992 %
18% %
19% %
20% Copyright 1999 ImageMagick Studio LLC, a non-profit organization %
21% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% https://imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
42#include "magick/studio.h"
43#include "magick/artifact.h"
44#include "magick/attribute.h"
45#include "magick/blob.h"
46#include "magick/cache.h"
47#include "magick/channel.h"
48#include "magick/client.h"
49#include "magick/color.h"
50#include "magick/colorspace.h"
51#include "magick/composite.h"
52#include "magick/constitute.h"
53#include "magick/decorate.h"
54#include "magick/delegate.h"
55#include "magick/display.h"
56#include "magick/display-private.h"
57#include "magick/distort.h"
58#include "magick/draw.h"
59#include "magick/effect.h"
60#include "magick/enhance.h"
61#include "magick/exception.h"
62#include "magick/exception-private.h"
63#include "magick/fx.h"
64#include "magick/geometry.h"
65#include "magick/image.h"
66#include "magick/image-private.h"
67#include "magick/list.h"
68#include "magick/locale-private.h"
69#include "magick/log.h"
70#include "magick/magick.h"
71#include "magick/memory_.h"
72#include "magick/monitor.h"
73#include "magick/monitor-private.h"
74#include "magick/montage.h"
75#include "magick/nt-base-private.h"
76#include "magick/option.h"
77#include "magick/paint.h"
78#include "magick/pixel.h"
79#include "magick/pixel-private.h"
80#include "magick/property.h"
81#include "magick/quantum.h"
82#include "magick/resize.h"
83#include "magick/resource_.h"
84#include "magick/shear.h"
85#include "magick/segment.h"
86#include "magick/statistic.h"
87#include "magick/string_.h"
88#include "magick/string-private.h"
89#include "magick/timer-private.h"
90#include "magick/transform.h"
91#include "magick/threshold.h"
92#include "magick/utility.h"
93#include "magick/utility-private.h"
94#include "magick/version.h"
95#include "magick/visual-effects.h"
96#include "magick/widget.h"
97#include "magick/xwindow-private.h"
98
99#if defined(MAGICKCORE_X11_DELEGATE)
100/*
101 Define declarations.
102*/
103#define MaxColors MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
104
105/*
106 Constant declarations.
107*/
108static const unsigned char
109 HighlightBitmap[8] =
110 {
111 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
112 },
113 OpaqueBitmap[8] =
114 {
115 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
116 },
117 ShadowBitmap[8] =
118 {
119 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
120 };
121
122/*
123 Help widget declarations.
124*/
125static const char
126 ImageAnnotateHelp[] =
127 {
128 "In annotate mode, the Command widget has these options:\n"
129 "\n"
130 " Font Name\n"
131 " fixed\n"
132 " variable\n"
133 " 5x8\n"
134 " 6x10\n"
135 " 7x13bold\n"
136 " 8x13bold\n"
137 " 9x15bold\n"
138 " 10x20\n"
139 " 12x24\n"
140 " Browser...\n"
141 " Font Color\n"
142 " black\n"
143 " blue\n"
144 " cyan\n"
145 " green\n"
146 " gray\n"
147 " red\n"
148 " magenta\n"
149 " yellow\n"
150 " white\n"
151 " transparent\n"
152 " Browser...\n"
153 " Font Color\n"
154 " black\n"
155 " blue\n"
156 " cyan\n"
157 " green\n"
158 " gray\n"
159 " red\n"
160 " magenta\n"
161 " yellow\n"
162 " white\n"
163 " transparent\n"
164 " Browser...\n"
165 " Rotate Text\n"
166 " -90\n"
167 " -45\n"
168 " -30\n"
169 " 0\n"
170 " 30\n"
171 " 45\n"
172 " 90\n"
173 " 180\n"
174 " Dialog...\n"
175 " Help\n"
176 " Dismiss\n"
177 "\n"
178 "Choose a font name from the Font Name sub-menu. Additional\n"
179 "font names can be specified with the font browser. You can\n"
180 "change the menu names by setting the X resources font1\n"
181 "through font9.\n"
182 "\n"
183 "Choose a font color from the Font Color sub-menu.\n"
184 "Additional font colors can be specified with the color\n"
185 "browser. You can change the menu colors by setting the X\n"
186 "resources pen1 through pen9.\n"
187 "\n"
188 "If you select the color browser and press Grab, you can\n"
189 "choose the font color by moving the pointer to the desired\n"
190 "color on the screen and press any button.\n"
191 "\n"
192 "If you choose to rotate the text, choose Rotate Text from the\n"
193 "menu and select an angle. Typically you will only want to\n"
194 "rotate one line of text at a time. Depending on the angle you\n"
195 "choose, subsequent lines may end up overwriting each other.\n"
196 "\n"
197 "Choosing a font and its color is optional. The default font\n"
198 "is fixed and the default color is black. However, you must\n"
199 "choose a location to begin entering text and press button 1.\n"
200 "An underscore character will appear at the location of the\n"
201 "pointer. The cursor changes to a pencil to indicate you are\n"
202 "in text mode. To exit immediately, press Dismiss.\n"
203 "\n"
204 "In text mode, any key presses will display the character at\n"
205 "the location of the underscore and advance the underscore\n"
206 "cursor. Enter your text and once completed press Apply to\n"
207 "finish your image annotation. To correct errors press BACK\n"
208 "SPACE. To delete an entire line of text, press DELETE. Any\n"
209 "text that exceeds the boundaries of the image window is\n"
210 "automagically continued onto the next line.\n"
211 "\n"
212 "The actual color you request for the font is saved in the\n"
213 "image. However, the color that appears in your image window\n"
214 "may be different. For example, on a monochrome screen the\n"
215 "text will appear black or white even if you choose the color\n"
216 "red as the font color. However, the image saved to a file\n"
217 "with -write is written with red lettering. To assure the\n"
218 "correct color text in the final image, any PseudoClass image\n"
219 "is promoted to DirectClass (see miff(5)). To force a\n"
220 "PseudoClass image to remain PseudoClass, use -colors.\n"
221 },
222 ImageChopHelp[] =
223 {
224 "In chop mode, the Command widget has these options:\n"
225 "\n"
226 " Direction\n"
227 " horizontal\n"
228 " vertical\n"
229 " Help\n"
230 " Dismiss\n"
231 "\n"
232 "If the you choose the horizontal direction (this the\n"
233 "default), the area of the image between the two horizontal\n"
234 "endpoints of the chop line is removed. Otherwise, the area\n"
235 "of the image between the two vertical endpoints of the chop\n"
236 "line is removed.\n"
237 "\n"
238 "Select a location within the image window to begin your chop,\n"
239 "press and hold any button. Next, move the pointer to\n"
240 "another location in the image. As you move a line will\n"
241 "connect the initial location and the pointer. When you\n"
242 "release the button, the area within the image to chop is\n"
243 "determined by which direction you choose from the Command\n"
244 "widget.\n"
245 "\n"
246 "To cancel the image chopping, move the pointer back to the\n"
247 "starting point of the line and release the button.\n"
248 },
249 ImageColorEditHelp[] =
250 {
251 "In color edit mode, the Command widget has these options:\n"
252 "\n"
253 " Method\n"
254 " point\n"
255 " replace\n"
256 " floodfill\n"
257 " filltoborder\n"
258 " reset\n"
259 " Pixel Color\n"
260 " black\n"
261 " blue\n"
262 " cyan\n"
263 " green\n"
264 " gray\n"
265 " red\n"
266 " magenta\n"
267 " yellow\n"
268 " white\n"
269 " Browser...\n"
270 " Border Color\n"
271 " black\n"
272 " blue\n"
273 " cyan\n"
274 " green\n"
275 " gray\n"
276 " red\n"
277 " magenta\n"
278 " yellow\n"
279 " white\n"
280 " Browser...\n"
281 " Fuzz\n"
282 " 0%\n"
283 " 2%\n"
284 " 5%\n"
285 " 10%\n"
286 " 15%\n"
287 " Dialog...\n"
288 " Undo\n"
289 " Help\n"
290 " Dismiss\n"
291 "\n"
292 "Choose a color editing method from the Method sub-menu\n"
293 "of the Command widget. The point method recolors any pixel\n"
294 "selected with the pointer until the button is released. The\n"
295 "replace method recolors any pixel that matches the color of\n"
296 "the pixel you select with a button press. Floodfill recolors\n"
297 "any pixel that matches the color of the pixel you select with\n"
298 "a button press and is a neighbor. Whereas filltoborder recolors\n"
299 "any neighbor pixel that is not the border color. Finally reset\n"
300 "changes the entire image to the designated color.\n"
301 "\n"
302 "Next, choose a pixel color from the Pixel Color sub-menu.\n"
303 "Additional pixel colors can be specified with the color\n"
304 "browser. You can change the menu colors by setting the X\n"
305 "resources pen1 through pen9.\n"
306 "\n"
307 "Now press button 1 to select a pixel within the image window\n"
308 "to change its color. Additional pixels may be recolored as\n"
309 "prescribed by the method you choose.\n"
310 "\n"
311 "If the Magnify widget is mapped, it can be helpful in positioning\n"
312 "your pointer within the image (refer to button 2).\n"
313 "\n"
314 "The actual color you request for the pixels is saved in the\n"
315 "image. However, the color that appears in your image window\n"
316 "may be different. For example, on a monochrome screen the\n"
317 "pixel will appear black or white even if you choose the\n"
318 "color red as the pixel color. However, the image saved to a\n"
319 "file with -write is written with red pixels. To assure the\n"
320 "correct color text in the final image, any PseudoClass image\n"
321 "is promoted to DirectClass (see miff(5)). To force a\n"
322 "PseudoClass image to remain PseudoClass, use -colors.\n"
323 },
324 ImageCompositeHelp[] =
325 {
326 "First a widget window is displayed requesting you to enter an\n"
327 "image name. Press Composite, Grab or type a file name.\n"
328 "Press Cancel if you choose not to create a composite image.\n"
329 "When you choose Grab, move the pointer to the desired window\n"
330 "and press any button.\n"
331 "\n"
332 "If the Composite image does not have any matte information,\n"
333 "you are informed and the file browser is displayed again.\n"
334 "Enter the name of a mask image. The image is typically\n"
335 "grayscale and the same size as the composite image. If the\n"
336 "image is not grayscale, it is converted to grayscale and the\n"
337 "resulting intensities are used as matte information.\n"
338 "\n"
339 "A small window appears showing the location of the cursor in\n"
340 "the image window. You are now in composite mode. To exit\n"
341 "immediately, press Dismiss. In composite mode, the Command\n"
342 "widget has these options:\n"
343 "\n"
344 " Operators\n"
345 " Over\n"
346 " In\n"
347 " Out\n"
348 " Atop\n"
349 " Xor\n"
350 " Plus\n"
351 " Minus\n"
352 " Add\n"
353 " Subtract\n"
354 " Difference\n"
355 " Multiply\n"
356 " Bumpmap\n"
357 " Copy\n"
358 " CopyRed\n"
359 " CopyGreen\n"
360 " CopyBlue\n"
361 " CopyOpacity\n"
362 " Clear\n"
363 " Dissolve\n"
364 " Displace\n"
365 " Help\n"
366 " Dismiss\n"
367 "\n"
368 "Choose a composite operation from the Operators sub-menu of\n"
369 "the Command widget. How each operator behaves is described\n"
370 "below. Image window is the image currently displayed on\n"
371 "your X server and image is the image obtained with the File\n"
372 "Browser widget.\n"
373 "\n"
374 "Over The result is the union of the two image shapes,\n"
375 " with image obscuring image window in the region of\n"
376 " overlap.\n"
377 "\n"
378 "In The result is simply image cut by the shape of\n"
379 " image window. None of the image data of image\n"
380 " window is in the result.\n"
381 "\n"
382 "Out The resulting image is image with the shape of\n"
383 " image window cut out.\n"
384 "\n"
385 "Atop The result is the same shape as the image window,\n"
386 " with image obscuring image window where the image\n"
387 " shapes overlap. Note this differs from over\n"
388 " because the portion of image outside image window's\n"
389 " shape does not appear in the result.\n"
390 "\n"
391 "Xor The result is the image data from both image and\n"
392 " image window that is outside the overlap region.\n"
393 " The overlap region is blank.\n"
394 "\n"
395 "Plus The result is just the sum of the image data.\n"
396 " Output values are cropped to QuantumRange (no overflow).\n"
397 "\n"
398 "Minus The result of image - image window, with underflow\n"
399 " cropped to zero.\n"
400 "\n"
401 "Add The result of image + image window, with overflow\n"
402 " wrapping around (mod 256).\n"
403 "\n"
404 "Subtract The result of image - image window, with underflow\n"
405 " wrapping around (mod 256). The add and subtract\n"
406 " operators can be used to perform reversible\n"
407 " transformations.\n"
408 "\n"
409 "Difference\n"
410 " The result of abs(image - image window). This\n"
411 " useful for comparing two very similar images.\n"
412 "\n"
413 "Multiply\n"
414 " The result of image * image window. This\n"
415 " useful for the creation of drop-shadows.\n"
416 "\n"
417 "Bumpmap The result of surface normals from image * image\n"
418 " window.\n"
419 "\n"
420 "Copy The resulting image is image window replaced with\n"
421 " image. Here the matte information is ignored.\n"
422 "\n"
423 "CopyRed The red layer of the image window is replace with\n"
424 " the red layer of the image. The other layers are\n"
425 " untouched.\n"
426 "\n"
427 "CopyGreen\n"
428 " The green layer of the image window is replace with\n"
429 " the green layer of the image. The other layers are\n"
430 " untouched.\n"
431 "\n"
432 "CopyBlue The blue layer of the image window is replace with\n"
433 " the blue layer of the image. The other layers are\n"
434 " untouched.\n"
435 "\n"
436 "CopyOpacity\n"
437 " The matte layer of the image window is replace with\n"
438 " the matte layer of the image. The other layers are\n"
439 " untouched.\n"
440 "\n"
441 "The image compositor requires a matte, or alpha channel in\n"
442 "the image for some operations. This extra channel usually\n"
443 "defines a mask which represents a sort of a cookie-cutter\n"
444 "for the image. This the case when matte is opaque (full\n"
445 "coverage) for pixels inside the shape, zero outside, and\n"
446 "between 0 and QuantumRange on the boundary. If image does not\n"
447 "have a matte channel, it is initialized with 0 for any pixel\n"
448 "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
449 "\n"
450 "If you choose Dissolve, the composite operator becomes Over. The\n"
451 "image matte channel percent transparency is initialized to factor.\n"
452 "The image window is initialized to (100-factor). Where factor is the\n"
453 "value you specify in the Dialog widget.\n"
454 "\n"
455 "Displace shifts the image pixels as defined by a displacement\n"
456 "map. With this option, image is used as a displacement map.\n"
457 "Black, within the displacement map, is a maximum positive\n"
458 "displacement. White is a maximum negative displacement and\n"
459 "middle gray is neutral. The displacement is scaled to determine\n"
460 "the pixel shift. By default, the displacement applies in both the\n"
461 "horizontal and vertical directions. However, if you specify a mask,\n"
462 "image is the horizontal X displacement and mask the vertical Y\n"
463 "displacement.\n"
464 "\n"
465 "Note that matte information for image window is not retained\n"
466 "for colormapped X server visuals (e.g. StaticColor,\n"
467 "StaticColor, GrayScale, PseudoColor). Correct compositing\n"
468 "behavior may require a TrueColor or DirectColor visual or a\n"
469 "Standard Colormap.\n"
470 "\n"
471 "Choosing a composite operator is optional. The default\n"
472 "operator is replace. However, you must choose a location to\n"
473 "composite your image and press button 1. Press and hold the\n"
474 "button before releasing and an outline of the image will\n"
475 "appear to help you identify your location.\n"
476 "\n"
477 "The actual colors of the composite image is saved. However,\n"
478 "the color that appears in image window may be different.\n"
479 "For example, on a monochrome screen image window will appear\n"
480 "black or white even though your composited image may have\n"
481 "many colors. If the image is saved to a file it is written\n"
482 "with the correct colors. To assure the correct colors are\n"
483 "saved in the final image, any PseudoClass image is promoted\n"
484 "to DirectClass (see miff(5)). To force a PseudoClass image\n"
485 "to remain PseudoClass, use -colors.\n"
486 },
487 ImageCutHelp[] =
488 {
489 "In cut mode, the Command widget has these options:\n"
490 "\n"
491 " Help\n"
492 " Dismiss\n"
493 "\n"
494 "To define a cut region, press button 1 and drag. The\n"
495 "cut region is defined by a highlighted rectangle that\n"
496 "expands or contracts as it follows the pointer. Once you\n"
497 "are satisfied with the cut region, release the button.\n"
498 "You are now in rectify mode. In rectify mode, the Command\n"
499 "widget has these options:\n"
500 "\n"
501 " Cut\n"
502 " Help\n"
503 " Dismiss\n"
504 "\n"
505 "You can make adjustments by moving the pointer to one of the\n"
506 "cut rectangle corners, pressing a button, and dragging.\n"
507 "Finally, press Cut to commit your copy region. To\n"
508 "exit without cutting the image, press Dismiss.\n"
509 },
510 ImageCopyHelp[] =
511 {
512 "In copy mode, the Command widget has these options:\n"
513 "\n"
514 " Help\n"
515 " Dismiss\n"
516 "\n"
517 "To define a copy region, press button 1 and drag. The\n"
518 "copy region is defined by a highlighted rectangle that\n"
519 "expands or contracts as it follows the pointer. Once you\n"
520 "are satisfied with the copy region, release the button.\n"
521 "You are now in rectify mode. In rectify mode, the Command\n"
522 "widget has these options:\n"
523 "\n"
524 " Copy\n"
525 " Help\n"
526 " Dismiss\n"
527 "\n"
528 "You can make adjustments by moving the pointer to one of the\n"
529 "copy rectangle corners, pressing a button, and dragging.\n"
530 "Finally, press Copy to commit your copy region. To\n"
531 "exit without copying the image, press Dismiss.\n"
532 },
533 ImageCropHelp[] =
534 {
535 "In crop mode, the Command widget has these options:\n"
536 "\n"
537 " Help\n"
538 " Dismiss\n"
539 "\n"
540 "To define a cropping region, press button 1 and drag. The\n"
541 "cropping region is defined by a highlighted rectangle that\n"
542 "expands or contracts as it follows the pointer. Once you\n"
543 "are satisfied with the cropping region, release the button.\n"
544 "You are now in rectify mode. In rectify mode, the Command\n"
545 "widget has these options:\n"
546 "\n"
547 " Crop\n"
548 " Help\n"
549 " Dismiss\n"
550 "\n"
551 "You can make adjustments by moving the pointer to one of the\n"
552 "cropping rectangle corners, pressing a button, and dragging.\n"
553 "Finally, press Crop to commit your cropping region. To\n"
554 "exit without cropping the image, press Dismiss.\n"
555 },
556 ImageDrawHelp[] =
557 {
558 "The cursor changes to a crosshair to indicate you are in\n"
559 "draw mode. To exit immediately, press Dismiss. In draw mode,\n"
560 "the Command widget has these options:\n"
561 "\n"
562 " Element\n"
563 " point\n"
564 " line\n"
565 " rectangle\n"
566 " fill rectangle\n"
567 " circle\n"
568 " fill circle\n"
569 " ellipse\n"
570 " fill ellipse\n"
571 " polygon\n"
572 " fill polygon\n"
573 " Color\n"
574 " black\n"
575 " blue\n"
576 " cyan\n"
577 " green\n"
578 " gray\n"
579 " red\n"
580 " magenta\n"
581 " yellow\n"
582 " white\n"
583 " transparent\n"
584 " Browser...\n"
585 " Stipple\n"
586 " Brick\n"
587 " Diagonal\n"
588 " Scales\n"
589 " Vertical\n"
590 " Wavy\n"
591 " Translucent\n"
592 " Opaque\n"
593 " Open...\n"
594 " Width\n"
595 " 1\n"
596 " 2\n"
597 " 4\n"
598 " 8\n"
599 " 16\n"
600 " Dialog...\n"
601 " Undo\n"
602 " Help\n"
603 " Dismiss\n"
604 "\n"
605 "Choose a drawing primitive from the Element sub-menu.\n"
606 "\n"
607 "Choose a color from the Color sub-menu. Additional\n"
608 "colors can be specified with the color browser.\n"
609 "\n"
610 "If you choose the color browser and press Grab, you can\n"
611 "select the color by moving the pointer to the desired\n"
612 "color on the screen and press any button. The transparent\n"
613 "color updates the image matte channel and is useful for\n"
614 "image compositing.\n"
615 "\n"
616 "Choose a stipple, if appropriate, from the Stipple sub-menu.\n"
617 "Additional stipples can be specified with the file browser.\n"
618 "Stipples obtained from the file browser must be on disk in the\n"
619 "X11 bitmap format.\n"
620 "\n"
621 "Choose a width, if appropriate, from the Width sub-menu. To\n"
622 "choose a specific width select the Dialog widget.\n"
623 "\n"
624 "Choose a point in the Image window and press button 1 and\n"
625 "hold. Next, move the pointer to another location in the\n"
626 "image. As you move, a line connects the initial location and\n"
627 "the pointer. When you release the button, the image is\n"
628 "updated with the primitive you just drew. For polygons, the\n"
629 "image is updated when you press and release the button without\n"
630 "moving the pointer.\n"
631 "\n"
632 "To cancel image drawing, move the pointer back to the\n"
633 "starting point of the line and release the button.\n"
634 },
635 DisplayHelp[] =
636 {
637 "BUTTONS\n"
638 " The effects of each button press is described below. Three\n"
639 " buttons are required. If you have a two button mouse,\n"
640 " button 1 and 3 are returned. Press ALT and button 3 to\n"
641 " simulate button 2.\n"
642 "\n"
643 " 1 Press this button to map or unmap the Command widget.\n"
644 "\n"
645 " 2 Press and drag to define a region of the image to\n"
646 " magnify.\n"
647 "\n"
648 " 3 Press and drag to choose from a select set of commands.\n"
649 " This button behaves differently if the image being\n"
650 " displayed is a visual image directory. Here, choose a\n"
651 " particular tile of the directory and press this button and\n"
652 " drag to select a command from a pop-up menu. Choose from\n"
653 " these menu items:\n"
654 "\n"
655 " Open\n"
656 " Next\n"
657 " Former\n"
658 " Delete\n"
659 " Update\n"
660 "\n"
661 " If you choose Open, the image represented by the tile is\n"
662 " displayed. To return to the visual image directory, choose\n"
663 " Next from the Command widget. Next and Former moves to the\n"
664 " next or former image respectively. Choose Delete to delete\n"
665 " a particular image tile. Finally, choose Update to\n"
666 " synchronize all the image tiles with their respective\n"
667 " images.\n"
668 "\n"
669 "COMMAND WIDGET\n"
670 " The Command widget lists a number of sub-menus and commands.\n"
671 " They are\n"
672 "\n"
673 " File\n"
674 " Open...\n"
675 " Next\n"
676 " Former\n"
677 " Select...\n"
678 " Save...\n"
679 " Print...\n"
680 " Delete...\n"
681 " New...\n"
682 " Visual Directory...\n"
683 " Quit\n"
684 " Edit\n"
685 " Undo\n"
686 " Redo\n"
687 " Cut\n"
688 " Copy\n"
689 " Paste\n"
690 " View\n"
691 " Half Size\n"
692 " Original Size\n"
693 " Double Size\n"
694 " Resize...\n"
695 " Apply\n"
696 " Refresh\n"
697 " Restore\n"
698 " Transform\n"
699 " Crop\n"
700 " Chop\n"
701 " Flop\n"
702 " Flip\n"
703 " Rotate Right\n"
704 " Rotate Left\n"
705 " Rotate...\n"
706 " Shear...\n"
707 " Roll...\n"
708 " Trim Edges\n"
709 " Enhance\n"
710 " Brightness...\n"
711 " Saturation...\n"
712 " Hue...\n"
713 " Gamma...\n"
714 " Sharpen...\n"
715 " Dull\n"
716 " Contrast Stretch...\n"
717 " Sigmoidal Contrast...\n"
718 " Normalize\n"
719 " Equalize\n"
720 " Negate\n"
721 " Grayscale\n"
722 " Map...\n"
723 " Quantize...\n"
724 " Effects\n"
725 " Despeckle\n"
726 " Emboss\n"
727 " Reduce Noise\n"
728 " Add Noise\n"
729 " Sharpen...\n"
730 " Blur...\n"
731 " Threshold...\n"
732 " Edge Detect...\n"
733 " Spread...\n"
734 " Shade...\n"
735 " Painting...\n"
736 " Segment...\n"
737 " F/X\n"
738 " Solarize...\n"
739 " Sepia Tone...\n"
740 " Swirl...\n"
741 " Implode...\n"
742 " Vignette...\n"
743 " Wave...\n"
744 " Oil Painting...\n"
745 " Charcoal Drawing...\n"
746 " Image Edit\n"
747 " Annotate...\n"
748 " Draw...\n"
749 " Color...\n"
750 " Matte...\n"
751 " Composite...\n"
752 " Add Border...\n"
753 " Add Frame...\n"
754 " Comment...\n"
755 " Launch...\n"
756 " Region of Interest...\n"
757 " Miscellany\n"
758 " Image Info\n"
759 " Zoom Image\n"
760 " Show Preview...\n"
761 " Show Histogram\n"
762 " Show Matte\n"
763 " Background...\n"
764 " Slide Show\n"
765 " Preferences...\n"
766 " Help\n"
767 " Overview\n"
768 " Browse Documentation\n"
769 " About Display\n"
770 "\n"
771 " Menu items with a indented triangle have a sub-menu. They\n"
772 " are represented above as the indented items. To access a\n"
773 " sub-menu item, move the pointer to the appropriate menu and\n"
774 " press a button and drag. When you find the desired sub-menu\n"
775 " item, release the button and the command is executed. Move\n"
776 " the pointer away from the sub-menu if you decide not to\n"
777 " execute a particular command.\n"
778 "\n"
779 "KEYBOARD ACCELERATORS\n"
780 " Accelerators are one or two key presses that effect a\n"
781 " particular command. The keyboard accelerators that\n"
782 " display(1) understands is:\n"
783 "\n"
784 " Ctl+O Press to open an image from a file.\n"
785 "\n"
786 " space Press to display the next image.\n"
787 "\n"
788 " If the image is a multi-paged document such as a Postscript\n"
789 " document, you can skip ahead several pages by preceding\n"
790 " this command with a number. For example to display the\n"
791 " third page beyond the current page, press 3<space>.\n"
792 "\n"
793 " backspace Press to display the former image.\n"
794 "\n"
795 " If the image is a multi-paged document such as a Postscript\n"
796 " document, you can skip behind several pages by preceding\n"
797 " this command with a number. For example to display the\n"
798 " third page preceding the current page, press 3<backspace>.\n"
799 "\n"
800 " Ctl+S Press to write the image to a file.\n"
801 "\n"
802 " Ctl+P Press to print the image to a Postscript printer.\n"
803 "\n"
804 " Ctl+D Press to delete an image file.\n"
805 "\n"
806 " Ctl+N Press to create a blank canvas.\n"
807 "\n"
808 " Ctl+Q Press to discard all images and exit program.\n"
809 "\n"
810 " Ctl+Z Press to undo last image transformation.\n"
811 "\n"
812 " Ctl+R Press to redo last image transformation.\n"
813 "\n"
814 " Ctl+X Press to cut a region of the image.\n"
815 "\n"
816 " Ctl+C Press to copy a region of the image.\n"
817 "\n"
818 " Ctl+V Press to paste a region to the image.\n"
819 "\n"
820 " < Press to half the image size.\n"
821 "\n"
822 " - Press to return to the original image size.\n"
823 "\n"
824 " > Press to double the image size.\n"
825 "\n"
826 " % Press to resize the image to a width and height you\n"
827 " specify.\n"
828 "\n"
829 "Cmd-A Press to make any image transformations permanent."
830 "\n"
831 " By default, any image size transformations are applied\n"
832 " to the original image to create the image displayed on\n"
833 " the X server. However, the transformations are not\n"
834 " permanent (i.e. the original image does not change\n"
835 " size only the X image does). For example, if you\n"
836 " press > the X image will appear to double in size,\n"
837 " but the original image will in fact remain the same size.\n"
838 " To force the original image to double in size, press >\n"
839 " followed by Cmd-A.\n"
840 "\n"
841 " @ Press to refresh the image window.\n"
842 "\n"
843 " C Press to cut out a rectangular region of the image.\n"
844 "\n"
845 " [ Press to chop the image.\n"
846 "\n"
847 " H Press to flop image in the horizontal direction.\n"
848 "\n"
849 " V Press to flip image in the vertical direction.\n"
850 "\n"
851 " / Press to rotate the image 90 degrees clockwise.\n"
852 "\n"
853 " \\ Press to rotate the image 90 degrees counter-clockwise.\n"
854 "\n"
855 " * Press to rotate the image the number of degrees you\n"
856 " specify.\n"
857 "\n"
858 " S Press to shear the image the number of degrees you\n"
859 " specify.\n"
860 "\n"
861 " R Press to roll the image.\n"
862 "\n"
863 " T Press to trim the image edges.\n"
864 "\n"
865 " Shft-H Press to vary the image hue.\n"
866 "\n"
867 " Shft-S Press to vary the color saturation.\n"
868 "\n"
869 " Shft-L Press to vary the color brightness.\n"
870 "\n"
871 " Shft-G Press to gamma correct the image.\n"
872 "\n"
873 " Shft-C Press to sharpen the image contrast.\n"
874 "\n"
875 " Shft-Z Press to dull the image contrast.\n"
876 "\n"
877 " = Press to perform histogram equalization on the image.\n"
878 "\n"
879 " Shft-N Press to perform histogram normalization on the image.\n"
880 "\n"
881 " Shft-~ Press to negate the colors of the image.\n"
882 "\n"
883 " . Press to convert the image colors to gray.\n"
884 "\n"
885 " Shft-# Press to set the maximum number of unique colors in the\n"
886 " image.\n"
887 "\n"
888 " F2 Press to reduce the speckles in an image.\n"
889 "\n"
890 " F3 Press to eliminate peak noise from an image.\n"
891 "\n"
892 " F4 Press to add noise to an image.\n"
893 "\n"
894 " F5 Press to sharpen an image.\n"
895 "\n"
896 " F6 Press to delete an image file.\n"
897 "\n"
898 " F7 Press to threshold the image.\n"
899 "\n"
900 " F8 Press to detect edges within an image.\n"
901 "\n"
902 " F9 Press to emboss an image.\n"
903 "\n"
904 " F10 Press to displace pixels by a random amount.\n"
905 "\n"
906 " F11 Press to negate all pixels above the threshold level.\n"
907 "\n"
908 " F12 Press to shade the image using a distant light source.\n"
909 "\n"
910 " F13 Press to lighten or darken image edges to create a 3-D effect.\n"
911 "\n"
912 " F14 Press to segment the image by color.\n"
913 "\n"
914 " Meta-S Press to swirl image pixels about the center.\n"
915 "\n"
916 " Meta-I Press to implode image pixels about the center.\n"
917 "\n"
918 " Meta-W Press to alter an image along a sine wave.\n"
919 "\n"
920 " Meta-P Press to simulate an oil painting.\n"
921 "\n"
922 " Meta-C Press to simulate a charcoal drawing.\n"
923 "\n"
924 " Alt-A Press to annotate the image with text.\n"
925 "\n"
926 " Alt-D Press to draw on an image.\n"
927 "\n"
928 " Alt-P Press to edit an image pixel color.\n"
929 "\n"
930 " Alt-M Press to edit the image matte information.\n"
931 "\n"
932 " Alt-V Press to composite the image with another.\n"
933 "\n"
934 " Alt-B Press to add a border to the image.\n"
935 "\n"
936 " Alt-F Press to add an ornamental border to the image.\n"
937 "\n"
938 " Alt-Shft-!\n"
939 " Press to add an image comment.\n"
940 "\n"
941 " Ctl-A Press to apply image processing techniques to a region\n"
942 " of interest.\n"
943 "\n"
944 " Shft-? Press to display information about the image.\n"
945 "\n"
946 " Shft-+ Press to map the zoom image window.\n"
947 "\n"
948 " Shft-P Press to preview an image enhancement, effect, or f/x.\n"
949 "\n"
950 " F1 Press to display helpful information about display(1).\n"
951 "\n"
952 " Find Press to browse documentation about ImageMagick.\n"
953 "\n"
954 " 1-9 Press to change the level of magnification.\n"
955 "\n"
956 " Use the arrow keys to move the image one pixel up, down,\n"
957 " left, or right within the magnify window. Be sure to first\n"
958 " map the magnify window by pressing button 2.\n"
959 "\n"
960 " Press ALT and one of the arrow keys to trim off one pixel\n"
961 " from any side of the image.\n"
962 },
963 ImageMatteEditHelp[] =
964 {
965 "Matte information within an image is useful for some\n"
966 "operations such as image compositing (See IMAGE\n"
967 "COMPOSITING). This extra channel usually defines a mask\n"
968 "which represents a sort of a cookie-cutter for the image.\n"
969 "This the case when matte is opaque (full coverage) for\n"
970 "pixels inside the shape, zero outside, and between 0 and\n"
971 "QuantumRange on the boundary.\n"
972 "\n"
973 "A small window appears showing the location of the cursor in\n"
974 "the image window. You are now in matte edit mode. To exit\n"
975 "immediately, press Dismiss. In matte edit mode, the Command\n"
976 "widget has these options:\n"
977 "\n"
978 " Method\n"
979 " point\n"
980 " replace\n"
981 " floodfill\n"
982 " filltoborder\n"
983 " reset\n"
984 " Border Color\n"
985 " black\n"
986 " blue\n"
987 " cyan\n"
988 " green\n"
989 " gray\n"
990 " red\n"
991 " magenta\n"
992 " yellow\n"
993 " white\n"
994 " Browser...\n"
995 " Fuzz\n"
996 " 0%\n"
997 " 2%\n"
998 " 5%\n"
999 " 10%\n"
1000 " 15%\n"
1001 " Dialog...\n"
1002 " Matte\n"
1003 " Opaque\n"
1004 " Transparent\n"
1005 " Dialog...\n"
1006 " Undo\n"
1007 " Help\n"
1008 " Dismiss\n"
1009 "\n"
1010 "Choose a matte editing method from the Method sub-menu of\n"
1011 "the Command widget. The point method changes the matte value\n"
1012 "of any pixel selected with the pointer until the button is\n"
1013 "is released. The replace method changes the matte value of\n"
1014 "any pixel that matches the color of the pixel you select with\n"
1015 "a button press. Floodfill changes the matte value of any pixel\n"
1016 "that matches the color of the pixel you select with a button\n"
1017 "press and is a neighbor. Whereas filltoborder changes the matte\n"
1018 "value any neighbor pixel that is not the border color. Finally\n"
1019 "reset changes the entire image to the designated matte value.\n"
1020 "\n"
1021 "Choose Matte Value and pick Opaque or Transparent. For other values\n"
1022 "select the Dialog entry. Here a dialog appears requesting a matte\n"
1023 "value. The value you select is assigned as the opacity value of the\n"
1024 "selected pixel or pixels.\n"
1025 "\n"
1026 "Now, press any button to select a pixel within the image\n"
1027 "window to change its matte value.\n"
1028 "\n"
1029 "If the Magnify widget is mapped, it can be helpful in positioning\n"
1030 "your pointer within the image (refer to button 2).\n"
1031 "\n"
1032 "Matte information is only valid in a DirectClass image.\n"
1033 "Therefore, any PseudoClass image is promoted to DirectClass\n"
1034 "(see miff(5)). Note that matte information for PseudoClass\n"
1035 "is not retained for colormapped X server visuals (e.g.\n"
1036 "StaticColor, StaticColor, GrayScale, PseudoColor) unless you\n"
1037 "immediately save your image to a file (refer to Write).\n"
1038 "Correct matte editing behavior may require a TrueColor or\n"
1039 "DirectColor visual or a Standard Colormap.\n"
1040 },
1041 ImagePanHelp[] =
1042 {
1043 "When an image exceeds the width or height of the X server\n"
1044 "screen, display maps a small panning icon. The rectangle\n"
1045 "within the panning icon shows the area that is currently\n"
1046 "displayed in the image window. To pan about the image,\n"
1047 "press any button and drag the pointer within the panning\n"
1048 "icon. The pan rectangle moves with the pointer and the\n"
1049 "image window is updated to reflect the location of the\n"
1050 "rectangle within the panning icon. When you have selected\n"
1051 "the area of the image you wish to view, release the button.\n"
1052 "\n"
1053 "Use the arrow keys to pan the image one pixel up, down,\n"
1054 "left, or right within the image window.\n"
1055 "\n"
1056 "The panning icon is withdrawn if the image becomes smaller\n"
1057 "than the dimensions of the X server screen.\n"
1058 },
1059 ImagePasteHelp[] =
1060 {
1061 "A small window appears showing the location of the cursor in\n"
1062 "the image window. You are now in paste mode. To exit\n"
1063 "immediately, press Dismiss. In paste mode, the Command\n"
1064 "widget has these options:\n"
1065 "\n"
1066 " Operators\n"
1067 " over\n"
1068 " in\n"
1069 " out\n"
1070 " atop\n"
1071 " xor\n"
1072 " plus\n"
1073 " minus\n"
1074 " add\n"
1075 " subtract\n"
1076 " difference\n"
1077 " replace\n"
1078 " Help\n"
1079 " Dismiss\n"
1080 "\n"
1081 "Choose a composite operation from the Operators sub-menu of\n"
1082 "the Command widget. How each operator behaves is described\n"
1083 "below. Image window is the image currently displayed on\n"
1084 "your X server and image is the image obtained with the File\n"
1085 "Browser widget.\n"
1086 "\n"
1087 "Over The result is the union of the two image shapes,\n"
1088 " with image obscuring image window in the region of\n"
1089 " overlap.\n"
1090 "\n"
1091 "In The result is simply image cut by the shape of\n"
1092 " image window. None of the image data of image\n"
1093 " window is in the result.\n"
1094 "\n"
1095 "Out The resulting image is image with the shape of\n"
1096 " image window cut out.\n"
1097 "\n"
1098 "Atop The result is the same shape as the image window,\n"
1099 " with image obscuring image window where the image\n"
1100 " shapes overlap. Note this differs from over\n"
1101 " because the portion of image outside image window's\n"
1102 " shape does not appear in the result.\n"
1103 "\n"
1104 "Xor The result is the image data from both image and\n"
1105 " image window that is outside the overlap region.\n"
1106 " The overlap region is blank.\n"
1107 "\n"
1108 "Plus The result is just the sum of the image data.\n"
1109 " Output values are cropped to QuantumRange (no overflow).\n"
1110 " This operation is independent of the matte\n"
1111 " channels.\n"
1112 "\n"
1113 "Minus The result of image - image window, with underflow\n"
1114 " cropped to zero.\n"
1115 "\n"
1116 "Add The result of image + image window, with overflow\n"
1117 " wrapping around (mod 256).\n"
1118 "\n"
1119 "Subtract The result of image - image window, with underflow\n"
1120 " wrapping around (mod 256). The add and subtract\n"
1121 " operators can be used to perform reversible\n"
1122 " transformations.\n"
1123 "\n"
1124 "Difference\n"
1125 " The result of abs(image - image window). This\n"
1126 " useful for comparing two very similar images.\n"
1127 "\n"
1128 "Copy The resulting image is image window replaced with\n"
1129 " image. Here the matte information is ignored.\n"
1130 "\n"
1131 "CopyRed The red layer of the image window is replace with\n"
1132 " the red layer of the image. The other layers are\n"
1133 " untouched.\n"
1134 "\n"
1135 "CopyGreen\n"
1136 " The green layer of the image window is replace with\n"
1137 " the green layer of the image. The other layers are\n"
1138 " untouched.\n"
1139 "\n"
1140 "CopyBlue The blue layer of the image window is replace with\n"
1141 " the blue layer of the image. The other layers are\n"
1142 " untouched.\n"
1143 "\n"
1144 "CopyOpacity\n"
1145 " The matte layer of the image window is replace with\n"
1146 " the matte layer of the image. The other layers are\n"
1147 " untouched.\n"
1148 "\n"
1149 "The image compositor requires a matte, or alpha channel in\n"
1150 "the image for some operations. This extra channel usually\n"
1151 "defines a mask which represents a sort of a cookie-cutter\n"
1152 "for the image. This the case when matte is opaque (full\n"
1153 "coverage) for pixels inside the shape, zero outside, and\n"
1154 "between 0 and QuantumRange on the boundary. If image does not\n"
1155 "have a matte channel, it is initialized with 0 for any pixel\n"
1156 "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
1157 "\n"
1158 "Note that matte information for image window is not retained\n"
1159 "for colormapped X server visuals (e.g. StaticColor,\n"
1160 "StaticColor, GrayScale, PseudoColor). Correct compositing\n"
1161 "behavior may require a TrueColor or DirectColor visual or a\n"
1162 "Standard Colormap.\n"
1163 "\n"
1164 "Choosing a composite operator is optional. The default\n"
1165 "operator is replace. However, you must choose a location to\n"
1166 "paste your image and press button 1. Press and hold the\n"
1167 "button before releasing and an outline of the image will\n"
1168 "appear to help you identify your location.\n"
1169 "\n"
1170 "The actual colors of the pasted image is saved. However,\n"
1171 "the color that appears in image window may be different.\n"
1172 "For example, on a monochrome screen image window will appear\n"
1173 "black or white even though your pasted image may have\n"
1174 "many colors. If the image is saved to a file it is written\n"
1175 "with the correct colors. To assure the correct colors are\n"
1176 "saved in the final image, any PseudoClass image is promoted\n"
1177 "to DirectClass (see miff(5)). To force a PseudoClass image\n"
1178 "to remain PseudoClass, use -colors.\n"
1179 },
1180 ImageROIHelp[] =
1181 {
1182 "In region of interest mode, the Command widget has these\n"
1183 "options:\n"
1184 "\n"
1185 " Help\n"
1186 " Dismiss\n"
1187 "\n"
1188 "To define a region of interest, press button 1 and drag.\n"
1189 "The region of interest is defined by a highlighted rectangle\n"
1190 "that expands or contracts as it follows the pointer. Once\n"
1191 "you are satisfied with the region of interest, release the\n"
1192 "button. You are now in apply mode. In apply mode the\n"
1193 "Command widget has these options:\n"
1194 "\n"
1195 " File\n"
1196 " Save...\n"
1197 " Print...\n"
1198 " Edit\n"
1199 " Undo\n"
1200 " Redo\n"
1201 " Transform\n"
1202 " Flop\n"
1203 " Flip\n"
1204 " Rotate Right\n"
1205 " Rotate Left\n"
1206 " Enhance\n"
1207 " Hue...\n"
1208 " Saturation...\n"
1209 " Brightness...\n"
1210 " Gamma...\n"
1211 " Spiff\n"
1212 " Dull\n"
1213 " Contrast Stretch\n"
1214 " Sigmoidal Contrast...\n"
1215 " Normalize\n"
1216 " Equalize\n"
1217 " Negate\n"
1218 " Grayscale\n"
1219 " Map...\n"
1220 " Quantize...\n"
1221 " Effects\n"
1222 " Despeckle\n"
1223 " Emboss\n"
1224 " Reduce Noise\n"
1225 " Sharpen...\n"
1226 " Blur...\n"
1227 " Threshold...\n"
1228 " Edge Detect...\n"
1229 " Spread...\n"
1230 " Shade...\n"
1231 " Raise...\n"
1232 " Segment...\n"
1233 " F/X\n"
1234 " Solarize...\n"
1235 " Sepia Tone...\n"
1236 " Swirl...\n"
1237 " Implode...\n"
1238 " Vignette...\n"
1239 " Wave...\n"
1240 " Oil Painting...\n"
1241 " Charcoal Drawing...\n"
1242 " Miscellany\n"
1243 " Image Info\n"
1244 " Zoom Image\n"
1245 " Show Preview...\n"
1246 " Show Histogram\n"
1247 " Show Matte\n"
1248 " Help\n"
1249 " Dismiss\n"
1250 "\n"
1251 "You can make adjustments to the region of interest by moving\n"
1252 "the pointer to one of the rectangle corners, pressing a\n"
1253 "button, and dragging. Finally, choose an image processing\n"
1254 "technique from the Command widget. You can choose more than\n"
1255 "one image processing technique to apply to an area.\n"
1256 "Alternatively, you can move the region of interest before\n"
1257 "applying another image processing technique. To exit, press\n"
1258 "Dismiss.\n"
1259 },
1260 ImageRotateHelp[] =
1261 {
1262 "In rotate mode, the Command widget has these options:\n"
1263 "\n"
1264 " Pixel Color\n"
1265 " black\n"
1266 " blue\n"
1267 " cyan\n"
1268 " green\n"
1269 " gray\n"
1270 " red\n"
1271 " magenta\n"
1272 " yellow\n"
1273 " white\n"
1274 " Browser...\n"
1275 " Direction\n"
1276 " horizontal\n"
1277 " vertical\n"
1278 " Help\n"
1279 " Dismiss\n"
1280 "\n"
1281 "Choose a background color from the Pixel Color sub-menu.\n"
1282 "Additional background colors can be specified with the color\n"
1283 "browser. You can change the menu colors by setting the X\n"
1284 "resources pen1 through pen9.\n"
1285 "\n"
1286 "If you choose the color browser and press Grab, you can\n"
1287 "select the background color by moving the pointer to the\n"
1288 "desired color on the screen and press any button.\n"
1289 "\n"
1290 "Choose a point in the image window and press this button and\n"
1291 "hold. Next, move the pointer to another location in the\n"
1292 "image. As you move a line connects the initial location and\n"
1293 "the pointer. When you release the button, the degree of\n"
1294 "image rotation is determined by the slope of the line you\n"
1295 "just drew. The slope is relative to the direction you\n"
1296 "choose from the Direction sub-menu of the Command widget.\n"
1297 "\n"
1298 "To cancel the image rotation, move the pointer back to the\n"
1299 "starting point of the line and release the button.\n"
1300 };
1301
1302/*
1303 Enumeration declarations.
1304*/
1305typedef enum
1306{
1307 CopyMode,
1308 CropMode,
1309 CutMode
1310} ClipboardMode;
1311
1312typedef enum
1313{
1314 OpenCommand,
1315 NextCommand,
1316 FormerCommand,
1317 SelectCommand,
1318 SaveCommand,
1319 PrintCommand,
1320 DeleteCommand,
1321 NewCommand,
1322 VisualDirectoryCommand,
1323 QuitCommand,
1324 UndoCommand,
1325 RedoCommand,
1326 CutCommand,
1327 CopyCommand,
1328 PasteCommand,
1329 HalfSizeCommand,
1330 OriginalSizeCommand,
1331 DoubleSizeCommand,
1332 ResizeCommand,
1333 ApplyCommand,
1334 RefreshCommand,
1335 RestoreCommand,
1336 CropCommand,
1337 ChopCommand,
1338 FlopCommand,
1339 FlipCommand,
1340 RotateRightCommand,
1341 RotateLeftCommand,
1342 RotateCommand,
1343 ShearCommand,
1344 RollCommand,
1345 TrimCommand,
1346 HueCommand,
1347 SaturationCommand,
1348 BrightnessCommand,
1349 GammaCommand,
1350 SpiffCommand,
1351 DullCommand,
1352 ContrastStretchCommand,
1353 SigmoidalContrastCommand,
1354 NormalizeCommand,
1355 EqualizeCommand,
1356 NegateCommand,
1357 GrayscaleCommand,
1358 MapCommand,
1359 QuantizeCommand,
1360 DespeckleCommand,
1361 EmbossCommand,
1362 ReduceNoiseCommand,
1363 AddNoiseCommand,
1364 SharpenCommand,
1365 BlurCommand,
1366 ThresholdCommand,
1367 EdgeDetectCommand,
1368 SpreadCommand,
1369 ShadeCommand,
1370 RaiseCommand,
1371 SegmentCommand,
1372 SolarizeCommand,
1373 SepiaToneCommand,
1374 SwirlCommand,
1375 ImplodeCommand,
1376 VignetteCommand,
1377 WaveCommand,
1378 OilPaintCommand,
1379 CharcoalDrawCommand,
1380 AnnotateCommand,
1381 DrawCommand,
1382 ColorCommand,
1383 MatteCommand,
1384 CompositeCommand,
1385 AddBorderCommand,
1386 AddFrameCommand,
1387 CommentCommand,
1388 LaunchCommand,
1389 RegionOfInterestCommand,
1390 ROIHelpCommand,
1391 ROIDismissCommand,
1392 InfoCommand,
1393 ZoomCommand,
1394 ShowPreviewCommand,
1395 ShowHistogramCommand,
1396 ShowMatteCommand,
1397 BackgroundCommand,
1398 SlideShowCommand,
1399 PreferencesCommand,
1400 HelpCommand,
1401 BrowseDocumentationCommand,
1402 VersionCommand,
1403 SaveToUndoBufferCommand,
1404 FreeBuffersCommand,
1405 NullCommand
1406} DisplayCommand;
1407
1408typedef enum
1409{
1410 AnnotateNameCommand,
1411 AnnotateFontColorCommand,
1412 AnnotateBackgroundColorCommand,
1413 AnnotateRotateCommand,
1414 AnnotateHelpCommand,
1415 AnnotateDismissCommand,
1416 TextHelpCommand,
1417 TextApplyCommand,
1418 ChopDirectionCommand,
1419 ChopHelpCommand,
1420 ChopDismissCommand,
1421 HorizontalChopCommand,
1422 VerticalChopCommand,
1423 ColorEditMethodCommand,
1424 ColorEditColorCommand,
1425 ColorEditBorderCommand,
1426 ColorEditFuzzCommand,
1427 ColorEditUndoCommand,
1428 ColorEditHelpCommand,
1429 ColorEditDismissCommand,
1430 CompositeOperatorsCommand,
1431 CompositeDissolveCommand,
1432 CompositeDisplaceCommand,
1433 CompositeHelpCommand,
1434 CompositeDismissCommand,
1435 CropHelpCommand,
1436 CropDismissCommand,
1437 RectifyCopyCommand,
1438 RectifyHelpCommand,
1439 RectifyDismissCommand,
1440 DrawElementCommand,
1441 DrawColorCommand,
1442 DrawStippleCommand,
1443 DrawWidthCommand,
1444 DrawUndoCommand,
1445 DrawHelpCommand,
1446 DrawDismissCommand,
1447 MatteEditMethod,
1448 MatteEditBorderCommand,
1449 MatteEditFuzzCommand,
1450 MatteEditValueCommand,
1451 MatteEditUndoCommand,
1452 MatteEditHelpCommand,
1453 MatteEditDismissCommand,
1454 PasteOperatorsCommand,
1455 PasteHelpCommand,
1456 PasteDismissCommand,
1457 RotateColorCommand,
1458 RotateDirectionCommand,
1459 RotateCropCommand,
1460 RotateSharpenCommand,
1461 RotateHelpCommand,
1462 RotateDismissCommand,
1463 HorizontalRotateCommand,
1464 VerticalRotateCommand,
1465 TileLoadCommand,
1466 TileNextCommand,
1467 TileFormerCommand,
1468 TileDeleteCommand,
1469 TileUpdateCommand
1470} ModeType;
1471
1472/*
1473 Stipples.
1474*/
1475#define BricksWidth 20
1476#define BricksHeight 20
1477#define DiagonalWidth 16
1478#define DiagonalHeight 16
1479#define HighlightWidth 8
1480#define HighlightHeight 8
1481#define OpaqueWidth 8
1482#define OpaqueHeight 8
1483#define ScalesWidth 16
1484#define ScalesHeight 16
1485#define ShadowWidth 8
1486#define ShadowHeight 8
1487#define VerticalWidth 16
1488#define VerticalHeight 16
1489#define WavyWidth 16
1490#define WavyHeight 16
1491
1492/*
1493 Constant declaration.
1494*/
1495static const int
1496 RoiDelta = 8;
1497
1498static const unsigned char
1499 BricksBitmap[] =
1500 {
1501 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1502 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1503 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1504 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1505 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1506 },
1507 DiagonalBitmap[] =
1508 {
1509 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1510 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1511 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1512 },
1513 ScalesBitmap[] =
1514 {
1515 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1516 0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1517 0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1518 },
1519 VerticalBitmap[] =
1520 {
1521 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1522 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1523 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1524 },
1525 WavyBitmap[] =
1526 {
1527 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1528 0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1529 0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1530 };
1531
1532/*
1533 Function prototypes.
1534*/
1535static DisplayCommand
1536 XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1537 const MagickStatusType,KeySym,Image **);
1538
1539static Image
1540 *XMagickCommand(Display *,XResourceInfo *,XWindows *,const DisplayCommand,
1541 Image **),
1542 *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1543 *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *),
1544 *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *);
1545
1546static MagickBooleanType
1547 XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *),
1548 XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1549 XChopImage(Display *,XResourceInfo *,XWindows *,Image **),
1550 XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode),
1551 XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **),
1552 XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1553 XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *),
1554 XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *),
1555 XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1556 XPasteImage(Display *,XResourceInfo *,XWindows *,Image *),
1557 XPrintImage(Display *,XResourceInfo *,XWindows *,Image *),
1558 XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **),
1559 XROIImage(Display *,XResourceInfo *,XWindows *,Image **),
1560 XSaveImage(Display *,XResourceInfo *,XWindows *,Image *),
1561 XTrimImage(Display *,XResourceInfo *,XWindows *,Image *);
1562
1563static void
1564 XDrawPanRectangle(Display *,XWindows *),
1565 XImageCache(Display *,XResourceInfo *,XWindows *,const DisplayCommand,Image **),
1566 XMagnifyImage(Display *,XWindows *,XEvent *),
1567 XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *),
1568 XPanImage(Display *,XWindows *,XEvent *),
1569 XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1570 const KeySym),
1571 XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1572 XScreenEvent(Display *,XWindows *,XEvent *),
1573 XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1574
1575/*
1576%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1577% %
1578% %
1579% %
1580% D i s p l a y I m a g e s %
1581% %
1582% %
1583% %
1584%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1585%
1586% DisplayImages() displays an image sequence to any X window screen. It
1587% returns a value other than 0 if successful. Check the exception member
1588% of image to determine the reason for any failure.
1589%
1590% The format of the DisplayImages method is:
1591%
1592% MagickBooleanType DisplayImages(const ImageInfo *image_info,
1593% Image *images)
1594%
1595% A description of each parameter follows:
1596%
1597% o image_info: the image info.
1598%
1599% o image: the image.
1600%
1601*/
1602MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1603 Image *images)
1604{
1605 char
1606 *argv[1];
1607
1608 Display
1609 *display;
1610
1611 Image
1612 *image;
1613
1614 size_t
1615 state;
1616
1617 ssize_t
1618 i;
1619
1620 XrmDatabase
1621 resource_database;
1622
1623 XResourceInfo
1624 resource_info;
1625
1626 assert(image_info != (const ImageInfo *) NULL);
1627 assert(image_info->signature == MagickCoreSignature);
1628 assert(images != (Image *) NULL);
1629 assert(images->signature == MagickCoreSignature);
1630 if (IsEventLogging() != MagickFalse)
1631 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1632 display=XOpenDisplay(image_info->server_name);
1633 if (display == (Display *) NULL)
1634 {
1635 (void) ThrowMagickException(&images->exception,GetMagickModule(),
1636 XServerError,"UnableToOpenXServer","`%s'",XDisplayName(
1637 image_info->server_name));
1638 return(MagickFalse);
1639 }
1640 if (images->exception.severity != UndefinedException)
1641 CatchException(&images->exception);
1642 (void) XSetErrorHandler(XError);
1643 resource_database=XGetResourceDatabase(display,GetClientName());
1644 (void) memset(&resource_info,0,sizeof(resource_info));
1645 XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1646 if (image_info->page != (char *) NULL)
1647 resource_info.image_geometry=AcquireString(image_info->page);
1648 resource_info.immutable=MagickTrue;
1649 argv[0]=AcquireString(GetClientName());
1650 state=DefaultState;
1651 for (i=0; (state & ExitState) == 0; i++)
1652 {
1653 if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1654 break;
1655 image=GetImageFromList(images,i % GetImageListLength(images));
1656 (void) XDisplayImage(display,&resource_info,argv,1,&image,&state);
1657 }
1658 (void) SetErrorHandler((ErrorHandler) NULL);
1659 (void) SetWarningHandler((WarningHandler) NULL);
1660 argv[0]=DestroyString(argv[0]);
1661 XDestroyResourceInfo(&resource_info);
1662 if (images->exception.severity != UndefinedException)
1663 return(MagickFalse);
1664 return(MagickTrue);
1665}
1666
1667/*
1668%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1669% %
1670% %
1671% %
1672% R e m o t e D i s p l a y C o m m a n d %
1673% %
1674% %
1675% %
1676%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1677%
1678% RemoteDisplayCommand() encourages a remote display program to display the
1679% specified image filename.
1680%
1681% The format of the RemoteDisplayCommand method is:
1682%
1683% MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1684% const char *window,const char *filename,ExceptionInfo *exception)
1685%
1686% A description of each parameter follows:
1687%
1688% o image_info: the image info.
1689%
1690% o window: Specifies the name or id of an X window.
1691%
1692% o filename: the name of the image filename to display.
1693%
1694% o exception: return any errors or warnings in this structure.
1695%
1696*/
1697MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1698 const char *window,const char *filename,ExceptionInfo *exception)
1699{
1700 Display
1701 *display;
1702
1703 MagickStatusType
1704 status;
1705
1706 assert(image_info != (const ImageInfo *) NULL);
1707 assert(image_info->signature == MagickCoreSignature);
1708 assert(filename != (char *) NULL);
1709 if (IsEventLogging() != MagickFalse)
1710 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1711 display=XOpenDisplay(image_info->server_name);
1712 if (display == (Display *) NULL)
1713 {
1714 (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1715 "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1716 return(MagickFalse);
1717 }
1718 (void) XSetErrorHandler(XError);
1719 status=XRemoteCommand(display,window,filename);
1720 (void) XCloseDisplay(display);
1721 return(status != 0 ? MagickTrue : MagickFalse);
1722}
1723
1724/*
1725%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1726% %
1727% %
1728% %
1729+ X A n n o t a t e E d i t I m a g e %
1730% %
1731% %
1732% %
1733%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1734%
1735% XAnnotateEditImage() annotates the image with text.
1736%
1737% The format of the XAnnotateEditImage method is:
1738%
1739% MagickBooleanType XAnnotateEditImage(Display *display,
1740% XResourceInfo *resource_info,XWindows *windows,Image *image)
1741%
1742% A description of each parameter follows:
1743%
1744% o display: Specifies a connection to an X server; returned from
1745% XOpenDisplay.
1746%
1747% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1748%
1749% o windows: Specifies a pointer to a XWindows structure.
1750%
1751% o image: the image; returned from ReadImage.
1752%
1753*/
1754
1755static MagickBooleanType XAnnotateEditImage(Display *display,
1756 XResourceInfo *resource_info,XWindows *windows,Image *image)
1757{
1758 const char
1759 *const AnnotateMenu[] =
1760 {
1761 "Font Name",
1762 "Font Color",
1763 "Box Color",
1764 "Rotate Text",
1765 "Help",
1766 "Dismiss",
1767 (char *) NULL
1768 },
1769 *const TextMenu[] =
1770 {
1771 "Help",
1772 "Apply",
1773 (char *) NULL
1774 };
1775
1776 static const ModeType
1777 AnnotateCommands[] =
1778 {
1779 AnnotateNameCommand,
1780 AnnotateFontColorCommand,
1781 AnnotateBackgroundColorCommand,
1782 AnnotateRotateCommand,
1783 AnnotateHelpCommand,
1784 AnnotateDismissCommand
1785 },
1786 TextCommands[] =
1787 {
1788 TextHelpCommand,
1789 TextApplyCommand
1790 };
1791
1792 static MagickBooleanType
1793 transparent_box = MagickTrue,
1794 transparent_pen = MagickFalse;
1795
1796 static MagickRealType
1797 degrees = 0.0;
1798
1799 static unsigned int
1800 box_id = MaxNumberPens-2,
1801 font_id = 0,
1802 pen_id = 0;
1803
1804 char
1805 command[MaxTextExtent],
1806 *p,
1807 text[MaxTextExtent];
1808
1809 const char
1810 *ColorMenu[MaxNumberPens+1];
1811
1812 Cursor
1813 cursor;
1814
1815 GC
1816 annotate_context;
1817
1818 int
1819 id,
1820 pen_number,
1821 status,
1822 x,
1823 y;
1824
1825 KeySym
1826 key_symbol;
1827
1828 size_t
1829 state;
1830
1831 ssize_t
1832 i;
1833
1834 unsigned int
1835 height,
1836 width;
1837
1838 XAnnotateInfo
1839 *annotate_info,
1840 *previous_info;
1841
1842 XColor
1843 color;
1844
1845 XFontStruct
1846 *font_info;
1847
1848 XEvent
1849 event,
1850 text_event;
1851
1852 /*
1853 Map Command widget.
1854 */
1855 (void) CloneString(&windows->command.name,"Annotate");
1856 windows->command.data=4;
1857 (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1858 (void) XMapRaised(display,windows->command.id);
1859 XClientMessage(display,windows->image.id,windows->im_protocols,
1860 windows->im_update_widget,CurrentTime);
1861 /*
1862 Track pointer until button 1 is pressed.
1863 */
1864 XQueryPosition(display,windows->image.id,&x,&y);
1865 (void) XSelectInput(display,windows->image.id,
1866 windows->image.attributes.event_mask | PointerMotionMask);
1867 cursor=XCreateFontCursor(display,XC_left_side);
1868 (void) XCheckDefineCursor(display,windows->image.id,cursor);
1869 state=DefaultState;
1870 do
1871 {
1872 if (windows->info.mapped != MagickFalse)
1873 {
1874 /*
1875 Display pointer position.
1876 */
1877 (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1878 x+windows->image.x,y+windows->image.y);
1879 XInfoWidget(display,windows,text);
1880 }
1881 /*
1882 Wait for next event.
1883 */
1884 XScreenEvent(display,windows,&event);
1885 if (event.xany.window == windows->command.id)
1886 {
1887 /*
1888 Select a command from the Command widget.
1889 */
1890 id=XCommandWidget(display,windows,AnnotateMenu,&event);
1891 (void) XCheckDefineCursor(display,windows->image.id,cursor);
1892 if (id < 0)
1893 continue;
1894 switch (AnnotateCommands[id])
1895 {
1896 case AnnotateNameCommand:
1897 {
1898 const char
1899 *FontMenu[MaxNumberFonts];
1900
1901 int
1902 font_number;
1903
1904 /*
1905 Initialize menu selections.
1906 */
1907 for (i=0; i < MaxNumberFonts; i++)
1908 FontMenu[i]=resource_info->font_name[i];
1909 FontMenu[MaxNumberFonts-2]="Browser...";
1910 FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1911 /*
1912 Select a font name from the pop-up menu.
1913 */
1914 font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1915 (const char **) FontMenu,command);
1916 if (font_number < 0)
1917 break;
1918 if (font_number == (MaxNumberFonts-2))
1919 {
1920 static char
1921 font_name[MaxTextExtent] = "fixed";
1922
1923 /*
1924 Select a font name from a browser.
1925 */
1926 resource_info->font_name[font_number]=font_name;
1927 XFontBrowserWidget(display,windows,"Select",font_name);
1928 if (*font_name == '\0')
1929 break;
1930 }
1931 /*
1932 Initialize font info.
1933 */
1934 font_info=XLoadQueryFont(display,resource_info->font_name[
1935 font_number]);
1936 if (font_info == (XFontStruct *) NULL)
1937 {
1938 XNoticeWidget(display,windows,"Unable to load font:",
1939 resource_info->font_name[font_number]);
1940 break;
1941 }
1942 font_id=(unsigned int) font_number;
1943 (void) XFreeFont(display,font_info);
1944 break;
1945 }
1946 case AnnotateFontColorCommand:
1947 {
1948 /*
1949 Initialize menu selections.
1950 */
1951 for (i=0; i < (int) (MaxNumberPens-2); i++)
1952 ColorMenu[i]=resource_info->pen_colors[i];
1953 ColorMenu[MaxNumberPens-2]="transparent";
1954 ColorMenu[MaxNumberPens-1]="Browser...";
1955 ColorMenu[MaxNumberPens]=(const char *) NULL;
1956 /*
1957 Select a pen color from the pop-up menu.
1958 */
1959 pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
1960 (const char **) ColorMenu,command);
1961 if (pen_number < 0)
1962 break;
1963 transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
1964 MagickFalse;
1965 if (transparent_pen != MagickFalse)
1966 break;
1967 if (pen_number == (MaxNumberPens-1))
1968 {
1969 static char
1970 color_name[MaxTextExtent] = "gray";
1971
1972 /*
1973 Select a pen color from a dialog.
1974 */
1975 resource_info->pen_colors[pen_number]=color_name;
1976 XColorBrowserWidget(display,windows,"Select",color_name);
1977 if (*color_name == '\0')
1978 break;
1979 }
1980 /*
1981 Set pen color.
1982 */
1983 (void) XParseColor(display,windows->map_info->colormap,
1984 resource_info->pen_colors[pen_number],&color);
1985 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
1986 (unsigned int) MaxColors,&color);
1987 windows->pixel_info->pen_colors[pen_number]=color;
1988 pen_id=(unsigned int) pen_number;
1989 break;
1990 }
1991 case AnnotateBackgroundColorCommand:
1992 {
1993 /*
1994 Initialize menu selections.
1995 */
1996 for (i=0; i < (int) (MaxNumberPens-2); i++)
1997 ColorMenu[i]=resource_info->pen_colors[i];
1998 ColorMenu[MaxNumberPens-2]="transparent";
1999 ColorMenu[MaxNumberPens-1]="Browser...";
2000 ColorMenu[MaxNumberPens]=(const char *) NULL;
2001 /*
2002 Select a pen color from the pop-up menu.
2003 */
2004 pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2005 (const char **) ColorMenu,command);
2006 if (pen_number < 0)
2007 break;
2008 transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2009 MagickFalse;
2010 if (transparent_box != MagickFalse)
2011 break;
2012 if (pen_number == (MaxNumberPens-1))
2013 {
2014 static char
2015 color_name[MaxTextExtent] = "gray";
2016
2017 /*
2018 Select a pen color from a dialog.
2019 */
2020 resource_info->pen_colors[pen_number]=color_name;
2021 XColorBrowserWidget(display,windows,"Select",color_name);
2022 if (*color_name == '\0')
2023 break;
2024 }
2025 /*
2026 Set pen color.
2027 */
2028 (void) XParseColor(display,windows->map_info->colormap,
2029 resource_info->pen_colors[pen_number],&color);
2030 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2031 (unsigned int) MaxColors,&color);
2032 windows->pixel_info->pen_colors[pen_number]=color;
2033 box_id=(unsigned int) pen_number;
2034 break;
2035 }
2036 case AnnotateRotateCommand:
2037 {
2038 int
2039 entry;
2040
2041 const char
2042 *const RotateMenu[] =
2043 {
2044 "-90",
2045 "-45",
2046 "-30",
2047 "0",
2048 "30",
2049 "45",
2050 "90",
2051 "180",
2052 "Dialog...",
2053 (char *) NULL,
2054 };
2055
2056 static char
2057 angle[MaxTextExtent] = "30.0";
2058
2059 /*
2060 Select a command from the pop-up menu.
2061 */
2062 entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2063 command);
2064 if (entry < 0)
2065 break;
2066 if (entry != 8)
2067 {
2068 degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2069 break;
2070 }
2071 (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2072 angle);
2073 if (*angle == '\0')
2074 break;
2075 degrees=StringToDouble(angle,(char **) NULL);
2076 break;
2077 }
2078 case AnnotateHelpCommand:
2079 {
2080 XTextViewHelp(display,resource_info,windows,MagickFalse,
2081 "Help Viewer - Image Annotation",ImageAnnotateHelp);
2082 break;
2083 }
2084 case AnnotateDismissCommand:
2085 {
2086 /*
2087 Prematurely exit.
2088 */
2089 state|=EscapeState;
2090 state|=ExitState;
2091 break;
2092 }
2093 default:
2094 break;
2095 }
2096 continue;
2097 }
2098 switch (event.type)
2099 {
2100 case ButtonPress:
2101 {
2102 if (event.xbutton.button != Button1)
2103 break;
2104 if (event.xbutton.window != windows->image.id)
2105 break;
2106 /*
2107 Change to text entering mode.
2108 */
2109 x=event.xbutton.x;
2110 y=event.xbutton.y;
2111 state|=ExitState;
2112 break;
2113 }
2114 case ButtonRelease:
2115 break;
2116 case Expose:
2117 break;
2118 case KeyPress:
2119 {
2120 if (event.xkey.window != windows->image.id)
2121 break;
2122 /*
2123 Respond to a user key press.
2124 */
2125 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2126 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2127 switch ((int) key_symbol)
2128 {
2129 case XK_Escape:
2130 case XK_F20:
2131 {
2132 /*
2133 Prematurely exit.
2134 */
2135 state|=EscapeState;
2136 state|=ExitState;
2137 break;
2138 }
2139 case XK_F1:
2140 case XK_Help:
2141 {
2142 XTextViewHelp(display,resource_info,windows,MagickFalse,
2143 "Help Viewer - Image Annotation",ImageAnnotateHelp);
2144 break;
2145 }
2146 default:
2147 {
2148 (void) XBell(display,0);
2149 break;
2150 }
2151 }
2152 break;
2153 }
2154 case MotionNotify:
2155 {
2156 /*
2157 Map and unmap Info widget as cursor crosses its boundaries.
2158 */
2159 x=event.xmotion.x;
2160 y=event.xmotion.y;
2161 if (windows->info.mapped != MagickFalse)
2162 {
2163 if ((x < (int) (windows->info.x+windows->info.width)) &&
2164 (y < (int) (windows->info.y+windows->info.height)))
2165 (void) XWithdrawWindow(display,windows->info.id,
2166 windows->info.screen);
2167 }
2168 else
2169 if ((x > (int) (windows->info.x+windows->info.width)) ||
2170 (y > (int) (windows->info.y+windows->info.height)))
2171 (void) XMapWindow(display,windows->info.id);
2172 break;
2173 }
2174 default:
2175 break;
2176 }
2177 } while ((state & ExitState) == 0);
2178 (void) XSelectInput(display,windows->image.id,
2179 windows->image.attributes.event_mask);
2180 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2181 if ((state & EscapeState) != 0)
2182 return(MagickTrue);
2183 /*
2184 Set font info and check boundary conditions.
2185 */
2186 font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2187 if (font_info == (XFontStruct *) NULL)
2188 {
2189 XNoticeWidget(display,windows,"Unable to load font:",
2190 resource_info->font_name[font_id]);
2191 font_info=windows->font_info;
2192 }
2193 if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2194 x=(int) windows->image.width-font_info->max_bounds.width;
2195 if (y < (int) (font_info->ascent+font_info->descent))
2196 y=(int) font_info->ascent+font_info->descent;
2197 if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2198 ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2199 return(MagickFalse);
2200 /*
2201 Initialize annotate structure.
2202 */
2203 annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2204 if (annotate_info == (XAnnotateInfo *) NULL)
2205 return(MagickFalse);
2206 XGetAnnotateInfo(annotate_info);
2207 annotate_info->x=x;
2208 annotate_info->y=y;
2209 if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2210 annotate_info->stencil=OpaqueStencil;
2211 else
2212 if (transparent_box == MagickFalse)
2213 annotate_info->stencil=BackgroundStencil;
2214 else
2215 annotate_info->stencil=ForegroundStencil;
2216 annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2217 annotate_info->degrees=degrees;
2218 annotate_info->font_info=font_info;
2219 annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2220 windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2221 sizeof(*annotate_info->text));
2222 if (annotate_info->text == (char *) NULL)
2223 return(MagickFalse);
2224 /*
2225 Create cursor and set graphic context.
2226 */
2227 cursor=XCreateFontCursor(display,XC_pencil);
2228 (void) XCheckDefineCursor(display,windows->image.id,cursor);
2229 annotate_context=windows->image.annotate_context;
2230 (void) XSetFont(display,annotate_context,font_info->fid);
2231 (void) XSetBackground(display,annotate_context,
2232 windows->pixel_info->pen_colors[box_id].pixel);
2233 (void) XSetForeground(display,annotate_context,
2234 windows->pixel_info->pen_colors[pen_id].pixel);
2235 /*
2236 Begin annotating the image with text.
2237 */
2238 (void) CloneString(&windows->command.name,"Text");
2239 windows->command.data=0;
2240 (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2241 state=DefaultState;
2242 (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2243 text_event.xexpose.width=(int) font_info->max_bounds.width;
2244 text_event.xexpose.height=font_info->max_bounds.ascent+
2245 font_info->max_bounds.descent;
2246 p=annotate_info->text;
2247 do
2248 {
2249 /*
2250 Display text cursor.
2251 */
2252 *p='\0';
2253 (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2254 /*
2255 Wait for next event.
2256 */
2257 XScreenEvent(display,windows,&event);
2258 if (event.xany.window == windows->command.id)
2259 {
2260 /*
2261 Select a command from the Command widget.
2262 */
2263 (void) XSetBackground(display,annotate_context,
2264 windows->pixel_info->background_color.pixel);
2265 (void) XSetForeground(display,annotate_context,
2266 windows->pixel_info->foreground_color.pixel);
2267 id=XCommandWidget(display,windows,AnnotateMenu,&event);
2268 (void) XSetBackground(display,annotate_context,
2269 windows->pixel_info->pen_colors[box_id].pixel);
2270 (void) XSetForeground(display,annotate_context,
2271 windows->pixel_info->pen_colors[pen_id].pixel);
2272 if (id < 0)
2273 continue;
2274 switch (TextCommands[id])
2275 {
2276 case TextHelpCommand:
2277 {
2278 XTextViewHelp(display,resource_info,windows,MagickFalse,
2279 "Help Viewer - Image Annotation",ImageAnnotateHelp);
2280 (void) XCheckDefineCursor(display,windows->image.id,cursor);
2281 break;
2282 }
2283 case TextApplyCommand:
2284 {
2285 /*
2286 Finished annotating.
2287 */
2288 annotate_info->width=(unsigned int) XTextWidth(font_info,
2289 annotate_info->text,(int) strlen(annotate_info->text));
2290 XRefreshWindow(display,&windows->image,&text_event);
2291 state|=ExitState;
2292 break;
2293 }
2294 default:
2295 break;
2296 }
2297 continue;
2298 }
2299 /*
2300 Erase text cursor.
2301 */
2302 text_event.xexpose.x=x;
2303 text_event.xexpose.y=y-font_info->max_bounds.ascent;
2304 (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2305 (unsigned int) text_event.xexpose.width,(unsigned int)
2306 text_event.xexpose.height,MagickFalse);
2307 XRefreshWindow(display,&windows->image,&text_event);
2308 switch (event.type)
2309 {
2310 case ButtonPress:
2311 {
2312 if (event.xbutton.window != windows->image.id)
2313 break;
2314 if (event.xbutton.button == Button2)
2315 {
2316 /*
2317 Request primary selection.
2318 */
2319 (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2320 windows->image.id,CurrentTime);
2321 break;
2322 }
2323 break;
2324 }
2325 case Expose:
2326 {
2327 if (event.xexpose.count == 0)
2328 {
2329 XAnnotateInfo
2330 *text_info;
2331
2332 /*
2333 Refresh Image window.
2334 */
2335 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2336 text_info=annotate_info;
2337 while (text_info != (XAnnotateInfo *) NULL)
2338 {
2339 if (annotate_info->stencil == ForegroundStencil)
2340 (void) XDrawString(display,windows->image.id,annotate_context,
2341 text_info->x,text_info->y,text_info->text,
2342 (int) strlen(text_info->text));
2343 else
2344 (void) XDrawImageString(display,windows->image.id,
2345 annotate_context,text_info->x,text_info->y,text_info->text,
2346 (int) strlen(text_info->text));
2347 text_info=text_info->previous;
2348 }
2349 (void) XDrawString(display,windows->image.id,annotate_context,
2350 x,y,"_",1);
2351 }
2352 break;
2353 }
2354 case KeyPress:
2355 {
2356 int
2357 length;
2358
2359 if (event.xkey.window != windows->image.id)
2360 break;
2361 /*
2362 Respond to a user key press.
2363 */
2364 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2365 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2366 *(command+length)='\0';
2367 if (((event.xkey.state & ControlMask) != 0) ||
2368 ((event.xkey.state & Mod1Mask) != 0))
2369 state|=ModifierState;
2370 if ((state & ModifierState) != 0)
2371 switch ((int) key_symbol)
2372 {
2373 case XK_u:
2374 case XK_U:
2375 {
2376 key_symbol=DeleteCommand;
2377 break;
2378 }
2379 default:
2380 break;
2381 }
2382 switch ((int) key_symbol)
2383 {
2384 case XK_BackSpace:
2385 {
2386 /*
2387 Erase one character.
2388 */
2389 if (p == annotate_info->text)
2390 {
2391 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2392 break;
2393 else
2394 {
2395 /*
2396 Go to end of the previous line of text.
2397 */
2398 annotate_info=annotate_info->previous;
2399 p=annotate_info->text;
2400 x=annotate_info->x+annotate_info->width;
2401 y=annotate_info->y;
2402 if (annotate_info->width != 0)
2403 p+=(ptrdiff_t) strlen(annotate_info->text);
2404 break;
2405 }
2406 }
2407 p--;
2408 x-=XTextWidth(font_info,p,1);
2409 text_event.xexpose.x=x;
2410 text_event.xexpose.y=y-font_info->max_bounds.ascent;
2411 XRefreshWindow(display,&windows->image,&text_event);
2412 break;
2413 }
2414 case XK_bracketleft:
2415 {
2416 key_symbol=XK_Escape;
2417 break;
2418 }
2419 case DeleteCommand:
2420 {
2421 /*
2422 Erase the entire line of text.
2423 */
2424 while (p != annotate_info->text)
2425 {
2426 p--;
2427 x-=XTextWidth(font_info,p,1);
2428 text_event.xexpose.x=x;
2429 XRefreshWindow(display,&windows->image,&text_event);
2430 }
2431 break;
2432 }
2433 case XK_Escape:
2434 case XK_F20:
2435 {
2436 /*
2437 Finished annotating.
2438 */
2439 annotate_info->width=(unsigned int) XTextWidth(font_info,
2440 annotate_info->text,(int) strlen(annotate_info->text));
2441 XRefreshWindow(display,&windows->image,&text_event);
2442 state|=ExitState;
2443 break;
2444 }
2445 default:
2446 {
2447 /*
2448 Draw a single character on the Image window.
2449 */
2450 if ((state & ModifierState) != 0)
2451 break;
2452 if (*command == '\0')
2453 break;
2454 *p=(*command);
2455 if (annotate_info->stencil == ForegroundStencil)
2456 (void) XDrawString(display,windows->image.id,annotate_context,
2457 x,y,p,1);
2458 else
2459 (void) XDrawImageString(display,windows->image.id,
2460 annotate_context,x,y,p,1);
2461 x+=XTextWidth(font_info,p,1);
2462 p++;
2463 if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2464 break;
2465 magick_fallthrough;
2466 }
2467 case XK_Return:
2468 case XK_KP_Enter:
2469 {
2470 /*
2471 Advance to the next line of text.
2472 */
2473 *p='\0';
2474 annotate_info->width=(unsigned int) XTextWidth(font_info,
2475 annotate_info->text,(int) strlen(annotate_info->text));
2476 if (annotate_info->next != (XAnnotateInfo *) NULL)
2477 {
2478 /*
2479 Line of text already exists.
2480 */
2481 annotate_info=annotate_info->next;
2482 x=annotate_info->x;
2483 y=annotate_info->y;
2484 p=annotate_info->text;
2485 break;
2486 }
2487 annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2488 sizeof(*annotate_info->next));
2489 if (annotate_info->next == (XAnnotateInfo *) NULL)
2490 return(MagickFalse);
2491 *annotate_info->next=(*annotate_info);
2492 annotate_info->next->previous=annotate_info;
2493 annotate_info=annotate_info->next;
2494 annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2495 windows->image.width/MagickMax((ssize_t)
2496 font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2497 if (annotate_info->text == (char *) NULL)
2498 return(MagickFalse);
2499 annotate_info->y+=annotate_info->height;
2500 if (annotate_info->y > (int) windows->image.height)
2501 annotate_info->y=(int) annotate_info->height;
2502 annotate_info->next=(XAnnotateInfo *) NULL;
2503 x=annotate_info->x;
2504 y=annotate_info->y;
2505 p=annotate_info->text;
2506 break;
2507 }
2508 }
2509 break;
2510 }
2511 case KeyRelease:
2512 {
2513 /*
2514 Respond to a user key release.
2515 */
2516 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2517 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2518 state&=(~ModifierState);
2519 break;
2520 }
2521 case SelectionNotify:
2522 {
2523 Atom
2524 type;
2525
2526 int
2527 format;
2528
2529 unsigned char
2530 *data;
2531
2532 unsigned long
2533 after,
2534 length;
2535
2536 /*
2537 Obtain response from primary selection.
2538 */
2539 if (event.xselection.property == (Atom) None)
2540 break;
2541 status=XGetWindowProperty(display,event.xselection.requestor,
2542 event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2543 &type,&format,&length,&after,&data);
2544 if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2545 (length == 0))
2546 break;
2547 /*
2548 Annotate Image window with primary selection.
2549 */
2550 for (i=0; i < (ssize_t) length; i++)
2551 {
2552 if ((char) data[i] != '\n')
2553 {
2554 /*
2555 Draw a single character on the Image window.
2556 */
2557 *p=(char) data[i];
2558 (void) XDrawString(display,windows->image.id,annotate_context,
2559 x,y,p,1);
2560 x+=XTextWidth(font_info,p,1);
2561 p++;
2562 if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2563 continue;
2564 }
2565 /*
2566 Advance to the next line of text.
2567 */
2568 *p='\0';
2569 annotate_info->width=(unsigned int) XTextWidth(font_info,
2570 annotate_info->text,(int) strlen(annotate_info->text));
2571 if (annotate_info->next != (XAnnotateInfo *) NULL)
2572 {
2573 /*
2574 Line of text already exists.
2575 */
2576 annotate_info=annotate_info->next;
2577 x=annotate_info->x;
2578 y=annotate_info->y;
2579 p=annotate_info->text;
2580 continue;
2581 }
2582 annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2583 sizeof(*annotate_info->next));
2584 if (annotate_info->next == (XAnnotateInfo *) NULL)
2585 return(MagickFalse);
2586 *annotate_info->next=(*annotate_info);
2587 annotate_info->next->previous=annotate_info;
2588 annotate_info=annotate_info->next;
2589 annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2590 windows->image.width/MagickMax((ssize_t)
2591 font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2592 if (annotate_info->text == (char *) NULL)
2593 return(MagickFalse);
2594 annotate_info->y+=annotate_info->height;
2595 if (annotate_info->y > (int) windows->image.height)
2596 annotate_info->y=(int) annotate_info->height;
2597 annotate_info->next=(XAnnotateInfo *) NULL;
2598 x=annotate_info->x;
2599 y=annotate_info->y;
2600 p=annotate_info->text;
2601 }
2602 (void) XFree((void *) data);
2603 break;
2604 }
2605 default:
2606 break;
2607 }
2608 } while ((state & ExitState) == 0);
2609 (void) XFreeCursor(display,cursor);
2610 /*
2611 Annotation is relative to image configuration.
2612 */
2613 width=(unsigned int) image->columns;
2614 height=(unsigned int) image->rows;
2615 x=0;
2616 y=0;
2617 if (windows->image.crop_geometry != (char *) NULL)
2618 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2619 /*
2620 Initialize annotated image.
2621 */
2622 XSetCursorState(display,windows,MagickTrue);
2623 XCheckRefreshWindows(display,windows);
2624 while (annotate_info != (XAnnotateInfo *) NULL)
2625 {
2626 if (annotate_info->width == 0)
2627 {
2628 /*
2629 No text on this line-- go to the next line of text.
2630 */
2631 previous_info=annotate_info->previous;
2632 annotate_info->text=(char *)
2633 RelinquishMagickMemory(annotate_info->text);
2634 annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2635 annotate_info=previous_info;
2636 continue;
2637 }
2638 /*
2639 Determine pixel index for box and pen color.
2640 */
2641 windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2642 if (windows->pixel_info->colors != 0)
2643 for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2644 if (windows->pixel_info->pixels[i] ==
2645 windows->pixel_info->pen_colors[box_id].pixel)
2646 {
2647 windows->pixel_info->box_index=(unsigned short) i;
2648 break;
2649 }
2650 windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2651 if (windows->pixel_info->colors != 0)
2652 for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2653 if (windows->pixel_info->pixels[i] ==
2654 windows->pixel_info->pen_colors[pen_id].pixel)
2655 {
2656 windows->pixel_info->pen_index=(unsigned short) i;
2657 break;
2658 }
2659 /*
2660 Define the annotate geometry string.
2661 */
2662 annotate_info->x=(int)
2663 width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2664 annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2665 windows->image.y)/windows->image.ximage->height;
2666 (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2667 "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2668 height*annotate_info->height/windows->image.ximage->height,
2669 annotate_info->x+x,annotate_info->y+y);
2670 /*
2671 Annotate image with text.
2672 */
2673 status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
2674 if (status == 0)
2675 return(MagickFalse);
2676 /*
2677 Free up memory.
2678 */
2679 previous_info=annotate_info->previous;
2680 annotate_info->text=DestroyString(annotate_info->text);
2681 annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2682 annotate_info=previous_info;
2683 }
2684 (void) XSetForeground(display,annotate_context,
2685 windows->pixel_info->foreground_color.pixel);
2686 (void) XSetBackground(display,annotate_context,
2687 windows->pixel_info->background_color.pixel);
2688 (void) XSetFont(display,annotate_context,windows->font_info->fid);
2689 XSetCursorState(display,windows,MagickFalse);
2690 (void) XFreeFont(display,font_info);
2691 /*
2692 Update image configuration.
2693 */
2694 XConfigureImageColormap(display,resource_info,windows,image);
2695 (void) XConfigureImage(display,resource_info,windows,image);
2696 return(MagickTrue);
2697}
2698
2699/*
2700%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2701% %
2702% %
2703% %
2704+ X B a c k g r o u n d I m a g e %
2705% %
2706% %
2707% %
2708%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2709%
2710% XBackgroundImage() displays the image in the background of a window.
2711%
2712% The format of the XBackgroundImage method is:
2713%
2714% MagickBooleanType XBackgroundImage(Display *display,
2715% XResourceInfo *resource_info,XWindows *windows,Image **image)
2716%
2717% A description of each parameter follows:
2718%
2719% o display: Specifies a connection to an X server; returned from
2720% XOpenDisplay.
2721%
2722% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2723%
2724% o windows: Specifies a pointer to a XWindows structure.
2725%
2726% o image: the image.
2727%
2728*/
2729static MagickBooleanType XBackgroundImage(Display *display,
2730 XResourceInfo *resource_info,XWindows *windows,Image **image)
2731{
2732#define BackgroundImageTag "Background/Image"
2733
2734 int
2735 status;
2736
2737 static char
2738 window_id[MaxTextExtent] = "root";
2739
2740 XResourceInfo
2741 background_resources;
2742
2743 /*
2744 Put image in background.
2745 */
2746 status=XDialogWidget(display,windows,"Background",
2747 "Enter window id (id 0x00 selects window with pointer):",window_id);
2748 if (*window_id == '\0')
2749 return(MagickFalse);
2750 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
2751 XInfoWidget(display,windows,BackgroundImageTag);
2752 XSetCursorState(display,windows,MagickTrue);
2753 XCheckRefreshWindows(display,windows);
2754 background_resources=(*resource_info);
2755 background_resources.window_id=window_id;
2756 background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2757 status=XDisplayBackgroundImage(display,&background_resources,*image);
2758 if (status != MagickFalse)
2759 XClientMessage(display,windows->image.id,windows->im_protocols,
2760 windows->im_retain_colors,CurrentTime);
2761 XSetCursorState(display,windows,MagickFalse);
2762 (void) XMagickCommand(display,resource_info,windows,UndoCommand,image);
2763 return(MagickTrue);
2764}
2765
2766/*
2767%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2768% %
2769% %
2770% %
2771+ X C h o p I m a g e %
2772% %
2773% %
2774% %
2775%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2776%
2777% XChopImage() chops the X image.
2778%
2779% The format of the XChopImage method is:
2780%
2781% MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2782% XWindows *windows,Image **image)
2783%
2784% A description of each parameter follows:
2785%
2786% o display: Specifies a connection to an X server; returned from
2787% XOpenDisplay.
2788%
2789% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2790%
2791% o windows: Specifies a pointer to a XWindows structure.
2792%
2793% o image: the image.
2794%
2795*/
2796static MagickBooleanType XChopImage(Display *display,
2797 XResourceInfo *resource_info,XWindows *windows,Image **image)
2798{
2799 const char
2800 *const ChopMenu[] =
2801 {
2802 "Direction",
2803 "Help",
2804 "Dismiss",
2805 (char *) NULL
2806 };
2807
2808 static ModeType
2809 direction = HorizontalChopCommand;
2810
2811 static const ModeType
2812 ChopCommands[] =
2813 {
2814 ChopDirectionCommand,
2815 ChopHelpCommand,
2816 ChopDismissCommand
2817 },
2818 DirectionCommands[] =
2819 {
2820 HorizontalChopCommand,
2821 VerticalChopCommand
2822 };
2823
2824 char
2825 text[MaxTextExtent];
2826
2827 Image
2828 *chop_image;
2829
2830 int
2831 id,
2832 x,
2833 y;
2834
2835 MagickRealType
2836 scale_factor;
2837
2839 chop_info;
2840
2841 unsigned int
2842 distance,
2843 height,
2844 width;
2845
2846 size_t
2847 state;
2848
2849 XEvent
2850 event;
2851
2852 XSegment
2853 segment_info;
2854
2855 /*
2856 Map Command widget.
2857 */
2858 (void) CloneString(&windows->command.name,"Chop");
2859 windows->command.data=1;
2860 (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2861 (void) XMapRaised(display,windows->command.id);
2862 XClientMessage(display,windows->image.id,windows->im_protocols,
2863 windows->im_update_widget,CurrentTime);
2864 /*
2865 Track pointer until button 1 is pressed.
2866 */
2867 XQueryPosition(display,windows->image.id,&x,&y);
2868 (void) XSelectInput(display,windows->image.id,
2869 windows->image.attributes.event_mask | PointerMotionMask);
2870 state=DefaultState;
2871 (void) memset(&segment_info,0,sizeof(segment_info));
2872 do
2873 {
2874 if (windows->info.mapped != MagickFalse)
2875 {
2876 /*
2877 Display pointer position.
2878 */
2879 (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2880 x+windows->image.x,y+windows->image.y);
2881 XInfoWidget(display,windows,text);
2882 }
2883 /*
2884 Wait for next event.
2885 */
2886 XScreenEvent(display,windows,&event);
2887 if (event.xany.window == windows->command.id)
2888 {
2889 /*
2890 Select a command from the Command widget.
2891 */
2892 id=XCommandWidget(display,windows,ChopMenu,&event);
2893 if (id < 0)
2894 continue;
2895 switch (ChopCommands[id])
2896 {
2897 case ChopDirectionCommand:
2898 {
2899 char
2900 command[MaxTextExtent];
2901
2902 const char
2903 *const Directions[] =
2904 {
2905 "horizontal",
2906 "vertical",
2907 (char *) NULL,
2908 };
2909
2910 /*
2911 Select a command from the pop-up menu.
2912 */
2913 id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2914 if (id >= 0)
2915 direction=DirectionCommands[id];
2916 break;
2917 }
2918 case ChopHelpCommand:
2919 {
2920 XTextViewHelp(display,resource_info,windows,MagickFalse,
2921 "Help Viewer - Image Chop",ImageChopHelp);
2922 break;
2923 }
2924 case ChopDismissCommand:
2925 {
2926 /*
2927 Prematurely exit.
2928 */
2929 state|=EscapeState;
2930 state|=ExitState;
2931 break;
2932 }
2933 default:
2934 break;
2935 }
2936 continue;
2937 }
2938 switch (event.type)
2939 {
2940 case ButtonPress:
2941 {
2942 if (event.xbutton.button != Button1)
2943 break;
2944 if (event.xbutton.window != windows->image.id)
2945 break;
2946 /*
2947 User has committed to start point of chopping line.
2948 */
2949 segment_info.x1=(short int) event.xbutton.x;
2950 segment_info.x2=(short int) event.xbutton.x;
2951 segment_info.y1=(short int) event.xbutton.y;
2952 segment_info.y2=(short int) event.xbutton.y;
2953 state|=ExitState;
2954 break;
2955 }
2956 case ButtonRelease:
2957 break;
2958 case Expose:
2959 break;
2960 case KeyPress:
2961 {
2962 char
2963 command[MaxTextExtent];
2964
2965 KeySym
2966 key_symbol;
2967
2968 if (event.xkey.window != windows->image.id)
2969 break;
2970 /*
2971 Respond to a user key press.
2972 */
2973 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2974 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2975 switch ((int) key_symbol)
2976 {
2977 case XK_Escape:
2978 case XK_F20:
2979 {
2980 /*
2981 Prematurely exit.
2982 */
2983 state|=EscapeState;
2984 state|=ExitState;
2985 break;
2986 }
2987 case XK_F1:
2988 case XK_Help:
2989 {
2990 (void) XSetFunction(display,windows->image.highlight_context,
2991 GXcopy);
2992 XTextViewHelp(display,resource_info,windows,MagickFalse,
2993 "Help Viewer - Image Chop",ImageChopHelp);
2994 (void) XSetFunction(display,windows->image.highlight_context,
2995 GXinvert);
2996 break;
2997 }
2998 default:
2999 {
3000 (void) XBell(display,0);
3001 break;
3002 }
3003 }
3004 break;
3005 }
3006 case MotionNotify:
3007 {
3008 /*
3009 Map and unmap Info widget as text cursor crosses its boundaries.
3010 */
3011 x=event.xmotion.x;
3012 y=event.xmotion.y;
3013 if (windows->info.mapped != MagickFalse)
3014 {
3015 if ((x < (int) (windows->info.x+windows->info.width)) &&
3016 (y < (int) (windows->info.y+windows->info.height)))
3017 (void) XWithdrawWindow(display,windows->info.id,
3018 windows->info.screen);
3019 }
3020 else
3021 if ((x > (int) (windows->info.x+windows->info.width)) ||
3022 (y > (int) (windows->info.y+windows->info.height)))
3023 (void) XMapWindow(display,windows->info.id);
3024 }
3025 }
3026 } while ((state & ExitState) == 0);
3027 (void) XSelectInput(display,windows->image.id,
3028 windows->image.attributes.event_mask);
3029 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3030 if ((state & EscapeState) != 0)
3031 return(MagickTrue);
3032 /*
3033 Draw line as pointer moves until the mouse button is released.
3034 */
3035 chop_info.width=0;
3036 chop_info.height=0;
3037 chop_info.x=0;
3038 chop_info.y=0;
3039 distance=0;
3040 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3041 state=DefaultState;
3042 do
3043 {
3044 if (distance > 9)
3045 {
3046 /*
3047 Display info and draw chopping line.
3048 */
3049 if (windows->info.mapped == MagickFalse)
3050 (void) XMapWindow(display,windows->info.id);
3051 (void) FormatLocaleString(text,MaxTextExtent,
3052 " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3053 chop_info.height,(double) chop_info.x,(double) chop_info.y);
3054 XInfoWidget(display,windows,text);
3055 XHighlightLine(display,windows->image.id,
3056 windows->image.highlight_context,&segment_info);
3057 }
3058 else
3059 if (windows->info.mapped != MagickFalse)
3060 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3061 /*
3062 Wait for next event.
3063 */
3064 XScreenEvent(display,windows,&event);
3065 if (distance > 9)
3066 XHighlightLine(display,windows->image.id,
3067 windows->image.highlight_context,&segment_info);
3068 switch (event.type)
3069 {
3070 case ButtonPress:
3071 {
3072 segment_info.x2=(short int) event.xmotion.x;
3073 segment_info.y2=(short int) event.xmotion.y;
3074 break;
3075 }
3076 case ButtonRelease:
3077 {
3078 /*
3079 User has committed to chopping line.
3080 */
3081 segment_info.x2=(short int) event.xbutton.x;
3082 segment_info.y2=(short int) event.xbutton.y;
3083 state|=ExitState;
3084 break;
3085 }
3086 case Expose:
3087 break;
3088 case MotionNotify:
3089 {
3090 segment_info.x2=(short int) event.xmotion.x;
3091 segment_info.y2=(short int) event.xmotion.y;
3092 }
3093 default:
3094 break;
3095 }
3096 /*
3097 Check boundary conditions.
3098 */
3099 if (segment_info.x2 < 0)
3100 segment_info.x2=0;
3101 else
3102 if (segment_info.x2 > windows->image.ximage->width)
3103 segment_info.x2=windows->image.ximage->width;
3104 if (segment_info.y2 < 0)
3105 segment_info.y2=0;
3106 else
3107 if (segment_info.y2 > windows->image.ximage->height)
3108 segment_info.y2=windows->image.ximage->height;
3109 distance=(unsigned int)
3110 (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3111 ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3112 /*
3113 Compute chopping geometry.
3114 */
3115 if (direction == HorizontalChopCommand)
3116 {
3117 chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3118 chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3119 chop_info.height=0;
3120 chop_info.y=0;
3121 if (segment_info.x1 > (int) segment_info.x2)
3122 {
3123 chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3124 chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3125 }
3126 }
3127 else
3128 {
3129 chop_info.width=0;
3130 chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3131 chop_info.x=0;
3132 chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3133 if (segment_info.y1 > segment_info.y2)
3134 {
3135 chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3136 chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3137 }
3138 }
3139 } while ((state & ExitState) == 0);
3140 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3141 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3142 if (distance <= 9)
3143 return(MagickTrue);
3144 /*
3145 Image chopping is relative to image configuration.
3146 */
3147 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
3148 XSetCursorState(display,windows,MagickTrue);
3149 XCheckRefreshWindows(display,windows);
3150 windows->image.window_changes.width=windows->image.ximage->width-
3151 (unsigned int) chop_info.width;
3152 windows->image.window_changes.height=windows->image.ximage->height-
3153 (unsigned int) chop_info.height;
3154 width=(unsigned int) (*image)->columns;
3155 height=(unsigned int) (*image)->rows;
3156 x=0;
3157 y=0;
3158 if (windows->image.crop_geometry != (char *) NULL)
3159 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3160 scale_factor=(MagickRealType) width/windows->image.ximage->width;
3161 chop_info.x+=x;
3162 chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3163 chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3164 scale_factor=(MagickRealType) height/windows->image.ximage->height;
3165 chop_info.y+=y;
3166 chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3167 chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3168 /*
3169 Chop image.
3170 */
3171 chop_image=ChopImage(*image,&chop_info,&(*image)->exception);
3172 XSetCursorState(display,windows,MagickFalse);
3173 if (chop_image == (Image *) NULL)
3174 return(MagickFalse);
3175 *image=DestroyImage(*image);
3176 *image=chop_image;
3177 /*
3178 Update image configuration.
3179 */
3180 XConfigureImageColormap(display,resource_info,windows,*image);
3181 (void) XConfigureImage(display,resource_info,windows,*image);
3182 return(MagickTrue);
3183}
3184
3185/*
3186%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3187% %
3188% %
3189% %
3190+ X C o l o r E d i t I m a g e %
3191% %
3192% %
3193% %
3194%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3195%
3196% XColorEditImage() allows the user to interactively change the color of one
3197% pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3198%
3199% The format of the XColorEditImage method is:
3200%
3201% MagickBooleanType XColorEditImage(Display *display,
3202% XResourceInfo *resource_info,XWindows *windows,Image **image)
3203%
3204% A description of each parameter follows:
3205%
3206% o display: Specifies a connection to an X server; returned from
3207% XOpenDisplay.
3208%
3209% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3210%
3211% o windows: Specifies a pointer to a XWindows structure.
3212%
3213% o image: the image; returned from ReadImage.
3214%
3215*/
3216
3217
3218static MagickBooleanType XColorEditImage(Display *display,
3219 XResourceInfo *resource_info,XWindows *windows,Image **image)
3220{
3221 const char
3222 *const ColorEditMenu[] =
3223 {
3224 "Method",
3225 "Pixel Color",
3226 "Border Color",
3227 "Fuzz",
3228 "Undo",
3229 "Help",
3230 "Dismiss",
3231 (char *) NULL
3232 };
3233
3234 static const ModeType
3235 ColorEditCommands[] =
3236 {
3237 ColorEditMethodCommand,
3238 ColorEditColorCommand,
3239 ColorEditBorderCommand,
3240 ColorEditFuzzCommand,
3241 ColorEditUndoCommand,
3242 ColorEditHelpCommand,
3243 ColorEditDismissCommand
3244 };
3245
3246 static PaintMethod
3247 method = PointMethod;
3248
3249 static unsigned int
3250 pen_id = 0;
3251
3252 static XColor
3253 border_color = { 0, 0, 0, 0, 0, 0 };
3254
3255 char
3256 command[MaxTextExtent] = "",
3257 text[MaxTextExtent] = "";
3258
3259 Cursor
3260 cursor;
3261
3263 *exception;
3264
3265 int
3266 entry,
3267 id,
3268 x,
3269 x_offset,
3270 y,
3271 y_offset;
3272
3274 *q;
3275
3276 size_t
3277 state;
3278
3279 ssize_t
3280 i;
3281
3282 unsigned int
3283 height,
3284 width;
3285
3286 XColor
3287 color;
3288
3289 XEvent
3290 event;
3291
3292 /*
3293 Map Command widget.
3294 */
3295 (void) CloneString(&windows->command.name,"Color Edit");
3296 windows->command.data=4;
3297 (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3298 (void) XMapRaised(display,windows->command.id);
3299 XClientMessage(display,windows->image.id,windows->im_protocols,
3300 windows->im_update_widget,CurrentTime);
3301 /*
3302 Make cursor.
3303 */
3304 cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3305 resource_info->background_color,resource_info->foreground_color);
3306 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3307 /*
3308 Track pointer until button 1 is pressed.
3309 */
3310 XQueryPosition(display,windows->image.id,&x,&y);
3311 (void) XSelectInput(display,windows->image.id,
3312 windows->image.attributes.event_mask | PointerMotionMask);
3313 state=DefaultState;
3314 do
3315 {
3316 if (windows->info.mapped != MagickFalse)
3317 {
3318 /*
3319 Display pointer position.
3320 */
3321 (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3322 x+windows->image.x,y+windows->image.y);
3323 XInfoWidget(display,windows,text);
3324 }
3325 /*
3326 Wait for next event.
3327 */
3328 XScreenEvent(display,windows,&event);
3329 if (event.xany.window == windows->command.id)
3330 {
3331 /*
3332 Select a command from the Command widget.
3333 */
3334 id=XCommandWidget(display,windows,ColorEditMenu,&event);
3335 if (id < 0)
3336 {
3337 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3338 continue;
3339 }
3340 switch (ColorEditCommands[id])
3341 {
3342 case ColorEditMethodCommand:
3343 {
3344 char
3345 **methods;
3346
3347 /*
3348 Select a method from the pop-up menu.
3349 */
3350 methods=(char **) GetCommandOptions(MagickMethodOptions);
3351 if (methods == (char **) NULL)
3352 break;
3353 entry=XMenuWidget(display,windows,ColorEditMenu[id],
3354 (const char **) methods,command);
3355 if (entry >= 0)
3356 method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3357 MagickFalse,methods[entry]);
3358 methods=DestroyStringList(methods);
3359 break;
3360 }
3361 case ColorEditColorCommand:
3362 {
3363 const char
3364 *ColorMenu[MaxNumberPens];
3365
3366 int
3367 pen_number;
3368
3369 /*
3370 Initialize menu selections.
3371 */
3372 for (i=0; i < (int) (MaxNumberPens-2); i++)
3373 ColorMenu[i]=resource_info->pen_colors[i];
3374 ColorMenu[MaxNumberPens-2]="Browser...";
3375 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3376 /*
3377 Select a pen color from the pop-up menu.
3378 */
3379 pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3380 (const char **) ColorMenu,command);
3381 if (pen_number < 0)
3382 break;
3383 if (pen_number == (MaxNumberPens-2))
3384 {
3385 static char
3386 color_name[MaxTextExtent] = "gray";
3387
3388 /*
3389 Select a pen color from a dialog.
3390 */
3391 resource_info->pen_colors[pen_number]=color_name;
3392 XColorBrowserWidget(display,windows,"Select",color_name);
3393 if (*color_name == '\0')
3394 break;
3395 }
3396 /*
3397 Set pen color.
3398 */
3399 (void) XParseColor(display,windows->map_info->colormap,
3400 resource_info->pen_colors[pen_number],&color);
3401 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3402 (unsigned int) MaxColors,&color);
3403 windows->pixel_info->pen_colors[pen_number]=color;
3404 pen_id=(unsigned int) pen_number;
3405 break;
3406 }
3407 case ColorEditBorderCommand:
3408 {
3409 const char
3410 *ColorMenu[MaxNumberPens];
3411
3412 int
3413 pen_number;
3414
3415 /*
3416 Initialize menu selections.
3417 */
3418 for (i=0; i < (int) (MaxNumberPens-2); i++)
3419 ColorMenu[i]=resource_info->pen_colors[i];
3420 ColorMenu[MaxNumberPens-2]="Browser...";
3421 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3422 /*
3423 Select a pen color from the pop-up menu.
3424 */
3425 pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3426 (const char **) ColorMenu,command);
3427 if (pen_number < 0)
3428 break;
3429 if (pen_number == (MaxNumberPens-2))
3430 {
3431 static char
3432 color_name[MaxTextExtent] = "gray";
3433
3434 /*
3435 Select a pen color from a dialog.
3436 */
3437 resource_info->pen_colors[pen_number]=color_name;
3438 XColorBrowserWidget(display,windows,"Select",color_name);
3439 if (*color_name == '\0')
3440 break;
3441 }
3442 /*
3443 Set border color.
3444 */
3445 (void) XParseColor(display,windows->map_info->colormap,
3446 resource_info->pen_colors[pen_number],&border_color);
3447 break;
3448 }
3449 case ColorEditFuzzCommand:
3450 {
3451 static char
3452 fuzz[MaxTextExtent];
3453
3454 static const char
3455 *FuzzMenu[] =
3456 {
3457 "0%",
3458 "2%",
3459 "5%",
3460 "10%",
3461 "15%",
3462 "Dialog...",
3463 (char *) NULL,
3464 };
3465
3466 /*
3467 Select a command from the pop-up menu.
3468 */
3469 entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3470 command);
3471 if (entry < 0)
3472 break;
3473 if (entry != 5)
3474 {
3475 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3476 QuantumRange+1.0);
3477 break;
3478 }
3479 (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3480 (void) XDialogWidget(display,windows,"Ok",
3481 "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3482 if (*fuzz == '\0')
3483 break;
3484 (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3485 (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3486 1.0);
3487 break;
3488 }
3489 case ColorEditUndoCommand:
3490 {
3491 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3492 image);
3493 break;
3494 }
3495 case ColorEditHelpCommand:
3496 default:
3497 {
3498 XTextViewHelp(display,resource_info,windows,MagickFalse,
3499 "Help Viewer - Image Annotation",ImageColorEditHelp);
3500 break;
3501 }
3502 case ColorEditDismissCommand:
3503 {
3504 /*
3505 Prematurely exit.
3506 */
3507 state|=EscapeState;
3508 state|=ExitState;
3509 break;
3510 }
3511 }
3512 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3513 continue;
3514 }
3515 switch (event.type)
3516 {
3517 case ButtonPress:
3518 {
3519 if (event.xbutton.button != Button1)
3520 break;
3521 if ((event.xbutton.window != windows->image.id) &&
3522 (event.xbutton.window != windows->magnify.id))
3523 break;
3524 /*
3525 exit loop.
3526 */
3527 x=event.xbutton.x;
3528 y=event.xbutton.y;
3529 (void) XMagickCommand(display,resource_info,windows,
3530 SaveToUndoBufferCommand,image);
3531 state|=UpdateConfigurationState;
3532 break;
3533 }
3534 case ButtonRelease:
3535 {
3536 if (event.xbutton.button != Button1)
3537 break;
3538 if ((event.xbutton.window != windows->image.id) &&
3539 (event.xbutton.window != windows->magnify.id))
3540 break;
3541 /*
3542 Update colormap information.
3543 */
3544 x=event.xbutton.x;
3545 y=event.xbutton.y;
3546 XConfigureImageColormap(display,resource_info,windows,*image);
3547 (void) XConfigureImage(display,resource_info,windows,*image);
3548 XInfoWidget(display,windows,text);
3549 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3550 state&=(~UpdateConfigurationState);
3551 break;
3552 }
3553 case Expose:
3554 break;
3555 case KeyPress:
3556 {
3557 KeySym
3558 key_symbol;
3559
3560 if (event.xkey.window == windows->magnify.id)
3561 {
3562 Window
3563 window;
3564
3565 window=windows->magnify.id;
3566 while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3567 }
3568 if (event.xkey.window != windows->image.id)
3569 break;
3570 /*
3571 Respond to a user key press.
3572 */
3573 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3574 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3575 switch ((int) key_symbol)
3576 {
3577 case XK_Escape:
3578 case XK_F20:
3579 {
3580 /*
3581 Prematurely exit.
3582 */
3583 state|=ExitState;
3584 break;
3585 }
3586 case XK_F1:
3587 case XK_Help:
3588 {
3589 XTextViewHelp(display,resource_info,windows,MagickFalse,
3590 "Help Viewer - Image Annotation",ImageColorEditHelp);
3591 break;
3592 }
3593 default:
3594 {
3595 (void) XBell(display,0);
3596 break;
3597 }
3598 }
3599 break;
3600 }
3601 case MotionNotify:
3602 {
3603 /*
3604 Map and unmap Info widget as cursor crosses its boundaries.
3605 */
3606 x=event.xmotion.x;
3607 y=event.xmotion.y;
3608 if (windows->info.mapped != MagickFalse)
3609 {
3610 if ((x < (int) (windows->info.x+windows->info.width)) &&
3611 (y < (int) (windows->info.y+windows->info.height)))
3612 (void) XWithdrawWindow(display,windows->info.id,
3613 windows->info.screen);
3614 }
3615 else
3616 if ((x > (int) (windows->info.x+windows->info.width)) ||
3617 (y > (int) (windows->info.y+windows->info.height)))
3618 (void) XMapWindow(display,windows->info.id);
3619 break;
3620 }
3621 default:
3622 break;
3623 }
3624 if (event.xany.window == windows->magnify.id)
3625 {
3626 x=windows->magnify.x-windows->image.x;
3627 y=windows->magnify.y-windows->image.y;
3628 }
3629 x_offset=x;
3630 y_offset=y;
3631 if ((state & UpdateConfigurationState) != 0)
3632 {
3633 CacheView
3634 *image_view;
3635
3636 int
3637 x,
3638 y;
3639
3640 /*
3641 Pixel edit is relative to image configuration.
3642 */
3643 (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3644 MagickTrue);
3645 color=windows->pixel_info->pen_colors[pen_id];
3646 XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3647 width=(unsigned int) (*image)->columns;
3648 height=(unsigned int) (*image)->rows;
3649 x=0;
3650 y=0;
3651 if (windows->image.crop_geometry != (char *) NULL)
3652 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3653 &width,&height);
3654 x_offset=(int)
3655 (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3656 y_offset=(int)
3657 (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3658 if ((x_offset < 0) || (y_offset < 0))
3659 continue;
3660 if ((x_offset >= (int) (*image)->columns) ||
3661 (y_offset >= (int) (*image)->rows))
3662 continue;
3663 exception=(&(*image)->exception);
3664 image_view=AcquireAuthenticCacheView(*image,exception);
3665 switch (method)
3666 {
3667 case PointMethod:
3668 default:
3669 {
3670 /*
3671 Update color information using point algorithm.
3672 */
3673 if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3674 return(MagickFalse);
3675 q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3676 (ssize_t)y_offset,1,1,exception);
3677 if (q == (PixelPacket *) NULL)
3678 break;
3679 q->red=ScaleShortToQuantum(color.red);
3680 q->green=ScaleShortToQuantum(color.green);
3681 q->blue=ScaleShortToQuantum(color.blue);
3682 (void) SyncCacheViewAuthenticPixels(image_view,
3683 &(*image)->exception);
3684 break;
3685 }
3686 case ReplaceMethod:
3687 {
3689 target;
3690
3691 /*
3692 Update color information using replace algorithm.
3693 */
3694 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3695 (ssize_t) y_offset,&target,&(*image)->exception);
3696 if ((*image)->storage_class == DirectClass)
3697 {
3698 for (y=0; y < (int) (*image)->rows; y++)
3699 {
3700 q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3701 (*image)->columns,1,exception);
3702 if (q == (PixelPacket *) NULL)
3703 break;
3704 for (x=0; x < (int) (*image)->columns; x++)
3705 {
3706 if (IsColorSimilar(*image,q,&target) != MagickFalse)
3707 {
3708 q->red=ScaleShortToQuantum(color.red);
3709 q->green=ScaleShortToQuantum(color.green);
3710 q->blue=ScaleShortToQuantum(color.blue);
3711 }
3712 q++;
3713 }
3714 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3715 break;
3716 }
3717 }
3718 else
3719 {
3720 for (i=0; i < (ssize_t) (*image)->colors; i++)
3721 if (IsColorSimilar(*image,(*image)->colormap+i,&target) != MagickFalse)
3722 {
3723 (*image)->colormap[i].red=ScaleShortToQuantum(color.red);
3724 (*image)->colormap[i].green=ScaleShortToQuantum(
3725 color.green);
3726 (*image)->colormap[i].blue=ScaleShortToQuantum(
3727 color.blue);
3728 }
3729 (void) SyncImage(*image);
3730 }
3731 break;
3732 }
3733 case FloodfillMethod:
3734 case FillToBorderMethod:
3735 {
3736 DrawInfo
3737 *draw_info;
3738
3740 target;
3741
3742 /*
3743 Update color information using floodfill algorithm.
3744 */
3745 (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
3746 (ssize_t) y_offset,&target,exception);
3747 if (method == FillToBorderMethod)
3748 {
3749 target.red=(MagickRealType)
3750 ScaleShortToQuantum(border_color.red);
3751 target.green=(MagickRealType)
3752 ScaleShortToQuantum(border_color.green);
3753 target.blue=(MagickRealType)
3754 ScaleShortToQuantum(border_color.blue);
3755 }
3756 draw_info=CloneDrawInfo(resource_info->image_info,
3757 (DrawInfo *) NULL);
3758 (void) QueryColorDatabase(resource_info->pen_colors[pen_id],
3759 &draw_info->fill,exception);
3760 (void) FloodfillPaintImage(*image,DefaultChannels,draw_info,&target,
3761 (ssize_t) x_offset,(ssize_t) y_offset,
3762 method == FloodfillMethod ? MagickFalse : MagickTrue);
3763 draw_info=DestroyDrawInfo(draw_info);
3764 break;
3765 }
3766 case ResetMethod:
3767 {
3768 /*
3769 Update color information using reset algorithm.
3770 */
3771 if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3772 return(MagickFalse);
3773 for (y=0; y < (int) (*image)->rows; y++)
3774 {
3775 q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3776 (*image)->columns,1,exception);
3777 if (q == (PixelPacket *) NULL)
3778 break;
3779 for (x=0; x < (int) (*image)->columns; x++)
3780 {
3781 q->red=ScaleShortToQuantum(color.red);
3782 q->green=ScaleShortToQuantum(color.green);
3783 q->blue=ScaleShortToQuantum(color.blue);
3784 q++;
3785 }
3786 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3787 break;
3788 }
3789 break;
3790 }
3791 }
3792 image_view=DestroyCacheView(image_view);
3793 state&=(~UpdateConfigurationState);
3794 }
3795 } while ((state & ExitState) == 0);
3796 (void) XSelectInput(display,windows->image.id,
3797 windows->image.attributes.event_mask);
3798 XSetCursorState(display,windows,MagickFalse);
3799 (void) XFreeCursor(display,cursor);
3800 return(MagickTrue);
3801}
3802
3803/*
3804%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3805% %
3806% %
3807% %
3808+ X C o m p o s i t e I m a g e %
3809% %
3810% %
3811% %
3812%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3813%
3814% XCompositeImage() requests an image name from the user, reads the image and
3815% composites it with the X window image at a location the user chooses with
3816% the pointer.
3817%
3818% The format of the XCompositeImage method is:
3819%
3820% MagickBooleanType XCompositeImage(Display *display,
3821% XResourceInfo *resource_info,XWindows *windows,Image *image)
3822%
3823% A description of each parameter follows:
3824%
3825% o display: Specifies a connection to an X server; returned from
3826% XOpenDisplay.
3827%
3828% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3829%
3830% o windows: Specifies a pointer to a XWindows structure.
3831%
3832% o image: the image; returned from ReadImage.
3833%
3834*/
3835static MagickBooleanType XCompositeImage(Display *display,
3836 XResourceInfo *resource_info,XWindows *windows,Image *image)
3837{
3838 const char
3839 *const CompositeMenu[] =
3840 {
3841 "Operators",
3842 "Dissolve",
3843 "Displace",
3844 "Help",
3845 "Dismiss",
3846 (char *) NULL
3847 };
3848
3849 static char
3850 displacement_geometry[MaxTextExtent] = "30x30",
3851 filename[MaxTextExtent] = "\0";
3852
3853 static CompositeOperator
3854 compose = CopyCompositeOp;
3855
3856 static const ModeType
3857 CompositeCommands[] =
3858 {
3859 CompositeOperatorsCommand,
3860 CompositeDissolveCommand,
3861 CompositeDisplaceCommand,
3862 CompositeHelpCommand,
3863 CompositeDismissCommand
3864 };
3865
3866 char
3867 text[MaxTextExtent];
3868
3869 Cursor
3870 cursor;
3871
3872 Image
3873 *composite_image;
3874
3875 int
3876 entry,
3877 id,
3878 x,
3879 y;
3880
3881 MagickRealType
3882 blend,
3883 scale_factor;
3884
3886 highlight_info,
3887 composite_info;
3888
3889 unsigned int
3890 height,
3891 width;
3892
3893 size_t
3894 state;
3895
3896 XEvent
3897 event;
3898
3899 /*
3900 Request image file name from user.
3901 */
3902 XFileBrowserWidget(display,windows,"Composite",filename);
3903 if (*filename == '\0')
3904 return(MagickTrue);
3905 /*
3906 Read image.
3907 */
3908 XSetCursorState(display,windows,MagickTrue);
3909 XCheckRefreshWindows(display,windows);
3910 (void) CopyMagickString(resource_info->image_info->filename,filename,
3911 MaxTextExtent);
3912 composite_image=ReadImage(resource_info->image_info,&image->exception);
3913 CatchException(&image->exception);
3914 XSetCursorState(display,windows,MagickFalse);
3915 if (composite_image == (Image *) NULL)
3916 return(MagickFalse);
3917 /*
3918 Map Command widget.
3919 */
3920 (void) CloneString(&windows->command.name,"Composite");
3921 windows->command.data=1;
3922 (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3923 (void) XMapRaised(display,windows->command.id);
3924 XClientMessage(display,windows->image.id,windows->im_protocols,
3925 windows->im_update_widget,CurrentTime);
3926 /*
3927 Track pointer until button 1 is pressed.
3928 */
3929 XQueryPosition(display,windows->image.id,&x,&y);
3930 (void) XSelectInput(display,windows->image.id,
3931 windows->image.attributes.event_mask | PointerMotionMask);
3932 composite_info.x=(ssize_t) windows->image.x+x;
3933 composite_info.y=(ssize_t) windows->image.y+y;
3934 composite_info.width=0;
3935 composite_info.height=0;
3936 cursor=XCreateFontCursor(display,XC_ul_angle);
3937 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3938 blend=0.0;
3939 state=DefaultState;
3940 do
3941 {
3942 if (windows->info.mapped != MagickFalse)
3943 {
3944 /*
3945 Display pointer position.
3946 */
3947 (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
3948 (long) composite_info.x,(long) composite_info.y);
3949 XInfoWidget(display,windows,text);
3950 }
3951 highlight_info=composite_info;
3952 highlight_info.x=composite_info.x-windows->image.x;
3953 highlight_info.y=composite_info.y-windows->image.y;
3954 XHighlightRectangle(display,windows->image.id,
3955 windows->image.highlight_context,&highlight_info);
3956 /*
3957 Wait for next event.
3958 */
3959 XScreenEvent(display,windows,&event);
3960 XHighlightRectangle(display,windows->image.id,
3961 windows->image.highlight_context,&highlight_info);
3962 if (event.xany.window == windows->command.id)
3963 {
3964 /*
3965 Select a command from the Command widget.
3966 */
3967 id=XCommandWidget(display,windows,CompositeMenu,&event);
3968 if (id < 0)
3969 continue;
3970 switch (CompositeCommands[id])
3971 {
3972 case CompositeOperatorsCommand:
3973 {
3974 char
3975 command[MaxTextExtent],
3976 **operators;
3977
3978 /*
3979 Select a command from the pop-up menu.
3980 */
3981 operators=GetCommandOptions(MagickComposeOptions);
3982 if (operators == (char **) NULL)
3983 break;
3984 entry=XMenuWidget(display,windows,CompositeMenu[id],
3985 (const char **) operators,command);
3986 if (entry >= 0)
3987 compose=(CompositeOperator) ParseCommandOption(
3988 MagickComposeOptions,MagickFalse,operators[entry]);
3989 operators=DestroyStringList(operators);
3990 break;
3991 }
3992 case CompositeDissolveCommand:
3993 {
3994 static char
3995 factor[MaxTextExtent] = "20.0";
3996
3997 /*
3998 Dissolve the two images a given percent.
3999 */
4000 (void) XSetFunction(display,windows->image.highlight_context,
4001 GXcopy);
4002 (void) XDialogWidget(display,windows,"Dissolve",
4003 "Enter the blend factor (0.0 - 99.9%):",factor);
4004 (void) XSetFunction(display,windows->image.highlight_context,
4005 GXinvert);
4006 if (*factor == '\0')
4007 break;
4008 blend=StringToDouble(factor,(char **) NULL);
4009 compose=DissolveCompositeOp;
4010 break;
4011 }
4012 case CompositeDisplaceCommand:
4013 {
4014 /*
4015 Get horizontal and vertical scale displacement geometry.
4016 */
4017 (void) XSetFunction(display,windows->image.highlight_context,
4018 GXcopy);
4019 (void) XDialogWidget(display,windows,"Displace",
4020 "Enter the horizontal and vertical scale:",displacement_geometry);
4021 (void) XSetFunction(display,windows->image.highlight_context,
4022 GXinvert);
4023 if (*displacement_geometry == '\0')
4024 break;
4025 compose=DisplaceCompositeOp;
4026 break;
4027 }
4028 case CompositeHelpCommand:
4029 {
4030 (void) XSetFunction(display,windows->image.highlight_context,
4031 GXcopy);
4032 XTextViewHelp(display,resource_info,windows,MagickFalse,
4033 "Help Viewer - Image Composite",ImageCompositeHelp);
4034 (void) XSetFunction(display,windows->image.highlight_context,
4035 GXinvert);
4036 break;
4037 }
4038 case CompositeDismissCommand:
4039 {
4040 /*
4041 Prematurely exit.
4042 */
4043 state|=EscapeState;
4044 state|=ExitState;
4045 break;
4046 }
4047 default:
4048 break;
4049 }
4050 continue;
4051 }
4052 switch (event.type)
4053 {
4054 case ButtonPress:
4055 {
4056 if (resource_info->debug != MagickFalse)
4057 (void) LogMagickEvent(X11Event,GetMagickModule(),
4058 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4059 event.xbutton.button,event.xbutton.x,event.xbutton.y);
4060 if (event.xbutton.button != Button1)
4061 break;
4062 if (event.xbutton.window != windows->image.id)
4063 break;
4064 /*
4065 Change cursor.
4066 */
4067 composite_info.width=composite_image->columns;
4068 composite_info.height=composite_image->rows;
4069 (void) XCheckDefineCursor(display,windows->image.id,cursor);
4070 composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4071 composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4072 break;
4073 }
4074 case ButtonRelease:
4075 {
4076 if (resource_info->debug != MagickFalse)
4077 (void) LogMagickEvent(X11Event,GetMagickModule(),
4078 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4079 event.xbutton.button,event.xbutton.x,event.xbutton.y);
4080 if (event.xbutton.button != Button1)
4081 break;
4082 if (event.xbutton.window != windows->image.id)
4083 break;
4084 if ((composite_info.width != 0) && (composite_info.height != 0))
4085 {
4086 /*
4087 User has selected the location of the composite image.
4088 */
4089 composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4090 composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4091 state|=ExitState;
4092 }
4093 break;
4094 }
4095 case Expose:
4096 break;
4097 case KeyPress:
4098 {
4099 char
4100 command[MaxTextExtent];
4101
4102 KeySym
4103 key_symbol;
4104
4105 int
4106 length;
4107
4108 if (event.xkey.window != windows->image.id)
4109 break;
4110 /*
4111 Respond to a user key press.
4112 */
4113 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4114 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4115 *(command+length)='\0';
4116 if (resource_info->debug != MagickFalse)
4117 (void) LogMagickEvent(X11Event,GetMagickModule(),
4118 "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4119 switch ((int) key_symbol)
4120 {
4121 case XK_Escape:
4122 case XK_F20:
4123 {
4124 /*
4125 Prematurely exit.
4126 */
4127 composite_image=DestroyImage(composite_image);
4128 state|=EscapeState;
4129 state|=ExitState;
4130 break;
4131 }
4132 case XK_F1:
4133 case XK_Help:
4134 {
4135 (void) XSetFunction(display,windows->image.highlight_context,
4136 GXcopy);
4137 XTextViewHelp(display,resource_info,windows,MagickFalse,
4138 "Help Viewer - Image Composite",ImageCompositeHelp);
4139 (void) XSetFunction(display,windows->image.highlight_context,
4140 GXinvert);
4141 break;
4142 }
4143 default:
4144 {
4145 (void) XBell(display,0);
4146 break;
4147 }
4148 }
4149 break;
4150 }
4151 case MotionNotify:
4152 {
4153 /*
4154 Map and unmap Info widget as text cursor crosses its boundaries.
4155 */
4156 x=event.xmotion.x;
4157 y=event.xmotion.y;
4158 if (windows->info.mapped != MagickFalse)
4159 {
4160 if ((x < (int) (windows->info.x+windows->info.width)) &&
4161 (y < (int) (windows->info.y+windows->info.height)))
4162 (void) XWithdrawWindow(display,windows->info.id,
4163 windows->info.screen);
4164 }
4165 else
4166 if ((x > (int) (windows->info.x+windows->info.width)) ||
4167 (y > (int) (windows->info.y+windows->info.height)))
4168 (void) XMapWindow(display,windows->info.id);
4169 composite_info.x=(ssize_t) windows->image.x+x;
4170 composite_info.y=(ssize_t) windows->image.y+y;
4171 break;
4172 }
4173 default:
4174 {
4175 if (resource_info->debug != MagickFalse)
4176 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4177 event.type);
4178 break;
4179 }
4180 }
4181 } while ((state & ExitState) == 0);
4182 (void) XSelectInput(display,windows->image.id,
4183 windows->image.attributes.event_mask);
4184 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4185 XSetCursorState(display,windows,MagickFalse);
4186 (void) XFreeCursor(display,cursor);
4187 if ((state & EscapeState) != 0)
4188 return(MagickTrue);
4189 /*
4190 Image compositing is relative to image configuration.
4191 */
4192 XSetCursorState(display,windows,MagickTrue);
4193 XCheckRefreshWindows(display,windows);
4194 width=(unsigned int) image->columns;
4195 height=(unsigned int) image->rows;
4196 x=0;
4197 y=0;
4198 if (windows->image.crop_geometry != (char *) NULL)
4199 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4200 scale_factor=(MagickRealType) width/windows->image.ximage->width;
4201 composite_info.x+=x;
4202 composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4203 composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4204 scale_factor=(MagickRealType) height/windows->image.ximage->height;
4205 composite_info.y+=y;
4206 composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4207 composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4208 if ((composite_info.width != composite_image->columns) ||
4209 (composite_info.height != composite_image->rows))
4210 {
4211 Image
4212 *resize_image;
4213
4214 /*
4215 Scale composite image.
4216 */
4217 resize_image=ResizeImage(composite_image,composite_info.width,
4218 composite_info.height,composite_image->filter,composite_image->blur,
4219 &image->exception);
4220 composite_image=DestroyImage(composite_image);
4221 if (resize_image == (Image *) NULL)
4222 {
4223 XSetCursorState(display,windows,MagickFalse);
4224 return(MagickFalse);
4225 }
4226 composite_image=resize_image;
4227 }
4228 if (compose == DisplaceCompositeOp)
4229 (void) SetImageArtifact(composite_image,"compose:args",
4230 displacement_geometry);
4231 if (blend != 0.0)
4232 {
4233 CacheView
4234 *image_view;
4235
4237 *exception;
4238
4239 int
4240 y;
4241
4242 Quantum
4243 opacity;
4244
4245 int
4246 x;
4247
4249 *q;
4250
4251 /*
4252 Create mattes for blending.
4253 */
4254 (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel);
4255 opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4256 ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4257 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
4258 return(MagickFalse);
4259 image->matte=MagickTrue;
4260 exception=(&image->exception);
4261 image_view=AcquireAuthenticCacheView(image,exception);
4262 for (y=0; y < (int) image->rows; y++)
4263 {
4264 q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4265 exception);
4266 if (q == (PixelPacket *) NULL)
4267 break;
4268 for (x=0; x < (int) image->columns; x++)
4269 {
4270 q->opacity=opacity;
4271 q++;
4272 }
4273 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4274 break;
4275 }
4276 image_view=DestroyCacheView(image_view);
4277 }
4278 /*
4279 Composite image with X Image window.
4280 */
4281 (void) CompositeImage(image,compose,composite_image,composite_info.x,
4282 composite_info.y);
4283 composite_image=DestroyImage(composite_image);
4284 XSetCursorState(display,windows,MagickFalse);
4285 /*
4286 Update image configuration.
4287 */
4288 XConfigureImageColormap(display,resource_info,windows,image);
4289 (void) XConfigureImage(display,resource_info,windows,image);
4290 return(MagickTrue);
4291}
4292
4293/*
4294%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4295% %
4296% %
4297% %
4298+ X C o n f i g u r e I m a g e %
4299% %
4300% %
4301% %
4302%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4303%
4304% XConfigureImage() creates a new X image. It also notifies the window
4305% manager of the new image size and configures the transient widows.
4306%
4307% The format of the XConfigureImage method is:
4308%
4309% MagickBooleanType XConfigureImage(Display *display,
4310% XResourceInfo *resource_info,XWindows *windows,Image *image)
4311%
4312% A description of each parameter follows:
4313%
4314% o display: Specifies a connection to an X server; returned from
4315% XOpenDisplay.
4316%
4317% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4318%
4319% o windows: Specifies a pointer to a XWindows structure.
4320%
4321% o image: the image.
4322%
4323%
4324*/
4325static MagickBooleanType XConfigureImage(Display *display,
4326 XResourceInfo *resource_info,XWindows *windows,Image *image)
4327{
4328 char
4329 geometry[MaxTextExtent];
4330
4331 MagickStatusType
4332 status;
4333
4334 size_t
4335 mask,
4336 height,
4337 width;
4338
4339 ssize_t
4340 x,
4341 y;
4342
4343 XSizeHints
4344 *size_hints;
4345
4346 XWindowChanges
4347 window_changes;
4348
4349 /*
4350 Dismiss if window dimensions are zero.
4351 */
4352 width=(unsigned int) windows->image.window_changes.width;
4353 height=(unsigned int) windows->image.window_changes.height;
4354 if (resource_info->debug != MagickFalse)
4355 (void) LogMagickEvent(X11Event,GetMagickModule(),
4356 "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4357 windows->image.ximage->height,(double) width,(double) height);
4358 if ((width*height) == 0)
4359 return(MagickTrue);
4360 x=0;
4361 y=0;
4362 /*
4363 Resize image to fit Image window dimensions.
4364 */
4365 XSetCursorState(display,windows,MagickTrue);
4366 (void) XFlush(display);
4367 if (((int) width != windows->image.ximage->width) ||
4368 ((int) height != windows->image.ximage->height))
4369 image->taint=MagickTrue;
4370 windows->magnify.x=(int)
4371 width*windows->magnify.x/windows->image.ximage->width;
4372 windows->magnify.y=(int)
4373 height*windows->magnify.y/windows->image.ximage->height;
4374 windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4375 windows->image.y=(int)
4376 (height*windows->image.y/windows->image.ximage->height);
4377 status=XMakeImage(display,resource_info,&windows->image,image,
4378 (unsigned int) width,(unsigned int) height);
4379 if (status == MagickFalse)
4380 XNoticeWidget(display,windows,"Unable to configure X image:",
4381 windows->image.name);
4382 /*
4383 Notify window manager of the new configuration.
4384 */
4385 if (resource_info->image_geometry != (char *) NULL)
4386 (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4387 resource_info->image_geometry);
4388 else
4389 (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4390 XDisplayWidth(display,windows->image.screen),
4391 XDisplayHeight(display,windows->image.screen));
4392 (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4393 window_changes.width=(int) width;
4394 if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4395 window_changes.width=XDisplayWidth(display,windows->image.screen);
4396 window_changes.height=(int) height;
4397 if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4398 window_changes.height=XDisplayHeight(display,windows->image.screen);
4399 mask=(size_t) (CWWidth | CWHeight);
4400 if (resource_info->backdrop)
4401 {
4402 mask|=CWX | CWY;
4403 window_changes.x=(int)
4404 ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4405 window_changes.y=(int)
4406 ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4407 }
4408 (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4409 (unsigned int) mask,&window_changes);
4410 (void) XClearWindow(display,windows->image.id);
4411 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4412 /*
4413 Update Magnify window configuration.
4414 */
4415 if (windows->magnify.mapped != MagickFalse)
4416 XMakeMagnifyImage(display,windows);
4417 windows->pan.crop_geometry=windows->image.crop_geometry;
4418 XBestIconSize(display,&windows->pan,image);
4419 while (((windows->pan.width << 1) < MaxIconSize) &&
4420 ((windows->pan.height << 1) < MaxIconSize))
4421 {
4422 windows->pan.width<<=1;
4423 windows->pan.height<<=1;
4424 }
4425 if (windows->pan.geometry != (char *) NULL)
4426 (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4427 &windows->pan.width,&windows->pan.height);
4428 window_changes.width=(int) windows->pan.width;
4429 window_changes.height=(int) windows->pan.height;
4430 size_hints=XAllocSizeHints();
4431 if (size_hints != (XSizeHints *) NULL)
4432 {
4433 /*
4434 Set new size hints.
4435 */
4436 size_hints->flags=PSize | PMinSize | PMaxSize;
4437 size_hints->width=window_changes.width;
4438 size_hints->height=window_changes.height;
4439 size_hints->min_width=size_hints->width;
4440 size_hints->min_height=size_hints->height;
4441 size_hints->max_width=size_hints->width;
4442 size_hints->max_height=size_hints->height;
4443 (void) XSetNormalHints(display,windows->pan.id,size_hints);
4444 (void) XFree((void *) size_hints);
4445 }
4446 (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4447 (unsigned int) (CWWidth | CWHeight),&window_changes);
4448 /*
4449 Update icon window configuration.
4450 */
4451 windows->icon.crop_geometry=windows->image.crop_geometry;
4452 XBestIconSize(display,&windows->icon,image);
4453 window_changes.width=(int) windows->icon.width;
4454 window_changes.height=(int) windows->icon.height;
4455 (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4456 (unsigned int) (CWWidth | CWHeight),&window_changes);
4457 XSetCursorState(display,windows,MagickFalse);
4458 return(status != 0 ? MagickTrue : MagickFalse);
4459}
4460
4461/*
4462%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4463% %
4464% %
4465% %
4466+ X C r o p I m a g e %
4467% %
4468% %
4469% %
4470%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4471%
4472% XCropImage() allows the user to select a region of the image and crop, copy,
4473% or cut it. For copy or cut, the image can subsequently be composited onto
4474% the image with XPasteImage.
4475%
4476% The format of the XCropImage method is:
4477%
4478% MagickBooleanType XCropImage(Display *display,
4479% XResourceInfo *resource_info,XWindows *windows,Image *image,
4480% const ClipboardMode mode)
4481%
4482% A description of each parameter follows:
4483%
4484% o display: Specifies a connection to an X server; returned from
4485% XOpenDisplay.
4486%
4487% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4488%
4489% o windows: Specifies a pointer to a XWindows structure.
4490%
4491% o image: the image; returned from ReadImage.
4492%
4493% o mode: This unsigned value specified whether the image should be
4494% cropped, copied, or cut.
4495%
4496*/
4497static MagickBooleanType XCropImage(Display *display,
4498 XResourceInfo *resource_info,XWindows *windows,Image *image,
4499 const ClipboardMode mode)
4500{
4501 static const char
4502 *CropModeMenu[] =
4503 {
4504 "Help",
4505 "Dismiss",
4506 (char *) NULL
4507 },
4508 *RectifyModeMenu[] =
4509 {
4510 "Crop",
4511 "Help",
4512 "Dismiss",
4513 (char *) NULL
4514 };
4515
4516 static const ModeType
4517 CropCommands[] =
4518 {
4519 CropHelpCommand,
4520 CropDismissCommand
4521 },
4522 RectifyCommands[] =
4523 {
4524 RectifyCopyCommand,
4525 RectifyHelpCommand,
4526 RectifyDismissCommand
4527 };
4528
4529 CacheView
4530 *image_view;
4531
4532 char
4533 command[MaxTextExtent],
4534 text[MaxTextExtent];
4535
4536 Cursor
4537 cursor;
4538
4540 *exception;
4541
4542 int
4543 id,
4544 x,
4545 y;
4546
4547 KeySym
4548 key_symbol;
4549
4550 Image
4551 *crop_image;
4552
4553 MagickRealType
4554 scale_factor;
4555
4557 crop_info,
4558 highlight_info;
4559
4561 *q;
4562
4563 unsigned int
4564 height,
4565 width;
4566
4567 size_t
4568 state;
4569
4570 XEvent
4571 event;
4572
4573 /*
4574 Map Command widget.
4575 */
4576 switch (mode)
4577 {
4578 case CopyMode:
4579 {
4580 (void) CloneString(&windows->command.name,"Copy");
4581 break;
4582 }
4583 case CropMode:
4584 {
4585 (void) CloneString(&windows->command.name,"Crop");
4586 break;
4587 }
4588 case CutMode:
4589 {
4590 (void) CloneString(&windows->command.name,"Cut");
4591 break;
4592 }
4593 }
4594 RectifyModeMenu[0]=windows->command.name;
4595 windows->command.data=0;
4596 (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4597 (void) XMapRaised(display,windows->command.id);
4598 XClientMessage(display,windows->image.id,windows->im_protocols,
4599 windows->im_update_widget,CurrentTime);
4600 /*
4601 Track pointer until button 1 is pressed.
4602 */
4603 XQueryPosition(display,windows->image.id,&x,&y);
4604 (void) XSelectInput(display,windows->image.id,
4605 windows->image.attributes.event_mask | PointerMotionMask);
4606 crop_info.x=(ssize_t) windows->image.x+x;
4607 crop_info.y=(ssize_t) windows->image.y+y;
4608 crop_info.width=0;
4609 crop_info.height=0;
4610 cursor=XCreateFontCursor(display,XC_fleur);
4611 state=DefaultState;
4612 do
4613 {
4614 if (windows->info.mapped != MagickFalse)
4615 {
4616 /*
4617 Display pointer position.
4618 */
4619 (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4620 (long) crop_info.x,(long) crop_info.y);
4621 XInfoWidget(display,windows,text);
4622 }
4623 /*
4624 Wait for next event.
4625 */
4626 XScreenEvent(display,windows,&event);
4627 if (event.xany.window == windows->command.id)
4628 {
4629 /*
4630 Select a command from the Command widget.
4631 */
4632 id=XCommandWidget(display,windows,CropModeMenu,&event);
4633 if (id < 0)
4634 continue;
4635 switch (CropCommands[id])
4636 {
4637 case CropHelpCommand:
4638 {
4639 switch (mode)
4640 {
4641 case CopyMode:
4642 {
4643 XTextViewHelp(display,resource_info,windows,MagickFalse,
4644 "Help Viewer - Image Copy",ImageCopyHelp);
4645 break;
4646 }
4647 case CropMode:
4648 {
4649 XTextViewHelp(display,resource_info,windows,MagickFalse,
4650 "Help Viewer - Image Crop",ImageCropHelp);
4651 break;
4652 }
4653 case CutMode:
4654 {
4655 XTextViewHelp(display,resource_info,windows,MagickFalse,
4656 "Help Viewer - Image Cut",ImageCutHelp);
4657 break;
4658 }
4659 }
4660 break;
4661 }
4662 case CropDismissCommand:
4663 {
4664 /*
4665 Prematurely exit.
4666 */
4667 state|=EscapeState;
4668 state|=ExitState;
4669 break;
4670 }
4671 default:
4672 break;
4673 }
4674 continue;
4675 }
4676 switch (event.type)
4677 {
4678 case ButtonPress:
4679 {
4680 if (event.xbutton.button != Button1)
4681 break;
4682 if (event.xbutton.window != windows->image.id)
4683 break;
4684 /*
4685 Note first corner of cropping rectangle-- exit loop.
4686 */
4687 (void) XCheckDefineCursor(display,windows->image.id,cursor);
4688 crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4689 crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4690 state|=ExitState;
4691 break;
4692 }
4693 case ButtonRelease:
4694 break;
4695 case Expose:
4696 break;
4697 case KeyPress:
4698 {
4699 if (event.xkey.window != windows->image.id)
4700 break;
4701 /*
4702 Respond to a user key press.
4703 */
4704 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4705 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4706 switch ((int) key_symbol)
4707 {
4708 case XK_Escape:
4709 case XK_F20:
4710 {
4711 /*
4712 Prematurely exit.
4713 */
4714 state|=EscapeState;
4715 state|=ExitState;
4716 break;
4717 }
4718 case XK_F1:
4719 case XK_Help:
4720 {
4721 switch (mode)
4722 {
4723 case CopyMode:
4724 {
4725 XTextViewHelp(display,resource_info,windows,MagickFalse,
4726 "Help Viewer - Image Copy",ImageCopyHelp);
4727 break;
4728 }
4729 case CropMode:
4730 {
4731 XTextViewHelp(display,resource_info,windows,MagickFalse,
4732 "Help Viewer - Image Crop",ImageCropHelp);
4733 break;
4734 }
4735 case CutMode:
4736 {
4737 XTextViewHelp(display,resource_info,windows,MagickFalse,
4738 "Help Viewer - Image Cut",ImageCutHelp);
4739 break;
4740 }
4741 }
4742 break;
4743 }
4744 default:
4745 {
4746 (void) XBell(display,0);
4747 break;
4748 }
4749 }
4750 break;
4751 }
4752 case MotionNotify:
4753 {
4754 if (event.xmotion.window != windows->image.id)
4755 break;
4756 /*
4757 Map and unmap Info widget as text cursor crosses its boundaries.
4758 */
4759 x=event.xmotion.x;
4760 y=event.xmotion.y;
4761 if (windows->info.mapped != MagickFalse)
4762 {
4763 if ((x < (int) (windows->info.x+windows->info.width)) &&
4764 (y < (int) (windows->info.y+windows->info.height)))
4765 (void) XWithdrawWindow(display,windows->info.id,
4766 windows->info.screen);
4767 }
4768 else
4769 if ((x > (int) (windows->info.x+windows->info.width)) ||
4770 (y > (int) (windows->info.y+windows->info.height)))
4771 (void) XMapWindow(display,windows->info.id);
4772 crop_info.x=(ssize_t) windows->image.x+x;
4773 crop_info.y=(ssize_t) windows->image.y+y;
4774 break;
4775 }
4776 default:
4777 break;
4778 }
4779 } while ((state & ExitState) == 0);
4780 (void) XSelectInput(display,windows->image.id,
4781 windows->image.attributes.event_mask);
4782 if ((state & EscapeState) != 0)
4783 {
4784 /*
4785 User want to exit without cropping.
4786 */
4787 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4788 (void) XFreeCursor(display,cursor);
4789 return(MagickTrue);
4790 }
4791 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4792 do
4793 {
4794 /*
4795 Size rectangle as pointer moves until the mouse button is released.
4796 */
4797 x=(int) crop_info.x;
4798 y=(int) crop_info.y;
4799 crop_info.width=0;
4800 crop_info.height=0;
4801 state=DefaultState;
4802 do
4803 {
4804 highlight_info=crop_info;
4805 highlight_info.x=crop_info.x-windows->image.x;
4806 highlight_info.y=crop_info.y-windows->image.y;
4807 if ((highlight_info.width > 3) && (highlight_info.height > 3))
4808 {
4809 /*
4810 Display info and draw cropping rectangle.
4811 */
4812 if (windows->info.mapped == MagickFalse)
4813 (void) XMapWindow(display,windows->info.id);
4814 (void) FormatLocaleString(text,MaxTextExtent,
4815 " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4816 crop_info.height,(double) crop_info.x,(double) crop_info.y);
4817 XInfoWidget(display,windows,text);
4818 XHighlightRectangle(display,windows->image.id,
4819 windows->image.highlight_context,&highlight_info);
4820 }
4821 else
4822 if (windows->info.mapped != MagickFalse)
4823 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4824 /*
4825 Wait for next event.
4826 */
4827 XScreenEvent(display,windows,&event);
4828 if ((highlight_info.width > 3) && (highlight_info.height > 3))
4829 XHighlightRectangle(display,windows->image.id,
4830 windows->image.highlight_context,&highlight_info);
4831 switch (event.type)
4832 {
4833 case ButtonPress:
4834 {
4835 crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4836 crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4837 break;
4838 }
4839 case ButtonRelease:
4840 {
4841 /*
4842 User has committed to cropping rectangle.
4843 */
4844 crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4845 crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4846 XSetCursorState(display,windows,MagickFalse);
4847 state|=ExitState;
4848 windows->command.data=0;
4849 (void) XCommandWidget(display,windows,RectifyModeMenu,
4850 (XEvent *) NULL);
4851 break;
4852 }
4853 case Expose:
4854 break;
4855 case MotionNotify:
4856 {
4857 crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4858 crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4859 }
4860 default:
4861 break;
4862 }
4863 if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4864 ((state & ExitState) != 0))
4865 {
4866 /*
4867 Check boundary conditions.
4868 */
4869 if (crop_info.x < 0)
4870 crop_info.x=0;
4871 else
4872 if (crop_info.x > (ssize_t) windows->image.ximage->width)
4873 crop_info.x=(ssize_t) windows->image.ximage->width;
4874 if ((int) crop_info.x < x)
4875 crop_info.width=(unsigned int) (x-crop_info.x);
4876 else
4877 {
4878 crop_info.width=(unsigned int) (crop_info.x-x);
4879 crop_info.x=(ssize_t) x;
4880 }
4881 if (crop_info.y < 0)
4882 crop_info.y=0;
4883 else
4884 if (crop_info.y > (ssize_t) windows->image.ximage->height)
4885 crop_info.y=(ssize_t) windows->image.ximage->height;
4886 if ((int) crop_info.y < y)
4887 crop_info.height=(unsigned int) (y-crop_info.y);
4888 else
4889 {
4890 crop_info.height=(unsigned int) (crop_info.y-y);
4891 crop_info.y=(ssize_t) y;
4892 }
4893 }
4894 } while ((state & ExitState) == 0);
4895 /*
4896 Wait for user to grab a corner of the rectangle or press return.
4897 */
4898 state=DefaultState;
4899 (void) XMapWindow(display,windows->info.id);
4900 do
4901 {
4902 if (windows->info.mapped != MagickFalse)
4903 {
4904 /*
4905 Display pointer position.
4906 */
4907 (void) FormatLocaleString(text,MaxTextExtent,
4908 " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4909 crop_info.height,(double) crop_info.x,(double) crop_info.y);
4910 XInfoWidget(display,windows,text);
4911 }
4912 highlight_info=crop_info;
4913 highlight_info.x=crop_info.x-windows->image.x;
4914 highlight_info.y=crop_info.y-windows->image.y;
4915 if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4916 {
4917 state|=EscapeState;
4918 state|=ExitState;
4919 break;
4920 }
4921 XHighlightRectangle(display,windows->image.id,
4922 windows->image.highlight_context,&highlight_info);
4923 XScreenEvent(display,windows,&event);
4924 if (event.xany.window == windows->command.id)
4925 {
4926 /*
4927 Select a command from the Command widget.
4928 */
4929 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4930 id=XCommandWidget(display,windows,RectifyModeMenu,&event);
4931 (void) XSetFunction(display,windows->image.highlight_context,
4932 GXinvert);
4933 XHighlightRectangle(display,windows->image.id,
4934 windows->image.highlight_context,&highlight_info);
4935 if (id >= 0)
4936 switch (RectifyCommands[id])
4937 {
4938 case RectifyCopyCommand:
4939 {
4940 state|=ExitState;
4941 break;
4942 }
4943 case RectifyHelpCommand:
4944 {
4945 (void) XSetFunction(display,windows->image.highlight_context,
4946 GXcopy);
4947 switch (mode)
4948 {
4949 case CopyMode:
4950 {
4951 XTextViewHelp(display,resource_info,windows,MagickFalse,
4952 "Help Viewer - Image Copy",ImageCopyHelp);
4953 break;
4954 }
4955 case CropMode:
4956 {
4957 XTextViewHelp(display,resource_info,windows,MagickFalse,
4958 "Help Viewer - Image Crop",ImageCropHelp);
4959 break;
4960 }
4961 case CutMode:
4962 {
4963 XTextViewHelp(display,resource_info,windows,MagickFalse,
4964 "Help Viewer - Image Cut",ImageCutHelp);
4965 break;
4966 }
4967 }
4968 (void) XSetFunction(display,windows->image.highlight_context,
4969 GXinvert);
4970 break;
4971 }
4972 case RectifyDismissCommand:
4973 {
4974 /*
4975 Prematurely exit.
4976 */
4977 state|=EscapeState;
4978 state|=ExitState;
4979 break;
4980 }
4981 default:
4982 break;
4983 }
4984 continue;
4985 }
4986 XHighlightRectangle(display,windows->image.id,
4987 windows->image.highlight_context,&highlight_info);
4988 switch (event.type)
4989 {
4990 case ButtonPress:
4991 {
4992 if (event.xbutton.button != Button1)
4993 break;
4994 if (event.xbutton.window != windows->image.id)
4995 break;
4996 x=windows->image.x+event.xbutton.x;
4997 y=windows->image.y+event.xbutton.y;
4998 if ((x < (int) (crop_info.x+RoiDelta)) &&
4999 (x > (int) (crop_info.x-RoiDelta)) &&
5000 (y < (int) (crop_info.y+RoiDelta)) &&
5001 (y > (int) (crop_info.y-RoiDelta)))
5002 {
5003 crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5004 crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5005 state|=UpdateConfigurationState;
5006 break;
5007 }
5008 if ((x < (int) (crop_info.x+RoiDelta)) &&
5009 (x > (int) (crop_info.x-RoiDelta)) &&
5010 (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5011 (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5012 {
5013 crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5014 state|=UpdateConfigurationState;
5015 break;
5016 }
5017 if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5018 (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5019 (y < (int) (crop_info.y+RoiDelta)) &&
5020 (y > (int) (crop_info.y-RoiDelta)))
5021 {
5022 crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5023 state|=UpdateConfigurationState;
5024 break;
5025 }
5026 if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5027 (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5028 (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5029 (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5030 {
5031 state|=UpdateConfigurationState;
5032 break;
5033 }
5034 magick_fallthrough;
5035 }
5036 case ButtonRelease:
5037 {
5038 if (event.xbutton.window == windows->pan.id)
5039 if ((highlight_info.x != crop_info.x-windows->image.x) ||
5040 (highlight_info.y != crop_info.y-windows->image.y))
5041 XHighlightRectangle(display,windows->image.id,
5042 windows->image.highlight_context,&highlight_info);
5043 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5044 event.xbutton.time);
5045 break;
5046 }
5047 case Expose:
5048 {
5049 if (event.xexpose.window == windows->image.id)
5050 if (event.xexpose.count == 0)
5051 {
5052 event.xexpose.x=(int) highlight_info.x;
5053 event.xexpose.y=(int) highlight_info.y;
5054 event.xexpose.width=(int) highlight_info.width;
5055 event.xexpose.height=(int) highlight_info.height;
5056 XRefreshWindow(display,&windows->image,&event);
5057 }
5058 if (event.xexpose.window == windows->info.id)
5059 if (event.xexpose.count == 0)
5060 XInfoWidget(display,windows,text);
5061 break;
5062 }
5063 case KeyPress:
5064 {
5065 if (event.xkey.window != windows->image.id)
5066 break;
5067 /*
5068 Respond to a user key press.
5069 */
5070 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5071 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5072 switch ((int) key_symbol)
5073 {
5074 case XK_Escape:
5075 case XK_F20:
5076 {
5077 state|=EscapeState;
5078 magick_fallthrough;
5079 }
5080 case XK_Return:
5081 {
5082 state|=ExitState;
5083 break;
5084 }
5085 case XK_Home:
5086 case XK_KP_Home:
5087 {
5088 crop_info.x=(ssize_t) (windows->image.width/2L-crop_info.width/
5089 2L);
5090 crop_info.y=(ssize_t) (windows->image.height/2L-crop_info.height/
5091 2L);
5092 break;
5093 }
5094 case XK_Left:
5095 case XK_KP_Left:
5096 {
5097 crop_info.x--;
5098 break;
5099 }
5100 case XK_Up:
5101 case XK_KP_Up:
5102 case XK_Next:
5103 {
5104 crop_info.y--;
5105 break;
5106 }
5107 case XK_Right:
5108 case XK_KP_Right:
5109 {
5110 crop_info.x++;
5111 break;
5112 }
5113 case XK_Prior:
5114 case XK_Down:
5115 case XK_KP_Down:
5116 {
5117 crop_info.y++;
5118 break;
5119 }
5120 case XK_F1:
5121 case XK_Help:
5122 {
5123 (void) XSetFunction(display,windows->image.highlight_context,
5124 GXcopy);
5125 switch (mode)
5126 {
5127 case CopyMode:
5128 {
5129 XTextViewHelp(display,resource_info,windows,MagickFalse,
5130 "Help Viewer - Image Copy",ImageCopyHelp);
5131 break;
5132 }
5133 case CropMode:
5134 {
5135 XTextViewHelp(display,resource_info,windows,MagickFalse,
5136 "Help Viewer - Image Cropg",ImageCropHelp);
5137 break;
5138 }
5139 case CutMode:
5140 {
5141 XTextViewHelp(display,resource_info,windows,MagickFalse,
5142 "Help Viewer - Image Cutg",ImageCutHelp);
5143 break;
5144 }
5145 }
5146 (void) XSetFunction(display,windows->image.highlight_context,
5147 GXinvert);
5148 break;
5149 }
5150 default:
5151 {
5152 (void) XBell(display,0);
5153 break;
5154 }
5155 }
5156 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5157 event.xkey.time);
5158 break;
5159 }
5160 case KeyRelease:
5161 break;
5162 case MotionNotify:
5163 {
5164 if (event.xmotion.window != windows->image.id)
5165 break;
5166 /*
5167 Map and unmap Info widget as text cursor crosses its boundaries.
5168 */
5169 x=event.xmotion.x;
5170 y=event.xmotion.y;
5171 if (windows->info.mapped != MagickFalse)
5172 {
5173 if ((x < (int) (windows->info.x+windows->info.width)) &&
5174 (y < (int) (windows->info.y+windows->info.height)))
5175 (void) XWithdrawWindow(display,windows->info.id,
5176 windows->info.screen);
5177 }
5178 else
5179 if ((x > (int) (windows->info.x+windows->info.width)) ||
5180 (y > (int) (windows->info.y+windows->info.height)))
5181 (void) XMapWindow(display,windows->info.id);
5182 crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5183 crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5184 break;
5185 }
5186 case SelectionRequest:
5187 {
5188 XSelectionEvent
5189 notify;
5190
5191 XSelectionRequestEvent
5192 *request;
5193
5194 /*
5195 Set primary selection.
5196 */
5197 (void) FormatLocaleString(text,MaxTextExtent,
5198 "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5199 crop_info.height,(double) crop_info.x,(double) crop_info.y);
5200 request=(&(event.xselectionrequest));
5201 (void) XChangeProperty(request->display,request->requestor,
5202 request->property,request->target,8,PropModeReplace,
5203 (unsigned char *) text,(int) strlen(text));
5204 notify.type=SelectionNotify;
5205 notify.display=request->display;
5206 notify.requestor=request->requestor;
5207 notify.selection=request->selection;
5208 notify.target=request->target;
5209 notify.time=request->time;
5210 if (request->property == None)
5211 notify.property=request->target;
5212 else
5213 notify.property=request->property;
5214 (void) XSendEvent(request->display,request->requestor,False,0,
5215 (XEvent *) &notify);
5216 }
5217 default:
5218 break;
5219 }
5220 if ((state & UpdateConfigurationState) != 0)
5221 {
5222 (void) XPutBackEvent(display,&event);
5223 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5224 break;
5225 }
5226 } while ((state & ExitState) == 0);
5227 } while ((state & ExitState) == 0);
5228 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5229 XSetCursorState(display,windows,MagickFalse);
5230 if ((state & EscapeState) != 0)
5231 return(MagickTrue);
5232 if (mode == CropMode)
5233 if (((int) crop_info.width != windows->image.ximage->width) ||
5234 ((int) crop_info.height != windows->image.ximage->height))
5235 {
5236 /*
5237 Reconfigure Image window as defined by cropping rectangle.
5238 */
5239 XSetCropGeometry(display,windows,&crop_info,image);
5240 windows->image.window_changes.width=(int) crop_info.width;
5241 windows->image.window_changes.height=(int) crop_info.height;
5242 (void) XConfigureImage(display,resource_info,windows,image);
5243 return(MagickTrue);
5244 }
5245 /*
5246 Copy image before applying image transforms.
5247 */
5248 XSetCursorState(display,windows,MagickTrue);
5249 XCheckRefreshWindows(display,windows);
5250 width=(unsigned int) image->columns;
5251 height=(unsigned int) image->rows;
5252 x=0;
5253 y=0;
5254 if (windows->image.crop_geometry != (char *) NULL)
5255 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5256 scale_factor=(MagickRealType) width/windows->image.ximage->width;
5257 crop_info.x+=x;
5258 crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5259 crop_info.x+=image->page.x;
5260 crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5261 scale_factor=(MagickRealType) height/windows->image.ximage->height;
5262 crop_info.y+=y;
5263 crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5264 crop_info.y+=image->page.y;
5265 crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5266 crop_image=CropImage(image,&crop_info,&image->exception);
5267 XSetCursorState(display,windows,MagickFalse);
5268 if (crop_image == (Image *) NULL)
5269 return(MagickFalse);
5270 if (resource_info->copy_image != (Image *) NULL)
5271 resource_info->copy_image=DestroyImage(resource_info->copy_image);
5272 resource_info->copy_image=crop_image;
5273 if (mode == CopyMode)
5274 {
5275 (void) XConfigureImage(display,resource_info,windows,image);
5276 return(MagickTrue);
5277 }
5278 /*
5279 Cut image.
5280 */
5281 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
5282 return(MagickFalse);
5283 image->matte=MagickTrue;
5284 exception=(&image->exception);
5285 image_view=AcquireAuthenticCacheView(image,exception);
5286 for (y=0; y < (int) crop_info.height; y++)
5287 {
5288 q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5289 crop_info.width,1,exception);
5290 if (q == (PixelPacket *) NULL)
5291 break;
5292 for (x=0; x < (int) crop_info.width; x++)
5293 {
5294 q->opacity=(Quantum) TransparentOpacity;
5295 q++;
5296 }
5297 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5298 break;
5299 }
5300 image_view=DestroyCacheView(image_view);
5301 /*
5302 Update image configuration.
5303 */
5304 XConfigureImageColormap(display,resource_info,windows,image);
5305 (void) XConfigureImage(display,resource_info,windows,image);
5306 return(MagickTrue);
5307}
5308
5309/*
5310%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5311% %
5312% %
5313% %
5314+ X D r a w I m a g e %
5315% %
5316% %
5317% %
5318%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5319%
5320% XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5321% the image.
5322%
5323% The format of the XDrawEditImage method is:
5324%
5325% MagickBooleanType XDrawEditImage(Display *display,
5326% XResourceInfo *resource_info,XWindows *windows,Image **image)
5327%
5328% A description of each parameter follows:
5329%
5330% o display: Specifies a connection to an X server; returned from
5331% XOpenDisplay.
5332%
5333% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5334%
5335% o windows: Specifies a pointer to a XWindows structure.
5336%
5337% o image: the image.
5338%
5339*/
5340static MagickBooleanType XDrawEditImage(Display *display,
5341 XResourceInfo *resource_info,XWindows *windows,Image **image)
5342{
5343 const char
5344 *const DrawMenu[] =
5345 {
5346 "Element",
5347 "Color",
5348 "Stipple",
5349 "Width",
5350 "Undo",
5351 "Help",
5352 "Dismiss",
5353 (char *) NULL
5354 };
5355
5356 static ElementType
5357 element = PointElement;
5358
5359 static const ModeType
5360 DrawCommands[] =
5361 {
5362 DrawElementCommand,
5363 DrawColorCommand,
5364 DrawStippleCommand,
5365 DrawWidthCommand,
5366 DrawUndoCommand,
5367 DrawHelpCommand,
5368 DrawDismissCommand
5369 };
5370
5371 static Pixmap
5372 stipple = (Pixmap) NULL;
5373
5374 static unsigned int
5375 pen_id = 0,
5376 line_width = 1;
5377
5378 char
5379 command[MaxTextExtent],
5380 text[MaxTextExtent];
5381
5382 Cursor
5383 cursor;
5384
5385 int
5386 entry,
5387 id,
5388 number_coordinates,
5389 x,
5390 y;
5391
5392 MagickRealType
5393 degrees;
5394
5395 MagickStatusType
5396 status;
5397
5399 rectangle_info;
5400
5401 int
5402 i;
5403
5404 unsigned int
5405 distance,
5406 height,
5407 max_coordinates,
5408 width;
5409
5410 size_t
5411 state;
5412
5413 Window
5414 root_window;
5415
5416 XDrawInfo
5417 draw_info;
5418
5419 XEvent
5420 event;
5421
5422 XPoint
5423 *coordinate_info;
5424
5425 XSegment
5426 line_info;
5427
5428 /*
5429 Allocate polygon info.
5430 */
5431 max_coordinates=2048;
5432 coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5433 sizeof(*coordinate_info));
5434 if (coordinate_info == (XPoint *) NULL)
5435 {
5436 (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
5437 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5438 return(MagickFalse);
5439 }
5440 /*
5441 Map Command widget.
5442 */
5443 (void) CloneString(&windows->command.name,"Draw");
5444 windows->command.data=4;
5445 (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5446 (void) XMapRaised(display,windows->command.id);
5447 XClientMessage(display,windows->image.id,windows->im_protocols,
5448 windows->im_update_widget,CurrentTime);
5449 /*
5450 Wait for first button press.
5451 */
5452 root_window=XRootWindow(display,XDefaultScreen(display));
5453 draw_info.stencil=OpaqueStencil;
5454 status=MagickTrue;
5455 cursor=XCreateFontCursor(display,XC_tcross);
5456 for ( ; ; )
5457 {
5458 XQueryPosition(display,windows->image.id,&x,&y);
5459 (void) XSelectInput(display,windows->image.id,
5460 windows->image.attributes.event_mask | PointerMotionMask);
5461 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5462 state=DefaultState;
5463 do
5464 {
5465 if (windows->info.mapped != MagickFalse)
5466 {
5467 /*
5468 Display pointer position.
5469 */
5470 (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5471 x+windows->image.x,y+windows->image.y);
5472 XInfoWidget(display,windows,text);
5473 }
5474 /*
5475 Wait for next event.
5476 */
5477 XScreenEvent(display,windows,&event);
5478 if (event.xany.window == windows->command.id)
5479 {
5480 /*
5481 Select a command from the Command widget.
5482 */
5483 id=XCommandWidget(display,windows,DrawMenu,&event);
5484 if (id < 0)
5485 continue;
5486 switch (DrawCommands[id])
5487 {
5488 case DrawElementCommand:
5489 {
5490 const char
5491 *const Elements[] =
5492 {
5493 "point",
5494 "line",
5495 "rectangle",
5496 "fill rectangle",
5497 "circle",
5498 "fill circle",
5499 "ellipse",
5500 "fill ellipse",
5501 "polygon",
5502 "fill polygon",
5503 (char *) NULL,
5504 };
5505
5506 /*
5507 Select a command from the pop-up menu.
5508 */
5509 element=(ElementType) (XMenuWidget(display,windows,
5510 DrawMenu[id],Elements,command)+1);
5511 break;
5512 }
5513 case DrawColorCommand:
5514 {
5515 const char
5516 *ColorMenu[MaxNumberPens+1];
5517
5518 int
5519 pen_number;
5520
5521 MagickBooleanType
5522 transparent;
5523
5524 XColor
5525 color;
5526
5527 /*
5528 Initialize menu selections.
5529 */
5530 for (i=0; i < (int) (MaxNumberPens-2); i++)
5531 ColorMenu[i]=resource_info->pen_colors[i];
5532 ColorMenu[MaxNumberPens-2]="transparent";
5533 ColorMenu[MaxNumberPens-1]="Browser...";
5534 ColorMenu[MaxNumberPens]=(char *) NULL;
5535 /*
5536 Select a pen color from the pop-up menu.
5537 */
5538 pen_number=XMenuWidget(display,windows,DrawMenu[id],
5539 (const char **) ColorMenu,command);
5540 if (pen_number < 0)
5541 break;
5542 transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5543 MagickFalse;
5544 if (transparent != MagickFalse)
5545 {
5546 draw_info.stencil=TransparentStencil;
5547 break;
5548 }
5549 if (pen_number == (MaxNumberPens-1))
5550 {
5551 static char
5552 color_name[MaxTextExtent] = "gray";
5553
5554 /*
5555 Select a pen color from a dialog.
5556 */
5557 resource_info->pen_colors[pen_number]=color_name;
5558 XColorBrowserWidget(display,windows,"Select",color_name);
5559 if (*color_name == '\0')
5560 break;
5561 }
5562 /*
5563 Set pen color.
5564 */
5565 (void) XParseColor(display,windows->map_info->colormap,
5566 resource_info->pen_colors[pen_number],&color);
5567 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5568 (unsigned int) MaxColors,&color);
5569 windows->pixel_info->pen_colors[pen_number]=color;
5570 pen_id=(unsigned int) pen_number;
5571 draw_info.stencil=OpaqueStencil;
5572 break;
5573 }
5574 case DrawStippleCommand:
5575 {
5576 const char
5577 *StipplesMenu[] =
5578 {
5579 "Brick",
5580 "Diagonal",
5581 "Scales",
5582 "Vertical",
5583 "Wavy",
5584 "Translucent",
5585 "Opaque",
5586 (char *) NULL,
5587 (char *) NULL,
5588 };
5589
5590 Image
5591 *stipple_image;
5592
5593 ImageInfo
5594 *image_info;
5595
5596 int
5597 status;
5598
5599 static char
5600 filename[MaxTextExtent] = "\0";
5601
5602 /*
5603 Select a command from the pop-up menu.
5604 */
5605 StipplesMenu[7]="Open...";
5606 entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5607 command);
5608 if (entry < 0)
5609 break;
5610 if (stipple != (Pixmap) NULL)
5611 (void) XFreePixmap(display,stipple);
5612 stipple=(Pixmap) NULL;
5613 if (entry != 7)
5614 {
5615 switch (entry)
5616 {
5617 case 0:
5618 {
5619 stipple=XCreateBitmapFromData(display,root_window,
5620 (char *) BricksBitmap,BricksWidth,BricksHeight);
5621 break;
5622 }
5623 case 1:
5624 {
5625 stipple=XCreateBitmapFromData(display,root_window,
5626 (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5627 break;
5628 }
5629 case 2:
5630 {
5631 stipple=XCreateBitmapFromData(display,root_window,
5632 (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5633 break;
5634 }
5635 case 3:
5636 {
5637 stipple=XCreateBitmapFromData(display,root_window,
5638 (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5639 break;
5640 }
5641 case 4:
5642 {
5643 stipple=XCreateBitmapFromData(display,root_window,
5644 (char *) WavyBitmap,WavyWidth,WavyHeight);
5645 break;
5646 }
5647 case 5:
5648 {
5649 stipple=XCreateBitmapFromData(display,root_window,
5650 (char *) HighlightBitmap,HighlightWidth,
5651 HighlightHeight);
5652 break;
5653 }
5654 case 6:
5655 default:
5656 {
5657 stipple=XCreateBitmapFromData(display,root_window,
5658 (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5659 break;
5660 }
5661 }
5662 break;
5663 }
5664 XFileBrowserWidget(display,windows,"Stipple",filename);
5665 if (*filename == '\0')
5666 break;
5667 /*
5668 Read image.
5669 */
5670 XSetCursorState(display,windows,MagickTrue);
5671 XCheckRefreshWindows(display,windows);
5672 image_info=AcquireImageInfo();
5673 (void) CopyMagickString(image_info->filename,filename,
5674 MaxTextExtent);
5675 stipple_image=ReadImage(image_info,&(*image)->exception);
5676 CatchException(&(*image)->exception);
5677 XSetCursorState(display,windows,MagickFalse);
5678 if (stipple_image == (Image *) NULL)
5679 break;
5680 (void) AcquireUniqueFileResource(filename);
5681 (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5682 "xbm:%s",filename);
5683 (void) WriteImage(image_info,stipple_image);
5684 stipple_image=DestroyImage(stipple_image);
5685 image_info=DestroyImageInfo(image_info);
5686 status=XReadBitmapFile(display,root_window,filename,&width,
5687 &height,&stipple,&x,&y);
5688 (void) RelinquishUniqueFileResource(filename);
5689 if ((status != BitmapSuccess) != 0)
5690 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5691 filename);
5692 break;
5693 }
5694 case DrawWidthCommand:
5695 {
5696 const char
5697 *const WidthsMenu[] =
5698 {
5699 "1",
5700 "2",
5701 "4",
5702 "8",
5703 "16",
5704 "Dialog...",
5705 (char *) NULL,
5706 };
5707
5708 static char
5709 width[MaxTextExtent] = "0";
5710
5711 /*
5712 Select a command from the pop-up menu.
5713 */
5714 entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5715 command);
5716 if (entry < 0)
5717 break;
5718 if (entry != 5)
5719 {
5720 line_width=(unsigned int) StringToUnsignedLong(
5721 WidthsMenu[entry]);
5722 break;
5723 }
5724 (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5725 width);
5726 if (*width == '\0')
5727 break;
5728 line_width=(unsigned int) StringToUnsignedLong(width);
5729 break;
5730 }
5731 case DrawUndoCommand:
5732 {
5733 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5734 image);
5735 break;
5736 }
5737 case DrawHelpCommand:
5738 {
5739 XTextViewHelp(display,resource_info,windows,MagickFalse,
5740 "Help Viewer - Image Rotation",ImageDrawHelp);
5741 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5742 break;
5743 }
5744 case DrawDismissCommand:
5745 {
5746 /*
5747 Prematurely exit.
5748 */
5749 state|=EscapeState;
5750 state|=ExitState;
5751 break;
5752 }
5753 default:
5754 break;
5755 }
5756 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5757 continue;
5758 }
5759 switch (event.type)
5760 {
5761 case ButtonPress:
5762 {
5763 if (event.xbutton.button != Button1)
5764 break;
5765 if (event.xbutton.window != windows->image.id)
5766 break;
5767 /*
5768 exit loop.
5769 */
5770 x=event.xbutton.x;
5771 y=event.xbutton.y;
5772 state|=ExitState;
5773 break;
5774 }
5775 case ButtonRelease:
5776 break;
5777 case Expose:
5778 break;
5779 case KeyPress:
5780 {
5781 KeySym
5782 key_symbol;
5783
5784 if (event.xkey.window != windows->image.id)
5785 break;
5786 /*
5787 Respond to a user key press.
5788 */
5789 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5790 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5791 switch ((int) key_symbol)
5792 {
5793 case XK_Escape:
5794 case XK_F20:
5795 {
5796 /*
5797 Prematurely exit.
5798 */
5799 state|=EscapeState;
5800 state|=ExitState;
5801 break;
5802 }
5803 case XK_F1:
5804 case XK_Help:
5805 {
5806 XTextViewHelp(display,resource_info,windows,MagickFalse,
5807 "Help Viewer - Image Rotation",ImageDrawHelp);
5808 break;
5809 }
5810 default:
5811 {
5812 (void) XBell(display,0);
5813 break;
5814 }
5815 }
5816 break;
5817 }
5818 case MotionNotify:
5819 {
5820 /*
5821 Map and unmap Info widget as text cursor crosses its boundaries.
5822 */
5823 x=event.xmotion.x;
5824 y=event.xmotion.y;
5825 if (windows->info.mapped != MagickFalse)
5826 {
5827 if ((x < (int) (windows->info.x+windows->info.width)) &&
5828 (y < (int) (windows->info.y+windows->info.height)))
5829 (void) XWithdrawWindow(display,windows->info.id,
5830 windows->info.screen);
5831 }
5832 else
5833 if ((x > (int) (windows->info.x+windows->info.width)) ||
5834 (y > (int) (windows->info.y+windows->info.height)))
5835 (void) XMapWindow(display,windows->info.id);
5836 break;
5837 }
5838 }
5839 } while ((state & ExitState) == 0);
5840 (void) XSelectInput(display,windows->image.id,
5841 windows->image.attributes.event_mask);
5842 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5843 if ((state & EscapeState) != 0)
5844 break;
5845 /*
5846 Draw element as pointer moves until the button is released.
5847 */
5848 distance=0;
5849 degrees=0.0;
5850 line_info.x1=x;
5851 line_info.y1=y;
5852 line_info.x2=x;
5853 line_info.y2=y;
5854 rectangle_info.x=(ssize_t) x;
5855 rectangle_info.y=(ssize_t) y;
5856 rectangle_info.width=0;
5857 rectangle_info.height=0;
5858 number_coordinates=1;
5859 coordinate_info->x=x;
5860 coordinate_info->y=y;
5861 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5862 state=DefaultState;
5863 do
5864 {
5865 switch (element)
5866 {
5867 case PointElement:
5868 default:
5869 {
5870 if (number_coordinates > 1)
5871 {
5872 (void) XDrawLines(display,windows->image.id,
5873 windows->image.highlight_context,coordinate_info,
5874 number_coordinates,CoordModeOrigin);
5875 (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5876 coordinate_info[number_coordinates-1].x,
5877 coordinate_info[number_coordinates-1].y);
5878 XInfoWidget(display,windows,text);
5879 }
5880 break;
5881 }
5882 case LineElement:
5883 {
5884 if (distance > 9)
5885 {
5886 /*
5887 Display angle of the line.
5888 */
5889 degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5890 line_info.y1),(double) (line_info.x2-line_info.x1)));
5891 (void) FormatLocaleString(text,MaxTextExtent," %g",
5892 (double) degrees);
5893 XInfoWidget(display,windows,text);
5894 XHighlightLine(display,windows->image.id,
5895 windows->image.highlight_context,&line_info);
5896 }
5897 else
5898 if (windows->info.mapped != MagickFalse)
5899 (void) XWithdrawWindow(display,windows->info.id,
5900 windows->info.screen);
5901 break;
5902 }
5903 case RectangleElement:
5904 case FillRectangleElement:
5905 {
5906 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5907 {
5908 /*
5909 Display info and draw drawing rectangle.
5910 */
5911 (void) FormatLocaleString(text,MaxTextExtent,
5912 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5913 (double) rectangle_info.height,(double) rectangle_info.x,
5914 (double) rectangle_info.y);
5915 XInfoWidget(display,windows,text);
5916 XHighlightRectangle(display,windows->image.id,
5917 windows->image.highlight_context,&rectangle_info);
5918 }
5919 else
5920 if (windows->info.mapped != MagickFalse)
5921 (void) XWithdrawWindow(display,windows->info.id,
5922 windows->info.screen);
5923 break;
5924 }
5925 case CircleElement:
5926 case FillCircleElement:
5927 case EllipseElement:
5928 case FillEllipseElement:
5929 {
5930 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5931 {
5932 /*
5933 Display info and draw drawing rectangle.
5934 */
5935 (void) FormatLocaleString(text,MaxTextExtent,
5936 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5937 (double) rectangle_info.height,(double) rectangle_info.x,
5938 (double) rectangle_info.y);
5939 XInfoWidget(display,windows,text);
5940 XHighlightEllipse(display,windows->image.id,
5941 windows->image.highlight_context,&rectangle_info);
5942 }
5943 else
5944 if (windows->info.mapped != MagickFalse)
5945 (void) XWithdrawWindow(display,windows->info.id,
5946 windows->info.screen);
5947 break;
5948 }
5949 case PolygonElement:
5950 case FillPolygonElement:
5951 {
5952 if (number_coordinates > 1)
5953 (void) XDrawLines(display,windows->image.id,
5954 windows->image.highlight_context,coordinate_info,
5955 number_coordinates,CoordModeOrigin);
5956 if (distance > 9)
5957 {
5958 /*
5959 Display angle of the line.
5960 */
5961 degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5962 line_info.y1),(double) (line_info.x2-line_info.x1)));
5963 (void) FormatLocaleString(text,MaxTextExtent," %g",
5964 (double) degrees);
5965 XInfoWidget(display,windows,text);
5966 XHighlightLine(display,windows->image.id,
5967 windows->image.highlight_context,&line_info);
5968 }
5969 else
5970 if (windows->info.mapped != MagickFalse)
5971 (void) XWithdrawWindow(display,windows->info.id,
5972 windows->info.screen);
5973 break;
5974 }
5975 }
5976 /*
5977 Wait for next event.
5978 */
5979 XScreenEvent(display,windows,&event);
5980 switch (element)
5981 {
5982 case PointElement:
5983 default:
5984 {
5985 if (number_coordinates > 1)
5986 (void) XDrawLines(display,windows->image.id,
5987 windows->image.highlight_context,coordinate_info,
5988 number_coordinates,CoordModeOrigin);
5989 break;
5990 }
5991 case LineElement:
5992 {
5993 if (distance > 9)
5994 XHighlightLine(display,windows->image.id,
5995 windows->image.highlight_context,&line_info);
5996 break;
5997 }
5998 case RectangleElement:
5999 case FillRectangleElement:
6000 {
6001 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6002 XHighlightRectangle(display,windows->image.id,
6003 windows->image.highlight_context,&rectangle_info);
6004 break;
6005 }
6006 case CircleElement:
6007 case FillCircleElement:
6008 case EllipseElement:
6009 case FillEllipseElement:
6010 {
6011 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6012 XHighlightEllipse(display,windows->image.id,
6013 windows->image.highlight_context,&rectangle_info);
6014 break;
6015 }
6016 case PolygonElement:
6017 case FillPolygonElement:
6018 {
6019 if (number_coordinates > 1)
6020 (void) XDrawLines(display,windows->image.id,
6021 windows->image.highlight_context,coordinate_info,
6022 number_coordinates,CoordModeOrigin);
6023 if (distance > 9)
6024 XHighlightLine(display,windows->image.id,
6025 windows->image.highlight_context,&line_info);
6026 break;
6027 }
6028 }
6029 switch (event.type)
6030 {
6031 case ButtonPress:
6032 break;
6033 case ButtonRelease:
6034 {
6035 /*
6036 User has committed to element.
6037 */
6038 line_info.x2=event.xbutton.x;
6039 line_info.y2=event.xbutton.y;
6040 rectangle_info.x=(ssize_t) event.xbutton.x;
6041 rectangle_info.y=(ssize_t) event.xbutton.y;
6042 coordinate_info[number_coordinates].x=event.xbutton.x;
6043 coordinate_info[number_coordinates].y=event.xbutton.y;
6044 if (((element != PolygonElement) &&
6045 (element != FillPolygonElement)) || (distance <= 9))
6046 {
6047 state|=ExitState;
6048 break;
6049 }
6050 number_coordinates++;
6051 if (number_coordinates < (int) max_coordinates)
6052 {
6053 line_info.x1=event.xbutton.x;
6054 line_info.y1=event.xbutton.y;
6055 break;
6056 }
6057 max_coordinates<<=1;
6058 coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6059 max_coordinates,sizeof(*coordinate_info));
6060 if (coordinate_info == (XPoint *) NULL)
6061 (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6062 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6063 break;
6064 }
6065 case Expose:
6066 break;
6067 case MotionNotify:
6068 {
6069 if (event.xmotion.window != windows->image.id)
6070 break;
6071 if (element != PointElement)
6072 {
6073 line_info.x2=event.xmotion.x;
6074 line_info.y2=event.xmotion.y;
6075 rectangle_info.x=(ssize_t) event.xmotion.x;
6076 rectangle_info.y=(ssize_t) event.xmotion.y;
6077 break;
6078 }
6079 coordinate_info[number_coordinates].x=event.xbutton.x;
6080 coordinate_info[number_coordinates].y=event.xbutton.y;
6081 number_coordinates++;
6082 if (number_coordinates < (int) max_coordinates)
6083 break;
6084 max_coordinates<<=1;
6085 coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6086 max_coordinates,sizeof(*coordinate_info));
6087 if (coordinate_info == (XPoint *) NULL)
6088 (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6089 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6090 break;
6091 }
6092 default:
6093 break;
6094 }
6095 /*
6096 Check boundary conditions.
6097 */
6098 if (line_info.x2 < 0)
6099 line_info.x2=0;
6100 else
6101 if (line_info.x2 > (int) windows->image.width)
6102 line_info.x2=(short) windows->image.width;
6103 if (line_info.y2 < 0)
6104 line_info.y2=0;
6105 else
6106 if (line_info.y2 > (int) windows->image.height)
6107 line_info.y2=(short) windows->image.height;
6108 distance=(unsigned int)
6109 (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6110 ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6111 if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6112 ((state & ExitState) != 0))
6113 {
6114 if (rectangle_info.x < 0)
6115 rectangle_info.x=0;
6116 else
6117 if (rectangle_info.x > (ssize_t) windows->image.width)
6118 rectangle_info.x=(ssize_t) windows->image.width;
6119 if ((int) rectangle_info.x < x)
6120 rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6121 else
6122 {
6123 rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6124 rectangle_info.x=(ssize_t) x;
6125 }
6126 if (rectangle_info.y < 0)
6127 rectangle_info.y=0;
6128 else
6129 if (rectangle_info.y > (ssize_t) windows->image.height)
6130 rectangle_info.y=(ssize_t) windows->image.height;
6131 if ((int) rectangle_info.y < y)
6132 rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6133 else
6134 {
6135 rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6136 rectangle_info.y=(ssize_t) y;
6137 }
6138 }
6139 } while ((state & ExitState) == 0);
6140 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6141 if ((element == PointElement) || (element == PolygonElement) ||
6142 (element == FillPolygonElement))
6143 {
6144 /*
6145 Determine polygon bounding box.
6146 */
6147 rectangle_info.x=(ssize_t) coordinate_info->x;
6148 rectangle_info.y=(ssize_t) coordinate_info->y;
6149 x=coordinate_info->x;
6150 y=coordinate_info->y;
6151 for (i=1; i < number_coordinates; i++)
6152 {
6153 if (coordinate_info[i].x > x)
6154 x=coordinate_info[i].x;
6155 if (coordinate_info[i].y > y)
6156 y=coordinate_info[i].y;
6157 if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6158 rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6159 if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6160 rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6161 }
6162 rectangle_info.width=(size_t) (x-rectangle_info.x);
6163 rectangle_info.height=(size_t) (y-rectangle_info.y);
6164 for (i=0; i < number_coordinates; i++)
6165 {
6166 coordinate_info[i].x-=rectangle_info.x;
6167 coordinate_info[i].y-=rectangle_info.y;
6168 }
6169 }
6170 else
6171 if (distance <= 9)
6172 continue;
6173 else
6174 if ((element == RectangleElement) ||
6175 (element == CircleElement) || (element == EllipseElement))
6176 {
6177 rectangle_info.width--;
6178 rectangle_info.height--;
6179 }
6180 /*
6181 Drawing is relative to image configuration.
6182 */
6183 draw_info.x=(int) rectangle_info.x;
6184 draw_info.y=(int) rectangle_info.y;
6185 (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6186 image);
6187 width=(unsigned int) (*image)->columns;
6188 height=(unsigned int) (*image)->rows;
6189 x=0;
6190 y=0;
6191 if (windows->image.crop_geometry != (char *) NULL)
6192 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6193 draw_info.x+=windows->image.x-(line_width/2);
6194 if (draw_info.x < 0)
6195 draw_info.x=0;
6196 draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6197 draw_info.y+=windows->image.y-(line_width/2);
6198 if (draw_info.y < 0)
6199 draw_info.y=0;
6200 draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6201 draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6202 if (draw_info.width > (unsigned int) (*image)->columns)
6203 draw_info.width=(unsigned int) (*image)->columns;
6204 draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6205 if (draw_info.height > (unsigned int) (*image)->rows)
6206 draw_info.height=(unsigned int) (*image)->rows;
6207 (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6208 width*draw_info.width/windows->image.ximage->width,
6209 height*draw_info.height/windows->image.ximage->height,
6210 draw_info.x+x,draw_info.y+y);
6211 /*
6212 Initialize drawing attributes.
6213 */
6214 draw_info.degrees=0.0;
6215 draw_info.element=element;
6216 draw_info.stipple=stipple;
6217 draw_info.line_width=line_width;
6218 draw_info.line_info=line_info;
6219 if (line_info.x1 > (int) (line_width/2))
6220 draw_info.line_info.x1=(short) line_width/2;
6221 if (line_info.y1 > (int) (line_width/2))
6222 draw_info.line_info.y1=(short) line_width/2;
6223 draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6224 draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6225 if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6226 {
6227 draw_info.line_info.x2=(-draw_info.line_info.x2);
6228 draw_info.line_info.y2=(-draw_info.line_info.y2);
6229 }
6230 if (draw_info.line_info.x2 < 0)
6231 {
6232 draw_info.line_info.x2=(-draw_info.line_info.x2);
6233 Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6234 }
6235 if (draw_info.line_info.y2 < 0)
6236 {
6237 draw_info.line_info.y2=(-draw_info.line_info.y2);
6238 Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6239 }
6240 draw_info.rectangle_info=rectangle_info;
6241 if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6242 draw_info.rectangle_info.x=(ssize_t) line_width/2;
6243 if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6244 draw_info.rectangle_info.y=(ssize_t) line_width/2;
6245 draw_info.number_coordinates=(unsigned int) number_coordinates;
6246 draw_info.coordinate_info=coordinate_info;
6247 windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6248 /*
6249 Draw element on image.
6250 */
6251 XSetCursorState(display,windows,MagickTrue);
6252 XCheckRefreshWindows(display,windows);
6253 status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6254 XSetCursorState(display,windows,MagickFalse);
6255 /*
6256 Update image colormap and return to image drawing.
6257 */
6258 XConfigureImageColormap(display,resource_info,windows,*image);
6259 (void) XConfigureImage(display,resource_info,windows,*image);
6260 }
6261 XSetCursorState(display,windows,MagickFalse);
6262 coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6263 return(status != 0 ? MagickTrue : MagickFalse);
6264}
6265
6266/*
6267%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6268% %
6269% %
6270% %
6271+ X D r a w P a n R e c t a n g l e %
6272% %
6273% %
6274% %
6275%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6276%
6277% XDrawPanRectangle() draws a rectangle in the pan window. The pan window
6278% displays a zoom image and the rectangle shows which portion of the image is
6279% displayed in the Image window.
6280%
6281% The format of the XDrawPanRectangle method is:
6282%
6283% XDrawPanRectangle(Display *display,XWindows *windows)
6284%
6285% A description of each parameter follows:
6286%
6287% o display: Specifies a connection to an X server; returned from
6288% XOpenDisplay.
6289%
6290% o windows: Specifies a pointer to a XWindows structure.
6291%
6292*/
6293static void XDrawPanRectangle(Display *display,XWindows *windows)
6294{
6295 MagickRealType
6296 scale_factor;
6297
6299 highlight_info;
6300
6301 /*
6302 Determine dimensions of the panning rectangle.
6303 */
6304 scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6305 highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6306 highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6307 scale_factor=(MagickRealType)
6308 windows->pan.height/windows->image.ximage->height;
6309 highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6310 highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6311 /*
6312 Display the panning rectangle.
6313 */
6314 (void) XClearWindow(display,windows->pan.id);
6315 XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6316 &highlight_info);
6317}
6318
6319/*
6320%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6321% %
6322% %
6323% %
6324+ X I m a g e C a c h e %
6325% %
6326% %
6327% %
6328%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6329%
6330% XImageCache() handles the creation, manipulation, and destruction of the
6331% image cache (undo and redo buffers).
6332%
6333% The format of the XImageCache method is:
6334%
6335% void XImageCache(Display *display,XResourceInfo *resource_info,
6336% XWindows *windows,const DisplayCommand command,Image **image)
6337%
6338% A description of each parameter follows:
6339%
6340% o display: Specifies a connection to an X server; returned from
6341% XOpenDisplay.
6342%
6343% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6344%
6345% o windows: Specifies a pointer to a XWindows structure.
6346%
6347% o command: Specifies a command to perform.
6348%
6349% o image: the image; XImageCache may transform the image and return a new
6350% image pointer.
6351%
6352*/
6353static void XImageCache(Display *display,XResourceInfo *resource_info,
6354 XWindows *windows,const DisplayCommand command,Image **image)
6355{
6356 Image
6357 *cache_image;
6358
6359 static Image
6360 *redo_image = (Image *) NULL,
6361 *undo_image = (Image *) NULL;
6362
6363 switch (command)
6364 {
6365 case FreeBuffersCommand:
6366 {
6367 /*
6368 Free memory from the undo and redo cache.
6369 */
6370 while (undo_image != (Image *) NULL)
6371 {
6372 cache_image=undo_image;
6373 undo_image=GetPreviousImageInList(undo_image);
6374 cache_image->list=DestroyImage(cache_image->list);
6375 cache_image=DestroyImage(cache_image);
6376 }
6377 undo_image=NewImageList();
6378 if (redo_image != (Image *) NULL)
6379 redo_image=DestroyImage(redo_image);
6380 redo_image=NewImageList();
6381 return;
6382 }
6383 case UndoCommand:
6384 {
6385 char
6386 image_geometry[MaxTextExtent];
6387
6388 /*
6389 Undo the last image transformation.
6390 */
6391 if (undo_image == (Image *) NULL)
6392 {
6393 (void) XBell(display,0);
6394 ThrowXWindowException(ImageError,"NoImagesWereFound",
6395 (*image)->filename);
6396 return;
6397 }
6398 cache_image=undo_image;
6399 undo_image=GetPreviousImageInList(undo_image);
6400 windows->image.window_changes.width=(int) cache_image->columns;
6401 windows->image.window_changes.height=(int) cache_image->rows;
6402 (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6403 windows->image.ximage->width,windows->image.ximage->height);
6404 (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
6405 if (windows->image.crop_geometry != (char *) NULL)
6406 windows->image.crop_geometry=(char *)
6407 RelinquishMagickMemory(windows->image.crop_geometry);
6408 windows->image.crop_geometry=cache_image->geometry;
6409 if (redo_image != (Image *) NULL)
6410 redo_image=DestroyImage(redo_image);
6411 redo_image=(*image);
6412 *image=cache_image->list;
6413 cache_image=DestroyImage(cache_image);
6414 if (windows->image.orphan != MagickFalse)
6415 return;
6416 XConfigureImageColormap(display,resource_info,windows,*image);
6417 (void) XConfigureImage(display,resource_info,windows,*image);
6418 return;
6419 }
6420 case CutCommand:
6421 case PasteCommand:
6422 case ApplyCommand:
6423 case HalfSizeCommand:
6424 case OriginalSizeCommand:
6425 case DoubleSizeCommand:
6426 case ResizeCommand:
6427 case TrimCommand:
6428 case CropCommand:
6429 case ChopCommand:
6430 case FlipCommand:
6431 case FlopCommand:
6432 case RotateRightCommand:
6433 case RotateLeftCommand:
6434 case RotateCommand:
6435 case ShearCommand:
6436 case RollCommand:
6437 case NegateCommand:
6438 case ContrastStretchCommand:
6439 case SigmoidalContrastCommand:
6440 case NormalizeCommand:
6441 case EqualizeCommand:
6442 case HueCommand:
6443 case SaturationCommand:
6444 case BrightnessCommand:
6445 case GammaCommand:
6446 case SpiffCommand:
6447 case DullCommand:
6448 case GrayscaleCommand:
6449 case MapCommand:
6450 case QuantizeCommand:
6451 case DespeckleCommand:
6452 case EmbossCommand:
6453 case ReduceNoiseCommand:
6454 case AddNoiseCommand:
6455 case SharpenCommand:
6456 case BlurCommand:
6457 case ThresholdCommand:
6458 case EdgeDetectCommand:
6459 case SpreadCommand:
6460 case ShadeCommand:
6461 case RaiseCommand:
6462 case SegmentCommand:
6463 case SolarizeCommand:
6464 case SepiaToneCommand:
6465 case SwirlCommand:
6466 case ImplodeCommand:
6467 case VignetteCommand:
6468 case WaveCommand:
6469 case OilPaintCommand:
6470 case CharcoalDrawCommand:
6471 case AnnotateCommand:
6472 case AddBorderCommand:
6473 case AddFrameCommand:
6474 case CompositeCommand:
6475 case CommentCommand:
6476 case LaunchCommand:
6477 case RegionOfInterestCommand:
6478 case SaveToUndoBufferCommand:
6479 case RedoCommand:
6480 {
6481 Image
6482 *previous_image;
6483
6484 ssize_t
6485 bytes;
6486
6487 bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
6488 if (undo_image != (Image *) NULL)
6489 {
6490 /*
6491 Ensure the undo cache has enough memory available.
6492 */
6493 previous_image=undo_image;
6494 while (previous_image != (Image *) NULL)
6495 {
6496 bytes+=previous_image->list->columns*previous_image->list->rows*
6497 sizeof(PixelPacket);
6498 if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6499 {
6500 previous_image=GetPreviousImageInList(previous_image);
6501 continue;
6502 }
6503 bytes-=previous_image->list->columns*previous_image->list->rows*
6504 sizeof(PixelPacket);
6505 if (previous_image == undo_image)
6506 undo_image=NewImageList();
6507 else
6508 previous_image->next->previous=NewImageList();
6509 break;
6510 }
6511 while (previous_image != (Image *) NULL)
6512 {
6513 /*
6514 Delete any excess memory from undo cache.
6515 */
6516 cache_image=previous_image;
6517 previous_image=GetPreviousImageInList(previous_image);
6518 cache_image->list=DestroyImage(cache_image->list);
6519 cache_image=DestroyImage(cache_image);
6520 }
6521 }
6522 if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6523 break;
6524 /*
6525 Save image before transformations are applied.
6526 */
6527 cache_image=AcquireImage((ImageInfo *) NULL);
6528 if (cache_image == (Image *) NULL)
6529 break;
6530 XSetCursorState(display,windows,MagickTrue);
6531 XCheckRefreshWindows(display,windows);
6532 cache_image->list=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
6533 XSetCursorState(display,windows,MagickFalse);
6534 if (cache_image->list == (Image *) NULL)
6535 {
6536 cache_image=DestroyImage(cache_image);
6537 break;
6538 }
6539 cache_image->columns=(size_t) windows->image.ximage->width;
6540 cache_image->rows=(size_t) windows->image.ximage->height;
6541 cache_image->geometry=windows->image.crop_geometry;
6542 if (windows->image.crop_geometry != (char *) NULL)
6543 {
6544 cache_image->geometry=AcquireString((char *) NULL);
6545 (void) CopyMagickString(cache_image->geometry,
6546 windows->image.crop_geometry,MaxTextExtent);
6547 }
6548 if (undo_image == (Image *) NULL)
6549 {
6550 undo_image=cache_image;
6551 break;
6552 }
6553 undo_image->next=cache_image;
6554 undo_image->next->previous=undo_image;
6555 undo_image=undo_image->next;
6556 break;
6557 }
6558 default:
6559 break;
6560 }
6561 if (command == RedoCommand)
6562 {
6563 /*
6564 Redo the last image transformation.
6565 */
6566 if (redo_image == (Image *) NULL)
6567 {
6568 (void) XBell(display,0);
6569 return;
6570 }
6571 windows->image.window_changes.width=(int) redo_image->columns;
6572 windows->image.window_changes.height=(int) redo_image->rows;
6573 if (windows->image.crop_geometry != (char *) NULL)
6574 windows->image.crop_geometry=(char *)
6575 RelinquishMagickMemory(windows->image.crop_geometry);
6576 windows->image.crop_geometry=redo_image->geometry;
6577 *image=DestroyImage(*image);
6578 *image=redo_image;
6579 redo_image=NewImageList();
6580 if (windows->image.orphan != MagickFalse)
6581 return;
6582 XConfigureImageColormap(display,resource_info,windows,*image);
6583 (void) XConfigureImage(display,resource_info,windows,*image);
6584 return;
6585 }
6586 if (command != InfoCommand)
6587 return;
6588 /*
6589 Display image info.
6590 */
6591 XSetCursorState(display,windows,MagickTrue);
6592 XCheckRefreshWindows(display,windows);
6593 XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6594 XSetCursorState(display,windows,MagickFalse);
6595}
6596
6597/*
6598%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6599% %
6600% %
6601% %
6602+ X I m a g e W i n d o w C o m m a n d %
6603% %
6604% %
6605% %
6606%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6607%
6608% XImageWindowCommand() makes a transform to the image or Image window as
6609% specified by a user menu button or keyboard command.
6610%
6611% The format of the XMagickCommand method is:
6612%
6613% DisplayCommand XImageWindowCommand(Display *display,
6614% XResourceInfo *resource_info,XWindows *windows,
6615% const MagickStatusType state,KeySym key_symbol,Image **image)
6616%
6617% A description of each parameter follows:
6618%
6619% o nexus: Method XImageWindowCommand returns an image when the
6620% user chooses 'Open Image' from the command menu. Otherwise a null
6621% image is returned.
6622%
6623% o display: Specifies a connection to an X server; returned from
6624% XOpenDisplay.
6625%
6626% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6627%
6628% o windows: Specifies a pointer to a XWindows structure.
6629%
6630% o state: key mask.
6631%
6632% o key_symbol: Specifies a command to perform.
6633%
6634% o image: the image; XImageWIndowCommand
6635% may transform the image and return a new image pointer.
6636%
6637*/
6638static DisplayCommand XImageWindowCommand(Display *display,
6639 XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6640 KeySym key_symbol,Image **image)
6641{
6642 static char
6643 delta[MaxTextExtent] = "";
6644
6645 static const char
6646 Digits[] = "01234567890";
6647
6648 static KeySym
6649 last_symbol = XK_0;
6650
6651 if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6652 {
6653 if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6654 {
6655 *delta='\0';
6656 resource_info->quantum=1;
6657 }
6658 last_symbol=key_symbol;
6659 delta[strlen(delta)+1]='\0';
6660 delta[strlen(delta)]=Digits[key_symbol-XK_0];
6661 resource_info->quantum=StringToLong(delta);
6662 return(NullCommand);
6663 }
6664 last_symbol=key_symbol;
6665 if (resource_info->immutable)
6666 {
6667 /*
6668 Virtual image window has a restricted command set.
6669 */
6670 switch (key_symbol)
6671 {
6672 case XK_question:
6673 return(InfoCommand);
6674 case XK_p:
6675 case XK_Print:
6676 return(PrintCommand);
6677 case XK_space:
6678 return(NextCommand);
6679 case XK_q:
6680 case XK_Escape:
6681 return(QuitCommand);
6682 default:
6683 break;
6684 }
6685 return(NullCommand);
6686 }
6687 switch ((int) key_symbol)
6688 {
6689 case XK_o:
6690 {
6691 if ((state & ControlMask) == 0)
6692 break;
6693 return(OpenCommand);
6694 }
6695 case XK_space:
6696 return(NextCommand);
6697 case XK_BackSpace:
6698 return(FormerCommand);
6699 case XK_s:
6700 {
6701 if ((state & Mod1Mask) != 0)
6702 return(SwirlCommand);
6703 if ((state & ControlMask) == 0)
6704 return(ShearCommand);
6705 return(SaveCommand);
6706 }
6707 case XK_p:
6708 case XK_Print:
6709 {
6710 if ((state & Mod1Mask) != 0)
6711 return(OilPaintCommand);
6712 if ((state & Mod4Mask) != 0)
6713 return(ColorCommand);
6714 if ((state & ControlMask) == 0)
6715 return(NullCommand);
6716 return(PrintCommand);
6717 }
6718 case XK_d:
6719 {
6720 if ((state & Mod4Mask) != 0)
6721 return(DrawCommand);
6722 if ((state & ControlMask) == 0)
6723 return(NullCommand);
6724 return(DeleteCommand);
6725 }
6726 case XK_Select:
6727 {
6728 if ((state & ControlMask) == 0)
6729 return(NullCommand);
6730 return(SelectCommand);
6731 }
6732 case XK_n:
6733 {
6734 if ((state & ControlMask) == 0)
6735 return(NullCommand);
6736 return(NewCommand);
6737 }
6738 case XK_q:
6739 case XK_Escape:
6740 return(QuitCommand);
6741 case XK_z:
6742 case XK_Undo:
6743 {
6744 if ((state & ControlMask) == 0)
6745 return(NullCommand);
6746 return(UndoCommand);
6747 }
6748 case XK_r:
6749 case XK_Redo:
6750 {
6751 if ((state & ControlMask) == 0)
6752 return(RollCommand);
6753 return(RedoCommand);
6754 }
6755 case XK_x:
6756 {
6757 if ((state & ControlMask) == 0)
6758 return(NullCommand);
6759 return(CutCommand);
6760 }
6761 case XK_c:
6762 {
6763 if ((state & Mod1Mask) != 0)
6764 return(CharcoalDrawCommand);
6765 if ((state & ControlMask) == 0)
6766 return(CropCommand);
6767 return(CopyCommand);
6768 }
6769 case XK_v:
6770 case XK_Insert:
6771 {
6772 if ((state & Mod4Mask) != 0)
6773 return(CompositeCommand);
6774 if ((state & ControlMask) == 0)
6775 return(FlipCommand);
6776 return(PasteCommand);
6777 }
6778 case XK_less:
6779 return(HalfSizeCommand);
6780 case XK_minus:
6781 return(OriginalSizeCommand);
6782 case XK_greater:
6783 return(DoubleSizeCommand);
6784 case XK_percent:
6785 return(ResizeCommand);
6786 case XK_at:
6787 return(RefreshCommand);
6788 case XK_bracketleft:
6789 return(ChopCommand);
6790 case XK_h:
6791 return(FlopCommand);
6792 case XK_slash:
6793 return(RotateRightCommand);
6794 case XK_backslash:
6795 return(RotateLeftCommand);
6796 case XK_asterisk:
6797 return(RotateCommand);
6798 case XK_t:
6799 return(TrimCommand);
6800 case XK_H:
6801 return(HueCommand);
6802 case XK_S:
6803 return(SaturationCommand);
6804 case XK_L:
6805 return(BrightnessCommand);
6806 case XK_G:
6807 return(GammaCommand);
6808 case XK_C:
6809 return(SpiffCommand);
6810 case XK_Z:
6811 return(DullCommand);
6812 case XK_N:
6813 return(NormalizeCommand);
6814 case XK_equal:
6815 return(EqualizeCommand);
6816 case XK_asciitilde:
6817 return(NegateCommand);
6818 case XK_period:
6819 return(GrayscaleCommand);
6820 case XK_numbersign:
6821 return(QuantizeCommand);
6822 case XK_F2:
6823 return(DespeckleCommand);
6824 case XK_F3:
6825 return(EmbossCommand);
6826 case XK_F4:
6827 return(ReduceNoiseCommand);
6828 case XK_F5:
6829 return(AddNoiseCommand);
6830 case XK_F6:
6831 return(SharpenCommand);
6832 case XK_F7:
6833 return(BlurCommand);
6834 case XK_F8:
6835 return(ThresholdCommand);
6836 case XK_F9:
6837 return(EdgeDetectCommand);
6838 case XK_F10:
6839 return(SpreadCommand);
6840 case XK_F11:
6841 return(ShadeCommand);
6842 case XK_F12:
6843 return(RaiseCommand);
6844 case XK_F13:
6845 return(SegmentCommand);
6846 case XK_i:
6847 {
6848 if ((state & Mod1Mask) == 0)
6849 return(NullCommand);
6850 return(ImplodeCommand);
6851 }
6852 case XK_w:
6853 {
6854 if ((state & Mod1Mask) == 0)
6855 return(NullCommand);
6856 return(WaveCommand);
6857 }
6858 case XK_m:
6859 {
6860 if ((state & Mod4Mask) == 0)
6861 return(NullCommand);
6862 return(MatteCommand);
6863 }
6864 case XK_b:
6865 {
6866 if ((state & Mod4Mask) == 0)
6867 return(NullCommand);
6868 return(AddBorderCommand);
6869 }
6870 case XK_f:
6871 {
6872 if ((state & Mod4Mask) == 0)
6873 return(NullCommand);
6874 return(AddFrameCommand);
6875 }
6876 case XK_exclam:
6877 {
6878 if ((state & Mod4Mask) == 0)
6879 return(NullCommand);
6880 return(CommentCommand);
6881 }
6882 case XK_a:
6883 {
6884 if ((state & Mod1Mask) != 0)
6885 return(ApplyCommand);
6886 if ((state & Mod4Mask) != 0)
6887 return(AnnotateCommand);
6888 if ((state & ControlMask) == 0)
6889 return(NullCommand);
6890 return(RegionOfInterestCommand);
6891 }
6892 case XK_question:
6893 return(InfoCommand);
6894 case XK_plus:
6895 return(ZoomCommand);
6896 case XK_P:
6897 {
6898 if ((state & ShiftMask) == 0)
6899 return(NullCommand);
6900 return(ShowPreviewCommand);
6901 }
6902 case XK_Execute:
6903 return(LaunchCommand);
6904 case XK_F1:
6905 return(HelpCommand);
6906 case XK_Find:
6907 return(BrowseDocumentationCommand);
6908 case XK_Menu:
6909 {
6910 (void) XMapRaised(display,windows->command.id);
6911 return(NullCommand);
6912 }
6913 case XK_Next:
6914 case XK_Prior:
6915 case XK_Home:
6916 case XK_KP_Home:
6917 {
6918 XTranslateImage(display,windows,*image,key_symbol);
6919 return(NullCommand);
6920 }
6921 case XK_Up:
6922 case XK_KP_Up:
6923 case XK_Down:
6924 case XK_KP_Down:
6925 case XK_Left:
6926 case XK_KP_Left:
6927 case XK_Right:
6928 case XK_KP_Right:
6929 {
6930 if ((state & Mod1Mask) != 0)
6931 {
6933 crop_info;
6934
6935 /*
6936 Trim one pixel from edge of image.
6937 */
6938 crop_info.x=0;
6939 crop_info.y=0;
6940 crop_info.width=(size_t) windows->image.ximage->width;
6941 crop_info.height=(size_t) windows->image.ximage->height;
6942 if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
6943 {
6944 if (resource_info->quantum >= (int) crop_info.height)
6945 resource_info->quantum=(int) crop_info.height-1;
6946 crop_info.height-=resource_info->quantum;
6947 }
6948 if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
6949 {
6950 if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
6951 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
6952 crop_info.y+=resource_info->quantum;
6953 crop_info.height-=resource_info->quantum;
6954 }
6955 if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
6956 {
6957 if (resource_info->quantum >= (int) crop_info.width)
6958 resource_info->quantum=(int) crop_info.width-1;
6959 crop_info.width-=resource_info->quantum;
6960 }
6961 if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
6962 {
6963 if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
6964 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
6965 crop_info.x+=resource_info->quantum;
6966 crop_info.width-=resource_info->quantum;
6967 }
6968 if ((int) (windows->image.x+windows->image.width) >
6969 (int) crop_info.width)
6970 windows->image.x=(int) (crop_info.width-windows->image.width);
6971 if ((int) (windows->image.y+windows->image.height) >
6972 (int) crop_info.height)
6973 windows->image.y=(int) (crop_info.height-windows->image.height);
6974 XSetCropGeometry(display,windows,&crop_info,*image);
6975 windows->image.window_changes.width=(int) crop_info.width;
6976 windows->image.window_changes.height=(int) crop_info.height;
6977 (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
6978 (void) XConfigureImage(display,resource_info,windows,*image);
6979 return(NullCommand);
6980 }
6981 XTranslateImage(display,windows,*image,key_symbol);
6982 return(NullCommand);
6983 }
6984 default:
6985 return(NullCommand);
6986 }
6987 return(NullCommand);
6988}
6989
6990/*
6991%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6992% %
6993% %
6994% %
6995+ X M a g i c k C o m m a n d %
6996% %
6997% %
6998% %
6999%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7000%
7001% XMagickCommand() makes a transform to the image or Image window as
7002% specified by a user menu button or keyboard command.
7003%
7004% The format of the XMagickCommand method is:
7005%
7006% Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7007% XWindows *windows,const DisplayCommand command,Image **image)
7008%
7009% A description of each parameter follows:
7010%
7011% o nexus: Method XMagickCommand returns an image when the
7012% user chooses 'Load Image' from the command menu. Otherwise a null
7013% image is returned.
7014%
7015% o display: Specifies a connection to an X server; returned from
7016% XOpenDisplay.
7017%
7018% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7019%
7020% o windows: Specifies a pointer to a XWindows structure.
7021%
7022% o command: Specifies a command to perform.
7023%
7024% o image: the image; XMagickCommand
7025% may transform the image and return a new image pointer.
7026%
7027*/
7028static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7029 XWindows *windows,const DisplayCommand command,Image **image)
7030{
7031 char
7032 filename[MaxTextExtent],
7033 geometry[MaxTextExtent],
7034 modulate_factors[MaxTextExtent];
7035
7037 geometry_info;
7038
7039 Image
7040 *nexus;
7041
7042 ImageInfo
7043 *image_info;
7044
7045 int
7046 x,
7047 y;
7048
7049 MagickStatusType
7050 flags,
7051 status;
7052
7054 quantize_info;
7055
7057 page_geometry;
7058
7059 int
7060 i;
7061
7062 static char
7063 color[MaxTextExtent] = "gray";
7064
7065 unsigned int
7066 height,
7067 width;
7068
7069 /*
7070 Process user command.
7071 */
7072 XCheckRefreshWindows(display,windows);
7073 XImageCache(display,resource_info,windows,command,image);
7074 nexus=NewImageList();
7075 windows->image.window_changes.width=windows->image.ximage->width;
7076 windows->image.window_changes.height=windows->image.ximage->height;
7077 image_info=CloneImageInfo(resource_info->image_info);
7078 SetGeometryInfo(&geometry_info);
7079 GetQuantizeInfo(&quantize_info);
7080 switch (command)
7081 {
7082 case OpenCommand:
7083 {
7084 /*
7085 Load image.
7086 */
7087 nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7088 break;
7089 }
7090 case NextCommand:
7091 {
7092 /*
7093 Display next image.
7094 */
7095 for (i=0; i < resource_info->quantum; i++)
7096 XClientMessage(display,windows->image.id,windows->im_protocols,
7097 windows->im_next_image,CurrentTime);
7098 break;
7099 }
7100 case FormerCommand:
7101 {
7102 /*
7103 Display former image.
7104 */
7105 for (i=0; i < resource_info->quantum; i++)
7106 XClientMessage(display,windows->image.id,windows->im_protocols,
7107 windows->im_former_image,CurrentTime);
7108 break;
7109 }
7110 case SelectCommand:
7111 {
7112 int
7113 status;
7114
7115 /*
7116 Select image.
7117 */
7118 if (*resource_info->home_directory == '\0')
7119 (void) CopyMagickString(resource_info->home_directory,".",
7120 MaxTextExtent);
7121 status=chdir(resource_info->home_directory);
7122 if (status == -1)
7123 (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
7124 FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
7125 nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7126 break;
7127 }
7128 case SaveCommand:
7129 {
7130 /*
7131 Save image.
7132 */
7133 status=XSaveImage(display,resource_info,windows,*image);
7134 if (status == MagickFalse)
7135 {
7136 char
7137 message[MaxTextExtent];
7138
7139 (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7140 (*image)->exception.reason != (char *) NULL ?
7141 (*image)->exception.reason : "",
7142 (*image)->exception.description != (char *) NULL ?
7143 (*image)->exception.description : "");
7144 XNoticeWidget(display,windows,"Unable to save file:",message);
7145 break;
7146 }
7147 break;
7148 }
7149 case PrintCommand:
7150 {
7151 /*
7152 Print image.
7153 */
7154 status=XPrintImage(display,resource_info,windows,*image);
7155 if (status == MagickFalse)
7156 {
7157 char
7158 message[MaxTextExtent];
7159
7160 (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7161 (*image)->exception.reason != (char *) NULL ?
7162 (*image)->exception.reason : "",
7163 (*image)->exception.description != (char *) NULL ?
7164 (*image)->exception.description : "");
7165 XNoticeWidget(display,windows,"Unable to print file:",message);
7166 break;
7167 }
7168 break;
7169 }
7170 case DeleteCommand:
7171 {
7172 static char
7173 filename[MaxTextExtent] = "\0";
7174
7175 /*
7176 Delete image file.
7177 */
7178 XFileBrowserWidget(display,windows,"Delete",filename);
7179 if (*filename == '\0')
7180 break;
7181 status=ShredFile(filename);
7182 status|=remove_utf8(filename);
7183 if (status != MagickFalse)
7184 XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7185 break;
7186 }
7187 case NewCommand:
7188 {
7189 int
7190 status;
7191
7192 static char
7193 color[MaxTextExtent] = "gray",
7194 geometry[MaxTextExtent] = "640x480";
7195
7196 static const char
7197 *format = "gradient";
7198
7199 /*
7200 Query user for canvas geometry.
7201 */
7202 status=XDialogWidget(display,windows,"New","Enter image geometry:",
7203 geometry);
7204 if (*geometry == '\0')
7205 break;
7206 if (status == 0)
7207 format="xc";
7208 XColorBrowserWidget(display,windows,"Select",color);
7209 if (*color == '\0')
7210 break;
7211 /*
7212 Create canvas.
7213 */
7214 (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7215 "%s:%s",format,color);
7216 (void) CloneString(&image_info->size,geometry);
7217 nexus=ReadImage(image_info,&(*image)->exception);
7218 CatchException(&(*image)->exception);
7219 XClientMessage(display,windows->image.id,windows->im_protocols,
7220 windows->im_next_image,CurrentTime);
7221 break;
7222 }
7223 case VisualDirectoryCommand:
7224 {
7225 /*
7226 Visual Image directory.
7227 */
7228 nexus=XVisualDirectoryImage(display,resource_info,windows);
7229 break;
7230 }
7231 case QuitCommand:
7232 {
7233 /*
7234 exit program.
7235 */
7236 if (resource_info->confirm_exit == MagickFalse)
7237 XClientMessage(display,windows->image.id,windows->im_protocols,
7238 windows->im_exit,CurrentTime);
7239 else
7240 {
7241 int
7242 status;
7243
7244 /*
7245 Confirm program exit.
7246 */
7247 status=XConfirmWidget(display,windows,"Do you really want to exit",
7248 resource_info->client_name);
7249 if (status > 0)
7250 XClientMessage(display,windows->image.id,windows->im_protocols,
7251 windows->im_exit,CurrentTime);
7252 }
7253 break;
7254 }
7255 case CutCommand:
7256 {
7257 /*
7258 Cut image.
7259 */
7260 (void) XCropImage(display,resource_info,windows,*image,CutMode);
7261 break;
7262 }
7263 case CopyCommand:
7264 {
7265 /*
7266 Copy image.
7267 */
7268 (void) XCropImage(display,resource_info,windows,*image,CopyMode);
7269 break;
7270 }
7271 case PasteCommand:
7272 {
7273 /*
7274 Paste image.
7275 */
7276 status=XPasteImage(display,resource_info,windows,*image);
7277 if (status == MagickFalse)
7278 {
7279 XNoticeWidget(display,windows,"Unable to paste X image",
7280 (*image)->filename);
7281 break;
7282 }
7283 break;
7284 }
7285 case HalfSizeCommand:
7286 {
7287 /*
7288 Half image size.
7289 */
7290 windows->image.window_changes.width=windows->image.ximage->width/2;
7291 windows->image.window_changes.height=windows->image.ximage->height/2;
7292 (void) XConfigureImage(display,resource_info,windows,*image);
7293 break;
7294 }
7295 case OriginalSizeCommand:
7296 {
7297 /*
7298 Original image size.
7299 */
7300 windows->image.window_changes.width=(int) (*image)->columns;
7301 windows->image.window_changes.height=(int) (*image)->rows;
7302 (void) XConfigureImage(display,resource_info,windows,*image);
7303 break;
7304 }
7305 case DoubleSizeCommand:
7306 {
7307 /*
7308 Double the image size.
7309 */
7310 windows->image.window_changes.width=windows->image.ximage->width << 1;
7311 windows->image.window_changes.height=windows->image.ximage->height << 1;
7312 (void) XConfigureImage(display,resource_info,windows,*image);
7313 break;
7314 }
7315 case ResizeCommand:
7316 {
7317 int
7318 status;
7319
7320 size_t
7321 height,
7322 width;
7323
7324 ssize_t
7325 x,
7326 y;
7327
7328 /*
7329 Resize image.
7330 */
7331 width=(size_t) windows->image.ximage->width;
7332 height=(size_t) windows->image.ximage->height;
7333 x=0;
7334 y=0;
7335 (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7336 (double) width,(double) height);
7337 status=XDialogWidget(display,windows,"Resize",
7338 "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7339 if (*geometry == '\0')
7340 break;
7341 if (status == 0)
7342 (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7343 (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7344 windows->image.window_changes.width=(int) width;
7345 windows->image.window_changes.height=(int) height;
7346 (void) XConfigureImage(display,resource_info,windows,*image);
7347 break;
7348 }
7349 case ApplyCommand:
7350 {
7351 char
7352 image_geometry[MaxTextExtent];
7353
7354 if ((windows->image.crop_geometry == (char *) NULL) &&
7355 ((int) (*image)->columns == windows->image.ximage->width) &&
7356 ((int) (*image)->rows == windows->image.ximage->height))
7357 break;
7358 /*
7359 Apply size transforms to image.
7360 */
7361 XSetCursorState(display,windows,MagickTrue);
7362 XCheckRefreshWindows(display,windows);
7363 /*
7364 Crop and/or scale displayed image.
7365 */
7366 (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7367 windows->image.ximage->width,windows->image.ximage->height);
7368 (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7369 if (windows->image.crop_geometry != (char *) NULL)
7370 windows->image.crop_geometry=(char *)
7371 RelinquishMagickMemory(windows->image.crop_geometry);
7372 windows->image.x=0;
7373 windows->image.y=0;
7374 XConfigureImageColormap(display,resource_info,windows,*image);
7375 (void) XConfigureImage(display,resource_info,windows,*image);
7376 break;
7377 }
7378 case RefreshCommand:
7379 {
7380 (void) XConfigureImage(display,resource_info,windows,*image);
7381 break;
7382 }
7383 case RestoreCommand:
7384 {
7385 /*
7386 Restore Image window to its original size.
7387 */
7388 if ((windows->image.width == (unsigned int) (*image)->columns) &&
7389 (windows->image.height == (unsigned int) (*image)->rows) &&
7390 (windows->image.crop_geometry == (char *) NULL))
7391 {
7392 (void) XBell(display,0);
7393 break;
7394 }
7395 windows->image.window_changes.width=(int) (*image)->columns;
7396 windows->image.window_changes.height=(int) (*image)->rows;
7397 if (windows->image.crop_geometry != (char *) NULL)
7398 {
7399 windows->image.crop_geometry=(char *)
7400 RelinquishMagickMemory(windows->image.crop_geometry);
7401 windows->image.crop_geometry=(char *) NULL;
7402 windows->image.x=0;
7403 windows->image.y=0;
7404 }
7405 XConfigureImageColormap(display,resource_info,windows,*image);
7406 (void) XConfigureImage(display,resource_info,windows,*image);
7407 break;
7408 }
7409 case CropCommand:
7410 {
7411 /*
7412 Crop image.
7413 */
7414 (void) XCropImage(display,resource_info,windows,*image,CropMode);
7415 break;
7416 }
7417 case ChopCommand:
7418 {
7419 /*
7420 Chop image.
7421 */
7422 status=XChopImage(display,resource_info,windows,image);
7423 if (status == MagickFalse)
7424 {
7425 XNoticeWidget(display,windows,"Unable to cut X image",
7426 (*image)->filename);
7427 break;
7428 }
7429 break;
7430 }
7431 case FlopCommand:
7432 {
7433 Image
7434 *flop_image;
7435
7436 /*
7437 Flop image scanlines.
7438 */
7439 XSetCursorState(display,windows,MagickTrue);
7440 XCheckRefreshWindows(display,windows);
7441 flop_image=FlopImage(*image,&(*image)->exception);
7442 if (flop_image != (Image *) NULL)
7443 {
7444 *image=DestroyImage(*image);
7445 *image=flop_image;
7446 }
7447 CatchException(&(*image)->exception);
7448 XSetCursorState(display,windows,MagickFalse);
7449 if (windows->image.crop_geometry != (char *) NULL)
7450 {
7451 /*
7452 Flop crop geometry.
7453 */
7454 width=(unsigned int) (*image)->columns;
7455 height=(unsigned int) (*image)->rows;
7456 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7457 &width,&height);
7458 (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7459 "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7460 }
7461 if (windows->image.orphan != MagickFalse)
7462 break;
7463 (void) XConfigureImage(display,resource_info,windows,*image);
7464 break;
7465 }
7466 case FlipCommand:
7467 {
7468 Image
7469 *flip_image;
7470
7471 /*
7472 Flip image scanlines.
7473 */
7474 XSetCursorState(display,windows,MagickTrue);
7475 XCheckRefreshWindows(display,windows);
7476 flip_image=FlipImage(*image,&(*image)->exception);
7477 if (flip_image != (Image *) NULL)
7478 {
7479 *image=DestroyImage(*image);
7480 *image=flip_image;
7481 }
7482 CatchException(&(*image)->exception);
7483 XSetCursorState(display,windows,MagickFalse);
7484 if (windows->image.crop_geometry != (char *) NULL)
7485 {
7486 /*
7487 Flip crop geometry.
7488 */
7489 width=(unsigned int) (*image)->columns;
7490 height=(unsigned int) (*image)->rows;
7491 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7492 &width,&height);
7493 (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7494 "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7495 }
7496 if (windows->image.orphan != MagickFalse)
7497 break;
7498 (void) XConfigureImage(display,resource_info,windows,*image);
7499 break;
7500 }
7501 case RotateRightCommand:
7502 {
7503 /*
7504 Rotate image 90 degrees clockwise.
7505 */
7506 status=XRotateImage(display,resource_info,windows,90.0,image);
7507 if (status == MagickFalse)
7508 {
7509 XNoticeWidget(display,windows,"Unable to rotate X image",
7510 (*image)->filename);
7511 break;
7512 }
7513 break;
7514 }
7515 case RotateLeftCommand:
7516 {
7517 /*
7518 Rotate image 90 degrees counter-clockwise.
7519 */
7520 status=XRotateImage(display,resource_info,windows,-90.0,image);
7521 if (status == MagickFalse)
7522 {
7523 XNoticeWidget(display,windows,"Unable to rotate X image",
7524 (*image)->filename);
7525 break;
7526 }
7527 break;
7528 }
7529 case RotateCommand:
7530 {
7531 /*
7532 Rotate image.
7533 */
7534 status=XRotateImage(display,resource_info,windows,0.0,image);
7535 if (status == MagickFalse)
7536 {
7537 XNoticeWidget(display,windows,"Unable to rotate X image",
7538 (*image)->filename);
7539 break;
7540 }
7541 break;
7542 }
7543 case ShearCommand:
7544 {
7545 Image
7546 *shear_image;
7547
7548 static char
7549 geometry[MaxTextExtent] = "45.0x45.0";
7550
7551 /*
7552 Query user for shear color and geometry.
7553 */
7554 XColorBrowserWidget(display,windows,"Select",color);
7555 if (*color == '\0')
7556 break;
7557 (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7558 geometry);
7559 if (*geometry == '\0')
7560 break;
7561 /*
7562 Shear image.
7563 */
7564 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7565 XSetCursorState(display,windows,MagickTrue);
7566 XCheckRefreshWindows(display,windows);
7567 (void) QueryColorDatabase(color,&(*image)->background_color,
7568 &(*image)->exception);
7569 flags=ParseGeometry(geometry,&geometry_info);
7570 if ((flags & SigmaValue) == 0)
7571 geometry_info.sigma=geometry_info.rho;
7572 shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7573 &(*image)->exception);
7574 if (shear_image != (Image *) NULL)
7575 {
7576 *image=DestroyImage(*image);
7577 *image=shear_image;
7578 }
7579 CatchException(&(*image)->exception);
7580 XSetCursorState(display,windows,MagickFalse);
7581 if (windows->image.orphan != MagickFalse)
7582 break;
7583 windows->image.window_changes.width=(int) (*image)->columns;
7584 windows->image.window_changes.height=(int) (*image)->rows;
7585 XConfigureImageColormap(display,resource_info,windows,*image);
7586 (void) XConfigureImage(display,resource_info,windows,*image);
7587 break;
7588 }
7589 case RollCommand:
7590 {
7591 Image
7592 *roll_image;
7593
7594 static char
7595 geometry[MaxTextExtent] = "+2+2";
7596
7597 /*
7598 Query user for the roll geometry.
7599 */
7600 (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7601 geometry);
7602 if (*geometry == '\0')
7603 break;
7604 /*
7605 Roll image.
7606 */
7607 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7608 XSetCursorState(display,windows,MagickTrue);
7609 XCheckRefreshWindows(display,windows);
7610 (void) ParsePageGeometry(*image,geometry,&page_geometry,
7611 &(*image)->exception);
7612 roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7613 &(*image)->exception);
7614 if (roll_image != (Image *) NULL)
7615 {
7616 *image=DestroyImage(*image);
7617 *image=roll_image;
7618 }
7619 CatchException(&(*image)->exception);
7620 XSetCursorState(display,windows,MagickFalse);
7621 if (windows->image.orphan != MagickFalse)
7622 break;
7623 windows->image.window_changes.width=(int) (*image)->columns;
7624 windows->image.window_changes.height=(int) (*image)->rows;
7625 XConfigureImageColormap(display,resource_info,windows,*image);
7626 (void) XConfigureImage(display,resource_info,windows,*image);
7627 break;
7628 }
7629 case TrimCommand:
7630 {
7631 static char
7632 fuzz[MaxTextExtent];
7633
7634 /*
7635 Query user for the fuzz factor.
7636 */
7637 (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7638 (*image)->fuzz/((double) QuantumRange+1.0));
7639 (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7640 if (*fuzz == '\0')
7641 break;
7642 (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7643 /*
7644 Trim image.
7645 */
7646 status=XTrimImage(display,resource_info,windows,*image);
7647 if (status == MagickFalse)
7648 {
7649 XNoticeWidget(display,windows,"Unable to trim X image",
7650 (*image)->filename);
7651 break;
7652 }
7653 break;
7654 }
7655 case HueCommand:
7656 {
7657 static char
7658 hue_percent[MaxTextExtent] = "110";
7659
7660 /*
7661 Query user for percent hue change.
7662 */
7663 (void) XDialogWidget(display,windows,"Apply",
7664 "Enter percent change in image hue (0-200):",hue_percent);
7665 if (*hue_percent == '\0')
7666 break;
7667 /*
7668 Vary the image hue.
7669 */
7670 XSetCursorState(display,windows,MagickTrue);
7671 XCheckRefreshWindows(display,windows);
7672 (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7673 (void) ConcatenateMagickString(modulate_factors,hue_percent,
7674 MaxTextExtent);
7675 (void) ModulateImage(*image,modulate_factors);
7676 XSetCursorState(display,windows,MagickFalse);
7677 if (windows->image.orphan != MagickFalse)
7678 break;
7679 XConfigureImageColormap(display,resource_info,windows,*image);
7680 (void) XConfigureImage(display,resource_info,windows,*image);
7681 break;
7682 }
7683 case SaturationCommand:
7684 {
7685 static char
7686 saturation_percent[MaxTextExtent] = "110";
7687
7688 /*
7689 Query user for percent saturation change.
7690 */
7691 (void) XDialogWidget(display,windows,"Apply",
7692 "Enter percent change in color saturation (0-200):",saturation_percent);
7693 if (*saturation_percent == '\0')
7694 break;
7695 /*
7696 Vary color saturation.
7697 */
7698 XSetCursorState(display,windows,MagickTrue);
7699 XCheckRefreshWindows(display,windows);
7700 (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7701 (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7702 MaxTextExtent);
7703 (void) ModulateImage(*image,modulate_factors);
7704 XSetCursorState(display,windows,MagickFalse);
7705 if (windows->image.orphan != MagickFalse)
7706 break;
7707 XConfigureImageColormap(display,resource_info,windows,*image);
7708 (void) XConfigureImage(display,resource_info,windows,*image);
7709 break;
7710 }
7711 case BrightnessCommand:
7712 {
7713 static char
7714 brightness_percent[MaxTextExtent] = "110";
7715
7716 /*
7717 Query user for percent brightness change.
7718 */
7719 (void) XDialogWidget(display,windows,"Apply",
7720 "Enter percent change in color brightness (0-200):",brightness_percent);
7721 if (*brightness_percent == '\0')
7722 break;
7723 /*
7724 Vary the color brightness.
7725 */
7726 XSetCursorState(display,windows,MagickTrue);
7727 XCheckRefreshWindows(display,windows);
7728 (void) CopyMagickString(modulate_factors,brightness_percent,
7729 MaxTextExtent);
7730 (void) ModulateImage(*image,modulate_factors);
7731 XSetCursorState(display,windows,MagickFalse);
7732 if (windows->image.orphan != MagickFalse)
7733 break;
7734 XConfigureImageColormap(display,resource_info,windows,*image);
7735 (void) XConfigureImage(display,resource_info,windows,*image);
7736 break;
7737 }
7738 case GammaCommand:
7739 {
7740 static char
7741 factor[MaxTextExtent] = "1.6";
7742
7743 /*
7744 Query user for gamma value.
7745 */
7746 (void) XDialogWidget(display,windows,"Gamma",
7747 "Enter gamma value (e.g. 1.0,1.0,1.6):",factor);
7748 if (*factor == '\0')
7749 break;
7750 /*
7751 Gamma correct image.
7752 */
7753 XSetCursorState(display,windows,MagickTrue);
7754 XCheckRefreshWindows(display,windows);
7755 (void) GammaImage(*image,factor);
7756 XSetCursorState(display,windows,MagickFalse);
7757 if (windows->image.orphan != MagickFalse)
7758 break;
7759 XConfigureImageColormap(display,resource_info,windows,*image);
7760 (void) XConfigureImage(display,resource_info,windows,*image);
7761 break;
7762 }
7763 case SpiffCommand:
7764 {
7765 /*
7766 Sharpen the image contrast.
7767 */
7768 XSetCursorState(display,windows,MagickTrue);
7769 XCheckRefreshWindows(display,windows);
7770 (void) ContrastImage(*image,MagickTrue);
7771 XSetCursorState(display,windows,MagickFalse);
7772 if (windows->image.orphan != MagickFalse)
7773 break;
7774 XConfigureImageColormap(display,resource_info,windows,*image);
7775 (void) XConfigureImage(display,resource_info,windows,*image);
7776 break;
7777 }
7778 case DullCommand:
7779 {
7780 /*
7781 Dull the image contrast.
7782 */
7783 XSetCursorState(display,windows,MagickTrue);
7784 XCheckRefreshWindows(display,windows);
7785 (void) ContrastImage(*image,MagickFalse);
7786 XSetCursorState(display,windows,MagickFalse);
7787 if (windows->image.orphan != MagickFalse)
7788 break;
7789 XConfigureImageColormap(display,resource_info,windows,*image);
7790 (void) XConfigureImage(display,resource_info,windows,*image);
7791 break;
7792 }
7793 case ContrastStretchCommand:
7794 {
7795 double
7796 black_point,
7797 white_point;
7798
7799 static char
7800 levels[MaxTextExtent] = "1%";
7801
7802 /*
7803 Query user for gamma value.
7804 */
7805 (void) XDialogWidget(display,windows,"Contrast Stretch",
7806 "Enter black and white points:",levels);
7807 if (*levels == '\0')
7808 break;
7809 /*
7810 Contrast stretch image.
7811 */
7812 XSetCursorState(display,windows,MagickTrue);
7813 XCheckRefreshWindows(display,windows);
7814 flags=ParseGeometry(levels,&geometry_info);
7815 black_point=geometry_info.rho;
7816 white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7817 if ((flags & PercentValue) != 0)
7818 {
7819 black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7820 white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7821 }
7822 white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7823 (void) ContrastStretchImageChannel(*image,DefaultChannels,black_point,
7824 white_point);
7825 XSetCursorState(display,windows,MagickFalse);
7826 if (windows->image.orphan != MagickFalse)
7827 break;
7828 XConfigureImageColormap(display,resource_info,windows,*image);
7829 (void) XConfigureImage(display,resource_info,windows,*image);
7830 break;
7831 }
7832 case SigmoidalContrastCommand:
7833 {
7834 static char
7835 levels[MaxTextExtent] = "3x50%";
7836
7837 /*
7838 Query user for gamma value.
7839 */
7840 (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7841 "Enter contrast and midpoint:",levels);
7842 if (*levels == '\0')
7843 break;
7844 /*
7845 Contrast stretch image.
7846 */
7847 XSetCursorState(display,windows,MagickTrue);
7848 XCheckRefreshWindows(display,windows);
7849 (void) SigmoidalContrastImage(*image,MagickTrue,levels);
7850 XSetCursorState(display,windows,MagickFalse);
7851 if (windows->image.orphan != MagickFalse)
7852 break;
7853 XConfigureImageColormap(display,resource_info,windows,*image);
7854 (void) XConfigureImage(display,resource_info,windows,*image);
7855 break;
7856 }
7857 case NormalizeCommand:
7858 {
7859 /*
7860 Perform histogram normalization on the image.
7861 */
7862 XSetCursorState(display,windows,MagickTrue);
7863 XCheckRefreshWindows(display,windows);
7864 (void) NormalizeImage(*image);
7865 XSetCursorState(display,windows,MagickFalse);
7866 if (windows->image.orphan != MagickFalse)
7867 break;
7868 XConfigureImageColormap(display,resource_info,windows,*image);
7869 (void) XConfigureImage(display,resource_info,windows,*image);
7870 break;
7871 }
7872 case EqualizeCommand:
7873 {
7874 /*
7875 Perform histogram equalization on the image.
7876 */
7877 XSetCursorState(display,windows,MagickTrue);
7878 XCheckRefreshWindows(display,windows);
7879 (void) EqualizeImage(*image);
7880 XSetCursorState(display,windows,MagickFalse);
7881 if (windows->image.orphan != MagickFalse)
7882 break;
7883 XConfigureImageColormap(display,resource_info,windows,*image);
7884 (void) XConfigureImage(display,resource_info,windows,*image);
7885 break;
7886 }
7887 case NegateCommand:
7888 {
7889 /*
7890 Negate colors in image.
7891 */
7892 XSetCursorState(display,windows,MagickTrue);
7893 XCheckRefreshWindows(display,windows);
7894 (void) NegateImage(*image,MagickFalse);
7895 XSetCursorState(display,windows,MagickFalse);
7896 if (windows->image.orphan != MagickFalse)
7897 break;
7898 XConfigureImageColormap(display,resource_info,windows,*image);
7899 (void) XConfigureImage(display,resource_info,windows,*image);
7900 break;
7901 }
7902 case GrayscaleCommand:
7903 {
7904 /*
7905 Convert image to grayscale.
7906 */
7907 XSetCursorState(display,windows,MagickTrue);
7908 XCheckRefreshWindows(display,windows);
7909 (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7910 GrayscaleType : GrayscaleMatteType);
7911 XSetCursorState(display,windows,MagickFalse);
7912 if (windows->image.orphan != MagickFalse)
7913 break;
7914 XConfigureImageColormap(display,resource_info,windows,*image);
7915 (void) XConfigureImage(display,resource_info,windows,*image);
7916 break;
7917 }
7918 case MapCommand:
7919 {
7920 Image
7921 *affinity_image;
7922
7923 static char
7924 filename[MaxTextExtent] = "\0";
7925
7926 /*
7927 Request image file name from user.
7928 */
7929 XFileBrowserWidget(display,windows,"Map",filename);
7930 if (*filename == '\0')
7931 break;
7932 /*
7933 Map image.
7934 */
7935 XSetCursorState(display,windows,MagickTrue);
7936 XCheckRefreshWindows(display,windows);
7937 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
7938 affinity_image=ReadImage(image_info,&(*image)->exception);
7939 if (affinity_image != (Image *) NULL)
7940 {
7941 (void) RemapImage(&quantize_info,*image,affinity_image);
7942 affinity_image=DestroyImage(affinity_image);
7943 }
7944 CatchException(&(*image)->exception);
7945 XSetCursorState(display,windows,MagickFalse);
7946 if (windows->image.orphan != MagickFalse)
7947 break;
7948 XConfigureImageColormap(display,resource_info,windows,*image);
7949 (void) XConfigureImage(display,resource_info,windows,*image);
7950 break;
7951 }
7952 case QuantizeCommand:
7953 {
7954 int
7955 status;
7956
7957 static char
7958 colors[MaxTextExtent] = "256";
7959
7960 /*
7961 Query user for maximum number of colors.
7962 */
7963 status=XDialogWidget(display,windows,"Quantize",
7964 "Maximum number of colors:",colors);
7965 if (*colors == '\0')
7966 break;
7967 /*
7968 Color reduce the image.
7969 */
7970 XSetCursorState(display,windows,MagickTrue);
7971 XCheckRefreshWindows(display,windows);
7972 quantize_info.number_colors=StringToUnsignedLong(colors);
7973 quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
7974 (void) QuantizeImage(&quantize_info,*image);
7975 XSetCursorState(display,windows,MagickFalse);
7976 if (windows->image.orphan != MagickFalse)
7977 break;
7978 XConfigureImageColormap(display,resource_info,windows,*image);
7979 (void) XConfigureImage(display,resource_info,windows,*image);
7980 break;
7981 }
7982 case DespeckleCommand:
7983 {
7984 Image
7985 *despeckle_image;
7986
7987 /*
7988 Despeckle image.
7989 */
7990 XSetCursorState(display,windows,MagickTrue);
7991 XCheckRefreshWindows(display,windows);
7992 despeckle_image=DespeckleImage(*image,&(*image)->exception);
7993 if (despeckle_image != (Image *) NULL)
7994 {
7995 *image=DestroyImage(*image);
7996 *image=despeckle_image;
7997 }
7998 CatchException(&(*image)->exception);
7999 XSetCursorState(display,windows,MagickFalse);
8000 if (windows->image.orphan != MagickFalse)
8001 break;
8002 XConfigureImageColormap(display,resource_info,windows,*image);
8003 (void) XConfigureImage(display,resource_info,windows,*image);
8004 break;
8005 }
8006 case EmbossCommand:
8007 {
8008 Image
8009 *emboss_image;
8010
8011 static char
8012 radius[MaxTextExtent] = "0.0x1.0";
8013
8014 /*
8015 Query user for emboss radius.
8016 */
8017 (void) XDialogWidget(display,windows,"Emboss",
8018 "Enter the emboss radius and standard deviation:",radius);
8019 if (*radius == '\0')
8020 break;
8021 /*
8022 Reduce noise in the image.
8023 */
8024 XSetCursorState(display,windows,MagickTrue);
8025 XCheckRefreshWindows(display,windows);
8026 flags=ParseGeometry(radius,&geometry_info);
8027 if ((flags & SigmaValue) == 0)
8028 geometry_info.sigma=1.0;
8029 emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8030 &(*image)->exception);
8031 if (emboss_image != (Image *) NULL)
8032 {
8033 *image=DestroyImage(*image);
8034 *image=emboss_image;
8035 }
8036 CatchException(&(*image)->exception);
8037 XSetCursorState(display,windows,MagickFalse);
8038 if (windows->image.orphan != MagickFalse)
8039 break;
8040 XConfigureImageColormap(display,resource_info,windows,*image);
8041 (void) XConfigureImage(display,resource_info,windows,*image);
8042 break;
8043 }
8044 case ReduceNoiseCommand:
8045 {
8046 Image
8047 *noise_image;
8048
8049 static char
8050 radius[MaxTextExtent] = "0";
8051
8052 /*
8053 Query user for noise radius.
8054 */
8055 (void) XDialogWidget(display,windows,"Reduce Noise",
8056 "Enter the noise radius:",radius);
8057 if (*radius == '\0')
8058 break;
8059 /*
8060 Reduce noise in the image.
8061 */
8062 XSetCursorState(display,windows,MagickTrue);
8063 XCheckRefreshWindows(display,windows);
8064 flags=ParseGeometry(radius,&geometry_info);
8065 noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8066 geometry_info.rho,(size_t) geometry_info.rho,&(*image)->exception);
8067 if (noise_image != (Image *) NULL)
8068 {
8069 *image=DestroyImage(*image);
8070 *image=noise_image;
8071 }
8072 CatchException(&(*image)->exception);
8073 XSetCursorState(display,windows,MagickFalse);
8074 if (windows->image.orphan != MagickFalse)
8075 break;
8076 XConfigureImageColormap(display,resource_info,windows,*image);
8077 (void) XConfigureImage(display,resource_info,windows,*image);
8078 break;
8079 }
8080 case AddNoiseCommand:
8081 {
8082 char
8083 **noises;
8084
8085 Image
8086 *noise_image;
8087
8088 static char
8089 noise_type[MaxTextExtent] = "Gaussian";
8090
8091 /*
8092 Add noise to the image.
8093 */
8094 noises=GetCommandOptions(MagickNoiseOptions);
8095 if (noises == (char **) NULL)
8096 break;
8097 XListBrowserWidget(display,windows,&windows->widget,
8098 (const char **) noises,"Add Noise",
8099 "Select a type of noise to add to your image:",noise_type);
8100 noises=DestroyStringList(noises);
8101 if (*noise_type == '\0')
8102 break;
8103 XSetCursorState(display,windows,MagickTrue);
8104 XCheckRefreshWindows(display,windows);
8105 noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8106 MagickNoiseOptions,MagickFalse,noise_type),&(*image)->exception);
8107 if (noise_image != (Image *) NULL)
8108 {
8109 *image=DestroyImage(*image);
8110 *image=noise_image;
8111 }
8112 CatchException(&(*image)->exception);
8113 XSetCursorState(display,windows,MagickFalse);
8114 if (windows->image.orphan != MagickFalse)
8115 break;
8116 XConfigureImageColormap(display,resource_info,windows,*image);
8117 (void) XConfigureImage(display,resource_info,windows,*image);
8118 break;
8119 }
8120 case SharpenCommand:
8121 {
8122 Image
8123 *sharp_image;
8124
8125 static char
8126 radius[MaxTextExtent] = "0.0x1.0";
8127
8128 /*
8129 Query user for sharpen radius.
8130 */
8131 (void) XDialogWidget(display,windows,"Sharpen",
8132 "Enter the sharpen radius and standard deviation:",radius);
8133 if (*radius == '\0')
8134 break;
8135 /*
8136 Sharpen image scanlines.
8137 */
8138 XSetCursorState(display,windows,MagickTrue);
8139 XCheckRefreshWindows(display,windows);
8140 flags=ParseGeometry(radius,&geometry_info);
8141 sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8142 &(*image)->exception);
8143 if (sharp_image != (Image *) NULL)
8144 {
8145 *image=DestroyImage(*image);
8146 *image=sharp_image;
8147 }
8148 CatchException(&(*image)->exception);
8149 XSetCursorState(display,windows,MagickFalse);
8150 if (windows->image.orphan != MagickFalse)
8151 break;
8152 XConfigureImageColormap(display,resource_info,windows,*image);
8153 (void) XConfigureImage(display,resource_info,windows,*image);
8154 break;
8155 }
8156 case BlurCommand:
8157 {
8158 Image
8159 *blur_image;
8160
8161 static char
8162 radius[MaxTextExtent] = "0.0x1.0";
8163
8164 /*
8165 Query user for blur radius.
8166 */
8167 (void) XDialogWidget(display,windows,"Blur",
8168 "Enter the blur radius and standard deviation:",radius);
8169 if (*radius == '\0')
8170 break;
8171 /*
8172 Blur an image.
8173 */
8174 XSetCursorState(display,windows,MagickTrue);
8175 XCheckRefreshWindows(display,windows);
8176 flags=ParseGeometry(radius,&geometry_info);
8177 blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8178 &(*image)->exception);
8179 if (blur_image != (Image *) NULL)
8180 {
8181 *image=DestroyImage(*image);
8182 *image=blur_image;
8183 }
8184 CatchException(&(*image)->exception);
8185 XSetCursorState(display,windows,MagickFalse);
8186 if (windows->image.orphan != MagickFalse)
8187 break;
8188 XConfigureImageColormap(display,resource_info,windows,*image);
8189 (void) XConfigureImage(display,resource_info,windows,*image);
8190 break;
8191 }
8192 case ThresholdCommand:
8193 {
8194 double
8195 threshold;
8196
8197 static char
8198 factor[MaxTextExtent] = "128";
8199
8200 /*
8201 Query user for threshold value.
8202 */
8203 (void) XDialogWidget(display,windows,"Threshold",
8204 "Enter threshold value:",factor);
8205 if (*factor == '\0')
8206 break;
8207 /*
8208 Gamma correct image.
8209 */
8210 XSetCursorState(display,windows,MagickTrue);
8211 XCheckRefreshWindows(display,windows);
8212 threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8213 (void) BilevelImage(*image,threshold);
8214 XSetCursorState(display,windows,MagickFalse);
8215 if (windows->image.orphan != MagickFalse)
8216 break;
8217 XConfigureImageColormap(display,resource_info,windows,*image);
8218 (void) XConfigureImage(display,resource_info,windows,*image);
8219 break;
8220 }
8221 case EdgeDetectCommand:
8222 {
8223 Image
8224 *edge_image;
8225
8226 static char
8227 radius[MaxTextExtent] = "0";
8228
8229 /*
8230 Query user for edge factor.
8231 */
8232 (void) XDialogWidget(display,windows,"Detect Edges",
8233 "Enter the edge detect radius:",radius);
8234 if (*radius == '\0')
8235 break;
8236 /*
8237 Detect edge in image.
8238 */
8239 XSetCursorState(display,windows,MagickTrue);
8240 XCheckRefreshWindows(display,windows);
8241 flags=ParseGeometry(radius,&geometry_info);
8242 edge_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8243 if (edge_image != (Image *) NULL)
8244 {
8245 *image=DestroyImage(*image);
8246 *image=edge_image;
8247 }
8248 CatchException(&(*image)->exception);
8249 XSetCursorState(display,windows,MagickFalse);
8250 if (windows->image.orphan != MagickFalse)
8251 break;
8252 XConfigureImageColormap(display,resource_info,windows,*image);
8253 (void) XConfigureImage(display,resource_info,windows,*image);
8254 break;
8255 }
8256 case SpreadCommand:
8257 {
8258 Image
8259 *spread_image;
8260
8261 static char
8262 amount[MaxTextExtent] = "2";
8263
8264 /*
8265 Query user for spread amount.
8266 */
8267 (void) XDialogWidget(display,windows,"Spread",
8268 "Enter the displacement amount:",amount);
8269 if (*amount == '\0')
8270 break;
8271 /*
8272 Displace image pixels by a random amount.
8273 */
8274 XSetCursorState(display,windows,MagickTrue);
8275 XCheckRefreshWindows(display,windows);
8276 flags=ParseGeometry(amount,&geometry_info);
8277 spread_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8278 if (spread_image != (Image *) NULL)
8279 {
8280 *image=DestroyImage(*image);
8281 *image=spread_image;
8282 }
8283 CatchException(&(*image)->exception);
8284 XSetCursorState(display,windows,MagickFalse);
8285 if (windows->image.orphan != MagickFalse)
8286 break;
8287 XConfigureImageColormap(display,resource_info,windows,*image);
8288 (void) XConfigureImage(display,resource_info,windows,*image);
8289 break;
8290 }
8291 case ShadeCommand:
8292 {
8293 Image
8294 *shade_image;
8295
8296 int
8297 status;
8298
8299 static char
8300 geometry[MaxTextExtent] = "30x30";
8301
8302 /*
8303 Query user for the shade geometry.
8304 */
8305 status=XDialogWidget(display,windows,"Shade",
8306 "Enter the azimuth and elevation of the light source:",geometry);
8307 if (*geometry == '\0')
8308 break;
8309 /*
8310 Shade image pixels.
8311 */
8312 XSetCursorState(display,windows,MagickTrue);
8313 XCheckRefreshWindows(display,windows);
8314 flags=ParseGeometry(geometry,&geometry_info);
8315 if ((flags & SigmaValue) == 0)
8316 geometry_info.sigma=1.0;
8317 shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8318 geometry_info.rho,geometry_info.sigma,&(*image)->exception);
8319 if (shade_image != (Image *) NULL)
8320 {
8321 *image=DestroyImage(*image);
8322 *image=shade_image;
8323 }
8324 CatchException(&(*image)->exception);
8325 XSetCursorState(display,windows,MagickFalse);
8326 if (windows->image.orphan != MagickFalse)
8327 break;
8328 XConfigureImageColormap(display,resource_info,windows,*image);
8329 (void) XConfigureImage(display,resource_info,windows,*image);
8330 break;
8331 }
8332 case RaiseCommand:
8333 {
8334 static char
8335 bevel_width[MaxTextExtent] = "10";
8336
8337 /*
8338 Query user for bevel width.
8339 */
8340 (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8341 if (*bevel_width == '\0')
8342 break;
8343 /*
8344 Raise an image.
8345 */
8346 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8347 XSetCursorState(display,windows,MagickTrue);
8348 XCheckRefreshWindows(display,windows);
8349 (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8350 &(*image)->exception);
8351 (void) RaiseImage(*image,&page_geometry,MagickTrue);
8352 XSetCursorState(display,windows,MagickFalse);
8353 if (windows->image.orphan != MagickFalse)
8354 break;
8355 XConfigureImageColormap(display,resource_info,windows,*image);
8356 (void) XConfigureImage(display,resource_info,windows,*image);
8357 break;
8358 }
8359 case SegmentCommand:
8360 {
8361 static char
8362 threshold[MaxTextExtent] = "1.0x1.5";
8363
8364 /*
8365 Query user for smoothing threshold.
8366 */
8367 (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8368 threshold);
8369 if (*threshold == '\0')
8370 break;
8371 /*
8372 Segment an image.
8373 */
8374 XSetCursorState(display,windows,MagickTrue);
8375 XCheckRefreshWindows(display,windows);
8376 flags=ParseGeometry(threshold,&geometry_info);
8377 if ((flags & SigmaValue) == 0)
8378 geometry_info.sigma=1.0;
8379 (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8380 geometry_info.sigma);
8381 XSetCursorState(display,windows,MagickFalse);
8382 if (windows->image.orphan != MagickFalse)
8383 break;
8384 XConfigureImageColormap(display,resource_info,windows,*image);
8385 (void) XConfigureImage(display,resource_info,windows,*image);
8386 break;
8387 }
8388 case SepiaToneCommand:
8389 {
8390 double
8391 threshold;
8392
8393 Image
8394 *sepia_image;
8395
8396 static char
8397 factor[MaxTextExtent] = "80%";
8398
8399 /*
8400 Query user for sepia-tone factor.
8401 */
8402 (void) XDialogWidget(display,windows,"Sepia Tone",
8403 "Enter the sepia tone factor (0 - 99.9%):",factor);
8404 if (*factor == '\0')
8405 break;
8406 /*
8407 Sepia tone image pixels.
8408 */
8409 XSetCursorState(display,windows,MagickTrue);
8410 XCheckRefreshWindows(display,windows);
8411 threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8412 sepia_image=SepiaToneImage(*image,threshold,&(*image)->exception);
8413 if (sepia_image != (Image *) NULL)
8414 {
8415 *image=DestroyImage(*image);
8416 *image=sepia_image;
8417 }
8418 CatchException(&(*image)->exception);
8419 XSetCursorState(display,windows,MagickFalse);
8420 if (windows->image.orphan != MagickFalse)
8421 break;
8422 XConfigureImageColormap(display,resource_info,windows,*image);
8423 (void) XConfigureImage(display,resource_info,windows,*image);
8424 break;
8425 }
8426 case SolarizeCommand:
8427 {
8428 double
8429 threshold;
8430
8431 static char
8432 factor[MaxTextExtent] = "60%";
8433
8434 /*
8435 Query user for solarize factor.
8436 */
8437 (void) XDialogWidget(display,windows,"Solarize",
8438 "Enter the solarize factor (0 - 99.9%):",factor);
8439 if (*factor == '\0')
8440 break;
8441 /*
8442 Solarize image pixels.
8443 */
8444 XSetCursorState(display,windows,MagickTrue);
8445 XCheckRefreshWindows(display,windows);
8446 threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8447 (void) SolarizeImage(*image,threshold);
8448 XSetCursorState(display,windows,MagickFalse);
8449 if (windows->image.orphan != MagickFalse)
8450 break;
8451 XConfigureImageColormap(display,resource_info,windows,*image);
8452 (void) XConfigureImage(display,resource_info,windows,*image);
8453 break;
8454 }
8455 case SwirlCommand:
8456 {
8457 Image
8458 *swirl_image;
8459
8460 static char
8461 degrees[MaxTextExtent] = "60";
8462
8463 /*
8464 Query user for swirl angle.
8465 */
8466 (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8467 degrees);
8468 if (*degrees == '\0')
8469 break;
8470 /*
8471 Swirl image pixels about the center.
8472 */
8473 XSetCursorState(display,windows,MagickTrue);
8474 XCheckRefreshWindows(display,windows);
8475 flags=ParseGeometry(degrees,&geometry_info);
8476 swirl_image=SwirlImage(*image,geometry_info.rho,&(*image)->exception);
8477 if (swirl_image != (Image *) NULL)
8478 {
8479 *image=DestroyImage(*image);
8480 *image=swirl_image;
8481 }
8482 CatchException(&(*image)->exception);
8483 XSetCursorState(display,windows,MagickFalse);
8484 if (windows->image.orphan != MagickFalse)
8485 break;
8486 XConfigureImageColormap(display,resource_info,windows,*image);
8487 (void) XConfigureImage(display,resource_info,windows,*image);
8488 break;
8489 }
8490 case ImplodeCommand:
8491 {
8492 Image
8493 *implode_image;
8494
8495 static char
8496 factor[MaxTextExtent] = "0.3";
8497
8498 /*
8499 Query user for implode factor.
8500 */
8501 (void) XDialogWidget(display,windows,"Implode",
8502 "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8503 if (*factor == '\0')
8504 break;
8505 /*
8506 Implode image pixels about the center.
8507 */
8508 XSetCursorState(display,windows,MagickTrue);
8509 XCheckRefreshWindows(display,windows);
8510 flags=ParseGeometry(factor,&geometry_info);
8511 implode_image=ImplodeImage(*image,geometry_info.rho,&(*image)->exception);
8512 if (implode_image != (Image *) NULL)
8513 {
8514 *image=DestroyImage(*image);
8515 *image=implode_image;
8516 }
8517 CatchException(&(*image)->exception);
8518 XSetCursorState(display,windows,MagickFalse);
8519 if (windows->image.orphan != MagickFalse)
8520 break;
8521 XConfigureImageColormap(display,resource_info,windows,*image);
8522 (void) XConfigureImage(display,resource_info,windows,*image);
8523 break;
8524 }
8525 case VignetteCommand:
8526 {
8527 Image
8528 *vignette_image;
8529
8530 static char
8531 geometry[MaxTextExtent] = "0x20";
8532
8533 /*
8534 Query user for the vignette geometry.
8535 */
8536 (void) XDialogWidget(display,windows,"Vignette",
8537 "Enter the radius, sigma, and x and y offsets:",geometry);
8538 if (*geometry == '\0')
8539 break;
8540 /*
8541 Soften the edges of the image in vignette style
8542 */
8543 XSetCursorState(display,windows,MagickTrue);
8544 XCheckRefreshWindows(display,windows);
8545 flags=ParseGeometry(geometry,&geometry_info);
8546 if ((flags & SigmaValue) == 0)
8547 geometry_info.sigma=1.0;
8548 if ((flags & XiValue) == 0)
8549 geometry_info.xi=0.1*(*image)->columns;
8550 if ((flags & PsiValue) == 0)
8551 geometry_info.psi=0.1*(*image)->rows;
8552 vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8553 (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8554 0.5),&(*image)->exception);
8555 if (vignette_image != (Image *) NULL)
8556 {
8557 *image=DestroyImage(*image);
8558 *image=vignette_image;
8559 }
8560 CatchException(&(*image)->exception);
8561 XSetCursorState(display,windows,MagickFalse);
8562 if (windows->image.orphan != MagickFalse)
8563 break;
8564 XConfigureImageColormap(display,resource_info,windows,*image);
8565 (void) XConfigureImage(display,resource_info,windows,*image);
8566 break;
8567 }
8568 case WaveCommand:
8569 {
8570 Image
8571 *wave_image;
8572
8573 static char
8574 geometry[MaxTextExtent] = "25x150";
8575
8576 /*
8577 Query user for the wave geometry.
8578 */
8579 (void) XDialogWidget(display,windows,"Wave",
8580 "Enter the amplitude and length of the wave:",geometry);
8581 if (*geometry == '\0')
8582 break;
8583 /*
8584 Alter an image along a sine wave.
8585 */
8586 XSetCursorState(display,windows,MagickTrue);
8587 XCheckRefreshWindows(display,windows);
8588 flags=ParseGeometry(geometry,&geometry_info);
8589 if ((flags & SigmaValue) == 0)
8590 geometry_info.sigma=1.0;
8591 wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8592 &(*image)->exception);
8593 if (wave_image != (Image *) NULL)
8594 {
8595 *image=DestroyImage(*image);
8596 *image=wave_image;
8597 }
8598 CatchException(&(*image)->exception);
8599 XSetCursorState(display,windows,MagickFalse);
8600 if (windows->image.orphan != MagickFalse)
8601 break;
8602 XConfigureImageColormap(display,resource_info,windows,*image);
8603 (void) XConfigureImage(display,resource_info,windows,*image);
8604 break;
8605 }
8606 case OilPaintCommand:
8607 {
8608 Image
8609 *paint_image;
8610
8611 static char
8612 radius[MaxTextExtent] = "0";
8613
8614 /*
8615 Query user for circular neighborhood radius.
8616 */
8617 (void) XDialogWidget(display,windows,"Oil Paint",
8618 "Enter the mask radius:",radius);
8619 if (*radius == '\0')
8620 break;
8621 /*
8622 OilPaint image scanlines.
8623 */
8624 XSetCursorState(display,windows,MagickTrue);
8625 XCheckRefreshWindows(display,windows);
8626 flags=ParseGeometry(radius,&geometry_info);
8627 paint_image=OilPaintImage(*image,geometry_info.rho,&(*image)->exception);
8628 if (paint_image != (Image *) NULL)
8629 {
8630 *image=DestroyImage(*image);
8631 *image=paint_image;
8632 }
8633 CatchException(&(*image)->exception);
8634 XSetCursorState(display,windows,MagickFalse);
8635 if (windows->image.orphan != MagickFalse)
8636 break;
8637 XConfigureImageColormap(display,resource_info,windows,*image);
8638 (void) XConfigureImage(display,resource_info,windows,*image);
8639 break;
8640 }
8641 case CharcoalDrawCommand:
8642 {
8643 Image
8644 *charcoal_image;
8645
8646 static char
8647 radius[MaxTextExtent] = "0x1";
8648
8649 /*
8650 Query user for charcoal radius.
8651 */
8652 (void) XDialogWidget(display,windows,"Charcoal Draw",
8653 "Enter the charcoal radius and sigma:",radius);
8654 if (*radius == '\0')
8655 break;
8656 /*
8657 Charcoal the image.
8658 */
8659 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8660 XSetCursorState(display,windows,MagickTrue);
8661 XCheckRefreshWindows(display,windows);
8662 flags=ParseGeometry(radius,&geometry_info);
8663 if ((flags & SigmaValue) == 0)
8664 geometry_info.sigma=geometry_info.rho;
8665 charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8666 &(*image)->exception);
8667 if (charcoal_image != (Image *) NULL)
8668 {
8669 *image=DestroyImage(*image);
8670 *image=charcoal_image;
8671 }
8672 CatchException(&(*image)->exception);
8673 XSetCursorState(display,windows,MagickFalse);
8674 if (windows->image.orphan != MagickFalse)
8675 break;
8676 XConfigureImageColormap(display,resource_info,windows,*image);
8677 (void) XConfigureImage(display,resource_info,windows,*image);
8678 break;
8679 }
8680 case AnnotateCommand:
8681 {
8682 /*
8683 Annotate the image with text.
8684 */
8685 status=XAnnotateEditImage(display,resource_info,windows,*image);
8686 if (status == MagickFalse)
8687 {
8688 XNoticeWidget(display,windows,"Unable to annotate X image",
8689 (*image)->filename);
8690 break;
8691 }
8692 break;
8693 }
8694 case DrawCommand:
8695 {
8696 /*
8697 Draw image.
8698 */
8699 status=XDrawEditImage(display,resource_info,windows,image);
8700 if (status == MagickFalse)
8701 {
8702 XNoticeWidget(display,windows,"Unable to draw on the X image",
8703 (*image)->filename);
8704 break;
8705 }
8706 break;
8707 }
8708 case ColorCommand:
8709 {
8710 /*
8711 Color edit.
8712 */
8713 status=XColorEditImage(display,resource_info,windows,image);
8714 if (status == MagickFalse)
8715 {
8716 XNoticeWidget(display,windows,"Unable to pixel edit X image",
8717 (*image)->filename);
8718 break;
8719 }
8720 break;
8721 }
8722 case MatteCommand:
8723 {
8724 /*
8725 Matte edit.
8726 */
8727 status=XMatteEditImage(display,resource_info,windows,image);
8728 if (status == MagickFalse)
8729 {
8730 XNoticeWidget(display,windows,"Unable to matte edit X image",
8731 (*image)->filename);
8732 break;
8733 }
8734 break;
8735 }
8736 case CompositeCommand:
8737 {
8738 /*
8739 Composite image.
8740 */
8741 status=XCompositeImage(display,resource_info,windows,*image);
8742 if (status == MagickFalse)
8743 {
8744 XNoticeWidget(display,windows,"Unable to composite X image",
8745 (*image)->filename);
8746 break;
8747 }
8748 break;
8749 }
8750 case AddBorderCommand:
8751 {
8752 Image
8753 *border_image;
8754
8755 static char
8756 geometry[MaxTextExtent] = "6x6";
8757
8758 /*
8759 Query user for border color and geometry.
8760 */
8761 XColorBrowserWidget(display,windows,"Select",color);
8762 if (*color == '\0')
8763 break;
8764 (void) XDialogWidget(display,windows,"Add Border",
8765 "Enter border geometry:",geometry);
8766 if (*geometry == '\0')
8767 break;
8768 /*
8769 Add a border to the image.
8770 */
8771 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8772 XSetCursorState(display,windows,MagickTrue);
8773 XCheckRefreshWindows(display,windows);
8774 (void) QueryColorDatabase(color,&(*image)->border_color,
8775 &(*image)->exception);
8776 (void) ParsePageGeometry(*image,geometry,&page_geometry,
8777 &(*image)->exception);
8778 border_image=BorderImage(*image,&page_geometry,&(*image)->exception);
8779 if (border_image != (Image *) NULL)
8780 {
8781 *image=DestroyImage(*image);
8782 *image=border_image;
8783 }
8784 CatchException(&(*image)->exception);
8785 XSetCursorState(display,windows,MagickFalse);
8786 if (windows->image.orphan != MagickFalse)
8787 break;
8788 windows->image.window_changes.width=(int) (*image)->columns;
8789 windows->image.window_changes.height=(int) (*image)->rows;
8790 XConfigureImageColormap(display,resource_info,windows,*image);
8791 (void) XConfigureImage(display,resource_info,windows,*image);
8792 break;
8793 }
8794 case AddFrameCommand:
8795 {
8796 FrameInfo
8797 frame_info;
8798
8799 Image
8800 *frame_image;
8801
8802 static char
8803 geometry[MaxTextExtent] = "6x6";
8804
8805 /*
8806 Query user for frame color and geometry.
8807 */
8808 XColorBrowserWidget(display,windows,"Select",color);
8809 if (*color == '\0')
8810 break;
8811 (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8812 geometry);
8813 if (*geometry == '\0')
8814 break;
8815 /*
8816 Surround image with an ornamental border.
8817 */
8818 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8819 XSetCursorState(display,windows,MagickTrue);
8820 XCheckRefreshWindows(display,windows);
8821 (void) QueryColorDatabase(color,&(*image)->matte_color,
8822 &(*image)->exception);
8823 (void) ParsePageGeometry(*image,geometry,&page_geometry,
8824 &(*image)->exception);
8825 frame_info.width=page_geometry.width;
8826 frame_info.height=page_geometry.height;
8827 frame_info.outer_bevel=page_geometry.x;
8828 frame_info.inner_bevel=page_geometry.y;
8829 frame_info.x=(ssize_t) frame_info.width;
8830 frame_info.y=(ssize_t) frame_info.height;
8831 frame_info.width=(*image)->columns+2*frame_info.width;
8832 frame_info.height=(*image)->rows+2*frame_info.height;
8833 frame_image=FrameImage(*image,&frame_info,&(*image)->exception);
8834 if (frame_image != (Image *) NULL)
8835 {
8836 *image=DestroyImage(*image);
8837 *image=frame_image;
8838 }
8839 CatchException(&(*image)->exception);
8840 XSetCursorState(display,windows,MagickFalse);
8841 if (windows->image.orphan != MagickFalse)
8842 break;
8843 windows->image.window_changes.width=(int) (*image)->columns;
8844 windows->image.window_changes.height=(int) (*image)->rows;
8845 XConfigureImageColormap(display,resource_info,windows,*image);
8846 (void) XConfigureImage(display,resource_info,windows,*image);
8847 break;
8848 }
8849 case CommentCommand:
8850 {
8851 const char
8852 *value;
8853
8854 FILE
8855 *file;
8856
8857 int
8858 unique_file;
8859
8860 /*
8861 Edit image comment.
8862 */
8863 unique_file=AcquireUniqueFileResource(image_info->filename);
8864 if (unique_file == -1)
8865 {
8866 XNoticeWidget(display,windows,"Unable to edit image comment",
8867 image_info->filename);
8868 break;
8869 }
8870 value=GetImageProperty(*image,"comment");
8871 if (value == (char *) NULL)
8872 unique_file=close(unique_file)-1;
8873 else
8874 {
8875 const char
8876 *p;
8877
8878 file=fdopen(unique_file,"w");
8879 if (file == (FILE *) NULL)
8880 {
8881 XNoticeWidget(display,windows,"Unable to edit image comment",
8882 image_info->filename);
8883 break;
8884 }
8885 for (p=value; *p != '\0'; p++)
8886 (void) fputc((int) *p,file);
8887 (void) fputc('\n',file);
8888 (void) fclose(file);
8889 }
8890 XSetCursorState(display,windows,MagickTrue);
8891 XCheckRefreshWindows(display,windows);
8892 status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8893 &(*image)->exception);
8894 if (status == MagickFalse)
8895 XNoticeWidget(display,windows,"Unable to edit image comment",
8896 (char *) NULL);
8897 else
8898 {
8899 char
8900 *comment;
8901
8902 comment=FileToString(image_info->filename,~0UL,&(*image)->exception);
8903 if (comment != (char *) NULL)
8904 {
8905 (void) SetImageProperty(*image,"comment",comment);
8906 (*image)->taint=MagickTrue;
8907 }
8908 }
8909 (void) RelinquishUniqueFileResource(image_info->filename);
8910 XSetCursorState(display,windows,MagickFalse);
8911 break;
8912 }
8913 case LaunchCommand:
8914 {
8915 /*
8916 Launch program.
8917 */
8918 XSetCursorState(display,windows,MagickTrue);
8919 XCheckRefreshWindows(display,windows);
8920 (void) AcquireUniqueFilename(filename);
8921 (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
8922 filename);
8923 status=WriteImage(image_info,*image);
8924 if (status == MagickFalse)
8925 XNoticeWidget(display,windows,"Unable to launch image editor",
8926 (char *) NULL);
8927 else
8928 {
8929 nexus=ReadImage(resource_info->image_info,&(*image)->exception);
8930 CatchException(&(*image)->exception);
8931 XClientMessage(display,windows->image.id,windows->im_protocols,
8932 windows->im_next_image,CurrentTime);
8933 }
8934 (void) RelinquishUniqueFileResource(filename);
8935 XSetCursorState(display,windows,MagickFalse);
8936 break;
8937 }
8938 case RegionOfInterestCommand:
8939 {
8940 /*
8941 Apply an image processing technique to a region of interest.
8942 */
8943 (void) XROIImage(display,resource_info,windows,image);
8944 break;
8945 }
8946 case InfoCommand:
8947 break;
8948 case ZoomCommand:
8949 {
8950 /*
8951 Zoom image.
8952 */
8953 if (windows->magnify.mapped != MagickFalse)
8954 (void) XRaiseWindow(display,windows->magnify.id);
8955 else
8956 {
8957 /*
8958 Make magnify image.
8959 */
8960 XSetCursorState(display,windows,MagickTrue);
8961 (void) XMapRaised(display,windows->magnify.id);
8962 XSetCursorState(display,windows,MagickFalse);
8963 }
8964 break;
8965 }
8966 case ShowPreviewCommand:
8967 {
8968 char
8969 **previews;
8970
8971 Image
8972 *preview_image;
8973
8974 static char
8975 preview_type[MaxTextExtent] = "Gamma";
8976
8977 /*
8978 Select preview type from menu.
8979 */
8980 previews=GetCommandOptions(MagickPreviewOptions);
8981 if (previews == (char **) NULL)
8982 break;
8983 XListBrowserWidget(display,windows,&windows->widget,
8984 (const char **) previews,"Preview",
8985 "Select an enhancement, effect, or F/X:",preview_type);
8986 previews=DestroyStringList(previews);
8987 if (*preview_type == '\0')
8988 break;
8989 /*
8990 Show image preview.
8991 */
8992 XSetCursorState(display,windows,MagickTrue);
8993 XCheckRefreshWindows(display,windows);
8994 image_info->preview_type=(PreviewType)
8995 ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
8996 image_info->group=(ssize_t) windows->image.id;
8997 (void) DeleteImageProperty(*image,"label");
8998 (void) SetImageProperty(*image,"label","Preview");
8999 (void) AcquireUniqueFilename(filename);
9000 (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9001 filename);
9002 status=WriteImage(image_info,*image);
9003 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9004 preview_image=ReadImage(image_info,&(*image)->exception);
9005 (void) RelinquishUniqueFileResource(filename);
9006 if (preview_image == (Image *) NULL)
9007 break;
9008 (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9009 filename);
9010 status=WriteImage(image_info,preview_image);
9011 preview_image=DestroyImage(preview_image);
9012 if (status == MagickFalse)
9013 XNoticeWidget(display,windows,"Unable to show image preview",
9014 (*image)->filename);
9015 XDelay(display,1500);
9016 XSetCursorState(display,windows,MagickFalse);
9017 break;
9018 }
9019 case ShowHistogramCommand:
9020 {
9021 Image
9022 *histogram_image;
9023
9024 /*
9025 Show image histogram.
9026 */
9027 XSetCursorState(display,windows,MagickTrue);
9028 XCheckRefreshWindows(display,windows);
9029 image_info->group=(ssize_t) windows->image.id;
9030 (void) DeleteImageProperty(*image,"label");
9031 (void) SetImageProperty(*image,"label","Histogram");
9032 (void) AcquireUniqueFilename(filename);
9033 (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9034 filename);
9035 status=WriteImage(image_info,*image);
9036 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9037 histogram_image=ReadImage(image_info,&(*image)->exception);
9038 (void) RelinquishUniqueFileResource(filename);
9039 if (histogram_image == (Image *) NULL)
9040 break;
9041 (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9042 "show:%s",filename);
9043 status=WriteImage(image_info,histogram_image);
9044 histogram_image=DestroyImage(histogram_image);
9045 if (status == MagickFalse)
9046 XNoticeWidget(display,windows,"Unable to show histogram",
9047 (*image)->filename);
9048 XDelay(display,1500);
9049 XSetCursorState(display,windows,MagickFalse);
9050 break;
9051 }
9052 case ShowMatteCommand:
9053 {
9054 Image
9055 *matte_image;
9056
9057 if ((*image)->matte == MagickFalse)
9058 {
9059 XNoticeWidget(display,windows,
9060 "Image does not have any matte information",(*image)->filename);
9061 break;
9062 }
9063 /*
9064 Show image matte.
9065 */
9066 XSetCursorState(display,windows,MagickTrue);
9067 XCheckRefreshWindows(display,windows);
9068 image_info->group=(ssize_t) windows->image.id;
9069 (void) DeleteImageProperty(*image,"label");
9070 (void) SetImageProperty(*image,"label","Matte");
9071 (void) AcquireUniqueFilename(filename);
9072 (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9073 filename);
9074 status=WriteImage(image_info,*image);
9075 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9076 matte_image=ReadImage(image_info,&(*image)->exception);
9077 (void) RelinquishUniqueFileResource(filename);
9078 if (matte_image == (Image *) NULL)
9079 break;
9080 (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9081 filename);
9082 status=WriteImage(image_info,matte_image);
9083 matte_image=DestroyImage(matte_image);
9084 if (status == MagickFalse)
9085 XNoticeWidget(display,windows,"Unable to show matte",
9086 (*image)->filename);
9087 XDelay(display,1500);
9088 XSetCursorState(display,windows,MagickFalse);
9089 break;
9090 }
9091 case BackgroundCommand:
9092 {
9093 /*
9094 Background image.
9095 */
9096 status=XBackgroundImage(display,resource_info,windows,image);
9097 if (status == MagickFalse)
9098 break;
9099 nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9100 if (nexus != (Image *) NULL)
9101 XClientMessage(display,windows->image.id,windows->im_protocols,
9102 windows->im_next_image,CurrentTime);
9103 break;
9104 }
9105 case SlideShowCommand:
9106 {
9107 static char
9108 delay[MaxTextExtent] = "5";
9109
9110 /*
9111 Display next image after pausing.
9112 */
9113 (void) XDialogWidget(display,windows,"Slide Show",
9114 "Pause how many 1/100ths of a second between images:",delay);
9115 if (*delay == '\0')
9116 break;
9117 resource_info->delay=StringToUnsignedLong(delay);
9118 XClientMessage(display,windows->image.id,windows->im_protocols,
9119 windows->im_next_image,CurrentTime);
9120 break;
9121 }
9122 case PreferencesCommand:
9123 {
9124 /*
9125 Set user preferences.
9126 */
9127 status=XPreferencesWidget(display,resource_info,windows);
9128 if (status == MagickFalse)
9129 break;
9130 nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9131 if (nexus != (Image *) NULL)
9132 XClientMessage(display,windows->image.id,windows->im_protocols,
9133 windows->im_next_image,CurrentTime);
9134 break;
9135 }
9136 case HelpCommand:
9137 {
9138 /*
9139 User requested help.
9140 */
9141 XTextViewHelp(display,resource_info,windows,MagickFalse,
9142 "Help Viewer - Display",DisplayHelp);
9143 break;
9144 }
9145 case BrowseDocumentationCommand:
9146 {
9147 Atom
9148 mozilla_atom;
9149
9150 Window
9151 mozilla_window,
9152 root_window;
9153
9154 /*
9155 Browse the ImageMagick documentation.
9156 */
9157 root_window=XRootWindow(display,XDefaultScreen(display));
9158 mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9159 mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9160 if (mozilla_window != (Window) NULL)
9161 {
9162 char
9163 command[MaxTextExtent];
9164
9165 /*
9166 Display documentation using Netscape remote control.
9167 */
9168 (void) FormatLocaleString(command,MaxTextExtent,
9169 "openurl(%s,new-tab)",MagickAuthoritativeURL);
9170 mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9171 (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9172 8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9173 XSetCursorState(display,windows,MagickFalse);
9174 break;
9175 }
9176 XSetCursorState(display,windows,MagickTrue);
9177 XCheckRefreshWindows(display,windows);
9178 status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9179 &(*image)->exception);
9180 if (status == MagickFalse)
9181 XNoticeWidget(display,windows,"Unable to browse documentation",
9182 (char *) NULL);
9183 XDelay(display,1500);
9184 XSetCursorState(display,windows,MagickFalse);
9185 break;
9186 }
9187 case VersionCommand:
9188 {
9189 XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9190 GetMagickCopyright());
9191 break;
9192 }
9193 case SaveToUndoBufferCommand:
9194 break;
9195 default:
9196 {
9197 (void) XBell(display,0);
9198 break;
9199 }
9200 }
9201 image_info=DestroyImageInfo(image_info);
9202 return(nexus);
9203}
9204
9205/*
9206%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9207% %
9208% %
9209% %
9210+ X M a g n i f y I m a g e %
9211% %
9212% %
9213% %
9214%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9215%
9216% XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9217% The magnified portion is displayed in a separate window.
9218%
9219% The format of the XMagnifyImage method is:
9220%
9221% void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9222%
9223% A description of each parameter follows:
9224%
9225% o display: Specifies a connection to an X server; returned from
9226% XOpenDisplay.
9227%
9228% o windows: Specifies a pointer to a XWindows structure.
9229%
9230% o event: Specifies a pointer to a XEvent structure. If it is NULL,
9231% the entire image is refreshed.
9232%
9233*/
9234static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9235{
9236 char
9237 text[MaxTextExtent];
9238
9239 int
9240 x,
9241 y;
9242
9243 size_t
9244 state;
9245
9246 /*
9247 Update magnified image until the mouse button is released.
9248 */
9249 (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9250 state=DefaultState;
9251 x=event->xbutton.x;
9252 y=event->xbutton.y;
9253 windows->magnify.x=(int) windows->image.x+x;
9254 windows->magnify.y=(int) windows->image.y+y;
9255 do
9256 {
9257 /*
9258 Map and unmap Info widget as text cursor crosses its boundaries.
9259 */
9260 if (windows->info.mapped != MagickFalse)
9261 {
9262 if ((x < (int) (windows->info.x+windows->info.width)) &&
9263 (y < (int) (windows->info.y+windows->info.height)))
9264 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9265 }
9266 else
9267 if ((x > (int) (windows->info.x+windows->info.width)) ||
9268 (y > (int) (windows->info.y+windows->info.height)))
9269 (void) XMapWindow(display,windows->info.id);
9270 if (windows->info.mapped != MagickFalse)
9271 {
9272 /*
9273 Display pointer position.
9274 */
9275 (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9276 windows->magnify.x,windows->magnify.y);
9277 XInfoWidget(display,windows,text);
9278 }
9279 /*
9280 Wait for next event.
9281 */
9282 XScreenEvent(display,windows,event);
9283 switch (event->type)
9284 {
9285 case ButtonPress:
9286 break;
9287 case ButtonRelease:
9288 {
9289 /*
9290 User has finished magnifying image.
9291 */
9292 x=event->xbutton.x;
9293 y=event->xbutton.y;
9294 state|=ExitState;
9295 break;
9296 }
9297 case Expose:
9298 break;
9299 case MotionNotify:
9300 {
9301 x=event->xmotion.x;
9302 y=event->xmotion.y;
9303 break;
9304 }
9305 default:
9306 break;
9307 }
9308 /*
9309 Check boundary conditions.
9310 */
9311 if (x < 0)
9312 x=0;
9313 else
9314 if (x >= (int) windows->image.width)
9315 x=(int) windows->image.width-1;
9316 if (y < 0)
9317 y=0;
9318 else
9319 if (y >= (int) windows->image.height)
9320 y=(int) windows->image.height-1;
9321 } while ((state & ExitState) == 0);
9322 /*
9323 Display magnified image.
9324 */
9325 XSetCursorState(display,windows,MagickFalse);
9326}
9327
9328/*
9329%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9330% %
9331% %
9332% %
9333+ X M a g n i f y W i n d o w C o m m a n d %
9334% %
9335% %
9336% %
9337%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9338%
9339% XMagnifyWindowCommand() moves the image within an Magnify window by one
9340% pixel as specified by the key symbol.
9341%
9342% The format of the XMagnifyWindowCommand method is:
9343%
9344% void XMagnifyWindowCommand(Display *display,XWindows *windows,
9345% const MagickStatusType state,const KeySym key_symbol)
9346%
9347% A description of each parameter follows:
9348%
9349% o display: Specifies a connection to an X server; returned from
9350% XOpenDisplay.
9351%
9352% o windows: Specifies a pointer to a XWindows structure.
9353%
9354% o state: key mask.
9355%
9356% o key_symbol: Specifies a KeySym which indicates which side of the image
9357% to trim.
9358%
9359*/
9360static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9361 const MagickStatusType state,const KeySym key_symbol)
9362{
9363 unsigned int
9364 quantum;
9365
9366 /*
9367 User specified a magnify factor or position.
9368 */
9369 quantum=1;
9370 if ((state & Mod1Mask) != 0)
9371 quantum=10;
9372 switch ((int) key_symbol)
9373 {
9374 case QuitCommand:
9375 {
9376 (void) XWithdrawWindow(display,windows->magnify.id,
9377 windows->magnify.screen);
9378 break;
9379 }
9380 case XK_Home:
9381 case XK_KP_Home:
9382 {
9383 windows->magnify.x=(int) windows->image.width/2;
9384 windows->magnify.y=(int) windows->image.height/2;
9385 break;
9386 }
9387 case XK_Left:
9388 case XK_KP_Left:
9389 {
9390 if (windows->magnify.x > 0)
9391 windows->magnify.x-=quantum;
9392 break;
9393 }
9394 case XK_Up:
9395 case XK_KP_Up:
9396 {
9397 if (windows->magnify.y > 0)
9398 windows->magnify.y-=quantum;
9399 break;
9400 }
9401 case XK_Right:
9402 case XK_KP_Right:
9403 {
9404 if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9405 windows->magnify.x+=quantum;
9406 break;
9407 }
9408 case XK_Down:
9409 case XK_KP_Down:
9410 {
9411 if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9412 windows->magnify.y+=quantum;
9413 break;
9414 }
9415 case XK_0:
9416 case XK_1:
9417 case XK_2:
9418 case XK_3:
9419 case XK_4:
9420 case XK_5:
9421 case XK_6:
9422 case XK_7:
9423 case XK_8:
9424 case XK_9:
9425 {
9426 windows->magnify.data=(key_symbol-XK_0);
9427 break;
9428 }
9429 case XK_KP_0:
9430 case XK_KP_1:
9431 case XK_KP_2:
9432 case XK_KP_3:
9433 case XK_KP_4:
9434 case XK_KP_5:
9435 case XK_KP_6:
9436 case XK_KP_7:
9437 case XK_KP_8:
9438 case XK_KP_9:
9439 {
9440 windows->magnify.data=(key_symbol-XK_KP_0);
9441 break;
9442 }
9443 default:
9444 break;
9445 }
9446 XMakeMagnifyImage(display,windows);
9447}
9448
9449/*
9450%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9451% %
9452% %
9453% %
9454+ X M a k e P a n I m a g e %
9455% %
9456% %
9457% %
9458%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9459%
9460% XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9461% icon window.
9462%
9463% The format of the XMakePanImage method is:
9464%
9465% void XMakePanImage(Display *display,XResourceInfo *resource_info,
9466% XWindows *windows,Image *image)
9467%
9468% A description of each parameter follows:
9469%
9470% o display: Specifies a connection to an X server; returned from
9471% XOpenDisplay.
9472%
9473% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9474%
9475% o windows: Specifies a pointer to a XWindows structure.
9476%
9477% o image: the image.
9478%
9479*/
9480static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9481 XWindows *windows,Image *image)
9482{
9483 MagickStatusType
9484 status;
9485
9486 /*
9487 Create and display image for panning icon.
9488 */
9489 XSetCursorState(display,windows,MagickTrue);
9490 XCheckRefreshWindows(display,windows);
9491 windows->pan.x=(int) windows->image.x;
9492 windows->pan.y=(int) windows->image.y;
9493 status=XMakeImage(display,resource_info,&windows->pan,image,
9494 windows->pan.width,windows->pan.height);
9495 if (status == MagickFalse)
9496 ThrowXWindowFatalException(XServerFatalError,image->exception.reason,
9497 image->exception.description);
9498 (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9499 windows->pan.pixmap);
9500 (void) XClearWindow(display,windows->pan.id);
9501 XDrawPanRectangle(display,windows);
9502 XSetCursorState(display,windows,MagickFalse);
9503}
9504
9505/*
9506%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9507% %
9508% %
9509% %
9510+ X M a t t a E d i t I m a g e %
9511% %
9512% %
9513% %
9514%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9515%
9516% XMatteEditImage() allows the user to interactively change the Matte channel
9517% of an image. If the image is PseudoClass it is promoted to DirectClass
9518% before the matte information is stored.
9519%
9520% The format of the XMatteEditImage method is:
9521%
9522% MagickBooleanType XMatteEditImage(Display *display,
9523% XResourceInfo *resource_info,XWindows *windows,Image **image)
9524%
9525% A description of each parameter follows:
9526%
9527% o display: Specifies a connection to an X server; returned from
9528% XOpenDisplay.
9529%
9530% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9531%
9532% o windows: Specifies a pointer to a XWindows structure.
9533%
9534% o image: the image; returned from ReadImage.
9535%
9536*/
9537static MagickBooleanType XMatteEditImage(Display *display,
9538 XResourceInfo *resource_info,XWindows *windows,Image **image)
9539{
9540 const char
9541 *const MatteEditMenu[] =
9542 {
9543 "Method",
9544 "Border Color",
9545 "Fuzz",
9546 "Matte Value",
9547 "Undo",
9548 "Help",
9549 "Dismiss",
9550 (char *) NULL
9551 };
9552
9553 static char
9554 matte[MaxTextExtent] = "0";
9555
9556 static const ModeType
9557 MatteEditCommands[] =
9558 {
9559 MatteEditMethod,
9560 MatteEditBorderCommand,
9561 MatteEditFuzzCommand,
9562 MatteEditValueCommand,
9563 MatteEditUndoCommand,
9564 MatteEditHelpCommand,
9565 MatteEditDismissCommand
9566 };
9567
9568 static PaintMethod
9569 method = PointMethod;
9570
9571 static XColor
9572 border_color = { 0, 0, 0, 0, 0, 0 };
9573
9574 char
9575 command[MaxTextExtent],
9576 text[MaxTextExtent] = "";
9577
9578 Cursor
9579 cursor;
9580
9581 int
9582 entry,
9583 id,
9584 x,
9585 x_offset,
9586 y,
9587 y_offset;
9588
9589 int
9590 i;
9591
9593 *q;
9594
9595 unsigned int
9596 height,
9597 width;
9598
9599 size_t
9600 state;
9601
9602 XEvent
9603 event;
9604
9605 /*
9606 Map Command widget.
9607 */
9608 (void) CloneString(&windows->command.name,"Matte Edit");
9609 windows->command.data=4;
9610 (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9611 (void) XMapRaised(display,windows->command.id);
9612 XClientMessage(display,windows->image.id,windows->im_protocols,
9613 windows->im_update_widget,CurrentTime);
9614 /*
9615 Make cursor.
9616 */
9617 cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9618 resource_info->background_color,resource_info->foreground_color);
9619 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9620 /*
9621 Track pointer until button 1 is pressed.
9622 */
9623 XQueryPosition(display,windows->image.id,&x,&y);
9624 (void) XSelectInput(display,windows->image.id,
9625 windows->image.attributes.event_mask | PointerMotionMask);
9626 state=DefaultState;
9627 do
9628 {
9629 if (windows->info.mapped != MagickFalse)
9630 {
9631 /*
9632 Display pointer position.
9633 */
9634 (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9635 x+windows->image.x,y+windows->image.y);
9636 XInfoWidget(display,windows,text);
9637 }
9638 /*
9639 Wait for next event.
9640 */
9641 XScreenEvent(display,windows,&event);
9642 if (event.xany.window == windows->command.id)
9643 {
9644 /*
9645 Select a command from the Command widget.
9646 */
9647 id=XCommandWidget(display,windows,MatteEditMenu,&event);
9648 if (id < 0)
9649 {
9650 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9651 continue;
9652 }
9653 switch (MatteEditCommands[id])
9654 {
9655 case MatteEditMethod:
9656 {
9657 char
9658 **methods;
9659
9660 /*
9661 Select a method from the pop-up menu.
9662 */
9663 methods=GetCommandOptions(MagickMethodOptions);
9664 if (methods == (char **) NULL)
9665 break;
9666 entry=XMenuWidget(display,windows,MatteEditMenu[id],
9667 (const char **) methods,command);
9668 if (entry >= 0)
9669 method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9670 MagickFalse,methods[entry]);
9671 methods=DestroyStringList(methods);
9672 break;
9673 }
9674 case MatteEditBorderCommand:
9675 {
9676 const char
9677 *ColorMenu[MaxNumberPens];
9678
9679 int
9680 pen_number;
9681
9682 /*
9683 Initialize menu selections.
9684 */
9685 for (i=0; i < (int) (MaxNumberPens-2); i++)
9686 ColorMenu[i]=resource_info->pen_colors[i];
9687 ColorMenu[MaxNumberPens-2]="Browser...";
9688 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9689 /*
9690 Select a pen color from the pop-up menu.
9691 */
9692 pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9693 (const char **) ColorMenu,command);
9694 if (pen_number < 0)
9695 break;
9696 if (pen_number == (MaxNumberPens-2))
9697 {
9698 static char
9699 color_name[MaxTextExtent] = "gray";
9700
9701 /*
9702 Select a pen color from a dialog.
9703 */
9704 resource_info->pen_colors[pen_number]=color_name;
9705 XColorBrowserWidget(display,windows,"Select",color_name);
9706 if (*color_name == '\0')
9707 break;
9708 }
9709 /*
9710 Set border color.
9711 */
9712 (void) XParseColor(display,windows->map_info->colormap,
9713 resource_info->pen_colors[pen_number],&border_color);
9714 break;
9715 }
9716 case MatteEditFuzzCommand:
9717 {
9718 const char
9719 *const FuzzMenu[] =
9720 {
9721 "0%",
9722 "2%",
9723 "5%",
9724 "10%",
9725 "15%",
9726 "Dialog...",
9727 (char *) NULL,
9728 };
9729
9730 static char
9731 fuzz[MaxTextExtent];
9732
9733 /*
9734 Select a command from the pop-up menu.
9735 */
9736 entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9737 command);
9738 if (entry < 0)
9739 break;
9740 if (entry != 5)
9741 {
9742 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9743 QuantumRange+1.0);
9744 break;
9745 }
9746 (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9747 (void) XDialogWidget(display,windows,"Ok",
9748 "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9749 if (*fuzz == '\0')
9750 break;
9751 (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9752 (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9753 1.0);
9754 break;
9755 }
9756 case MatteEditValueCommand:
9757 {
9758 const char
9759 *const MatteMenu[] =
9760 {
9761 "Opaque",
9762 "Transparent",
9763 "Dialog...",
9764 (char *) NULL,
9765 };
9766
9767 static char
9768 message[MaxTextExtent];
9769
9770 /*
9771 Select a command from the pop-up menu.
9772 */
9773 entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9774 command);
9775 if (entry < 0)
9776 break;
9777 if (entry != 2)
9778 {
9779 (void) FormatLocaleString(matte,MaxTextExtent,"%g",
9780 (double) OpaqueOpacity);
9781 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9782 (void) FormatLocaleString(matte,MaxTextExtent,"%g",
9783 (double) TransparentOpacity);
9784 break;
9785 }
9786 (void) FormatLocaleString(message,MaxTextExtent,
9787 "Enter matte value (0 - " "%g" "):",(double) QuantumRange);
9788 (void) XDialogWidget(display,windows,"Matte",message,matte);
9789 if (*matte == '\0')
9790 break;
9791 break;
9792 }
9793 case MatteEditUndoCommand:
9794 {
9795 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9796 image);
9797 break;
9798 }
9799 case MatteEditHelpCommand:
9800 {
9801 XTextViewHelp(display,resource_info,windows,MagickFalse,
9802 "Help Viewer - Matte Edit",ImageMatteEditHelp);
9803 break;
9804 }
9805 case MatteEditDismissCommand:
9806 {
9807 /*
9808 Prematurely exit.
9809 */
9810 state|=EscapeState;
9811 state|=ExitState;
9812 break;
9813 }
9814 default:
9815 break;
9816 }
9817 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9818 continue;
9819 }
9820 switch (event.type)
9821 {
9822 case ButtonPress:
9823 {
9824 if (event.xbutton.button != Button1)
9825 break;
9826 if ((event.xbutton.window != windows->image.id) &&
9827 (event.xbutton.window != windows->magnify.id))
9828 break;
9829 /*
9830 Update matte data.
9831 */
9832 x=event.xbutton.x;
9833 y=event.xbutton.y;
9834 (void) XMagickCommand(display,resource_info,windows,
9835 SaveToUndoBufferCommand,image);
9836 state|=UpdateConfigurationState;
9837 break;
9838 }
9839 case ButtonRelease:
9840 {
9841 if (event.xbutton.button != Button1)
9842 break;
9843 if ((event.xbutton.window != windows->image.id) &&
9844 (event.xbutton.window != windows->magnify.id))
9845 break;
9846 /*
9847 Update colormap information.
9848 */
9849 x=event.xbutton.x;
9850 y=event.xbutton.y;
9851 XConfigureImageColormap(display,resource_info,windows,*image);
9852 (void) XConfigureImage(display,resource_info,windows,*image);
9853 XInfoWidget(display,windows,text);
9854 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9855 state&=(~UpdateConfigurationState);
9856 break;
9857 }
9858 case Expose:
9859 break;
9860 case KeyPress:
9861 {
9862 char
9863 command[MaxTextExtent];
9864
9865 KeySym
9866 key_symbol;
9867
9868 if (event.xkey.window == windows->magnify.id)
9869 {
9870 Window
9871 window;
9872
9873 window=windows->magnify.id;
9874 while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9875 }
9876 if (event.xkey.window != windows->image.id)
9877 break;
9878 /*
9879 Respond to a user key press.
9880 */
9881 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9882 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9883 switch ((int) key_symbol)
9884 {
9885 case XK_Escape:
9886 case XK_F20:
9887 {
9888 /*
9889 Prematurely exit.
9890 */
9891 state|=ExitState;
9892 break;
9893 }
9894 case XK_F1:
9895 case XK_Help:
9896 {
9897 XTextViewHelp(display,resource_info,windows,MagickFalse,
9898 "Help Viewer - Matte Edit",ImageMatteEditHelp);
9899 break;
9900 }
9901 default:
9902 {
9903 (void) XBell(display,0);
9904 break;
9905 }
9906 }
9907 break;
9908 }
9909 case MotionNotify:
9910 {
9911 /*
9912 Map and unmap Info widget as cursor crosses its boundaries.
9913 */
9914 x=event.xmotion.x;
9915 y=event.xmotion.y;
9916 if (windows->info.mapped != MagickFalse)
9917 {
9918 if ((x < (int) (windows->info.x+windows->info.width)) &&
9919 (y < (int) (windows->info.y+windows->info.height)))
9920 (void) XWithdrawWindow(display,windows->info.id,
9921 windows->info.screen);
9922 }
9923 else
9924 if ((x > (int) (windows->info.x+windows->info.width)) ||
9925 (y > (int) (windows->info.y+windows->info.height)))
9926 (void) XMapWindow(display,windows->info.id);
9927 break;
9928 }
9929 default:
9930 break;
9931 }
9932 if (event.xany.window == windows->magnify.id)
9933 {
9934 x=windows->magnify.x-windows->image.x;
9935 y=windows->magnify.y-windows->image.y;
9936 }
9937 x_offset=x;
9938 y_offset=y;
9939 if ((state & UpdateConfigurationState) != 0)
9940 {
9941 CacheView
9942 *image_view;
9943
9945 *exception;
9946
9947 int
9948 x,
9949 y;
9950
9951 /*
9952 Matte edit is relative to image configuration.
9953 */
9954 (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
9955 MagickTrue);
9956 XPutPixel(windows->image.ximage,x_offset,y_offset,
9957 windows->pixel_info->background_color.pixel);
9958 width=(unsigned int) (*image)->columns;
9959 height=(unsigned int) (*image)->rows;
9960 x=0;
9961 y=0;
9962 if (windows->image.crop_geometry != (char *) NULL)
9963 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
9964 &width,&height);
9965 x_offset=(int)
9966 (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
9967 y_offset=(int)
9968 (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
9969 if ((x_offset < 0) || (y_offset < 0))
9970 continue;
9971 if ((x_offset >= (int) (*image)->columns) ||
9972 (y_offset >= (int) (*image)->rows))
9973 continue;
9974 if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
9975 return(MagickFalse);
9976 (*image)->matte=MagickTrue;
9977 exception=(&(*image)->exception);
9978 image_view=AcquireAuthenticCacheView(*image,exception);
9979 switch (method)
9980 {
9981 case PointMethod:
9982 default:
9983 {
9984 /*
9985 Update matte information using point algorithm.
9986 */
9987 q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
9988 (ssize_t) y_offset,1,1,exception);
9989 if (q == (PixelPacket *) NULL)
9990 break;
9991 q->opacity=(Quantum) StringToLong(matte);
9992 (void) SyncCacheViewAuthenticPixels(image_view,exception);
9993 break;
9994 }
9995 case ReplaceMethod:
9996 {
9998 target;
9999
10000 /*
10001 Update matte information using replace algorithm.
10002 */
10003 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10004 (ssize_t) y_offset,&target,exception);
10005 for (y=0; y < (int) (*image)->rows; y++)
10006 {
10007 q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10008 (*image)->columns,1,&(*image)->exception);
10009 if (q == (PixelPacket *) NULL)
10010 break;
10011 for (x=0; x < (int) (*image)->columns; x++)
10012 {
10013 if (IsColorSimilar(*image,q,&target))
10014 q->opacity=(Quantum) StringToLong(matte);
10015 q++;
10016 }
10017 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10018 break;
10019 }
10020 break;
10021 }
10022 case FloodfillMethod:
10023 case FillToBorderMethod:
10024 {
10025 DrawInfo
10026 *draw_info;
10027
10029 target;
10030
10031 /*
10032 Update matte information using floodfill algorithm.
10033 */
10034 (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10035 (ssize_t) y_offset,&target,exception);
10036 if (method == FillToBorderMethod)
10037 {
10038 target.red=(MagickRealType)
10039 ScaleShortToQuantum(border_color.red);
10040 target.green=(MagickRealType)
10041 ScaleShortToQuantum(border_color.green);
10042 target.blue=(MagickRealType)
10043 ScaleShortToQuantum(border_color.blue);
10044 }
10045 draw_info=CloneDrawInfo(resource_info->image_info,
10046 (DrawInfo *) NULL);
10047 draw_info->fill.opacity=ClampToQuantum(StringToDouble(matte,
10048 (char **) NULL));
10049 (void) FloodfillPaintImage(*image,OpacityChannel,draw_info,&target,
10050 (ssize_t) x_offset,(ssize_t) y_offset,
10051 method == FloodfillMethod ? MagickFalse : MagickTrue);
10052 draw_info=DestroyDrawInfo(draw_info);
10053 break;
10054 }
10055 case ResetMethod:
10056 {
10057 /*
10058 Update matte information using reset algorithm.
10059 */
10060 if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
10061 return(MagickFalse);
10062 for (y=0; y < (int) (*image)->rows; y++)
10063 {
10064 q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10065 (*image)->columns,1,exception);
10066 if (q == (PixelPacket *) NULL)
10067 break;
10068 for (x=0; x < (int) (*image)->columns; x++)
10069 {
10070 q->opacity=(Quantum) StringToLong(matte);
10071 q++;
10072 }
10073 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10074 break;
10075 }
10076 if (StringToLong(matte) == OpaqueOpacity)
10077 (*image)->matte=MagickFalse;
10078 break;
10079 }
10080 }
10081 image_view=DestroyCacheView(image_view);
10082 state&=(~UpdateConfigurationState);
10083 }
10084 } while ((state & ExitState) == 0);
10085 (void) XSelectInput(display,windows->image.id,
10086 windows->image.attributes.event_mask);
10087 XSetCursorState(display,windows,MagickFalse);
10088 (void) XFreeCursor(display,cursor);
10089 return(MagickTrue);
10090}
10091
10092/*
10093%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10094% %
10095% %
10096% %
10097+ X O p e n I m a g e %
10098% %
10099% %
10100% %
10101%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10102%
10103% XOpenImage() loads an image from a file.
10104%
10105% The format of the XOpenImage method is:
10106%
10107% Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10108% XWindows *windows,const unsigned int command)
10109%
10110% A description of each parameter follows:
10111%
10112% o display: Specifies a connection to an X server; returned from
10113% XOpenDisplay.
10114%
10115% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10116%
10117% o windows: Specifies a pointer to a XWindows structure.
10118%
10119% o command: A value other than zero indicates that the file is selected
10120% from the command line argument list.
10121%
10122*/
10123static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10124 XWindows *windows,const MagickBooleanType command)
10125{
10126 const MagickInfo
10127 *magick_info;
10128
10130 *exception;
10131
10132 Image
10133 *nexus;
10134
10135 ImageInfo
10136 *image_info;
10137
10138 static char
10139 filename[MaxTextExtent] = "\0";
10140
10141 /*
10142 Request file name from user.
10143 */
10144 if (command == MagickFalse)
10145 XFileBrowserWidget(display,windows,"Open",filename);
10146 else
10147 {
10148 char
10149 **filelist,
10150 **files;
10151
10152 int
10153 count,
10154 status;
10155
10156 int
10157 i,
10158 j;
10159
10160 /*
10161 Select next image from the command line.
10162 */
10163 status=XGetCommand(display,windows->image.id,&files,&count);
10164 if (status == 0)
10165 ThrowXWindowException(XServerError,"UnableToGetProperty","...");
10166 filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10167 if (filelist == (char **) NULL)
10168 {
10169 (void) XFreeStringList(files);
10170 ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
10171 "...");
10172 return((Image *) NULL);
10173 }
10174 j=0;
10175 for (i=1; i < count; i++)
10176 if (*files[i] != '-')
10177 filelist[j++]=files[i];
10178 filelist[j]=(char *) NULL;
10179 XListBrowserWidget(display,windows,&windows->widget,
10180 (const char **) filelist,"Load","Select Image to Load:",filename);
10181 filelist=(char **) RelinquishMagickMemory(filelist);
10182 (void) XFreeStringList(files);
10183 }
10184 if (*filename == '\0')
10185 return((Image *) NULL);
10186 image_info=CloneImageInfo(resource_info->image_info);
10187 (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10188 (void *) NULL);
10189 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10190 exception=AcquireExceptionInfo();
10191 (void) SetImageInfo(image_info,0,exception);
10192 if (LocaleCompare(image_info->magick,"X") == 0)
10193 {
10194 char
10195 seconds[MaxTextExtent];
10196
10197 /*
10198 User may want to delay the X server screen grab.
10199 */
10200 (void) CopyMagickString(seconds,"0",MaxTextExtent);
10201 (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10202 seconds);
10203 if (*seconds == '\0')
10204 return((Image *) NULL);
10205 XDelay(display,(size_t) (1000*StringToLong(seconds)));
10206 }
10207 magick_info=GetMagickInfo(image_info->magick,exception);
10208 if ((magick_info != (const MagickInfo *) NULL) &&
10209 (magick_info->raw != MagickFalse))
10210 {
10211 char
10212 geometry[MaxTextExtent];
10213
10214 /*
10215 Request image size from the user.
10216 */
10217 (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10218 if (image_info->size != (char *) NULL)
10219 (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10220 (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10221 geometry);
10222 (void) CloneString(&image_info->size,geometry);
10223 }
10224 /*
10225 Load the image.
10226 */
10227 XSetCursorState(display,windows,MagickTrue);
10228 XCheckRefreshWindows(display,windows);
10229 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10230 nexus=ReadImage(image_info,exception);
10231 CatchException(exception);
10232 XSetCursorState(display,windows,MagickFalse);
10233 if (nexus != (Image *) NULL)
10234 XClientMessage(display,windows->image.id,windows->im_protocols,
10235 windows->im_next_image,CurrentTime);
10236 else
10237 {
10238 char
10239 *text,
10240 **textlist;
10241
10242 /*
10243 Unknown image format.
10244 */
10245 text=FileToString(filename,~0UL,exception);
10246 if (text == (char *) NULL)
10247 return((Image *) NULL);
10248 textlist=StringToList(text);
10249 if (textlist != (char **) NULL)
10250 {
10251 char
10252 title[MaxTextExtent];
10253
10254 int
10255 i;
10256
10257 (void) FormatLocaleString(title,MaxTextExtent,
10258 "Unknown format: %s",filename);
10259 XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10260 (const char **) textlist);
10261 for (i=0; textlist[i] != (char *) NULL; i++)
10262 textlist[i]=DestroyString(textlist[i]);
10263 textlist=(char **) RelinquishMagickMemory(textlist);
10264 }
10265 text=DestroyString(text);
10266 }
10267 exception=DestroyExceptionInfo(exception);
10268 image_info=DestroyImageInfo(image_info);
10269 return(nexus);
10270}
10271
10272/*
10273%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10274% %
10275% %
10276% %
10277+ X P a n I m a g e %
10278% %
10279% %
10280% %
10281%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10282%
10283% XPanImage() pans the image until the mouse button is released.
10284%
10285% The format of the XPanImage method is:
10286%
10287% void XPanImage(Display *display,XWindows *windows,XEvent *event)
10288%
10289% A description of each parameter follows:
10290%
10291% o display: Specifies a connection to an X server; returned from
10292% XOpenDisplay.
10293%
10294% o windows: Specifies a pointer to a XWindows structure.
10295%
10296% o event: Specifies a pointer to a XEvent structure. If it is NULL,
10297% the entire image is refreshed.
10298%
10299*/
10300static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10301{
10302 char
10303 text[MaxTextExtent];
10304
10305 Cursor
10306 cursor;
10307
10308 MagickRealType
10309 x_factor,
10310 y_factor;
10311
10313 pan_info;
10314
10315 size_t
10316 state;
10317
10318 /*
10319 Define cursor.
10320 */
10321 if ((windows->image.ximage->width > (int) windows->image.width) &&
10322 (windows->image.ximage->height > (int) windows->image.height))
10323 cursor=XCreateFontCursor(display,XC_fleur);
10324 else
10325 if (windows->image.ximage->width > (int) windows->image.width)
10326 cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10327 else
10328 if (windows->image.ximage->height > (int) windows->image.height)
10329 cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10330 else
10331 cursor=XCreateFontCursor(display,XC_arrow);
10332 (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10333 /*
10334 Pan image as pointer moves until the mouse button is released.
10335 */
10336 x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10337 y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10338 pan_info.width=windows->pan.width*windows->image.width/
10339 windows->image.ximage->width;
10340 pan_info.height=windows->pan.height*windows->image.height/
10341 windows->image.ximage->height;
10342 pan_info.x=0;
10343 pan_info.y=0;
10344 state=UpdateConfigurationState;
10345 do
10346 {
10347 switch (event->type)
10348 {
10349 case ButtonPress:
10350 {
10351 /*
10352 User choose an initial pan location.
10353 */
10354 pan_info.x=(ssize_t) event->xbutton.x;
10355 pan_info.y=(ssize_t) event->xbutton.y;
10356 state|=UpdateConfigurationState;
10357 break;
10358 }
10359 case ButtonRelease:
10360 {
10361 /*
10362 User has finished panning the image.
10363 */
10364 pan_info.x=(ssize_t) event->xbutton.x;
10365 pan_info.y=(ssize_t) event->xbutton.y;
10366 state|=UpdateConfigurationState | ExitState;
10367 break;
10368 }
10369 case MotionNotify:
10370 {
10371 pan_info.x=(ssize_t) event->xmotion.x;
10372 pan_info.y=(ssize_t) event->xmotion.y;
10373 state|=UpdateConfigurationState;
10374 }
10375 default:
10376 break;
10377 }
10378 if ((state & UpdateConfigurationState) != 0)
10379 {
10380 /*
10381 Check boundary conditions.
10382 */
10383 if (pan_info.x < (ssize_t) (pan_info.width/2))
10384 pan_info.x=0;
10385 else
10386 pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10387 if (pan_info.x < 0)
10388 pan_info.x=0;
10389 else
10390 if ((int) (pan_info.x+windows->image.width) >
10391 windows->image.ximage->width)
10392 pan_info.x=(ssize_t)
10393 (windows->image.ximage->width-windows->image.width);
10394 if (pan_info.y < (ssize_t) (pan_info.height/2))
10395 pan_info.y=0;
10396 else
10397 pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10398 if (pan_info.y < 0)
10399 pan_info.y=0;
10400 else
10401 if ((int) (pan_info.y+windows->image.height) >
10402 windows->image.ximage->height)
10403 pan_info.y=(ssize_t)
10404 (windows->image.ximage->height-windows->image.height);
10405 if ((windows->image.x != (int) pan_info.x) ||
10406 (windows->image.y != (int) pan_info.y))
10407 {
10408 /*
10409 Display image pan offset.
10410 */
10411 windows->image.x=(int) pan_info.x;
10412 windows->image.y=(int) pan_info.y;
10413 (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10414 windows->image.width,windows->image.height,windows->image.x,
10415 windows->image.y);
10416 XInfoWidget(display,windows,text);
10417 /*
10418 Refresh Image window.
10419 */
10420 XDrawPanRectangle(display,windows);
10421 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10422 }
10423 state&=(~UpdateConfigurationState);
10424 }
10425 /*
10426 Wait for next event.
10427 */
10428 if ((state & ExitState) == 0)
10429 XScreenEvent(display,windows,event);
10430 } while ((state & ExitState) == 0);
10431 /*
10432 Restore cursor.
10433 */
10434 (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10435 (void) XFreeCursor(display,cursor);
10436 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10437}
10438
10439/*
10440%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10441% %
10442% %
10443% %
10444+ X P a s t e I m a g e %
10445% %
10446% %
10447% %
10448%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10449%
10450% XPasteImage() pastes an image previously saved with XCropImage in the X
10451% window image at a location the user chooses with the pointer.
10452%
10453% The format of the XPasteImage method is:
10454%
10455% MagickBooleanType XPasteImage(Display *display,
10456% XResourceInfo *resource_info,XWindows *windows,Image *image)
10457%
10458% A description of each parameter follows:
10459%
10460% o display: Specifies a connection to an X server; returned from
10461% XOpenDisplay.
10462%
10463% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10464%
10465% o windows: Specifies a pointer to a XWindows structure.
10466%
10467% o image: the image; returned from ReadImage.
10468%
10469*/
10470static MagickBooleanType XPasteImage(Display *display,
10471 XResourceInfo *resource_info,XWindows *windows,Image *image)
10472{
10473 const char
10474 *const PasteMenu[] =
10475 {
10476 "Operator",
10477 "Help",
10478 "Dismiss",
10479 (char *) NULL
10480 };
10481
10482 static const ModeType
10483 PasteCommands[] =
10484 {
10485 PasteOperatorsCommand,
10486 PasteHelpCommand,
10487 PasteDismissCommand
10488 };
10489
10490 static CompositeOperator
10491 compose = CopyCompositeOp;
10492
10493 char
10494 text[MaxTextExtent];
10495
10496 Cursor
10497 cursor;
10498
10499 Image
10500 *paste_image;
10501
10502 int
10503 entry,
10504 id,
10505 x,
10506 y;
10507
10508 MagickRealType
10509 scale_factor;
10510
10512 highlight_info,
10513 paste_info;
10514
10515 unsigned int
10516 height,
10517 width;
10518
10519 size_t
10520 state;
10521
10522 XEvent
10523 event;
10524
10525 /*
10526 Copy image.
10527 */
10528 if (resource_info->copy_image == (Image *) NULL)
10529 return(MagickFalse);
10530 paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,
10531 &image->exception);
10532 if (paste_image == (Image *) NULL)
10533 return(MagickFalse);
10534 /*
10535 Map Command widget.
10536 */
10537 (void) CloneString(&windows->command.name,"Paste");
10538 windows->command.data=1;
10539 (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10540 (void) XMapRaised(display,windows->command.id);
10541 XClientMessage(display,windows->image.id,windows->im_protocols,
10542 windows->im_update_widget,CurrentTime);
10543 /*
10544 Track pointer until button 1 is pressed.
10545 */
10546 XSetCursorState(display,windows,MagickFalse);
10547 XQueryPosition(display,windows->image.id,&x,&y);
10548 (void) XSelectInput(display,windows->image.id,
10549 windows->image.attributes.event_mask | PointerMotionMask);
10550 paste_info.x=(ssize_t) windows->image.x+x;
10551 paste_info.y=(ssize_t) windows->image.y+y;
10552 paste_info.width=0;
10553 paste_info.height=0;
10554 cursor=XCreateFontCursor(display,XC_ul_angle);
10555 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10556 state=DefaultState;
10557 do
10558 {
10559 if (windows->info.mapped != MagickFalse)
10560 {
10561 /*
10562 Display pointer position.
10563 */
10564 (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10565 (long) paste_info.x,(long) paste_info.y);
10566 XInfoWidget(display,windows,text);
10567 }
10568 highlight_info=paste_info;
10569 highlight_info.x=paste_info.x-windows->image.x;
10570 highlight_info.y=paste_info.y-windows->image.y;
10571 XHighlightRectangle(display,windows->image.id,
10572 windows->image.highlight_context,&highlight_info);
10573 /*
10574 Wait for next event.
10575 */
10576 XScreenEvent(display,windows,&event);
10577 XHighlightRectangle(display,windows->image.id,
10578 windows->image.highlight_context,&highlight_info);
10579 if (event.xany.window == windows->command.id)
10580 {
10581 /*
10582 Select a command from the Command widget.
10583 */
10584 id=XCommandWidget(display,windows,PasteMenu,&event);
10585 if (id < 0)
10586 continue;
10587 switch (PasteCommands[id])
10588 {
10589 case PasteOperatorsCommand:
10590 {
10591 char
10592 command[MaxTextExtent],
10593 **operators;
10594
10595 /*
10596 Select a command from the pop-up menu.
10597 */
10598 operators=GetCommandOptions(MagickComposeOptions);
10599 if (operators == (char **) NULL)
10600 break;
10601 entry=XMenuWidget(display,windows,PasteMenu[id],
10602 (const char **) operators,command);
10603 if (entry >= 0)
10604 compose=(CompositeOperator) ParseCommandOption(
10605 MagickComposeOptions,MagickFalse,operators[entry]);
10606 operators=DestroyStringList(operators);
10607 break;
10608 }
10609 case PasteHelpCommand:
10610 {
10611 XTextViewHelp(display,resource_info,windows,MagickFalse,
10612 "Help Viewer - Image Composite",ImagePasteHelp);
10613 break;
10614 }
10615 case PasteDismissCommand:
10616 {
10617 /*
10618 Prematurely exit.
10619 */
10620 state|=EscapeState;
10621 state|=ExitState;
10622 break;
10623 }
10624 default:
10625 break;
10626 }
10627 continue;
10628 }
10629 switch (event.type)
10630 {
10631 case ButtonPress:
10632 {
10633 if (resource_info->debug != MagickFalse)
10634 (void) LogMagickEvent(X11Event,GetMagickModule(),
10635 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10636 event.xbutton.button,event.xbutton.x,event.xbutton.y);
10637 if (event.xbutton.button != Button1)
10638 break;
10639 if (event.xbutton.window != windows->image.id)
10640 break;
10641 /*
10642 Paste rectangle is relative to image configuration.
10643 */
10644 width=(unsigned int) image->columns;
10645 height=(unsigned int) image->rows;
10646 x=0;
10647 y=0;
10648 if (windows->image.crop_geometry != (char *) NULL)
10649 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10650 &width,&height);
10651 scale_factor=(MagickRealType) windows->image.ximage->width/width;
10652 paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10653 scale_factor=(MagickRealType) windows->image.ximage->height/height;
10654 paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10655 (void) XCheckDefineCursor(display,windows->image.id,cursor);
10656 paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10657 paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10658 break;
10659 }
10660 case ButtonRelease:
10661 {
10662 if (resource_info->debug != MagickFalse)
10663 (void) LogMagickEvent(X11Event,GetMagickModule(),
10664 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10665 event.xbutton.button,event.xbutton.x,event.xbutton.y);
10666 if (event.xbutton.button != Button1)
10667 break;
10668 if (event.xbutton.window != windows->image.id)
10669 break;
10670 if ((paste_info.width != 0) && (paste_info.height != 0))
10671 {
10672 /*
10673 User has selected the location of the paste image.
10674 */
10675 paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10676 paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10677 state|=ExitState;
10678 }
10679 break;
10680 }
10681 case Expose:
10682 break;
10683 case KeyPress:
10684 {
10685 char
10686 command[MaxTextExtent];
10687
10688 KeySym
10689 key_symbol;
10690
10691 int
10692 length;
10693
10694 if (event.xkey.window != windows->image.id)
10695 break;
10696 /*
10697 Respond to a user key press.
10698 */
10699 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10700 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10701 *(command+length)='\0';
10702 if (resource_info->debug != MagickFalse)
10703 (void) LogMagickEvent(X11Event,GetMagickModule(),
10704 "Key press: 0x%lx (%s)",(long) key_symbol,command);
10705 switch ((int) key_symbol)
10706 {
10707 case XK_Escape:
10708 case XK_F20:
10709 {
10710 /*
10711 Prematurely exit.
10712 */
10713 paste_image=DestroyImage(paste_image);
10714 state|=EscapeState;
10715 state|=ExitState;
10716 break;
10717 }
10718 case XK_F1:
10719 case XK_Help:
10720 {
10721 (void) XSetFunction(display,windows->image.highlight_context,
10722 GXcopy);
10723 XTextViewHelp(display,resource_info,windows,MagickFalse,
10724 "Help Viewer - Image Composite",ImagePasteHelp);
10725 (void) XSetFunction(display,windows->image.highlight_context,
10726 GXinvert);
10727 break;
10728 }
10729 default:
10730 {
10731 (void) XBell(display,0);
10732 break;
10733 }
10734 }
10735 break;
10736 }
10737 case MotionNotify:
10738 {
10739 /*
10740 Map and unmap Info widget as text cursor crosses its boundaries.
10741 */
10742 x=event.xmotion.x;
10743 y=event.xmotion.y;
10744 if (windows->info.mapped != MagickFalse)
10745 {
10746 if ((x < (int) (windows->info.x+windows->info.width)) &&
10747 (y < (int) (windows->info.y+windows->info.height)))
10748 (void) XWithdrawWindow(display,windows->info.id,
10749 windows->info.screen);
10750 }
10751 else
10752 if ((x > (int) (windows->info.x+windows->info.width)) ||
10753 (y > (int) (windows->info.y+windows->info.height)))
10754 (void) XMapWindow(display,windows->info.id);
10755 paste_info.x=(ssize_t) windows->image.x+x;
10756 paste_info.y=(ssize_t) windows->image.y+y;
10757 break;
10758 }
10759 default:
10760 {
10761 if (resource_info->debug != MagickFalse)
10762 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10763 event.type);
10764 break;
10765 }
10766 }
10767 } while ((state & ExitState) == 0);
10768 (void) XSelectInput(display,windows->image.id,
10769 windows->image.attributes.event_mask);
10770 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10771 XSetCursorState(display,windows,MagickFalse);
10772 (void) XFreeCursor(display,cursor);
10773 if ((state & EscapeState) != 0)
10774 return(MagickTrue);
10775 /*
10776 Image pasting is relative to image configuration.
10777 */
10778 XSetCursorState(display,windows,MagickTrue);
10779 XCheckRefreshWindows(display,windows);
10780 width=(unsigned int) image->columns;
10781 height=(unsigned int) image->rows;
10782 x=0;
10783 y=0;
10784 if (windows->image.crop_geometry != (char *) NULL)
10785 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10786 scale_factor=(MagickRealType) width/windows->image.ximage->width;
10787 paste_info.x+=x;
10788 paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10789 paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10790 scale_factor=(MagickRealType) height/windows->image.ximage->height;
10791 paste_info.y+=y;
10792 paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10793 paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10794 /*
10795 Paste image with X Image window.
10796 */
10797 (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10798 paste_image=DestroyImage(paste_image);
10799 XSetCursorState(display,windows,MagickFalse);
10800 /*
10801 Update image colormap.
10802 */
10803 XConfigureImageColormap(display,resource_info,windows,image);
10804 (void) XConfigureImage(display,resource_info,windows,image);
10805 return(MagickTrue);
10806}
10807
10808/*
10809%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10810% %
10811% %
10812% %
10813+ X P r i n t I m a g e %
10814% %
10815% %
10816% %
10817%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10818%
10819% XPrintImage() prints an image to a Postscript printer.
10820%
10821% The format of the XPrintImage method is:
10822%
10823% MagickBooleanType XPrintImage(Display *display,
10824% XResourceInfo *resource_info,XWindows *windows,Image *image)
10825%
10826% A description of each parameter follows:
10827%
10828% o display: Specifies a connection to an X server; returned from
10829% XOpenDisplay.
10830%
10831% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10832%
10833% o windows: Specifies a pointer to a XWindows structure.
10834%
10835% o image: the image.
10836%
10837*/
10838static MagickBooleanType XPrintImage(Display *display,
10839 XResourceInfo *resource_info,XWindows *windows,Image *image)
10840{
10841 char
10842 filename[MaxTextExtent],
10843 geometry[MaxTextExtent];
10844
10845 const char
10846 *const PageSizes[] =
10847 {
10848 "Letter",
10849 "Tabloid",
10850 "Ledger",
10851 "Legal",
10852 "Statement",
10853 "Executive",
10854 "A3",
10855 "A4",
10856 "A5",
10857 "B4",
10858 "B5",
10859 "Folio",
10860 "Quarto",
10861 "10x14",
10862 (char *) NULL
10863 };
10864
10865 Image
10866 *print_image;
10867
10868 ImageInfo
10869 *image_info;
10870
10871 MagickStatusType
10872 status;
10873
10874 /*
10875 Request Postscript page geometry from user.
10876 */
10877 image_info=CloneImageInfo(resource_info->image_info);
10878 (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10879 if (image_info->page != (char *) NULL)
10880 (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10881 XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10882 "Select Postscript Page Geometry:",geometry);
10883 if (*geometry == '\0')
10884 return(MagickTrue);
10885 image_info->page=GetPageGeometry(geometry);
10886 /*
10887 Apply image transforms.
10888 */
10889 XSetCursorState(display,windows,MagickTrue);
10890 XCheckRefreshWindows(display,windows);
10891 print_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10892 if (print_image == (Image *) NULL)
10893 return(MagickFalse);
10894 (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
10895 windows->image.ximage->width,windows->image.ximage->height);
10896 (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10897 /*
10898 Print image.
10899 */
10900 (void) AcquireUniqueFilename(filename);
10901 (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
10902 filename);
10903 status=WriteImage(image_info,print_image);
10904 (void) RelinquishUniqueFileResource(filename);
10905 print_image=DestroyImage(print_image);
10906 image_info=DestroyImageInfo(image_info);
10907 XSetCursorState(display,windows,MagickFalse);
10908 return(status != 0 ? MagickTrue : MagickFalse);
10909}
10910
10911/*
10912%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10913% %
10914% %
10915% %
10916+ X R O I I m a g e %
10917% %
10918% %
10919% %
10920%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10921%
10922% XROIImage() applies an image processing technique to a region of interest.
10923%
10924% The format of the XROIImage method is:
10925%
10926% MagickBooleanType XROIImage(Display *display,
10927% XResourceInfo *resource_info,XWindows *windows,Image **image)
10928%
10929% A description of each parameter follows:
10930%
10931% o display: Specifies a connection to an X server; returned from
10932% XOpenDisplay.
10933%
10934% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10935%
10936% o windows: Specifies a pointer to a XWindows structure.
10937%
10938% o image: the image; returned from ReadImage.
10939%
10940*/
10941static MagickBooleanType XROIImage(Display *display,
10942 XResourceInfo *resource_info,XWindows *windows,Image **image)
10943{
10944#define ApplyMenus 7
10945
10946 const char
10947 *const ROIMenu[] =
10948 {
10949 "Help",
10950 "Dismiss",
10951 (char *) NULL
10952 },
10953 *const ApplyMenu[] =
10954 {
10955 "File",
10956 "Edit",
10957 "Transform",
10958 "Enhance",
10959 "Effects",
10960 "F/X",
10961 "Miscellany",
10962 "Help",
10963 "Dismiss",
10964 (char *) NULL
10965 },
10966 *const FileMenu[] =
10967 {
10968 "Save...",
10969 "Print...",
10970 (char *) NULL
10971 },
10972 *const EditMenu[] =
10973 {
10974 "Undo",
10975 "Redo",
10976 (char *) NULL
10977 },
10978 *const TransformMenu[] =
10979 {
10980 "Flop",
10981 "Flip",
10982 "Rotate Right",
10983 "Rotate Left",
10984 (char *) NULL
10985 },
10986 *const EnhanceMenu[] =
10987 {
10988 "Hue...",
10989 "Saturation...",
10990 "Brightness...",
10991 "Gamma...",
10992 "Spiff",
10993 "Dull",
10994 "Contrast Stretch...",
10995 "Sigmoidal Contrast...",
10996 "Normalize",
10997 "Equalize",
10998 "Negate",
10999 "Grayscale",
11000 "Map...",
11001 "Quantize...",
11002 (char *) NULL
11003 },
11004 *const EffectsMenu[] =
11005 {
11006 "Despeckle",
11007 "Emboss",
11008 "Reduce Noise",
11009 "Add Noise",
11010 "Sharpen...",
11011 "Blur...",
11012 "Threshold...",
11013 "Edge Detect...",
11014 "Spread...",
11015 "Shade...",
11016 "Raise...",
11017 "Segment...",
11018 (char *) NULL
11019 },
11020 *const FXMenu[] =
11021 {
11022 "Solarize...",
11023 "Sepia Tone...",
11024 "Swirl...",
11025 "Implode...",
11026 "Vignette...",
11027 "Wave...",
11028 "Oil Paint...",
11029 "Charcoal Draw...",
11030 (char *) NULL
11031 },
11032 *const MiscellanyMenu[] =
11033 {
11034 "Image Info",
11035 "Zoom Image",
11036 "Show Preview...",
11037 "Show Histogram",
11038 "Show Matte",
11039 (char *) NULL
11040 };
11041
11042 const char
11043 *const *Menus[ApplyMenus] =
11044 {
11045 FileMenu,
11046 EditMenu,
11047 TransformMenu,
11048 EnhanceMenu,
11049 EffectsMenu,
11050 FXMenu,
11051 MiscellanyMenu
11052 };
11053
11054 static const DisplayCommand
11055 ApplyCommands[] =
11056 {
11057 NullCommand,
11058 NullCommand,
11059 NullCommand,
11060 NullCommand,
11061 NullCommand,
11062 NullCommand,
11063 NullCommand,
11064 HelpCommand,
11065 QuitCommand
11066 },
11067 FileCommands[] =
11068 {
11069 SaveCommand,
11070 PrintCommand
11071 },
11072 EditCommands[] =
11073 {
11074 UndoCommand,
11075 RedoCommand
11076 },
11077 TransformCommands[] =
11078 {
11079 FlopCommand,
11080 FlipCommand,
11081 RotateRightCommand,
11082 RotateLeftCommand
11083 },
11084 EnhanceCommands[] =
11085 {
11086 HueCommand,
11087 SaturationCommand,
11088 BrightnessCommand,
11089 GammaCommand,
11090 SpiffCommand,
11091 DullCommand,
11092 ContrastStretchCommand,
11093 SigmoidalContrastCommand,
11094 NormalizeCommand,
11095 EqualizeCommand,
11096 NegateCommand,
11097 GrayscaleCommand,
11098 MapCommand,
11099 QuantizeCommand
11100 },
11101 EffectsCommands[] =
11102 {
11103 DespeckleCommand,
11104 EmbossCommand,
11105 ReduceNoiseCommand,
11106 AddNoiseCommand,
11107 SharpenCommand,
11108 BlurCommand,
11109 ThresholdCommand,
11110 EdgeDetectCommand,
11111 SpreadCommand,
11112 ShadeCommand,
11113 RaiseCommand,
11114 SegmentCommand
11115 },
11116 FXCommands[] =
11117 {
11118 SolarizeCommand,
11119 SepiaToneCommand,
11120 SwirlCommand,
11121 ImplodeCommand,
11122 VignetteCommand,
11123 WaveCommand,
11124 OilPaintCommand,
11125 CharcoalDrawCommand
11126 },
11127 MiscellanyCommands[] =
11128 {
11129 InfoCommand,
11130 ZoomCommand,
11131 ShowPreviewCommand,
11132 ShowHistogramCommand,
11133 ShowMatteCommand
11134 },
11135 ROICommands[] =
11136 {
11137 ROIHelpCommand,
11138 ROIDismissCommand
11139 };
11140
11141 static const DisplayCommand
11142 *Commands[ApplyMenus] =
11143 {
11144 FileCommands,
11145 EditCommands,
11146 TransformCommands,
11147 EnhanceCommands,
11148 EffectsCommands,
11149 FXCommands,
11150 MiscellanyCommands
11151 };
11152
11153 char
11154 command[MaxTextExtent],
11155 text[MaxTextExtent];
11156
11157 DisplayCommand
11158 display_command;
11159
11160 Cursor
11161 cursor;
11162
11163 Image
11164 *roi_image;
11165
11166 int
11167 entry,
11168 id,
11169 x,
11170 y;
11171
11172 MagickRealType
11173 scale_factor;
11174
11175 MagickProgressMonitor
11176 progress_monitor;
11177
11179 crop_info,
11180 highlight_info,
11181 roi_info;
11182
11183 unsigned int
11184 height,
11185 width;
11186
11187 size_t
11188 state;
11189
11190 XEvent
11191 event;
11192
11193 /*
11194 Map Command widget.
11195 */
11196 (void) CloneString(&windows->command.name,"ROI");
11197 windows->command.data=0;
11198 (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11199 (void) XMapRaised(display,windows->command.id);
11200 XClientMessage(display,windows->image.id,windows->im_protocols,
11201 windows->im_update_widget,CurrentTime);
11202 /*
11203 Track pointer until button 1 is pressed.
11204 */
11205 XQueryPosition(display,windows->image.id,&x,&y);
11206 (void) XSelectInput(display,windows->image.id,
11207 windows->image.attributes.event_mask | PointerMotionMask);
11208 crop_info.width=0;
11209 crop_info.height=0;
11210 crop_info.x=0;
11211 crop_info.y=0;
11212 roi_info.x=(ssize_t) windows->image.x+x;
11213 roi_info.y=(ssize_t) windows->image.y+y;
11214 roi_info.width=0;
11215 roi_info.height=0;
11216 cursor=XCreateFontCursor(display,XC_fleur);
11217 state=DefaultState;
11218 do
11219 {
11220 if (windows->info.mapped != MagickFalse)
11221 {
11222 /*
11223 Display pointer position.
11224 */
11225 (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11226 (long) roi_info.x,(long) roi_info.y);
11227 XInfoWidget(display,windows,text);
11228 }
11229 /*
11230 Wait for next event.
11231 */
11232 XScreenEvent(display,windows,&event);
11233 if (event.xany.window == windows->command.id)
11234 {
11235 /*
11236 Select a command from the Command widget.
11237 */
11238 id=XCommandWidget(display,windows,ROIMenu,&event);
11239 if (id < 0)
11240 continue;
11241 switch (ROICommands[id])
11242 {
11243 case ROIHelpCommand:
11244 {
11245 XTextViewHelp(display,resource_info,windows,MagickFalse,
11246 "Help Viewer - Region of Interest",ImageROIHelp);
11247 break;
11248 }
11249 case ROIDismissCommand:
11250 {
11251 /*
11252 Prematurely exit.
11253 */
11254 state|=EscapeState;
11255 state|=ExitState;
11256 break;
11257 }
11258 default:
11259 break;
11260 }
11261 continue;
11262 }
11263 switch (event.type)
11264 {
11265 case ButtonPress:
11266 {
11267 if (event.xbutton.button != Button1)
11268 break;
11269 if (event.xbutton.window != windows->image.id)
11270 break;
11271 /*
11272 Note first corner of region of interest rectangle-- exit loop.
11273 */
11274 (void) XCheckDefineCursor(display,windows->image.id,cursor);
11275 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11276 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11277 state|=ExitState;
11278 break;
11279 }
11280 case ButtonRelease:
11281 break;
11282 case Expose:
11283 break;
11284 case KeyPress:
11285 {
11286 KeySym
11287 key_symbol;
11288
11289 if (event.xkey.window != windows->image.id)
11290 break;
11291 /*
11292 Respond to a user key press.
11293 */
11294 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11295 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11296 switch ((int) key_symbol)
11297 {
11298 case XK_Escape:
11299 case XK_F20:
11300 {
11301 /*
11302 Prematurely exit.
11303 */
11304 state|=EscapeState;
11305 state|=ExitState;
11306 break;
11307 }
11308 case XK_F1:
11309 case XK_Help:
11310 {
11311 XTextViewHelp(display,resource_info,windows,MagickFalse,
11312 "Help Viewer - Region of Interest",ImageROIHelp);
11313 break;
11314 }
11315 default:
11316 {
11317 (void) XBell(display,0);
11318 break;
11319 }
11320 }
11321 break;
11322 }
11323 case MotionNotify:
11324 {
11325 /*
11326 Map and unmap Info widget as text cursor crosses its boundaries.
11327 */
11328 x=event.xmotion.x;
11329 y=event.xmotion.y;
11330 if (windows->info.mapped != MagickFalse)
11331 {
11332 if ((x < (int) (windows->info.x+windows->info.width)) &&
11333 (y < (int) (windows->info.y+windows->info.height)))
11334 (void) XWithdrawWindow(display,windows->info.id,
11335 windows->info.screen);
11336 }
11337 else
11338 if ((x > (int) (windows->info.x+windows->info.width)) ||
11339 (y > (int) (windows->info.y+windows->info.height)))
11340 (void) XMapWindow(display,windows->info.id);
11341 roi_info.x=(ssize_t) windows->image.x+x;
11342 roi_info.y=(ssize_t) windows->image.y+y;
11343 break;
11344 }
11345 default:
11346 break;
11347 }
11348 } while ((state & ExitState) == 0);
11349 (void) XSelectInput(display,windows->image.id,
11350 windows->image.attributes.event_mask);
11351 if ((state & EscapeState) != 0)
11352 {
11353 /*
11354 User want to exit without region of interest.
11355 */
11356 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11357 (void) XFreeCursor(display,cursor);
11358 return(MagickTrue);
11359 }
11360 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11361 do
11362 {
11363 /*
11364 Size rectangle as pointer moves until the mouse button is released.
11365 */
11366 x=(int) roi_info.x;
11367 y=(int) roi_info.y;
11368 roi_info.width=0;
11369 roi_info.height=0;
11370 state=DefaultState;
11371 do
11372 {
11373 highlight_info=roi_info;
11374 highlight_info.x=roi_info.x-windows->image.x;
11375 highlight_info.y=roi_info.y-windows->image.y;
11376 if ((highlight_info.width > 3) && (highlight_info.height > 3))
11377 {
11378 /*
11379 Display info and draw region of interest rectangle.
11380 */
11381 if (windows->info.mapped == MagickFalse)
11382 (void) XMapWindow(display,windows->info.id);
11383 (void) FormatLocaleString(text,MaxTextExtent,
11384 " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11385 roi_info.height,(double) roi_info.x,(double) roi_info.y);
11386 XInfoWidget(display,windows,text);
11387 XHighlightRectangle(display,windows->image.id,
11388 windows->image.highlight_context,&highlight_info);
11389 }
11390 else
11391 if (windows->info.mapped != MagickFalse)
11392 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11393 /*
11394 Wait for next event.
11395 */
11396 XScreenEvent(display,windows,&event);
11397 if ((highlight_info.width > 3) && (highlight_info.height > 3))
11398 XHighlightRectangle(display,windows->image.id,
11399 windows->image.highlight_context,&highlight_info);
11400 switch (event.type)
11401 {
11402 case ButtonPress:
11403 {
11404 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11405 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11406 break;
11407 }
11408 case ButtonRelease:
11409 {
11410 /*
11411 User has committed to region of interest rectangle.
11412 */
11413 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11414 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11415 XSetCursorState(display,windows,MagickFalse);
11416 state|=ExitState;
11417 if (LocaleCompare(windows->command.name,"Apply") == 0)
11418 break;
11419 (void) CloneString(&windows->command.name,"Apply");
11420 windows->command.data=ApplyMenus;
11421 (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11422 break;
11423 }
11424 case Expose:
11425 break;
11426 case MotionNotify:
11427 {
11428 roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11429 roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11430 }
11431 default:
11432 break;
11433 }
11434 if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11435 ((state & ExitState) != 0))
11436 {
11437 /*
11438 Check boundary conditions.
11439 */
11440 if (roi_info.x < 0)
11441 roi_info.x=0;
11442 else
11443 if (roi_info.x > (ssize_t) windows->image.ximage->width)
11444 roi_info.x=(ssize_t) windows->image.ximage->width;
11445 if ((int) roi_info.x < x)
11446 roi_info.width=(unsigned int) (x-roi_info.x);
11447 else
11448 {
11449 roi_info.width=(unsigned int) (roi_info.x-x);
11450 roi_info.x=(ssize_t) x;
11451 }
11452 if (roi_info.y < 0)
11453 roi_info.y=0;
11454 else
11455 if (roi_info.y > (ssize_t) windows->image.ximage->height)
11456 roi_info.y=(ssize_t) windows->image.ximage->height;
11457 if ((int) roi_info.y < y)
11458 roi_info.height=(unsigned int) (y-roi_info.y);
11459 else
11460 {
11461 roi_info.height=(unsigned int) (roi_info.y-y);
11462 roi_info.y=(ssize_t) y;
11463 }
11464 }
11465 } while ((state & ExitState) == 0);
11466 /*
11467 Wait for user to grab a corner of the rectangle or press return.
11468 */
11469 state=DefaultState;
11470 display_command=NullCommand;
11471 (void) XMapWindow(display,windows->info.id);
11472 do
11473 {
11474 if (windows->info.mapped != MagickFalse)
11475 {
11476 /*
11477 Display pointer position.
11478 */
11479 (void) FormatLocaleString(text,MaxTextExtent,
11480 " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11481 roi_info.height,(double) roi_info.x,(double) roi_info.y);
11482 XInfoWidget(display,windows,text);
11483 }
11484 highlight_info=roi_info;
11485 highlight_info.x=roi_info.x-windows->image.x;
11486 highlight_info.y=roi_info.y-windows->image.y;
11487 if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11488 {
11489 state|=EscapeState;
11490 state|=ExitState;
11491 break;
11492 }
11493 if ((state & UpdateRegionState) != 0)
11494 {
11495 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11496 switch (display_command)
11497 {
11498 case UndoCommand:
11499 case RedoCommand:
11500 {
11501 (void) XMagickCommand(display,resource_info,windows,
11502 display_command,image);
11503 break;
11504 }
11505 default:
11506 {
11507 /*
11508 Region of interest is relative to image configuration.
11509 */
11510 progress_monitor=SetImageProgressMonitor(*image,
11511 (MagickProgressMonitor) NULL,(*image)->client_data);
11512 crop_info=roi_info;
11513 width=(unsigned int) (*image)->columns;
11514 height=(unsigned int) (*image)->rows;
11515 x=0;
11516 y=0;
11517 if (windows->image.crop_geometry != (char *) NULL)
11518 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11519 &width,&height);
11520 scale_factor=(MagickRealType) width/windows->image.ximage->width;
11521 crop_info.x+=x;
11522 crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11523 crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11524 scale_factor=(MagickRealType)
11525 height/windows->image.ximage->height;
11526 crop_info.y+=y;
11527 crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11528 crop_info.height=(unsigned int)
11529 (scale_factor*crop_info.height+0.5);
11530 roi_image=CropImage(*image,&crop_info,&(*image)->exception);
11531 (void) SetImageProgressMonitor(*image,progress_monitor,
11532 (*image)->client_data);
11533 if (roi_image == (Image *) NULL)
11534 continue;
11535 /*
11536 Apply image processing technique to the region of interest.
11537 */
11538 windows->image.orphan=MagickTrue;
11539 (void) XMagickCommand(display,resource_info,windows,
11540 display_command,&roi_image);
11541 progress_monitor=SetImageProgressMonitor(*image,
11542 (MagickProgressMonitor) NULL,(*image)->client_data);
11543 (void) XMagickCommand(display,resource_info,windows,
11544 SaveToUndoBufferCommand,image);
11545 windows->image.orphan=MagickFalse;
11546 (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11547 crop_info.x,crop_info.y);
11548 roi_image=DestroyImage(roi_image);
11549 (void) SetImageProgressMonitor(*image,progress_monitor,
11550 (*image)->client_data);
11551 break;
11552 }
11553 }
11554 if (display_command != InfoCommand)
11555 {
11556 XConfigureImageColormap(display,resource_info,windows,*image);
11557 (void) XConfigureImage(display,resource_info,windows,*image);
11558 }
11559 XCheckRefreshWindows(display,windows);
11560 XInfoWidget(display,windows,text);
11561 (void) XSetFunction(display,windows->image.highlight_context,
11562 GXinvert);
11563 state&=(~UpdateRegionState);
11564 }
11565 XHighlightRectangle(display,windows->image.id,
11566 windows->image.highlight_context,&highlight_info);
11567 XScreenEvent(display,windows,&event);
11568 if (event.xany.window == windows->command.id)
11569 {
11570 /*
11571 Select a command from the Command widget.
11572 */
11573 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11574 display_command=NullCommand;
11575 id=XCommandWidget(display,windows,ApplyMenu,&event);
11576 if (id >= 0)
11577 {
11578 (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11579 display_command=ApplyCommands[id];
11580 if (id < ApplyMenus)
11581 {
11582 /*
11583 Select a command from a pop-up menu.
11584 */
11585 entry=XMenuWidget(display,windows,ApplyMenu[id],
11586 (const char **) Menus[id],command);
11587 if (entry >= 0)
11588 {
11589 (void) CopyMagickString(command,Menus[id][entry],
11590 MaxTextExtent);
11591 display_command=Commands[id][entry];
11592 }
11593 }
11594 }
11595 (void) XSetFunction(display,windows->image.highlight_context,
11596 GXinvert);
11597 XHighlightRectangle(display,windows->image.id,
11598 windows->image.highlight_context,&highlight_info);
11599 if (display_command == HelpCommand)
11600 {
11601 (void) XSetFunction(display,windows->image.highlight_context,
11602 GXcopy);
11603 XTextViewHelp(display,resource_info,windows,MagickFalse,
11604 "Help Viewer - Region of Interest",ImageROIHelp);
11605 (void) XSetFunction(display,windows->image.highlight_context,
11606 GXinvert);
11607 continue;
11608 }
11609 if (display_command == QuitCommand)
11610 {
11611 /*
11612 exit.
11613 */
11614 state|=EscapeState;
11615 state|=ExitState;
11616 continue;
11617 }
11618 if (display_command != NullCommand)
11619 state|=UpdateRegionState;
11620 continue;
11621 }
11622 XHighlightRectangle(display,windows->image.id,
11623 windows->image.highlight_context,&highlight_info);
11624 switch (event.type)
11625 {
11626 case ButtonPress:
11627 {
11628 x=windows->image.x;
11629 y=windows->image.y;
11630 if (event.xbutton.button != Button1)
11631 break;
11632 if (event.xbutton.window != windows->image.id)
11633 break;
11634 x=windows->image.x+event.xbutton.x;
11635 y=windows->image.y+event.xbutton.y;
11636 if ((x < (int) (roi_info.x+RoiDelta)) &&
11637 (x > (int) (roi_info.x-RoiDelta)) &&
11638 (y < (int) (roi_info.y+RoiDelta)) &&
11639 (y > (int) (roi_info.y-RoiDelta)))
11640 {
11641 roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11642 roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11643 state|=UpdateConfigurationState;
11644 break;
11645 }
11646 if ((x < (int) (roi_info.x+RoiDelta)) &&
11647 (x > (int) (roi_info.x-RoiDelta)) &&
11648 (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11649 (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11650 {
11651 roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11652 state|=UpdateConfigurationState;
11653 break;
11654 }
11655 if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11656 (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11657 (y < (int) (roi_info.y+RoiDelta)) &&
11658 (y > (int) (roi_info.y-RoiDelta)))
11659 {
11660 roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11661 state|=UpdateConfigurationState;
11662 break;
11663 }
11664 if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11665 (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11666 (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11667 (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11668 {
11669 state|=UpdateConfigurationState;
11670 break;
11671 }
11672 magick_fallthrough;
11673 }
11674 case ButtonRelease:
11675 {
11676 if (event.xbutton.window == windows->pan.id)
11677 if ((highlight_info.x != crop_info.x-windows->image.x) ||
11678 (highlight_info.y != crop_info.y-windows->image.y))
11679 XHighlightRectangle(display,windows->image.id,
11680 windows->image.highlight_context,&highlight_info);
11681 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11682 event.xbutton.time);
11683 break;
11684 }
11685 case Expose:
11686 {
11687 if (event.xexpose.window == windows->image.id)
11688 if (event.xexpose.count == 0)
11689 {
11690 event.xexpose.x=(int) highlight_info.x;
11691 event.xexpose.y=(int) highlight_info.y;
11692 event.xexpose.width=(int) highlight_info.width;
11693 event.xexpose.height=(int) highlight_info.height;
11694 XRefreshWindow(display,&windows->image,&event);
11695 }
11696 if (event.xexpose.window == windows->info.id)
11697 if (event.xexpose.count == 0)
11698 XInfoWidget(display,windows,text);
11699 break;
11700 }
11701 case KeyPress:
11702 {
11703 KeySym
11704 key_symbol;
11705
11706 if (event.xkey.window != windows->image.id)
11707 break;
11708 /*
11709 Respond to a user key press.
11710 */
11711 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11712 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11713 switch ((int) key_symbol)
11714 {
11715 case XK_Shift_L:
11716 case XK_Shift_R:
11717 break;
11718 case XK_Escape:
11719 case XK_F20:
11720 {
11721 state|=EscapeState;
11722 magick_fallthrough;
11723 }
11724 case XK_Return:
11725 {
11726 state|=ExitState;
11727 break;
11728 }
11729 case XK_Home:
11730 case XK_KP_Home:
11731 {
11732 roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11733 roi_info.y=(ssize_t) (windows->image.height/2L-
11734 roi_info.height/2L);
11735 break;
11736 }
11737 case XK_Left:
11738 case XK_KP_Left:
11739 {
11740 roi_info.x--;
11741 break;
11742 }
11743 case XK_Up:
11744 case XK_KP_Up:
11745 case XK_Next:
11746 {
11747 roi_info.y--;
11748 break;
11749 }
11750 case XK_Right:
11751 case XK_KP_Right:
11752 {
11753 roi_info.x++;
11754 break;
11755 }
11756 case XK_Prior:
11757 case XK_Down:
11758 case XK_KP_Down:
11759 {
11760 roi_info.y++;
11761 break;
11762 }
11763 case XK_F1:
11764 case XK_Help:
11765 {
11766 (void) XSetFunction(display,windows->image.highlight_context,
11767 GXcopy);
11768 XTextViewHelp(display,resource_info,windows,MagickFalse,
11769 "Help Viewer - Region of Interest",ImageROIHelp);
11770 (void) XSetFunction(display,windows->image.highlight_context,
11771 GXinvert);
11772 break;
11773 }
11774 default:
11775 {
11776 display_command=XImageWindowCommand(display,resource_info,windows,
11777 event.xkey.state,key_symbol,image);
11778 if (display_command != NullCommand)
11779 state|=UpdateRegionState;
11780 break;
11781 }
11782 }
11783 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11784 event.xkey.time);
11785 break;
11786 }
11787 case KeyRelease:
11788 break;
11789 case MotionNotify:
11790 {
11791 if (event.xbutton.window != windows->image.id)
11792 break;
11793 /*
11794 Map and unmap Info widget as text cursor crosses its boundaries.
11795 */
11796 x=event.xmotion.x;
11797 y=event.xmotion.y;
11798 if (windows->info.mapped != MagickFalse)
11799 {
11800 if ((x < (int) (windows->info.x+windows->info.width)) &&
11801 (y < (int) (windows->info.y+windows->info.height)))
11802 (void) XWithdrawWindow(display,windows->info.id,
11803 windows->info.screen);
11804 }
11805 else
11806 if ((x > (int) (windows->info.x+windows->info.width)) ||
11807 (y > (int) (windows->info.y+windows->info.height)))
11808 (void) XMapWindow(display,windows->info.id);
11809 roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11810 roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11811 break;
11812 }
11813 case SelectionRequest:
11814 {
11815 XSelectionEvent
11816 notify;
11817
11818 XSelectionRequestEvent
11819 *request;
11820
11821 /*
11822 Set primary selection.
11823 */
11824 (void) FormatLocaleString(text,MaxTextExtent,
11825 "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11826 roi_info.height,(double) roi_info.x,(double) roi_info.y);
11827 request=(&(event.xselectionrequest));
11828 (void) XChangeProperty(request->display,request->requestor,
11829 request->property,request->target,8,PropModeReplace,
11830 (unsigned char *) text,(int) strlen(text));
11831 notify.type=SelectionNotify;
11832 notify.display=request->display;
11833 notify.requestor=request->requestor;
11834 notify.selection=request->selection;
11835 notify.target=request->target;
11836 notify.time=request->time;
11837 if (request->property == None)
11838 notify.property=request->target;
11839 else
11840 notify.property=request->property;
11841 (void) XSendEvent(request->display,request->requestor,False,0,
11842 (XEvent *) &notify);
11843 }
11844 default:
11845 break;
11846 }
11847 if ((state & UpdateConfigurationState) != 0)
11848 {
11849 (void) XPutBackEvent(display,&event);
11850 (void) XCheckDefineCursor(display,windows->image.id,cursor);
11851 break;
11852 }
11853 } while ((state & ExitState) == 0);
11854 } while ((state & ExitState) == 0);
11855 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11856 XSetCursorState(display,windows,MagickFalse);
11857 if ((state & EscapeState) != 0)
11858 return(MagickTrue);
11859 return(MagickTrue);
11860}
11861
11862/*
11863%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11864% %
11865% %
11866% %
11867+ X R o t a t e I m a g e %
11868% %
11869% %
11870% %
11871%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11872%
11873% XRotateImage() rotates the X image. If the degrees parameter if zero, the
11874% rotation angle is computed from the slope of a line drawn by the user.
11875%
11876% The format of the XRotateImage method is:
11877%
11878% MagickBooleanType XRotateImage(Display *display,
11879% XResourceInfo *resource_info,XWindows *windows,double degrees,
11880% Image **image)
11881%
11882% A description of each parameter follows:
11883%
11884% o display: Specifies a connection to an X server; returned from
11885% XOpenDisplay.
11886%
11887% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11888%
11889% o windows: Specifies a pointer to a XWindows structure.
11890%
11891% o degrees: Specifies the number of degrees to rotate the image.
11892%
11893% o image: the image.
11894%
11895*/
11896static MagickBooleanType XRotateImage(Display *display,
11897 XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image)
11898{
11899 const char
11900 *const RotateMenu[] =
11901 {
11902 "Pixel Color",
11903 "Direction",
11904 "Help",
11905 "Dismiss",
11906 (char *) NULL
11907 };
11908
11909 static ModeType
11910 direction = HorizontalRotateCommand;
11911
11912 static const ModeType
11913 DirectionCommands[] =
11914 {
11915 HorizontalRotateCommand,
11916 VerticalRotateCommand
11917 },
11918 RotateCommands[] =
11919 {
11920 RotateColorCommand,
11921 RotateDirectionCommand,
11922 RotateHelpCommand,
11923 RotateDismissCommand
11924 };
11925
11926 static unsigned int
11927 pen_id = 0;
11928
11929 char
11930 command[MaxTextExtent],
11931 text[MaxTextExtent];
11932
11933 Image
11934 *rotate_image;
11935
11936 int
11937 id,
11938 x,
11939 y;
11940
11941 MagickRealType
11942 normalized_degrees;
11943
11944 int
11945 i;
11946
11947 unsigned int
11948 height,
11949 rotations,
11950 width;
11951
11952 if (degrees == 0.0)
11953 {
11954 unsigned int
11955 distance;
11956
11957 size_t
11958 state;
11959
11960 XEvent
11961 event;
11962
11963 XSegment
11964 rotate_info;
11965
11966 /*
11967 Map Command widget.
11968 */
11969 (void) CloneString(&windows->command.name,"Rotate");
11970 windows->command.data=2;
11971 (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
11972 (void) XMapRaised(display,windows->command.id);
11973 XClientMessage(display,windows->image.id,windows->im_protocols,
11974 windows->im_update_widget,CurrentTime);
11975 /*
11976 Wait for first button press.
11977 */
11978 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11979 XQueryPosition(display,windows->image.id,&x,&y);
11980 rotate_info.x1=x;
11981 rotate_info.y1=y;
11982 rotate_info.x2=x;
11983 rotate_info.y2=y;
11984 state=DefaultState;
11985 do
11986 {
11987 XHighlightLine(display,windows->image.id,
11988 windows->image.highlight_context,&rotate_info);
11989 /*
11990 Wait for next event.
11991 */
11992 XScreenEvent(display,windows,&event);
11993 XHighlightLine(display,windows->image.id,
11994 windows->image.highlight_context,&rotate_info);
11995 if (event.xany.window == windows->command.id)
11996 {
11997 /*
11998 Select a command from the Command widget.
11999 */
12000 id=XCommandWidget(display,windows,RotateMenu,&event);
12001 if (id < 0)
12002 continue;
12003 (void) XSetFunction(display,windows->image.highlight_context,
12004 GXcopy);
12005 switch (RotateCommands[id])
12006 {
12007 case RotateColorCommand:
12008 {
12009 const char
12010 *ColorMenu[MaxNumberPens];
12011
12012 int
12013 pen_number;
12014
12015 XColor
12016 color;
12017
12018 /*
12019 Initialize menu selections.
12020 */
12021 for (i=0; i < (int) (MaxNumberPens-2); i++)
12022 ColorMenu[i]=resource_info->pen_colors[i];
12023 ColorMenu[MaxNumberPens-2]="Browser...";
12024 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12025 /*
12026 Select a pen color from the pop-up menu.
12027 */
12028 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12029 (const char **) ColorMenu,command);
12030 if (pen_number < 0)
12031 break;
12032 if (pen_number == (MaxNumberPens-2))
12033 {
12034 static char
12035 color_name[MaxTextExtent] = "gray";
12036
12037 /*
12038 Select a pen color from a dialog.
12039 */
12040 resource_info->pen_colors[pen_number]=color_name;
12041 XColorBrowserWidget(display,windows,"Select",color_name);
12042 if (*color_name == '\0')
12043 break;
12044 }
12045 /*
12046 Set pen color.
12047 */
12048 (void) XParseColor(display,windows->map_info->colormap,
12049 resource_info->pen_colors[pen_number],&color);
12050 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12051 (unsigned int) MaxColors,&color);
12052 windows->pixel_info->pen_colors[pen_number]=color;
12053 pen_id=(unsigned int) pen_number;
12054 break;
12055 }
12056 case RotateDirectionCommand:
12057 {
12058 const char
12059 *const Directions[] =
12060 {
12061 "horizontal",
12062 "vertical",
12063 (char *) NULL,
12064 };
12065
12066 /*
12067 Select a command from the pop-up menu.
12068 */
12069 id=XMenuWidget(display,windows,RotateMenu[id],
12070 Directions,command);
12071 if (id >= 0)
12072 direction=DirectionCommands[id];
12073 break;
12074 }
12075 case RotateHelpCommand:
12076 {
12077 XTextViewHelp(display,resource_info,windows,MagickFalse,
12078 "Help Viewer - Image Rotation",ImageRotateHelp);
12079 break;
12080 }
12081 case RotateDismissCommand:
12082 {
12083 /*
12084 Prematurely exit.
12085 */
12086 state|=EscapeState;
12087 state|=ExitState;
12088 break;
12089 }
12090 default:
12091 break;
12092 }
12093 (void) XSetFunction(display,windows->image.highlight_context,
12094 GXinvert);
12095 continue;
12096 }
12097 switch (event.type)
12098 {
12099 case ButtonPress:
12100 {
12101 if (event.xbutton.button != Button1)
12102 break;
12103 if (event.xbutton.window != windows->image.id)
12104 break;
12105 /*
12106 exit loop.
12107 */
12108 (void) XSetFunction(display,windows->image.highlight_context,
12109 GXcopy);
12110 rotate_info.x1=event.xbutton.x;
12111 rotate_info.y1=event.xbutton.y;
12112 state|=ExitState;
12113 break;
12114 }
12115 case ButtonRelease:
12116 break;
12117 case Expose:
12118 break;
12119 case KeyPress:
12120 {
12121 char
12122 command[MaxTextExtent];
12123
12124 KeySym
12125 key_symbol;
12126
12127 if (event.xkey.window != windows->image.id)
12128 break;
12129 /*
12130 Respond to a user key press.
12131 */
12132 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12133 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12134 switch ((int) key_symbol)
12135 {
12136 case XK_Escape:
12137 case XK_F20:
12138 {
12139 /*
12140 Prematurely exit.
12141 */
12142 state|=EscapeState;
12143 state|=ExitState;
12144 break;
12145 }
12146 case XK_F1:
12147 case XK_Help:
12148 {
12149 (void) XSetFunction(display,windows->image.highlight_context,
12150 GXcopy);
12151 XTextViewHelp(display,resource_info,windows,MagickFalse,
12152 "Help Viewer - Image Rotation",ImageRotateHelp);
12153 (void) XSetFunction(display,windows->image.highlight_context,
12154 GXinvert);
12155 break;
12156 }
12157 default:
12158 {
12159 (void) XBell(display,0);
12160 break;
12161 }
12162 }
12163 break;
12164 }
12165 case MotionNotify:
12166 {
12167 rotate_info.x1=event.xmotion.x;
12168 rotate_info.y1=event.xmotion.y;
12169 }
12170 }
12171 rotate_info.x2=rotate_info.x1;
12172 rotate_info.y2=rotate_info.y1;
12173 if (direction == HorizontalRotateCommand)
12174 rotate_info.x2+=32;
12175 else
12176 rotate_info.y2-=32;
12177 } while ((state & ExitState) == 0);
12178 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12179 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12180 if ((state & EscapeState) != 0)
12181 return(MagickTrue);
12182 /*
12183 Draw line as pointer moves until the mouse button is released.
12184 */
12185 distance=0;
12186 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12187 state=DefaultState;
12188 do
12189 {
12190 if (distance > 9)
12191 {
12192 /*
12193 Display info and draw rotation line.
12194 */
12195 if (windows->info.mapped == MagickFalse)
12196 (void) XMapWindow(display,windows->info.id);
12197 (void) FormatLocaleString(text,MaxTextExtent," %g",
12198 direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12199 XInfoWidget(display,windows,text);
12200 XHighlightLine(display,windows->image.id,
12201 windows->image.highlight_context,&rotate_info);
12202 }
12203 else
12204 if (windows->info.mapped != MagickFalse)
12205 (void) XWithdrawWindow(display,windows->info.id,
12206 windows->info.screen);
12207 /*
12208 Wait for next event.
12209 */
12210 XScreenEvent(display,windows,&event);
12211 if (distance > 9)
12212 XHighlightLine(display,windows->image.id,
12213 windows->image.highlight_context,&rotate_info);
12214 switch (event.type)
12215 {
12216 case ButtonPress:
12217 break;
12218 case ButtonRelease:
12219 {
12220 /*
12221 User has committed to rotation line.
12222 */
12223 rotate_info.x2=event.xbutton.x;
12224 rotate_info.y2=event.xbutton.y;
12225 state|=ExitState;
12226 break;
12227 }
12228 case Expose:
12229 break;
12230 case MotionNotify:
12231 {
12232 rotate_info.x2=event.xmotion.x;
12233 rotate_info.y2=event.xmotion.y;
12234 }
12235 default:
12236 break;
12237 }
12238 /*
12239 Check boundary conditions.
12240 */
12241 if (rotate_info.x2 < 0)
12242 rotate_info.x2=0;
12243 else
12244 if (rotate_info.x2 > (int) windows->image.width)
12245 rotate_info.x2=(short) windows->image.width;
12246 if (rotate_info.y2 < 0)
12247 rotate_info.y2=0;
12248 else
12249 if (rotate_info.y2 > (int) windows->image.height)
12250 rotate_info.y2=(short) windows->image.height;
12251 /*
12252 Compute rotation angle from the slope of the line.
12253 */
12254 degrees=0.0;
12255 distance=(unsigned int)
12256 ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12257 ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12258 if (distance > 9)
12259 degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12260 rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12261 } while ((state & ExitState) == 0);
12262 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12263 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12264 if (distance <= 9)
12265 return(MagickTrue);
12266 }
12267 if (direction == VerticalRotateCommand)
12268 degrees-=90.0;
12269 if (degrees == 0.0)
12270 return(MagickTrue);
12271 /*
12272 Rotate image.
12273 */
12274 normalized_degrees=degrees;
12275 while (normalized_degrees < -45.0)
12276 normalized_degrees+=360.0;
12277 for (rotations=0; normalized_degrees > 45.0; rotations++)
12278 normalized_degrees-=90.0;
12279 if (normalized_degrees != 0.0)
12280 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
12281 XSetCursorState(display,windows,MagickTrue);
12282 XCheckRefreshWindows(display,windows);
12283 (*image)->background_color.red=ScaleShortToQuantum(
12284 windows->pixel_info->pen_colors[pen_id].red);
12285 (*image)->background_color.green=ScaleShortToQuantum(
12286 windows->pixel_info->pen_colors[pen_id].green);
12287 (*image)->background_color.blue=ScaleShortToQuantum(
12288 windows->pixel_info->pen_colors[pen_id].blue);
12289 rotate_image=RotateImage(*image,degrees,&(*image)->exception);
12290 XSetCursorState(display,windows,MagickFalse);
12291 if (rotate_image == (Image *) NULL)
12292 return(MagickFalse);
12293 *image=DestroyImage(*image);
12294 *image=rotate_image;
12295 if (windows->image.crop_geometry != (char *) NULL)
12296 {
12297 /*
12298 Rotate crop geometry.
12299 */
12300 width=(unsigned int) (*image)->columns;
12301 height=(unsigned int) (*image)->rows;
12302 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12303 switch (rotations % 4)
12304 {
12305 default:
12306 case 0:
12307 break;
12308 case 1:
12309 {
12310 /*
12311 Rotate 90 degrees.
12312 */
12313 (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12314 "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12315 (int) height-y,x);
12316 break;
12317 }
12318 case 2:
12319 {
12320 /*
12321 Rotate 180 degrees.
12322 */
12323 (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12324 "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12325 break;
12326 }
12327 case 3:
12328 {
12329 /*
12330 Rotate 270 degrees.
12331 */
12332 (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12333 "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12334 break;
12335 }
12336 }
12337 }
12338 if (windows->image.orphan != MagickFalse)
12339 return(MagickTrue);
12340 if (normalized_degrees != 0.0)
12341 {
12342 /*
12343 Update image colormap.
12344 */
12345 windows->image.window_changes.width=(int) (*image)->columns;
12346 windows->image.window_changes.height=(int) (*image)->rows;
12347 if (windows->image.crop_geometry != (char *) NULL)
12348 {
12349 /*
12350 Obtain dimensions of image from crop geometry.
12351 */
12352 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12353 &width,&height);
12354 windows->image.window_changes.width=(int) width;
12355 windows->image.window_changes.height=(int) height;
12356 }
12357 XConfigureImageColormap(display,resource_info,windows,*image);
12358 }
12359 else
12360 if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12361 {
12362 windows->image.window_changes.width=windows->image.ximage->height;
12363 windows->image.window_changes.height=windows->image.ximage->width;
12364 }
12365 /*
12366 Update image configuration.
12367 */
12368 (void) XConfigureImage(display,resource_info,windows,*image);
12369 return(MagickTrue);
12370}
12371
12372/*
12373%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12374% %
12375% %
12376% %
12377+ X S a v e I m a g e %
12378% %
12379% %
12380% %
12381%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12382%
12383% XSaveImage() saves an image to a file.
12384%
12385% The format of the XSaveImage method is:
12386%
12387% MagickBooleanType XSaveImage(Display *display,
12388% XResourceInfo *resource_info,XWindows *windows,Image *image)
12389%
12390% A description of each parameter follows:
12391%
12392% o display: Specifies a connection to an X server; returned from
12393% XOpenDisplay.
12394%
12395% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12396%
12397% o windows: Specifies a pointer to a XWindows structure.
12398%
12399% o image: the image.
12400%
12401*/
12402static MagickBooleanType XSaveImage(Display *display,
12403 XResourceInfo *resource_info,XWindows *windows,Image *image)
12404{
12405 char
12406 filename[MaxTextExtent],
12407 geometry[MaxTextExtent];
12408
12409 Image
12410 *save_image;
12411
12412 ImageInfo
12413 *image_info;
12414
12415 MagickStatusType
12416 status;
12417
12418 /*
12419 Request file name from user.
12420 */
12421 if (resource_info->write_filename != (char *) NULL)
12422 (void) CopyMagickString(filename,resource_info->write_filename,
12423 MaxTextExtent);
12424 else
12425 {
12426 char
12427 path[MaxTextExtent];
12428
12429 int
12430 status;
12431
12432 GetPathComponent(image->filename,HeadPath,path);
12433 GetPathComponent(image->filename,TailPath,filename);
12434 if (*path != '\0')
12435 {
12436 status=chdir(path);
12437 if (status == -1)
12438 (void) ThrowMagickException(&image->exception,GetMagickModule(),
12439 FileOpenError,"UnableToOpenFile","%s",path);
12440 }
12441 }
12442 XFileBrowserWidget(display,windows,"Save",filename);
12443 if (*filename == '\0')
12444 return(MagickTrue);
12445 if (IsPathAccessible(filename) != MagickFalse)
12446 {
12447 int
12448 status;
12449
12450 /*
12451 File exists-- seek user's permission before overwriting.
12452 */
12453 status=XConfirmWidget(display,windows,"Overwrite",filename);
12454 if (status <= 0)
12455 return(MagickTrue);
12456 }
12457 image_info=CloneImageInfo(resource_info->image_info);
12458 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12459 (void) SetImageInfo(image_info,1,&image->exception);
12460 if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12461 (LocaleCompare(image_info->magick,"JPG") == 0))
12462 {
12463 char
12464 quality[MaxTextExtent];
12465
12466 int
12467 status;
12468
12469 /*
12470 Request JPEG quality from user.
12471 */
12472 (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12473 image->quality);
12474 status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12475 quality);
12476 if (*quality == '\0')
12477 return(MagickTrue);
12478 image->quality=StringToUnsignedLong(quality);
12479 image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12480 }
12481 if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12482 (LocaleCompare(image_info->magick,"PDF") == 0) ||
12483 (LocaleCompare(image_info->magick,"PS") == 0) ||
12484 (LocaleCompare(image_info->magick,"PS2") == 0))
12485 {
12486 char
12487 geometry[MaxTextExtent];
12488
12489 const char
12490 *const PageSizes[] =
12491 {
12492 "Letter",
12493 "Tabloid",
12494 "Ledger",
12495 "Legal",
12496 "Statement",
12497 "Executive",
12498 "A3",
12499 "A4",
12500 "A5",
12501 "B4",
12502 "B5",
12503 "Folio",
12504 "Quarto",
12505 "10x14",
12506 (char *) NULL
12507 };
12508
12509 /*
12510 Request page geometry from user.
12511 */
12512 (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12513 if (LocaleCompare(image_info->magick,"PDF") == 0)
12514 (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12515 if (image_info->page != (char *) NULL)
12516 (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12517 XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12518 "Select page geometry:",geometry);
12519 if (*geometry != '\0')
12520 image_info->page=GetPageGeometry(geometry);
12521 }
12522 /*
12523 Apply image transforms.
12524 */
12525 XSetCursorState(display,windows,MagickTrue);
12526 XCheckRefreshWindows(display,windows);
12527 save_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12528 if (save_image == (Image *) NULL)
12529 return(MagickFalse);
12530 (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12531 windows->image.ximage->width,windows->image.ximage->height);
12532 (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12533 /*
12534 Write image.
12535 */
12536 (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12537 status=WriteImage(image_info,save_image);
12538 if (status != MagickFalse)
12539 image->taint=MagickFalse;
12540 save_image=DestroyImage(save_image);
12541 image_info=DestroyImageInfo(image_info);
12542 XSetCursorState(display,windows,MagickFalse);
12543 return(status != 0 ? MagickTrue : MagickFalse);
12544}
12545
12546/*
12547%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12548% %
12549% %
12550% %
12551+ X S c r e e n E v e n t %
12552% %
12553% %
12554% %
12555%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12556%
12557% XScreenEvent() handles global events associated with the Pan and Magnify
12558% windows.
12559%
12560% The format of the XScreenEvent function is:
12561%
12562% void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12563%
12564% A description of each parameter follows:
12565%
12566% o display: Specifies a pointer to the Display structure; returned from
12567% XOpenDisplay.
12568%
12569% o windows: Specifies a pointer to a XWindows structure.
12570%
12571% o event: Specifies a pointer to a X11 XEvent structure.
12572%
12573%
12574*/
12575
12576#if defined(__cplusplus) || defined(c_plusplus)
12577extern "C" {
12578#endif
12579
12580static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12581{
12582 XWindows
12583 *windows;
12584
12585 magick_unreferenced(display);
12586
12587 windows=(XWindows *) data;
12588 if ((event->type == ClientMessage) &&
12589 (event->xclient.window == windows->image.id))
12590 return(MagickFalse);
12591 return(MagickTrue);
12592}
12593
12594#if defined(__cplusplus) || defined(c_plusplus)
12595}
12596#endif
12597
12598static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12599{
12600 int
12601 x,
12602 y;
12603
12604 (void) XIfEvent(display,event,XPredicate,(char *) windows);
12605 if (event->xany.window == windows->command.id)
12606 return;
12607 switch (event->type)
12608 {
12609 case ButtonPress:
12610 case ButtonRelease:
12611 {
12612 if ((event->xbutton.button == Button3) &&
12613 (event->xbutton.state & Mod1Mask))
12614 {
12615 /*
12616 Convert Alt-Button3 to Button2.
12617 */
12618 event->xbutton.button=Button2;
12619 event->xbutton.state&=(~Mod1Mask);
12620 }
12621 if (event->xbutton.window == windows->backdrop.id)
12622 {
12623 (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12624 event->xbutton.time);
12625 break;
12626 }
12627 if (event->xbutton.window == windows->pan.id)
12628 {
12629 XPanImage(display,windows,event);
12630 break;
12631 }
12632 if (event->xbutton.window == windows->image.id)
12633 if (event->xbutton.button == Button2)
12634 {
12635 /*
12636 Update magnified image.
12637 */
12638 x=event->xbutton.x;
12639 y=event->xbutton.y;
12640 if (x < 0)
12641 x=0;
12642 else
12643 if (x >= (int) windows->image.width)
12644 x=(int) (windows->image.width-1);
12645 windows->magnify.x=(int) windows->image.x+x;
12646 if (y < 0)
12647 y=0;
12648 else
12649 if (y >= (int) windows->image.height)
12650 y=(int) (windows->image.height-1);
12651 windows->magnify.y=windows->image.y+y;
12652 if (windows->magnify.mapped == MagickFalse)
12653 (void) XMapRaised(display,windows->magnify.id);
12654 XMakeMagnifyImage(display,windows);
12655 if (event->type == ButtonRelease)
12656 (void) XWithdrawWindow(display,windows->info.id,
12657 windows->info.screen);
12658 break;
12659 }
12660 break;
12661 }
12662 case ClientMessage:
12663 {
12664 /*
12665 If client window delete message, exit.
12666 */
12667 if (event->xclient.message_type != windows->wm_protocols)
12668 break;
12669 if (*event->xclient.data.l != (long) windows->wm_delete_window)
12670 break;
12671 if (event->xclient.window == windows->magnify.id)
12672 {
12673 (void) XWithdrawWindow(display,windows->magnify.id,
12674 windows->magnify.screen);
12675 break;
12676 }
12677 break;
12678 }
12679 case ConfigureNotify:
12680 {
12681 if (event->xconfigure.window == windows->magnify.id)
12682 {
12683 unsigned int
12684 magnify;
12685
12686 /*
12687 Magnify window has a new configuration.
12688 */
12689 windows->magnify.width=(unsigned int) event->xconfigure.width;
12690 windows->magnify.height=(unsigned int) event->xconfigure.height;
12691 if (windows->magnify.mapped == MagickFalse)
12692 break;
12693 magnify=1;
12694 while ((int) magnify <= event->xconfigure.width)
12695 magnify<<=1;
12696 while ((int) magnify <= event->xconfigure.height)
12697 magnify<<=1;
12698 magnify>>=1;
12699 if (((int) magnify != event->xconfigure.width) ||
12700 ((int) magnify != event->xconfigure.height))
12701 {
12702 XWindowChanges
12703 window_changes;
12704
12705 window_changes.width=(int) magnify;
12706 window_changes.height=(int) magnify;
12707 (void) XReconfigureWMWindow(display,windows->magnify.id,
12708 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12709 &window_changes);
12710 break;
12711 }
12712 XMakeMagnifyImage(display,windows);
12713 break;
12714 }
12715 break;
12716 }
12717 case Expose:
12718 {
12719 if (event->xexpose.window == windows->image.id)
12720 {
12721 XRefreshWindow(display,&windows->image,event);
12722 break;
12723 }
12724 if (event->xexpose.window == windows->pan.id)
12725 if (event->xexpose.count == 0)
12726 {
12727 XDrawPanRectangle(display,windows);
12728 break;
12729 }
12730 if (event->xexpose.window == windows->magnify.id)
12731 if (event->xexpose.count == 0)
12732 {
12733 XMakeMagnifyImage(display,windows);
12734 break;
12735 }
12736 break;
12737 }
12738 case KeyPress:
12739 {
12740 char
12741 command[MaxTextExtent];
12742
12743 KeySym
12744 key_symbol;
12745
12746 if (event->xkey.window != windows->magnify.id)
12747 break;
12748 /*
12749 Respond to a user key press.
12750 */
12751 (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12752 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12753 XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12754 break;
12755 }
12756 case MapNotify:
12757 {
12758 if (event->xmap.window == windows->magnify.id)
12759 {
12760 windows->magnify.mapped=MagickTrue;
12761 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12762 break;
12763 }
12764 if (event->xmap.window == windows->info.id)
12765 {
12766 windows->info.mapped=MagickTrue;
12767 break;
12768 }
12769 break;
12770 }
12771 case MotionNotify:
12772 {
12773 while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12774 if (event->xmotion.window == windows->image.id)
12775 if (windows->magnify.mapped != MagickFalse)
12776 {
12777 /*
12778 Update magnified image.
12779 */
12780 x=event->xmotion.x;
12781 y=event->xmotion.y;
12782 if (x < 0)
12783 x=0;
12784 else
12785 if (x >= (int) windows->image.width)
12786 x=(int) (windows->image.width-1);
12787 windows->magnify.x=(int) windows->image.x+x;
12788 if (y < 0)
12789 y=0;
12790 else
12791 if (y >= (int) windows->image.height)
12792 y=(int) (windows->image.height-1);
12793 windows->magnify.y=windows->image.y+y;
12794 XMakeMagnifyImage(display,windows);
12795 }
12796 break;
12797 }
12798 case UnmapNotify:
12799 {
12800 if (event->xunmap.window == windows->magnify.id)
12801 {
12802 windows->magnify.mapped=MagickFalse;
12803 break;
12804 }
12805 if (event->xunmap.window == windows->info.id)
12806 {
12807 windows->info.mapped=MagickFalse;
12808 break;
12809 }
12810 break;
12811 }
12812 default:
12813 break;
12814 }
12815}
12816
12817/*
12818%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12819% %
12820% %
12821% %
12822+ X S e t C r o p G e o m e t r y %
12823% %
12824% %
12825% %
12826%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12827%
12828% XSetCropGeometry() accepts a cropping geometry relative to the Image window
12829% and translates it to a cropping geometry relative to the image.
12830%
12831% The format of the XSetCropGeometry method is:
12832%
12833% void XSetCropGeometry(Display *display,XWindows *windows,
12834% RectangleInfo *crop_info,Image *image)
12835%
12836% A description of each parameter follows:
12837%
12838% o display: Specifies a connection to an X server; returned from
12839% XOpenDisplay.
12840%
12841% o windows: Specifies a pointer to a XWindows structure.
12842%
12843% o crop_info: A pointer to a RectangleInfo that defines a region of the
12844% Image window to crop.
12845%
12846% o image: the image.
12847%
12848*/
12849static void XSetCropGeometry(Display *display,XWindows *windows,
12850 RectangleInfo *crop_info,Image *image)
12851{
12852 char
12853 text[MaxTextExtent];
12854
12855 int
12856 x,
12857 y;
12858
12859 MagickRealType
12860 scale_factor;
12861
12862 unsigned int
12863 height,
12864 width;
12865
12866 if (windows->info.mapped != MagickFalse)
12867 {
12868 /*
12869 Display info on cropping rectangle.
12870 */
12871 (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12872 (double) crop_info->width,(double) crop_info->height,(double)
12873 crop_info->x,(double) crop_info->y);
12874 XInfoWidget(display,windows,text);
12875 }
12876 /*
12877 Cropping geometry is relative to any previous crop geometry.
12878 */
12879 x=0;
12880 y=0;
12881 width=(unsigned int) image->columns;
12882 height=(unsigned int) image->rows;
12883 if (windows->image.crop_geometry != (char *) NULL)
12884 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12885 else
12886 windows->image.crop_geometry=AcquireString((char *) NULL);
12887 /*
12888 Define the crop geometry string from the cropping rectangle.
12889 */
12890 scale_factor=(MagickRealType) width/windows->image.ximage->width;
12891 if (crop_info->x > 0)
12892 x+=(int) (scale_factor*crop_info->x+0.5);
12893 width=(unsigned int) (scale_factor*crop_info->width+0.5);
12894 if (width == 0)
12895 width=1;
12896 scale_factor=(MagickRealType) height/windows->image.ximage->height;
12897 if (crop_info->y > 0)
12898 y+=(int) (scale_factor*crop_info->y+0.5);
12899 height=(unsigned int) (scale_factor*crop_info->height+0.5);
12900 if (height == 0)
12901 height=1;
12902 (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12903 "%ux%u%+d%+d",width,height,x,y);
12904}
12905
12906/*
12907%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12908% %
12909% %
12910% %
12911+ X T i l e I m a g e %
12912% %
12913% %
12914% %
12915%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12916%
12917% XTileImage() loads or deletes a selected tile from a visual image directory.
12918% The load or delete command is chosen from a menu.
12919%
12920% The format of the XTileImage method is:
12921%
12922% Image *XTileImage(Display *display,XResourceInfo *resource_info,
12923% XWindows *windows,Image *image,XEvent *event)
12924%
12925% A description of each parameter follows:
12926%
12927% o tile_image: XTileImage reads or deletes the tile image
12928% and returns it. A null image is returned if an error occurs.
12929%
12930% o display: Specifies a connection to an X server; returned from
12931% XOpenDisplay.
12932%
12933% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12934%
12935% o windows: Specifies a pointer to a XWindows structure.
12936%
12937% o image: the image; returned from ReadImage.
12938%
12939% o event: Specifies a pointer to a XEvent structure. If it is NULL,
12940% the entire image is refreshed.
12941%
12942*/
12943static Image *XTileImage(Display *display,XResourceInfo *resource_info,
12944 XWindows *windows,Image *image,XEvent *event)
12945{
12946 const char
12947 *const VerbMenu[] =
12948 {
12949 "Load",
12950 "Next",
12951 "Former",
12952 "Delete",
12953 "Update",
12954 (char *) NULL,
12955 };
12956
12957 static const ModeType
12958 TileCommands[] =
12959 {
12960 TileLoadCommand,
12961 TileNextCommand,
12962 TileFormerCommand,
12963 TileDeleteCommand,
12964 TileUpdateCommand
12965 };
12966
12967 char
12968 command[MaxTextExtent],
12969 filename[MaxTextExtent];
12970
12971 Image
12972 *tile_image;
12973
12974 int
12975 id,
12976 status,
12977 tile,
12978 x,
12979 y;
12980
12981 MagickRealType
12982 scale_factor;
12983
12984 char
12985 *p,
12986 *q;
12987
12988 int
12989 i;
12990
12991 unsigned int
12992 height,
12993 width;
12994
12995 /*
12996 Tile image is relative to montage image configuration.
12997 */
12998 x=0;
12999 y=0;
13000 width=(unsigned int) image->columns;
13001 height=(unsigned int) image->rows;
13002 if (windows->image.crop_geometry != (char *) NULL)
13003 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13004 scale_factor=(MagickRealType) width/windows->image.ximage->width;
13005 event->xbutton.x+=windows->image.x;
13006 event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13007 scale_factor=(MagickRealType) height/windows->image.ximage->height;
13008 event->xbutton.y+=windows->image.y;
13009 event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13010 /*
13011 Determine size and location of each tile in the visual image directory.
13012 */
13013 width=(unsigned int) image->columns;
13014 height=(unsigned int) image->rows;
13015 x=0;
13016 y=0;
13017 (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13018 tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13019 (event->xbutton.x-x)/width;
13020 if (tile < 0)
13021 {
13022 /*
13023 Button press is outside any tile.
13024 */
13025 (void) XBell(display,0);
13026 return((Image *) NULL);
13027 }
13028 /*
13029 Determine file name from the tile directory.
13030 */
13031 p=image->directory;
13032 for (i=tile; (i != 0) && (*p != '\0'); )
13033 {
13034 if (*p == '\xff')
13035 i--;
13036 p++;
13037 }
13038 if (*p == '\0')
13039 {
13040 /*
13041 Button press is outside any tile.
13042 */
13043 (void) XBell(display,0);
13044 return((Image *) NULL);
13045 }
13046 /*
13047 Select a command from the pop-up menu.
13048 */
13049 id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13050 if (id < 0)
13051 return((Image *) NULL);
13052 q=p;
13053 while ((*q != '\xff') && (*q != '\0'))
13054 q++;
13055 (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13056 /*
13057 Perform command for the selected tile.
13058 */
13059 XSetCursorState(display,windows,MagickTrue);
13060 XCheckRefreshWindows(display,windows);
13061 tile_image=NewImageList();
13062 switch (TileCommands[id])
13063 {
13064 case TileLoadCommand:
13065 {
13066 /*
13067 Load tile image.
13068 */
13069 XCheckRefreshWindows(display,windows);
13070 (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13071 MaxTextExtent);
13072 (void) CopyMagickString(resource_info->image_info->filename,filename,
13073 MaxTextExtent);
13074 tile_image=ReadImage(resource_info->image_info,&image->exception);
13075 CatchException(&image->exception);
13076 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13077 break;
13078 }
13079 case TileNextCommand:
13080 {
13081 /*
13082 Display next image.
13083 */
13084 XClientMessage(display,windows->image.id,windows->im_protocols,
13085 windows->im_next_image,CurrentTime);
13086 break;
13087 }
13088 case TileFormerCommand:
13089 {
13090 /*
13091 Display former image.
13092 */
13093 XClientMessage(display,windows->image.id,windows->im_protocols,
13094 windows->im_former_image,CurrentTime);
13095 break;
13096 }
13097 case TileDeleteCommand:
13098 {
13099 /*
13100 Delete tile image.
13101 */
13102 if (IsPathAccessible(filename) == MagickFalse)
13103 {
13104 XNoticeWidget(display,windows,"Image file does not exist:",filename);
13105 break;
13106 }
13107 status=XConfirmWidget(display,windows,"Really delete tile",filename);
13108 if (status <= 0)
13109 break;
13110 status=ShredFile(filename);
13111 status|=remove_utf8(filename);
13112 if (status != MagickFalse)
13113 {
13114 XNoticeWidget(display,windows,"Unable to delete image file:",
13115 filename);
13116 break;
13117 }
13118 magick_fallthrough;
13119 }
13120 case TileUpdateCommand:
13121 {
13123 *exception;
13124
13125 int
13126 x_offset,
13127 y_offset;
13128
13130 pixel;
13131
13132 int
13133 j;
13134
13136 *s;
13137
13138 /*
13139 Ensure all the images exist.
13140 */
13141 tile=0;
13142 for (p=image->directory; *p != '\0'; p++)
13143 {
13144 CacheView
13145 *image_view;
13146
13147 q=p;
13148 while ((*q != '\xff') && (*q != '\0'))
13149 q++;
13150 (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13151 p=q;
13152 if (IsPathAccessible(filename) != MagickFalse)
13153 {
13154 tile++;
13155 continue;
13156 }
13157 /*
13158 Overwrite tile with background color.
13159 */
13160 x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13161 y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13162 exception=(&image->exception);
13163 image_view=AcquireAuthenticCacheView(image,exception);
13164 (void) GetOneCacheViewVirtualPixel(image_view,0,0,&pixel,exception);
13165 for (i=0; i < (int) height; i++)
13166 {
13167 s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13168 y_offset+i,width,1,exception);
13169 if (s == (PixelPacket *) NULL)
13170 break;
13171 for (j=0; j < (int) width; j++)
13172 *s++=pixel;
13173 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13174 break;
13175 }
13176 image_view=DestroyCacheView(image_view);
13177 tile++;
13178 }
13179 windows->image.window_changes.width=(int) image->columns;
13180 windows->image.window_changes.height=(int) image->rows;
13181 XConfigureImageColormap(display,resource_info,windows,image);
13182 (void) XConfigureImage(display,resource_info,windows,image);
13183 break;
13184 }
13185 default:
13186 break;
13187 }
13188 XSetCursorState(display,windows,MagickFalse);
13189 return(tile_image);
13190}
13191
13192/*
13193%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13194% %
13195% %
13196% %
13197+ X T r a n s l a t e I m a g e %
13198% %
13199% %
13200% %
13201%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13202%
13203% XTranslateImage() translates the image within an Image window by one pixel
13204% as specified by the key symbol. If the image has a `montage string the
13205% translation is respect to the width and height contained within the string.
13206%
13207% The format of the XTranslateImage method is:
13208%
13209% void XTranslateImage(Display *display,XWindows *windows,
13210% Image *image,const KeySym key_symbol)
13211%
13212% A description of each parameter follows:
13213%
13214% o display: Specifies a connection to an X server; returned from
13215% XOpenDisplay.
13216%
13217% o windows: Specifies a pointer to a XWindows structure.
13218%
13219% o image: the image.
13220%
13221% o key_symbol: Specifies a KeySym which indicates which side of the image
13222% to trim.
13223%
13224*/
13225static void XTranslateImage(Display *display,XWindows *windows,
13226 Image *image,const KeySym key_symbol)
13227{
13228 char
13229 text[MaxTextExtent];
13230
13231 int
13232 x,
13233 y;
13234
13235 unsigned int
13236 x_offset,
13237 y_offset;
13238
13239 /*
13240 User specified a pan position offset.
13241 */
13242 x_offset=windows->image.width;
13243 y_offset=windows->image.height;
13244 if (image->montage != (char *) NULL)
13245 (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13246 switch ((int) key_symbol)
13247 {
13248 case XK_Home:
13249 case XK_KP_Home:
13250 {
13251 windows->image.x=(int) windows->image.width/2;
13252 windows->image.y=(int) windows->image.height/2;
13253 break;
13254 }
13255 case XK_Left:
13256 case XK_KP_Left:
13257 {
13258 windows->image.x-=x_offset;
13259 break;
13260 }
13261 case XK_Next:
13262 case XK_Up:
13263 case XK_KP_Up:
13264 {
13265 windows->image.y-=y_offset;
13266 break;
13267 }
13268 case XK_Right:
13269 case XK_KP_Right:
13270 {
13271 windows->image.x+=x_offset;
13272 break;
13273 }
13274 case XK_Prior:
13275 case XK_Down:
13276 case XK_KP_Down:
13277 {
13278 windows->image.y+=y_offset;
13279 break;
13280 }
13281 default:
13282 return;
13283 }
13284 /*
13285 Check boundary conditions.
13286 */
13287 if (windows->image.x < 0)
13288 windows->image.x=0;
13289 else
13290 if ((int) (windows->image.x+windows->image.width) >
13291 windows->image.ximage->width)
13292 windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13293 if (windows->image.y < 0)
13294 windows->image.y=0;
13295 else
13296 if ((int) (windows->image.y+windows->image.height) >
13297 windows->image.ximage->height)
13298 windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13299 /*
13300 Refresh Image window.
13301 */
13302 (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13303 windows->image.width,windows->image.height,windows->image.x,
13304 windows->image.y);
13305 XInfoWidget(display,windows,text);
13306 XCheckRefreshWindows(display,windows);
13307 XDrawPanRectangle(display,windows);
13308 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13309 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13310}
13311
13312/*
13313%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13314% %
13315% %
13316% %
13317+ X T r i m I m a g e %
13318% %
13319% %
13320% %
13321%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13322%
13323% XTrimImage() trims the edges from the Image window.
13324%
13325% The format of the XTrimImage method is:
13326%
13327% MagickBooleanType XTrimImage(Display *display,
13328% XResourceInfo *resource_info,XWindows *windows,Image *image)
13329%
13330% A description of each parameter follows:
13331%
13332% o display: Specifies a connection to an X server; returned from
13333% XOpenDisplay.
13334%
13335% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13336%
13337% o windows: Specifies a pointer to a XWindows structure.
13338%
13339% o image: the image.
13340%
13341*/
13342static MagickBooleanType XTrimImage(Display *display,
13343 XResourceInfo *resource_info,XWindows *windows,Image *image)
13344{
13346 trim_info;
13347
13348 int
13349 x,
13350 y;
13351
13352 size_t
13353 background,
13354 pixel;
13355
13356 /*
13357 Trim edges from image.
13358 */
13359 XSetCursorState(display,windows,MagickTrue);
13360 XCheckRefreshWindows(display,windows);
13361 /*
13362 Crop the left edge.
13363 */
13364 background=XGetPixel(windows->image.ximage,0,0);
13365 trim_info.width=(size_t) windows->image.ximage->width;
13366 for (x=0; x < windows->image.ximage->width; x++)
13367 {
13368 for (y=0; y < windows->image.ximage->height; y++)
13369 {
13370 pixel=XGetPixel(windows->image.ximage,x,y);
13371 if (pixel != background)
13372 break;
13373 }
13374 if (y < windows->image.ximage->height)
13375 break;
13376 }
13377 trim_info.x=(ssize_t) x;
13378 if (trim_info.x == (ssize_t) windows->image.ximage->width)
13379 {
13380 XSetCursorState(display,windows,MagickFalse);
13381 return(MagickFalse);
13382 }
13383 /*
13384 Crop the right edge.
13385 */
13386 background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13387 for (x=windows->image.ximage->width-1; x != 0; x--)
13388 {
13389 for (y=0; y < windows->image.ximage->height; y++)
13390 {
13391 pixel=XGetPixel(windows->image.ximage,x,y);
13392 if (pixel != background)
13393 break;
13394 }
13395 if (y < windows->image.ximage->height)
13396 break;
13397 }
13398 trim_info.width=(size_t) (x-trim_info.x+1);
13399 /*
13400 Crop the top edge.
13401 */
13402 background=XGetPixel(windows->image.ximage,0,0);
13403 trim_info.height=(size_t) windows->image.ximage->height;
13404 for (y=0; y < windows->image.ximage->height; y++)
13405 {
13406 for (x=0; x < windows->image.ximage->width; x++)
13407 {
13408 pixel=XGetPixel(windows->image.ximage,x,y);
13409 if (pixel != background)
13410 break;
13411 }
13412 if (x < windows->image.ximage->width)
13413 break;
13414 }
13415 trim_info.y=(ssize_t) y;
13416 /*
13417 Crop the bottom edge.
13418 */
13419 background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13420 for (y=windows->image.ximage->height-1; y != 0; y--)
13421 {
13422 for (x=0; x < windows->image.ximage->width; x++)
13423 {
13424 pixel=XGetPixel(windows->image.ximage,x,y);
13425 if (pixel != background)
13426 break;
13427 }
13428 if (x < windows->image.ximage->width)
13429 break;
13430 }
13431 trim_info.height=(size_t) y-trim_info.y+1;
13432 if (((unsigned int) trim_info.width != windows->image.width) ||
13433 ((unsigned int) trim_info.height != windows->image.height))
13434 {
13435 /*
13436 Reconfigure Image window as defined by the trimming rectangle.
13437 */
13438 XSetCropGeometry(display,windows,&trim_info,image);
13439 windows->image.window_changes.width=(int) trim_info.width;
13440 windows->image.window_changes.height=(int) trim_info.height;
13441 (void) XConfigureImage(display,resource_info,windows,image);
13442 }
13443 XSetCursorState(display,windows,MagickFalse);
13444 return(MagickTrue);
13445}
13446
13447/*
13448%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13449% %
13450% %
13451% %
13452+ X V i s u a l D i r e c t o r y I m a g e %
13453% %
13454% %
13455% %
13456%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13457%
13458% XVisualDirectoryImage() creates a Visual Image Directory.
13459%
13460% The format of the XVisualDirectoryImage method is:
13461%
13462% Image *XVisualDirectoryImage(Display *display,
13463% XResourceInfo *resource_info,XWindows *windows)
13464%
13465% A description of each parameter follows:
13466%
13467% o nexus: Method XVisualDirectoryImage returns a visual image
13468% directory if it can be created successfully. Otherwise a null image
13469% is returned.
13470%
13471% o display: Specifies a connection to an X server; returned from
13472% XOpenDisplay.
13473%
13474% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13475%
13476% o windows: Specifies a pointer to a XWindows structure.
13477%
13478*/
13479static Image *XVisualDirectoryImage(Display *display,
13480 XResourceInfo *resource_info,XWindows *windows)
13481{
13482#define TileImageTag "Scale/Image"
13483#define XClientName "montage"
13484
13485 char
13486 **filelist;
13487
13489 *exception;
13490
13491 Image
13492 *images,
13493 *montage_image,
13494 *next_image,
13495 *thumbnail_image;
13496
13497 ImageInfo
13498 *read_info;
13499
13500 int
13501 number_files;
13502
13503 MagickBooleanType
13504 backdrop;
13505
13506 MagickStatusType
13507 status;
13508
13510 *montage_info;
13511
13513 geometry;
13514
13515 int
13516 i;
13517
13518 static char
13519 filename[MaxTextExtent] = "\0",
13520 filenames[MaxTextExtent] = "*";
13521
13522 XResourceInfo
13523 background_resources;
13524
13525 /*
13526 Request file name from user.
13527 */
13528 XFileBrowserWidget(display,windows,"Directory",filenames);
13529 if (*filenames == '\0')
13530 return((Image *) NULL);
13531 /*
13532 Expand the filenames.
13533 */
13534 filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13535 if (filelist == (char **) NULL)
13536 {
13537 ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13538 filenames);
13539 return((Image *) NULL);
13540 }
13541 number_files=1;
13542 filelist[0]=filenames;
13543 status=ExpandFilenames(&number_files,&filelist);
13544 if ((status == MagickFalse) || (number_files == 0))
13545 {
13546 if (number_files == 0)
13547 ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13548 else
13549 ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13550 filenames);
13551 return((Image *) NULL);
13552 }
13553 /*
13554 Set image background resources.
13555 */
13556 background_resources=(*resource_info);
13557 background_resources.window_id=AcquireString("");
13558 (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13559 "0x%lx",windows->image.id);
13560 background_resources.backdrop=MagickTrue;
13561 /*
13562 Read each image and convert them to a tile.
13563 */
13564 backdrop=(windows->visual_info->klass == TrueColor) ||
13565 (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13566 read_info=CloneImageInfo(resource_info->image_info);
13567 (void) SetImageOption(read_info,"jpeg:size","120x120");
13568 (void) CloneString(&read_info->size,DefaultTileGeometry);
13569 (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13570 (void *) NULL);
13571 images=NewImageList();
13572 exception=AcquireExceptionInfo();
13573 XSetCursorState(display,windows,MagickTrue);
13574 XCheckRefreshWindows(display,windows);
13575 for (i=0; i < (int) number_files; i++)
13576 {
13577 (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13578 filelist[i]=DestroyString(filelist[i]);
13579 *read_info->magick='\0';
13580 next_image=ReadImage(read_info,exception);
13581 CatchException(exception);
13582 if (next_image != (Image *) NULL)
13583 {
13584 (void) DeleteImageProperty(next_image,"label");
13585 (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13586 read_info,next_image,DefaultTileLabel));
13587 (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13588 exception);
13589 thumbnail_image=ThumbnailImage(next_image,geometry.width,
13590 geometry.height,exception);
13591 if (thumbnail_image != (Image *) NULL)
13592 {
13593 next_image=DestroyImage(next_image);
13594 next_image=thumbnail_image;
13595 }
13596 if (backdrop)
13597 {
13598 (void) XDisplayBackgroundImage(display,&background_resources,
13599 next_image);
13600 XSetCursorState(display,windows,MagickTrue);
13601 }
13602 AppendImageToList(&images,next_image);
13603 if (images->progress_monitor != (MagickProgressMonitor) NULL)
13604 {
13605 MagickBooleanType
13606 proceed;
13607
13608 proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13609 (MagickSizeType) number_files);
13610 if (proceed == MagickFalse)
13611 break;
13612 }
13613 }
13614 }
13615 exception=DestroyExceptionInfo(exception);
13616 filelist=(char **) RelinquishMagickMemory(filelist);
13617 if (images == (Image *) NULL)
13618 {
13619 read_info=DestroyImageInfo(read_info);
13620 XSetCursorState(display,windows,MagickFalse);
13621 ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13622 return((Image *) NULL);
13623 }
13624 /*
13625 Create the Visual Image Directory.
13626 */
13627 montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13628 montage_info->pointsize=10;
13629 if (resource_info->font != (char *) NULL)
13630 (void) CloneString(&montage_info->font,resource_info->font);
13631 (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13632 montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13633 images),&images->exception);
13634 images=DestroyImageList(images);
13635 montage_info=DestroyMontageInfo(montage_info);
13636 read_info=DestroyImageInfo(read_info);
13637 XSetCursorState(display,windows,MagickFalse);
13638 if (montage_image == (Image *) NULL)
13639 return(montage_image);
13640 XClientMessage(display,windows->image.id,windows->im_protocols,
13641 windows->im_next_image,CurrentTime);
13642 return(montage_image);
13643}
13644
13645/*
13646%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13647% %
13648% %
13649% %
13650% X D i s p l a y B a c k g r o u n d I m a g e %
13651% %
13652% %
13653% %
13654%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13655%
13656% XDisplayBackgroundImage() displays an image in the background of a window.
13657%
13658% The format of the XDisplayBackgroundImage method is:
13659%
13660% MagickBooleanType XDisplayBackgroundImage(Display *display,
13661% XResourceInfo *resource_info,Image *image)
13662%
13663% A description of each parameter follows:
13664%
13665% o display: Specifies a connection to an X server; returned from
13666% XOpenDisplay.
13667%
13668% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13669%
13670% o image: the image.
13671%
13672*/
13673MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13674 XResourceInfo *resource_info,Image *image)
13675{
13676 char
13677 geometry[MaxTextExtent],
13678 visual_type[MaxTextExtent];
13679
13680 int
13681 height,
13682 status,
13683 width;
13684
13686 geometry_info;
13687
13688 static XPixelInfo
13689 pixel;
13690
13691 static XStandardColormap
13692 *map_info;
13693
13694 static XVisualInfo
13695 *visual_info = (XVisualInfo *) NULL;
13696
13697 static XWindowInfo
13698 window_info;
13699
13700 size_t
13701 delay;
13702
13703 Window
13704 root_window;
13705
13706 XGCValues
13707 context_values;
13708
13709 XResourceInfo
13710 resources;
13711
13712 XWindowAttributes
13713 window_attributes;
13714
13715 /*
13716 Determine target window.
13717 */
13718 assert(image != (Image *) NULL);
13719 assert(image->signature == MagickCoreSignature);
13720 if (IsEventLogging() != MagickFalse)
13721 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13722 resources=(*resource_info);
13723 window_info.id=(Window) NULL;
13724 root_window=XRootWindow(display,XDefaultScreen(display));
13725 if (LocaleCompare(resources.window_id,"root") == 0)
13726 window_info.id=root_window;
13727 else
13728 {
13729 if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13730 window_info.id=XWindowByID(display,root_window,
13731 (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13732 if (window_info.id == (Window) NULL)
13733 window_info.id=XWindowByName(display,root_window,resources.window_id);
13734 }
13735 if (window_info.id == (Window) NULL)
13736 {
13737 ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13738 resources.window_id);
13739 }
13740 /*
13741 Determine window visual id.
13742 */
13743 window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13744 window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13745 (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13746 status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13747 if (status != 0)
13748 (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13749 XVisualIDFromVisual(window_attributes.visual));
13750 if (visual_info == (XVisualInfo *) NULL)
13751 {
13752 /*
13753 Allocate standard colormap.
13754 */
13755 map_info=XAllocStandardColormap();
13756 if (map_info == (XStandardColormap *) NULL)
13757 ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13758 image->filename);
13759 map_info->colormap=(Colormap) NULL;
13760 pixel.pixels=(unsigned long *) NULL;
13761 /*
13762 Initialize visual info.
13763 */
13764 resources.map_type=(char *) NULL;
13765 resources.visual_type=visual_type;
13766 visual_info=XBestVisualInfo(display,map_info,&resources);
13767 if (visual_info == (XVisualInfo *) NULL)
13768 ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13769 resources.visual_type);
13770 /*
13771 Initialize window info.
13772 */
13773 window_info.ximage=(XImage *) NULL;
13774 window_info.matte_image=(XImage *) NULL;
13775 window_info.pixmap=(Pixmap) NULL;
13776 window_info.matte_pixmap=(Pixmap) NULL;
13777 }
13778 /*
13779 Free previous root colors.
13780 */
13781 if (window_info.id == root_window)
13782 (void) XDestroyWindowColors(display,root_window);
13783 /*
13784 Initialize Standard Colormap.
13785 */
13786 resources.colormap=SharedColormap;
13787 XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13788 /*
13789 Graphic context superclass.
13790 */
13791 context_values.background=pixel.foreground_color.pixel;
13792 context_values.foreground=pixel.background_color.pixel;
13793 pixel.annotate_context=XCreateGC(display,window_info.id,
13794 (size_t) (GCBackground | GCForeground),&context_values);
13795 if (pixel.annotate_context == (GC) NULL)
13796 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13797 image->filename);
13798 /*
13799 Initialize Image window attributes.
13800 */
13801 window_info.name=AcquireString("\0");
13802 window_info.icon_name=AcquireString("\0");
13803 XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13804 &resources,&window_info);
13805 /*
13806 Create the X image.
13807 */
13808 window_info.width=(unsigned int) image->columns;
13809 window_info.height=(unsigned int) image->rows;
13810 if ((image->columns != window_info.width) ||
13811 (image->rows != window_info.height))
13812 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13813 image->filename);
13814 (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13815 window_attributes.width,window_attributes.height);
13816 geometry_info.width=window_info.width;
13817 geometry_info.height=window_info.height;
13818 geometry_info.x=(ssize_t) window_info.x;
13819 geometry_info.y=(ssize_t) window_info.y;
13820 (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13821 &geometry_info.width,&geometry_info.height);
13822 window_info.width=(unsigned int) geometry_info.width;
13823 window_info.height=(unsigned int) geometry_info.height;
13824 window_info.x=(int) geometry_info.x;
13825 window_info.y=(int) geometry_info.y;
13826 status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13827 window_info.height);
13828 if (status == MagickFalse)
13829 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13830 image->filename);
13831 window_info.x=0;
13832 window_info.y=0;
13833 if (resource_info->debug != MagickFalse)
13834 {
13835 (void) LogMagickEvent(X11Event,GetMagickModule(),
13836 "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13837 (double) image->columns,(double) image->rows);
13838 if (image->colors != 0)
13839 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13840 image->colors);
13841 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13842 }
13843 /*
13844 Adjust image dimensions as specified by backdrop or geometry options.
13845 */
13846 width=(int) window_info.width;
13847 height=(int) window_info.height;
13848 if (resources.backdrop != MagickFalse)
13849 {
13850 /*
13851 Center image on window.
13852 */
13853 window_info.x=(window_attributes.width/2)-(window_info.ximage->width/2);
13854 window_info.y=(window_attributes.height/2)-(window_info.ximage->height/2);
13855 width=window_attributes.width;
13856 height=window_attributes.height;
13857 }
13858 if ((resources.image_geometry != (char *) NULL) &&
13859 (*resources.image_geometry != '\0'))
13860 {
13861 char
13862 default_geometry[MaxTextExtent];
13863
13864 int
13865 flags,
13866 gravity;
13867
13868 XSizeHints
13869 *size_hints;
13870
13871 /*
13872 User specified geometry.
13873 */
13874 size_hints=XAllocSizeHints();
13875 if (size_hints == (XSizeHints *) NULL)
13876 ThrowXWindowFatalException(ResourceLimitFatalError,
13877 "MemoryAllocationFailed",image->filename);
13878 size_hints->flags=0L;
13879 (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13880 width,height);
13881 flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13882 default_geometry,window_info.border_width,size_hints,&window_info.x,
13883 &window_info.y,&width,&height,&gravity);
13884 if (flags & (XValue | YValue))
13885 {
13886 width=window_attributes.width;
13887 height=window_attributes.height;
13888 }
13889 (void) XFree((void *) size_hints);
13890 }
13891 /*
13892 Create the X pixmap.
13893 */
13894 window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13895 (unsigned int) height,window_info.depth);
13896 if (window_info.pixmap == (Pixmap) NULL)
13897 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13898 image->filename);
13899 /*
13900 Display pixmap on the window.
13901 */
13902 if (((unsigned int) width > window_info.width) ||
13903 ((unsigned int) height > window_info.height))
13904 (void) XFillRectangle(display,window_info.pixmap,
13905 window_info.annotate_context,0,0,(unsigned int) width,
13906 (unsigned int) height);
13907 (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13908 window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13909 window_info.width,(unsigned int) window_info.height);
13910 (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13911 (void) XClearWindow(display,window_info.id);
13912 delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13913 XDelay(display,delay == 0UL ? 10UL : delay);
13914 (void) XSync(display,MagickFalse);
13915 return(window_info.id == root_window ? MagickTrue : MagickFalse);
13916}
13917
13918/*
13919%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13920% %
13921% %
13922% %
13923+ X D i s p l a y I m a g e %
13924% %
13925% %
13926% %
13927%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13928%
13929% XDisplayImage() displays an image via X11. A new image is created and
13930% returned if the user interactively transforms the displayed image.
13931%
13932% The format of the XDisplayImage method is:
13933%
13934% Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13935% char **argv,int argc,Image **image,size_t *state)
13936%
13937% A description of each parameter follows:
13938%
13939% o nexus: Method XDisplayImage returns an image when the
13940% user chooses 'Open Image' from the command menu or picks a tile
13941% from the image directory. Otherwise a null image is returned.
13942%
13943% o display: Specifies a connection to an X server; returned from
13944% XOpenDisplay.
13945%
13946% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13947%
13948% o argv: Specifies the application's argument list.
13949%
13950% o argc: Specifies the number of arguments.
13951%
13952% o image: Specifies an address to an address of an Image structure;
13953%
13954*/
13955MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13956 char **argv,int argc,Image **image,size_t *state)
13957{
13958#define MagnifySize 256 /* must be a power of 2 */
13959#define MagickMenus 10
13960#define MagickTitle "Commands"
13961
13962 const char
13963 *const CommandMenu[] =
13964 {
13965 "File",
13966 "Edit",
13967 "View",
13968 "Transform",
13969 "Enhance",
13970 "Effects",
13971 "F/X",
13972 "Image Edit",
13973 "Miscellany",
13974 "Help",
13975 (char *) NULL
13976 },
13977 *const FileMenu[] =
13978 {
13979 "Open...",
13980 "Next",
13981 "Former",
13982 "Select...",
13983 "Save...",
13984 "Print...",
13985 "Delete...",
13986 "New...",
13987 "Visual Directory...",
13988 "Quit",
13989 (char *) NULL
13990 },
13991 *const EditMenu[] =
13992 {
13993 "Undo",
13994 "Redo",
13995 "Cut",
13996 "Copy",
13997 "Paste",
13998 (char *) NULL
13999 },
14000 *const ViewMenu[] =
14001 {
14002 "Half Size",
14003 "Original Size",
14004 "Double Size",
14005 "Resize...",
14006 "Apply",
14007 "Refresh",
14008 "Restore",
14009 (char *) NULL
14010 },
14011 *const TransformMenu[] =
14012 {
14013 "Crop",
14014 "Chop",
14015 "Flop",
14016 "Flip",
14017 "Rotate Right",
14018 "Rotate Left",
14019 "Rotate...",
14020 "Shear...",
14021 "Roll...",
14022 "Trim Edges",
14023 (char *) NULL
14024 },
14025 *const EnhanceMenu[] =
14026 {
14027 "Hue...",
14028 "Saturation...",
14029 "Brightness...",
14030 "Gamma...",
14031 "Spiff",
14032 "Dull",
14033 "Contrast Stretch...",
14034 "Sigmoidal Contrast...",
14035 "Normalize",
14036 "Equalize",
14037 "Negate",
14038 "Grayscale",
14039 "Map...",
14040 "Quantize...",
14041 (char *) NULL
14042 },
14043 *const EffectsMenu[] =
14044 {
14045 "Despeckle",
14046 "Emboss",
14047 "Reduce Noise",
14048 "Add Noise...",
14049 "Sharpen...",
14050 "Blur...",
14051 "Threshold...",
14052 "Edge Detect...",
14053 "Spread...",
14054 "Shade...",
14055 "Raise...",
14056 "Segment...",
14057 (char *) NULL
14058 },
14059 *const FXMenu[] =
14060 {
14061 "Solarize...",
14062 "Sepia Tone...",
14063 "Swirl...",
14064 "Implode...",
14065 "Vignette...",
14066 "Wave...",
14067 "Oil Paint...",
14068 "Charcoal Draw...",
14069 (char *) NULL
14070 },
14071 *const ImageEditMenu[] =
14072 {
14073 "Annotate...",
14074 "Draw...",
14075 "Color...",
14076 "Matte...",
14077 "Composite...",
14078 "Add Border...",
14079 "Add Frame...",
14080 "Comment...",
14081 "Launch...",
14082 "Region of Interest...",
14083 (char *) NULL
14084 },
14085 *const MiscellanyMenu[] =
14086 {
14087 "Image Info",
14088 "Zoom Image",
14089 "Show Preview...",
14090 "Show Histogram",
14091 "Show Matte",
14092 "Background...",
14093 "Slide Show...",
14094 "Preferences...",
14095 (char *) NULL
14096 },
14097 *const HelpMenu[] =
14098 {
14099 "Overview",
14100 "Browse Documentation",
14101 "About Display",
14102 (char *) NULL
14103 },
14104 *const ShortCutsMenu[] =
14105 {
14106 "Next",
14107 "Former",
14108 "Open...",
14109 "Save...",
14110 "Print...",
14111 "Undo",
14112 "Restore",
14113 "Image Info",
14114 "Quit",
14115 (char *) NULL
14116 },
14117 *const VirtualMenu[] =
14118 {
14119 "Image Info",
14120 "Print",
14121 "Next",
14122 "Quit",
14123 (char *) NULL
14124 };
14125
14126 const char
14127 *const *Menus[MagickMenus] =
14128 {
14129 FileMenu,
14130 EditMenu,
14131 ViewMenu,
14132 TransformMenu,
14133 EnhanceMenu,
14134 EffectsMenu,
14135 FXMenu,
14136 ImageEditMenu,
14137 MiscellanyMenu,
14138 HelpMenu
14139 };
14140
14141 static DisplayCommand
14142 CommandMenus[] =
14143 {
14144 NullCommand,
14145 NullCommand,
14146 NullCommand,
14147 NullCommand,
14148 NullCommand,
14149 NullCommand,
14150 NullCommand,
14151 NullCommand,
14152 NullCommand,
14153 NullCommand,
14154 },
14155 FileCommands[] =
14156 {
14157 OpenCommand,
14158 NextCommand,
14159 FormerCommand,
14160 SelectCommand,
14161 SaveCommand,
14162 PrintCommand,
14163 DeleteCommand,
14164 NewCommand,
14165 VisualDirectoryCommand,
14166 QuitCommand
14167 },
14168 EditCommands[] =
14169 {
14170 UndoCommand,
14171 RedoCommand,
14172 CutCommand,
14173 CopyCommand,
14174 PasteCommand
14175 },
14176 ViewCommands[] =
14177 {
14178 HalfSizeCommand,
14179 OriginalSizeCommand,
14180 DoubleSizeCommand,
14181 ResizeCommand,
14182 ApplyCommand,
14183 RefreshCommand,
14184 RestoreCommand
14185 },
14186 TransformCommands[] =
14187 {
14188 CropCommand,
14189 ChopCommand,
14190 FlopCommand,
14191 FlipCommand,
14192 RotateRightCommand,
14193 RotateLeftCommand,
14194 RotateCommand,
14195 ShearCommand,
14196 RollCommand,
14197 TrimCommand
14198 },
14199 EnhanceCommands[] =
14200 {
14201 HueCommand,
14202 SaturationCommand,
14203 BrightnessCommand,
14204 GammaCommand,
14205 SpiffCommand,
14206 DullCommand,
14207 ContrastStretchCommand,
14208 SigmoidalContrastCommand,
14209 NormalizeCommand,
14210 EqualizeCommand,
14211 NegateCommand,
14212 GrayscaleCommand,
14213 MapCommand,
14214 QuantizeCommand
14215 },
14216 EffectsCommands[] =
14217 {
14218 DespeckleCommand,
14219 EmbossCommand,
14220 ReduceNoiseCommand,
14221 AddNoiseCommand,
14222 SharpenCommand,
14223 BlurCommand,
14224 ThresholdCommand,
14225 EdgeDetectCommand,
14226 SpreadCommand,
14227 ShadeCommand,
14228 RaiseCommand,
14229 SegmentCommand
14230 },
14231 FXCommands[] =
14232 {
14233 SolarizeCommand,
14234 SepiaToneCommand,
14235 SwirlCommand,
14236 ImplodeCommand,
14237 VignetteCommand,
14238 WaveCommand,
14239 OilPaintCommand,
14240 CharcoalDrawCommand
14241 },
14242 ImageEditCommands[] =
14243 {
14244 AnnotateCommand,
14245 DrawCommand,
14246 ColorCommand,
14247 MatteCommand,
14248 CompositeCommand,
14249 AddBorderCommand,
14250 AddFrameCommand,
14251 CommentCommand,
14252 LaunchCommand,
14253 RegionOfInterestCommand
14254 },
14255 MiscellanyCommands[] =
14256 {
14257 InfoCommand,
14258 ZoomCommand,
14259 ShowPreviewCommand,
14260 ShowHistogramCommand,
14261 ShowMatteCommand,
14262 BackgroundCommand,
14263 SlideShowCommand,
14264 PreferencesCommand
14265 },
14266 HelpCommands[] =
14267 {
14268 HelpCommand,
14269 BrowseDocumentationCommand,
14270 VersionCommand
14271 },
14272 ShortCutsCommands[] =
14273 {
14274 NextCommand,
14275 FormerCommand,
14276 OpenCommand,
14277 SaveCommand,
14278 PrintCommand,
14279 UndoCommand,
14280 RestoreCommand,
14281 InfoCommand,
14282 QuitCommand
14283 },
14284 VirtualCommands[] =
14285 {
14286 InfoCommand,
14287 PrintCommand,
14288 NextCommand,
14289 QuitCommand
14290 };
14291
14292 static DisplayCommand
14293 *Commands[MagickMenus] =
14294 {
14295 FileCommands,
14296 EditCommands,
14297 ViewCommands,
14298 TransformCommands,
14299 EnhanceCommands,
14300 EffectsCommands,
14301 FXCommands,
14302 ImageEditCommands,
14303 MiscellanyCommands,
14304 HelpCommands
14305 };
14306
14307 char
14308 command[MaxTextExtent],
14309 *directory,
14310 geometry[MaxTextExtent],
14311 resource_name[MaxTextExtent];
14312
14313 DisplayCommand
14314 display_command;
14315
14316 Image
14317 *display_image,
14318 *nexus;
14319
14320 int
14321 entry,
14322 id;
14323
14324 KeySym
14325 key_symbol;
14326
14327 MagickStatusType
14328 context_mask,
14329 status;
14330
14332 geometry_info;
14333
14334 int
14335 i;
14336
14337 static char
14338 working_directory[MaxTextExtent];
14339
14340 static XPoint
14341 vid_info;
14342
14343 static XWindowInfo
14344 *magick_windows[MaxXWindows];
14345
14346 static unsigned int
14347 number_windows;
14348
14349 struct stat
14350 attributes;
14351
14352 time_t
14353 timer,
14354 timestamp,
14355 update_time;
14356
14357 unsigned int
14358 height,
14359 width;
14360
14361 size_t
14362 delay;
14363
14364 WarningHandler
14365 warning_handler;
14366
14367 Window
14368 root_window;
14369
14370 XClassHint
14371 *class_hints;
14372
14373 XEvent
14374 event;
14375
14376 XFontStruct
14377 *font_info;
14378
14379 XGCValues
14380 context_values;
14381
14382 XPixelInfo
14383 *icon_pixel,
14384 *pixel;
14385
14386 XResourceInfo
14387 *icon_resources;
14388
14389 XStandardColormap
14390 *icon_map,
14391 *map_info;
14392
14393 XVisualInfo
14394 *icon_visual,
14395 *visual_info;
14396
14397 XWindowChanges
14398 window_changes;
14399
14400 XWindows
14401 *windows;
14402
14403 XWMHints
14404 *manager_hints;
14405
14406 assert(image != (Image **) NULL);
14407 assert((*image)->signature == MagickCoreSignature);
14408 if (IsEventLogging() != MagickFalse)
14409 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14410 display_image=(*image);
14411 warning_handler=(WarningHandler) NULL;
14412 windows=XSetWindows((XWindows *) ~0);
14413 if (windows != (XWindows *) NULL)
14414 {
14415 int
14416 status;
14417
14418 if (*working_directory == '\0')
14419 (void) CopyMagickString(working_directory,".",MaxTextExtent);
14420 status=chdir(working_directory);
14421 if (status == -1)
14422 (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
14423 FileOpenError,"UnableToOpenFile","%s",working_directory);
14424 warning_handler=resource_info->display_warnings ?
14425 SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14426 warning_handler=resource_info->display_warnings ?
14427 SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14428 }
14429 else
14430 {
14431 /*
14432 Allocate windows structure.
14433 */
14434 resource_info->colors=display_image->colors;
14435 windows=XSetWindows(XInitializeWindows(display,resource_info));
14436 if (windows == (XWindows *) NULL)
14437 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14438 (*image)->filename);
14439 /*
14440 Initialize window id's.
14441 */
14442 number_windows=0;
14443 magick_windows[number_windows++]=(&windows->icon);
14444 magick_windows[number_windows++]=(&windows->backdrop);
14445 magick_windows[number_windows++]=(&windows->image);
14446 magick_windows[number_windows++]=(&windows->info);
14447 magick_windows[number_windows++]=(&windows->command);
14448 magick_windows[number_windows++]=(&windows->widget);
14449 magick_windows[number_windows++]=(&windows->popup);
14450 magick_windows[number_windows++]=(&windows->magnify);
14451 magick_windows[number_windows++]=(&windows->pan);
14452 for (i=0; i < (int) number_windows; i++)
14453 magick_windows[i]->id=(Window) NULL;
14454 vid_info.x=0;
14455 vid_info.y=0;
14456 }
14457 /*
14458 Initialize font info.
14459 */
14460 if (windows->font_info != (XFontStruct *) NULL)
14461 (void) XFreeFont(display,windows->font_info);
14462 windows->font_info=XBestFont(display,resource_info,MagickFalse);
14463 if (windows->font_info == (XFontStruct *) NULL)
14464 ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14465 resource_info->font);
14466 /*
14467 Initialize Standard Colormap.
14468 */
14469 map_info=windows->map_info;
14470 icon_map=windows->icon_map;
14471 visual_info=windows->visual_info;
14472 icon_visual=windows->icon_visual;
14473 pixel=windows->pixel_info;
14474 icon_pixel=windows->icon_pixel;
14475 font_info=windows->font_info;
14476 icon_resources=windows->icon_resources;
14477 class_hints=windows->class_hints;
14478 manager_hints=windows->manager_hints;
14479 root_window=XRootWindow(display,visual_info->screen);
14480 nexus=NewImageList();
14481 if (resource_info->debug != MagickFalse)
14482 {
14483 (void) LogMagickEvent(X11Event,GetMagickModule(),
14484 "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14485 (double) display_image->scene,(double) display_image->columns,
14486 (double) display_image->rows);
14487 if (display_image->colors != 0)
14488 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14489 display_image->colors);
14490 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14491 display_image->magick);
14492 }
14493 XMakeStandardColormap(display,visual_info,resource_info,display_image,
14494 map_info,pixel);
14495 display_image->taint=MagickFalse;
14496 /*
14497 Initialize graphic context.
14498 */
14499 windows->context.id=(Window) NULL;
14500 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14501 resource_info,&windows->context);
14502 (void) CloneString(&class_hints->res_name,resource_info->client_name);
14503 (void) CloneString(&class_hints->res_class,resource_info->client_name);
14504 class_hints->res_class[0]=(char) LocaleToUppercase((int)
14505 class_hints->res_class[0]);
14506 manager_hints->flags=InputHint | StateHint;
14507 manager_hints->input=MagickFalse;
14508 manager_hints->initial_state=WithdrawnState;
14509 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14510 &windows->context);
14511 if (resource_info->debug != MagickFalse)
14512 (void) LogMagickEvent(X11Event,GetMagickModule(),
14513 "Window id: 0x%lx (context)",windows->context.id);
14514 context_values.background=pixel->background_color.pixel;
14515 context_values.font=font_info->fid;
14516 context_values.foreground=pixel->foreground_color.pixel;
14517 context_values.graphics_exposures=MagickFalse;
14518 context_mask=(MagickStatusType)
14519 (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14520 if (pixel->annotate_context != (GC) NULL)
14521 (void) XFreeGC(display,pixel->annotate_context);
14522 pixel->annotate_context=XCreateGC(display,windows->context.id,
14523 context_mask,&context_values);
14524 if (pixel->annotate_context == (GC) NULL)
14525 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14526 display_image->filename);
14527 context_values.background=pixel->depth_color.pixel;
14528 if (pixel->widget_context != (GC) NULL)
14529 (void) XFreeGC(display,pixel->widget_context);
14530 pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14531 &context_values);
14532 if (pixel->widget_context == (GC) NULL)
14533 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14534 display_image->filename);
14535 context_values.background=pixel->foreground_color.pixel;
14536 context_values.foreground=pixel->background_color.pixel;
14537 context_values.plane_mask=context_values.background ^
14538 context_values.foreground;
14539 if (pixel->highlight_context != (GC) NULL)
14540 (void) XFreeGC(display,pixel->highlight_context);
14541 pixel->highlight_context=XCreateGC(display,windows->context.id,
14542 (size_t) (context_mask | GCPlaneMask),&context_values);
14543 if (pixel->highlight_context == (GC) NULL)
14544 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14545 display_image->filename);
14546 (void) XDestroyWindow(display,windows->context.id);
14547 /*
14548 Initialize icon window.
14549 */
14550 XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14551 icon_resources,&windows->icon);
14552 windows->icon.geometry=resource_info->icon_geometry;
14553 XBestIconSize(display,&windows->icon,display_image);
14554 windows->icon.attributes.colormap=XDefaultColormap(display,
14555 icon_visual->screen);
14556 windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14557 manager_hints->flags=InputHint | StateHint;
14558 manager_hints->input=MagickFalse;
14559 manager_hints->initial_state=IconicState;
14560 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14561 &windows->icon);
14562 if (resource_info->debug != MagickFalse)
14563 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14564 windows->icon.id);
14565 /*
14566 Initialize graphic context for icon window.
14567 */
14568 if (icon_pixel->annotate_context != (GC) NULL)
14569 (void) XFreeGC(display,icon_pixel->annotate_context);
14570 context_values.background=icon_pixel->background_color.pixel;
14571 context_values.foreground=icon_pixel->foreground_color.pixel;
14572 icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14573 (size_t) (GCBackground | GCForeground),&context_values);
14574 if (icon_pixel->annotate_context == (GC) NULL)
14575 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14576 display_image->filename);
14577 windows->icon.annotate_context=icon_pixel->annotate_context;
14578 /*
14579 Initialize Image window.
14580 */
14581 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14582 &windows->image);
14583 windows->image.shape=MagickTrue; /* non-rectangular shape hint */
14584 if (resource_info->use_shared_memory == MagickFalse)
14585 windows->image.shared_memory=MagickFalse;
14586 if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14587 {
14588 char
14589 *title;
14590
14591 title=InterpretImageProperties(resource_info->image_info,display_image,
14592 resource_info->title);
14593 (void) CloneString(&windows->image.name,title);
14594 (void) CloneString(&windows->image.icon_name,title);
14595 title=DestroyString(title);
14596 }
14597 else
14598 {
14599 char
14600 filename[MaxTextExtent],
14601 window_name[MaxTextExtent];
14602
14603 /*
14604 Window name is the base of the filename.
14605 */
14606 GetPathComponent(display_image->magick_filename,TailPath,filename);
14607 if (display_image->scene == 0)
14608 (void) FormatLocaleString(window_name,MaxTextExtent,"%s: %s",
14609 MagickPackageName,filename);
14610 else
14611 (void) FormatLocaleString(window_name,MaxTextExtent,
14612 "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14613 (double) display_image->scene,(double) GetImageListLength(
14614 display_image));
14615 (void) CloneString(&windows->image.name,window_name);
14616 (void) CloneString(&windows->image.icon_name,filename);
14617 }
14618 if (resource_info->immutable)
14619 windows->image.immutable=MagickTrue;
14620 windows->image.use_pixmap=resource_info->use_pixmap;
14621 windows->image.geometry=resource_info->image_geometry;
14622 (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14623 XDisplayWidth(display,visual_info->screen),
14624 XDisplayHeight(display,visual_info->screen));
14625 geometry_info.width=display_image->columns;
14626 geometry_info.height=display_image->rows;
14627 geometry_info.x=0;
14628 geometry_info.y=0;
14629 (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14630 &geometry_info.width,&geometry_info.height);
14631 windows->image.width=(unsigned int) geometry_info.width;
14632 windows->image.height=(unsigned int) geometry_info.height;
14633 windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14634 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14635 KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14636 PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14637 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14638 resource_info,&windows->backdrop);
14639 if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14640 {
14641 /*
14642 Initialize backdrop window.
14643 */
14644 windows->backdrop.x=0;
14645 windows->backdrop.y=0;
14646 (void) CloneString(&windows->backdrop.name,"Backdrop");
14647 windows->backdrop.flags=(size_t) (USSize | USPosition);
14648 windows->backdrop.width=(unsigned int)
14649 XDisplayWidth(display,visual_info->screen);
14650 windows->backdrop.height=(unsigned int)
14651 XDisplayHeight(display,visual_info->screen);
14652 windows->backdrop.border_width=0;
14653 windows->backdrop.immutable=MagickTrue;
14654 windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14655 ButtonReleaseMask;
14656 windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14657 StructureNotifyMask;
14658 manager_hints->flags=IconWindowHint | InputHint | StateHint;
14659 manager_hints->icon_window=windows->icon.id;
14660 manager_hints->input=MagickTrue;
14661 manager_hints->initial_state=resource_info->iconic ? IconicState :
14662 NormalState;
14663 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14664 &windows->backdrop);
14665 if (resource_info->debug != MagickFalse)
14666 (void) LogMagickEvent(X11Event,GetMagickModule(),
14667 "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14668 (void) XMapWindow(display,windows->backdrop.id);
14669 (void) XClearWindow(display,windows->backdrop.id);
14670 if (windows->image.id != (Window) NULL)
14671 {
14672 (void) XDestroyWindow(display,windows->image.id);
14673 windows->image.id=(Window) NULL;
14674 }
14675 /*
14676 Position image in the center the backdrop.
14677 */
14678 windows->image.flags|=USPosition;
14679 windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14680 (windows->image.width/2);
14681 windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14682 (windows->image.height/2);
14683 }
14684 manager_hints->flags=IconWindowHint | InputHint | StateHint;
14685 manager_hints->icon_window=windows->icon.id;
14686 manager_hints->input=MagickTrue;
14687 manager_hints->initial_state=resource_info->iconic ? IconicState :
14688 NormalState;
14689 if (windows->group_leader.id != (Window) NULL)
14690 {
14691 /*
14692 Follow the leader.
14693 */
14694 manager_hints->flags|=WindowGroupHint;
14695 manager_hints->window_group=windows->group_leader.id;
14696 (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14697 if (resource_info->debug != MagickFalse)
14698 (void) LogMagickEvent(X11Event,GetMagickModule(),
14699 "Window id: 0x%lx (group leader)",windows->group_leader.id);
14700 }
14701 XMakeWindow(display,
14702 (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14703 argv,argc,class_hints,manager_hints,&windows->image);
14704 (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14705 XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14706 if (windows->group_leader.id != (Window) NULL)
14707 (void) XSetTransientForHint(display,windows->image.id,
14708 windows->group_leader.id);
14709 if (resource_info->debug != MagickFalse)
14710 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14711 windows->image.id);
14712 /*
14713 Initialize Info widget.
14714 */
14715 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14716 &windows->info);
14717 (void) CloneString(&windows->info.name,"Info");
14718 (void) CloneString(&windows->info.icon_name,"Info");
14719 windows->info.border_width=1;
14720 windows->info.x=2;
14721 windows->info.y=2;
14722 windows->info.flags|=PPosition;
14723 windows->info.attributes.win_gravity=UnmapGravity;
14724 windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14725 StructureNotifyMask;
14726 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14727 manager_hints->input=MagickFalse;
14728 manager_hints->initial_state=NormalState;
14729 manager_hints->window_group=windows->image.id;
14730 XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14731 &windows->info);
14732 windows->info.highlight_stipple=XCreateBitmapFromData(display,
14733 windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14734 windows->info.shadow_stipple=XCreateBitmapFromData(display,
14735 windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14736 (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14737 if (windows->image.mapped != MagickFalse)
14738 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14739 if (resource_info->debug != MagickFalse)
14740 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14741 windows->info.id);
14742 /*
14743 Initialize Command widget.
14744 */
14745 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14746 resource_info,&windows->command);
14747 windows->command.data=MagickMenus;
14748 (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14749 (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14750 resource_info->client_name);
14751 windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14752 resource_name,"geometry",(char *) NULL);
14753 (void) CloneString(&windows->command.name,MagickTitle);
14754 windows->command.border_width=0;
14755 windows->command.flags|=PPosition;
14756 windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14757 ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14758 OwnerGrabButtonMask | StructureNotifyMask;
14759 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14760 manager_hints->input=MagickTrue;
14761 manager_hints->initial_state=NormalState;
14762 manager_hints->window_group=windows->image.id;
14763 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14764 &windows->command);
14765 windows->command.highlight_stipple=XCreateBitmapFromData(display,
14766 windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14767 HighlightHeight);
14768 windows->command.shadow_stipple=XCreateBitmapFromData(display,
14769 windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14770 (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14771 if (windows->command.mapped != MagickFalse)
14772 (void) XMapRaised(display,windows->command.id);
14773 if (resource_info->debug != MagickFalse)
14774 (void) LogMagickEvent(X11Event,GetMagickModule(),
14775 "Window id: 0x%lx (command)",windows->command.id);
14776 /*
14777 Initialize Widget window.
14778 */
14779 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14780 resource_info,&windows->widget);
14781 (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14782 resource_info->client_name);
14783 windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14784 resource_name,"geometry",(char *) NULL);
14785 windows->widget.border_width=0;
14786 windows->widget.flags|=PPosition;
14787 windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14788 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14789 KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14790 StructureNotifyMask;
14791 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14792 manager_hints->input=MagickTrue;
14793 manager_hints->initial_state=NormalState;
14794 manager_hints->window_group=windows->image.id;
14795 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14796 &windows->widget);
14797 windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14798 windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14799 windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14800 windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14801 (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14802 if (resource_info->debug != MagickFalse)
14803 (void) LogMagickEvent(X11Event,GetMagickModule(),
14804 "Window id: 0x%lx (widget)",windows->widget.id);
14805 /*
14806 Initialize popup window.
14807 */
14808 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14809 resource_info,&windows->popup);
14810 windows->popup.border_width=0;
14811 windows->popup.flags|=PPosition;
14812 windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14813 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14814 KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14815 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14816 manager_hints->input=MagickTrue;
14817 manager_hints->initial_state=NormalState;
14818 manager_hints->window_group=windows->image.id;
14819 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14820 &windows->popup);
14821 windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14822 windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14823 windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14824 windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14825 (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14826 if (resource_info->debug != MagickFalse)
14827 (void) LogMagickEvent(X11Event,GetMagickModule(),
14828 "Window id: 0x%lx (pop up)",windows->popup.id);
14829 /*
14830 Initialize Magnify window and cursor.
14831 */
14832 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14833 resource_info,&windows->magnify);
14834 if (resource_info->use_shared_memory == MagickFalse)
14835 windows->magnify.shared_memory=MagickFalse;
14836 (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14837 resource_info->client_name);
14838 windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14839 resource_name,"geometry",(char *) NULL);
14840 (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14841 resource_info->magnify);
14842 if (windows->magnify.cursor != (Cursor) NULL)
14843 (void) XFreeCursor(display,windows->magnify.cursor);
14844 windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14845 map_info->colormap,resource_info->background_color,
14846 resource_info->foreground_color);
14847 if (windows->magnify.cursor == (Cursor) NULL)
14848 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14849 display_image->filename);
14850 windows->magnify.width=MagnifySize;
14851 windows->magnify.height=MagnifySize;
14852 windows->magnify.flags|=PPosition;
14853 windows->magnify.min_width=MagnifySize;
14854 windows->magnify.min_height=MagnifySize;
14855 windows->magnify.width_inc=MagnifySize;
14856 windows->magnify.height_inc=MagnifySize;
14857 windows->magnify.data=resource_info->magnify;
14858 windows->magnify.attributes.cursor=windows->magnify.cursor;
14859 windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14860 ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14861 StructureNotifyMask;
14862 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14863 manager_hints->input=MagickTrue;
14864 manager_hints->initial_state=NormalState;
14865 manager_hints->window_group=windows->image.id;
14866 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14867 &windows->magnify);
14868 if (resource_info->debug != MagickFalse)
14869 (void) LogMagickEvent(X11Event,GetMagickModule(),
14870 "Window id: 0x%lx (magnify)",windows->magnify.id);
14871 (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14872 /*
14873 Initialize panning window.
14874 */
14875 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14876 resource_info,&windows->pan);
14877 (void) CloneString(&windows->pan.name,"Pan Icon");
14878 windows->pan.width=windows->icon.width;
14879 windows->pan.height=windows->icon.height;
14880 (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14881 resource_info->client_name);
14882 windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14883 resource_name,"geometry",(char *) NULL);
14884 (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14885 &windows->pan.width,&windows->pan.height);
14886 windows->pan.flags|=PPosition;
14887 windows->pan.immutable=MagickTrue;
14888 windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14889 ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14890 StructureNotifyMask;
14891 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14892 manager_hints->input=MagickFalse;
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->pan);
14897 if (resource_info->debug != MagickFalse)
14898 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14899 windows->pan.id);
14900 (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14901 if (windows->info.mapped != MagickFalse)
14902 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14903 if ((windows->image.mapped == MagickFalse) ||
14904 (windows->backdrop.id != (Window) NULL))
14905 (void) XMapWindow(display,windows->image.id);
14906 /*
14907 Set our progress monitor and warning handlers.
14908 */
14909 if (warning_handler == (WarningHandler) NULL)
14910 {
14911 warning_handler=resource_info->display_warnings ?
14912 SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14913 warning_handler=resource_info->display_warnings ?
14914 SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14915 }
14916 /*
14917 Initialize Image and Magnify X images.
14918 */
14919 windows->image.x=0;
14920 windows->image.y=0;
14921 windows->magnify.shape=MagickFalse;
14922 width=(unsigned int) display_image->columns;
14923 height=(unsigned int) display_image->rows;
14924 if ((display_image->columns != width) || (display_image->rows != height))
14925 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14926 display_image->filename);
14927 status=XMakeImage(display,resource_info,&windows->image,display_image,
14928 width,height);
14929 if (status == MagickFalse)
14930 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14931 display_image->filename);
14932 status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14933 windows->magnify.width,windows->magnify.height);
14934 if (status == MagickFalse)
14935 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14936 display_image->filename);
14937 if (windows->magnify.mapped != MagickFalse)
14938 (void) XMapRaised(display,windows->magnify.id);
14939 if (windows->pan.mapped != MagickFalse)
14940 (void) XMapRaised(display,windows->pan.id);
14941 windows->image.window_changes.width=(int) display_image->columns;
14942 windows->image.window_changes.height=(int) display_image->rows;
14943 (void) XConfigureImage(display,resource_info,windows,display_image);
14944 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14945 (void) XSync(display,MagickFalse);
14946 /*
14947 Respond to events.
14948 */
14949 delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
14950 timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
14951 update_time=0;
14952 if (resource_info->update != MagickFalse)
14953 {
14954 MagickBooleanType
14955 status;
14956
14957 /*
14958 Determine when file data was last modified.
14959 */
14960 status=GetPathAttributes(display_image->filename,&attributes);
14961 if (status != MagickFalse)
14962 update_time=attributes.st_mtime;
14963 }
14964 *state&=(~FormerImageState);
14965 *state&=(~MontageImageState);
14966 *state&=(~NextImageState);
14967 do
14968 {
14969 /*
14970 Handle a window event.
14971 */
14972 if (windows->image.mapped != MagickFalse)
14973 if ((display_image->delay != 0) || (resource_info->update != 0))
14974 {
14975 if (timer < GetMagickTime())
14976 {
14977 if (resource_info->update == MagickFalse)
14978 *state|=NextImageState | ExitState;
14979 else
14980 {
14981 MagickBooleanType
14982 status;
14983
14984 /*
14985 Determine if image file was modified.
14986 */
14987 status=GetPathAttributes(display_image->filename,&attributes);
14988 if (status != MagickFalse)
14989 if (update_time != attributes.st_mtime)
14990 {
14991 /*
14992 Redisplay image.
14993 */
14994 (void) FormatLocaleString(
14995 resource_info->image_info->filename,MaxTextExtent,
14996 "%s:%s",display_image->magick,
14997 display_image->filename);
14998 nexus=ReadImage(resource_info->image_info,
14999 &display_image->exception);
15000 if (nexus != (Image *) NULL)
15001 *state|=NextImageState | ExitState;
15002 }
15003 delay=display_image->delay/MagickMax(
15004 display_image->ticks_per_second,1L);
15005 timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15006 }
15007 }
15008 if (XEventsQueued(display,QueuedAfterFlush) == 0)
15009 {
15010 /*
15011 Do not block if delay > 0.
15012 */
15013 XDelay(display,SuspendTime << 2);
15014 continue;
15015 }
15016 }
15017 timestamp=GetMagickTime();
15018 (void) XNextEvent(display,&event);
15019 if (windows->image.stasis == MagickFalse)
15020 windows->image.stasis=(GetMagickTime()-timestamp) > 0 ?
15021 MagickTrue : MagickFalse;
15022 if (windows->magnify.stasis == MagickFalse)
15023 windows->magnify.stasis=(GetMagickTime()-timestamp) > 0 ?
15024 MagickTrue : MagickFalse;
15025 if (event.xany.window == windows->command.id)
15026 {
15027 /*
15028 Select a command from the Command widget.
15029 */
15030 id=XCommandWidget(display,windows,CommandMenu,&event);
15031 if (id < 0)
15032 continue;
15033 (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15034 display_command=CommandMenus[id];
15035 if (id < MagickMenus)
15036 {
15037 /*
15038 Select a command from a pop-up menu.
15039 */
15040 entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15041 command);
15042 if (entry < 0)
15043 continue;
15044 (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15045 display_command=Commands[id][entry];
15046 }
15047 if (display_command != NullCommand)
15048 nexus=XMagickCommand(display,resource_info,windows,display_command,
15049 &display_image);
15050 continue;
15051 }
15052 switch (event.type)
15053 {
15054 case ButtonPress:
15055 {
15056 if (resource_info->debug != MagickFalse)
15057 (void) LogMagickEvent(X11Event,GetMagickModule(),
15058 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15059 event.xbutton.button,event.xbutton.x,event.xbutton.y);
15060 if ((event.xbutton.button == Button3) &&
15061 (event.xbutton.state & Mod1Mask))
15062 {
15063 /*
15064 Convert Alt-Button3 to Button2.
15065 */
15066 event.xbutton.button=Button2;
15067 event.xbutton.state&=(~Mod1Mask);
15068 }
15069 if (event.xbutton.window == windows->backdrop.id)
15070 {
15071 (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15072 event.xbutton.time);
15073 break;
15074 }
15075 if (event.xbutton.window == windows->image.id)
15076 {
15077 switch (event.xbutton.button)
15078 {
15079 case Button1:
15080 {
15081 if (resource_info->immutable)
15082 {
15083 /*
15084 Select a command from the Virtual menu.
15085 */
15086 entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15087 command);
15088 if (entry >= 0)
15089 nexus=XMagickCommand(display,resource_info,windows,
15090 VirtualCommands[entry],&display_image);
15091 break;
15092 }
15093 /*
15094 Map/unmap Command widget.
15095 */
15096 if (windows->command.mapped != MagickFalse)
15097 (void) XWithdrawWindow(display,windows->command.id,
15098 windows->command.screen);
15099 else
15100 {
15101 (void) XCommandWidget(display,windows,CommandMenu,
15102 (XEvent *) NULL);
15103 (void) XMapRaised(display,windows->command.id);
15104 }
15105 break;
15106 }
15107 case Button2:
15108 {
15109 /*
15110 User pressed the image magnify button.
15111 */
15112 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15113 &display_image);
15114 XMagnifyImage(display,windows,&event);
15115 break;
15116 }
15117 case Button3:
15118 {
15119 if (resource_info->immutable)
15120 {
15121 /*
15122 Select a command from the Virtual menu.
15123 */
15124 entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15125 command);
15126 if (entry >= 0)
15127 nexus=XMagickCommand(display,resource_info,windows,
15128 VirtualCommands[entry],&display_image);
15129 break;
15130 }
15131 if (display_image->montage != (char *) NULL)
15132 {
15133 /*
15134 Open or delete a tile from a visual image directory.
15135 */
15136 nexus=XTileImage(display,resource_info,windows,
15137 display_image,&event);
15138 if (nexus != (Image *) NULL)
15139 *state|=MontageImageState | NextImageState | ExitState;
15140 vid_info.x=(short int) windows->image.x;
15141 vid_info.y=(short int) windows->image.y;
15142 break;
15143 }
15144 /*
15145 Select a command from the Short Cuts menu.
15146 */
15147 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15148 command);
15149 if (entry >= 0)
15150 nexus=XMagickCommand(display,resource_info,windows,
15151 ShortCutsCommands[entry],&display_image);
15152 break;
15153 }
15154 case Button4:
15155 {
15156 /*
15157 Wheel up.
15158 */
15159 XTranslateImage(display,windows,*image,XK_Up);
15160 break;
15161 }
15162 case Button5:
15163 {
15164 /*
15165 Wheel down.
15166 */
15167 XTranslateImage(display,windows,*image,XK_Down);
15168 break;
15169 }
15170 default:
15171 break;
15172 }
15173 break;
15174 }
15175 if (event.xbutton.window == windows->magnify.id)
15176 {
15177 const char
15178 *const MagnifyMenu[] =
15179 {
15180 "2",
15181 "4",
15182 "5",
15183 "6",
15184 "7",
15185 "8",
15186 "9",
15187 "3",
15188 (char *) NULL,
15189 };
15190
15191 int
15192 factor;
15193
15194 static KeySym
15195 MagnifyCommands[] =
15196 {
15197 XK_2,
15198 XK_4,
15199 XK_5,
15200 XK_6,
15201 XK_7,
15202 XK_8,
15203 XK_9,
15204 XK_3
15205 };
15206
15207 /*
15208 Select a magnify factor from the pop-up menu.
15209 */
15210 factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15211 if (factor >= 0)
15212 XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15213 break;
15214 }
15215 if (event.xbutton.window == windows->pan.id)
15216 {
15217 switch (event.xbutton.button)
15218 {
15219 case Button4:
15220 {
15221 /*
15222 Wheel up.
15223 */
15224 XTranslateImage(display,windows,*image,XK_Up);
15225 break;
15226 }
15227 case Button5:
15228 {
15229 /*
15230 Wheel down.
15231 */
15232 XTranslateImage(display,windows,*image,XK_Down);
15233 break;
15234 }
15235 default:
15236 {
15237 XPanImage(display,windows,&event);
15238 break;
15239 }
15240 }
15241 break;
15242 }
15243 delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15244 1L);
15245 timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15246 break;
15247 }
15248 case ButtonRelease:
15249 {
15250 if (resource_info->debug != MagickFalse)
15251 (void) LogMagickEvent(X11Event,GetMagickModule(),
15252 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15253 event.xbutton.button,event.xbutton.x,event.xbutton.y);
15254 break;
15255 }
15256 case ClientMessage:
15257 {
15258 if (resource_info->debug != MagickFalse)
15259 (void) LogMagickEvent(X11Event,GetMagickModule(),
15260 "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15261 event.xclient.message_type,event.xclient.format,(unsigned long)
15262 event.xclient.data.l[0]);
15263 if (event.xclient.message_type == windows->im_protocols)
15264 {
15265 if (*event.xclient.data.l == (long) windows->im_update_widget)
15266 {
15267 (void) CloneString(&windows->command.name,MagickTitle);
15268 windows->command.data=MagickMenus;
15269 (void) XCommandWidget(display,windows,CommandMenu,
15270 (XEvent *) NULL);
15271 break;
15272 }
15273 if (*event.xclient.data.l == (long) windows->im_update_colormap)
15274 {
15275 /*
15276 Update graphic context and window colormap.
15277 */
15278 for (i=0; i < (int) number_windows; i++)
15279 {
15280 if (magick_windows[i]->id == windows->icon.id)
15281 continue;
15282 context_values.background=pixel->background_color.pixel;
15283 context_values.foreground=pixel->foreground_color.pixel;
15284 (void) XChangeGC(display,magick_windows[i]->annotate_context,
15285 context_mask,&context_values);
15286 (void) XChangeGC(display,magick_windows[i]->widget_context,
15287 context_mask,&context_values);
15288 context_values.background=pixel->foreground_color.pixel;
15289 context_values.foreground=pixel->background_color.pixel;
15290 context_values.plane_mask=context_values.background ^
15291 context_values.foreground;
15292 (void) XChangeGC(display,magick_windows[i]->highlight_context,
15293 (size_t) (context_mask | GCPlaneMask),
15294 &context_values);
15295 magick_windows[i]->attributes.background_pixel=
15296 pixel->background_color.pixel;
15297 magick_windows[i]->attributes.border_pixel=
15298 pixel->border_color.pixel;
15299 magick_windows[i]->attributes.colormap=map_info->colormap;
15300 (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15301 (unsigned long) magick_windows[i]->mask,
15302 &magick_windows[i]->attributes);
15303 }
15304 if (windows->pan.mapped != MagickFalse)
15305 {
15306 (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15307 windows->pan.pixmap);
15308 (void) XClearWindow(display,windows->pan.id);
15309 XDrawPanRectangle(display,windows);
15310 }
15311 if (windows->backdrop.id != (Window) NULL)
15312 (void) XInstallColormap(display,map_info->colormap);
15313 break;
15314 }
15315 if (*event.xclient.data.l == (long) windows->im_former_image)
15316 {
15317 *state|=FormerImageState | ExitState;
15318 break;
15319 }
15320 if (*event.xclient.data.l == (long) windows->im_next_image)
15321 {
15322 *state|=NextImageState | ExitState;
15323 break;
15324 }
15325 if (*event.xclient.data.l == (long) windows->im_retain_colors)
15326 {
15327 *state|=RetainColorsState;
15328 break;
15329 }
15330 if (*event.xclient.data.l == (long) windows->im_exit)
15331 {
15332 *state|=ExitState;
15333 break;
15334 }
15335 break;
15336 }
15337 if (event.xclient.message_type == windows->dnd_protocols)
15338 {
15339 Atom
15340 selection,
15341 type;
15342
15343 int
15344 format,
15345 status;
15346
15347 unsigned char
15348 *data;
15349
15350 unsigned long
15351 after,
15352 length;
15353
15354 /*
15355 Display image named by the Drag-and-Drop selection.
15356 */
15357 if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15358 break;
15359 selection=XInternAtom(display,"DndSelection",MagickFalse);
15360 status=XGetWindowProperty(display,root_window,selection,0L,(long)
15361 MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15362 &length,&after,&data);
15363 if ((status != Success) || (length == 0))
15364 break;
15365 if (*event.xclient.data.l == 2)
15366 {
15367 /*
15368 Offix DND.
15369 */
15370 (void) CopyMagickString(resource_info->image_info->filename,
15371 (char *) data,MaxTextExtent);
15372 }
15373 else
15374 {
15375 /*
15376 XDND.
15377 */
15378 if (strncmp((char *) data, "file:", 5) != 0)
15379 {
15380 (void) XFree((void *) data);
15381 break;
15382 }
15383 (void) CopyMagickString(resource_info->image_info->filename,
15384 ((char *) data)+5,MaxTextExtent);
15385 }
15386 nexus=ReadImage(resource_info->image_info,
15387 &display_image->exception);
15388 CatchException(&display_image->exception);
15389 if (nexus != (Image *) NULL)
15390 *state|=NextImageState | ExitState;
15391 (void) XFree((void *) data);
15392 break;
15393 }
15394 /*
15395 If client window delete message, exit.
15396 */
15397 if (event.xclient.message_type != windows->wm_protocols)
15398 break;
15399 if (*event.xclient.data.l != (long) windows->wm_delete_window)
15400 break;
15401 (void) XWithdrawWindow(display,event.xclient.window,
15402 visual_info->screen);
15403 if (event.xclient.window == windows->image.id)
15404 {
15405 *state|=ExitState;
15406 break;
15407 }
15408 if (event.xclient.window == windows->pan.id)
15409 {
15410 /*
15411 Restore original image size when pan window is deleted.
15412 */
15413 windows->image.window_changes.width=windows->image.ximage->width;
15414 windows->image.window_changes.height=windows->image.ximage->height;
15415 (void) XConfigureImage(display,resource_info,windows,
15416 display_image);
15417 }
15418 break;
15419 }
15420 case ConfigureNotify:
15421 {
15422 if (resource_info->debug != MagickFalse)
15423 (void) LogMagickEvent(X11Event,GetMagickModule(),
15424 "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15425 event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15426 event.xconfigure.y,event.xconfigure.send_event);
15427 if (event.xconfigure.window == windows->image.id)
15428 {
15429 /*
15430 Image window has a new configuration.
15431 */
15432 if (event.xconfigure.send_event != 0)
15433 {
15434 XWindowChanges
15435 window_changes;
15436
15437 /*
15438 Position the transient windows relative of the Image window.
15439 */
15440 if (windows->command.geometry == (char *) NULL)
15441 if (windows->command.mapped == MagickFalse)
15442 {
15443 windows->command.x=event.xconfigure.x-
15444 windows->command.width-25;
15445 windows->command.y=event.xconfigure.y;
15446 XConstrainWindowPosition(display,&windows->command);
15447 window_changes.x=windows->command.x;
15448 window_changes.y=windows->command.y;
15449 (void) XReconfigureWMWindow(display,windows->command.id,
15450 windows->command.screen,(unsigned int) (CWX | CWY),
15451 &window_changes);
15452 }
15453 if (windows->widget.geometry == (char *) NULL)
15454 if (windows->widget.mapped == MagickFalse)
15455 {
15456 windows->widget.x=event.xconfigure.x+
15457 event.xconfigure.width/10;
15458 windows->widget.y=event.xconfigure.y+
15459 event.xconfigure.height/10;
15460 XConstrainWindowPosition(display,&windows->widget);
15461 window_changes.x=windows->widget.x;
15462 window_changes.y=windows->widget.y;
15463 (void) XReconfigureWMWindow(display,windows->widget.id,
15464 windows->widget.screen,(unsigned int) (CWX | CWY),
15465 &window_changes);
15466 }
15467 if (windows->magnify.geometry == (char *) NULL)
15468 if (windows->magnify.mapped == MagickFalse)
15469 {
15470 windows->magnify.x=event.xconfigure.x+
15471 event.xconfigure.width+25;
15472 windows->magnify.y=event.xconfigure.y;
15473 XConstrainWindowPosition(display,&windows->magnify);
15474 window_changes.x=windows->magnify.x;
15475 window_changes.y=windows->magnify.y;
15476 (void) XReconfigureWMWindow(display,windows->magnify.id,
15477 windows->magnify.screen,(unsigned int) (CWX | CWY),
15478 &window_changes);
15479 }
15480 if (windows->pan.geometry == (char *) NULL)
15481 if (windows->pan.mapped == MagickFalse)
15482 {
15483 windows->pan.x=event.xconfigure.x+
15484 event.xconfigure.width+25;
15485 windows->pan.y=event.xconfigure.y+
15486 windows->magnify.height+50;
15487 XConstrainWindowPosition(display,&windows->pan);
15488 window_changes.x=windows->pan.x;
15489 window_changes.y=windows->pan.y;
15490 (void) XReconfigureWMWindow(display,windows->pan.id,
15491 windows->pan.screen,(unsigned int) (CWX | CWY),
15492 &window_changes);
15493 }
15494 }
15495 if ((event.xconfigure.width == (int) windows->image.width) &&
15496 (event.xconfigure.height == (int) windows->image.height))
15497 break;
15498 windows->image.width=(unsigned int) event.xconfigure.width;
15499 windows->image.height=(unsigned int) event.xconfigure.height;
15500 windows->image.x=0;
15501 windows->image.y=0;
15502 if (display_image->montage != (char *) NULL)
15503 {
15504 windows->image.x=vid_info.x;
15505 windows->image.y=vid_info.y;
15506 }
15507 if ((windows->image.mapped != MagickFalse) &&
15508 (windows->image.stasis != MagickFalse))
15509 {
15510 /*
15511 Update image window configuration.
15512 */
15513 windows->image.window_changes.width=event.xconfigure.width;
15514 windows->image.window_changes.height=event.xconfigure.height;
15515 (void) XConfigureImage(display,resource_info,windows,
15516 display_image);
15517 }
15518 /*
15519 Update pan window configuration.
15520 */
15521 if ((event.xconfigure.width < windows->image.ximage->width) ||
15522 (event.xconfigure.height < windows->image.ximage->height))
15523 {
15524 (void) XMapRaised(display,windows->pan.id);
15525 XDrawPanRectangle(display,windows);
15526 }
15527 else
15528 if (windows->pan.mapped != MagickFalse)
15529 (void) XWithdrawWindow(display,windows->pan.id,
15530 windows->pan.screen);
15531 break;
15532 }
15533 if (event.xconfigure.window == windows->magnify.id)
15534 {
15535 unsigned int
15536 magnify;
15537
15538 /*
15539 Magnify window has a new configuration.
15540 */
15541 windows->magnify.width=(unsigned int) event.xconfigure.width;
15542 windows->magnify.height=(unsigned int) event.xconfigure.height;
15543 if (windows->magnify.mapped == MagickFalse)
15544 break;
15545 magnify=1;
15546 while ((int) magnify <= event.xconfigure.width)
15547 magnify<<=1;
15548 while ((int) magnify <= event.xconfigure.height)
15549 magnify<<=1;
15550 magnify>>=1;
15551 if (((int) magnify != event.xconfigure.width) ||
15552 ((int) magnify != event.xconfigure.height))
15553 {
15554 window_changes.width=(int) magnify;
15555 window_changes.height=(int) magnify;
15556 (void) XReconfigureWMWindow(display,windows->magnify.id,
15557 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15558 &window_changes);
15559 break;
15560 }
15561 if ((windows->magnify.mapped != MagickFalse) &&
15562 (windows->magnify.stasis != MagickFalse))
15563 {
15564 status=XMakeImage(display,resource_info,&windows->magnify,
15565 display_image,windows->magnify.width,windows->magnify.height);
15566 XMakeMagnifyImage(display,windows);
15567 }
15568 break;
15569 }
15570 if ((windows->magnify.mapped != MagickFalse) &&
15571 (event.xconfigure.window == windows->pan.id))
15572 {
15573 /*
15574 Pan icon window has a new configuration.
15575 */
15576 if (event.xconfigure.send_event != 0)
15577 {
15578 windows->pan.x=event.xconfigure.x;
15579 windows->pan.y=event.xconfigure.y;
15580 }
15581 windows->pan.width=(unsigned int) event.xconfigure.width;
15582 windows->pan.height=(unsigned int) event.xconfigure.height;
15583 break;
15584 }
15585 if (event.xconfigure.window == windows->icon.id)
15586 {
15587 /*
15588 Icon window has a new configuration.
15589 */
15590 windows->icon.width=(unsigned int) event.xconfigure.width;
15591 windows->icon.height=(unsigned int) event.xconfigure.height;
15592 break;
15593 }
15594 break;
15595 }
15596 case DestroyNotify:
15597 {
15598 /*
15599 Group leader has exited.
15600 */
15601 if (resource_info->debug != MagickFalse)
15602 (void) LogMagickEvent(X11Event,GetMagickModule(),
15603 "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15604 if (event.xdestroywindow.window == windows->group_leader.id)
15605 {
15606 *state|=ExitState;
15607 break;
15608 }
15609 break;
15610 }
15611 case EnterNotify:
15612 {
15613 /*
15614 Selectively install colormap.
15615 */
15616 if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15617 if (event.xcrossing.mode != NotifyUngrab)
15618 XInstallColormap(display,map_info->colormap);
15619 break;
15620 }
15621 case Expose:
15622 {
15623 if (resource_info->debug != MagickFalse)
15624 (void) LogMagickEvent(X11Event,GetMagickModule(),
15625 "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15626 event.xexpose.width,event.xexpose.height,event.xexpose.x,
15627 event.xexpose.y);
15628 /*
15629 Refresh windows that are now exposed.
15630 */
15631 if ((event.xexpose.window == windows->image.id) &&
15632 (windows->image.mapped != MagickFalse))
15633 {
15634 XRefreshWindow(display,&windows->image,&event);
15635 delay=display_image->delay/MagickMax(
15636 display_image->ticks_per_second,1L);
15637 timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15638 break;
15639 }
15640 if ((event.xexpose.window == windows->magnify.id) &&
15641 (windows->magnify.mapped != MagickFalse))
15642 {
15643 XMakeMagnifyImage(display,windows);
15644 break;
15645 }
15646 if (event.xexpose.window == windows->pan.id)
15647 {
15648 XDrawPanRectangle(display,windows);
15649 break;
15650 }
15651 if (event.xexpose.window == windows->icon.id)
15652 {
15653 XRefreshWindow(display,&windows->icon,&event);
15654 break;
15655 }
15656 break;
15657 }
15658 case KeyPress:
15659 {
15660 int
15661 length;
15662
15663 /*
15664 Respond to a user key press.
15665 */
15666 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15667 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15668 *(command+length)='\0';
15669 if (resource_info->debug != MagickFalse)
15670 (void) LogMagickEvent(X11Event,GetMagickModule(),
15671 "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15672 key_symbol,command);
15673 if (event.xkey.window == windows->image.id)
15674 {
15675 display_command=XImageWindowCommand(display,resource_info,windows,
15676 event.xkey.state,key_symbol,&display_image);
15677 if (display_command != NullCommand)
15678 nexus=XMagickCommand(display,resource_info,windows,display_command,
15679 &display_image);
15680 }
15681 if (event.xkey.window == windows->magnify.id)
15682 XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15683 if (event.xkey.window == windows->pan.id)
15684 {
15685 if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15686 (void) XWithdrawWindow(display,windows->pan.id,
15687 windows->pan.screen);
15688 else
15689 if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15690 XTextViewHelp(display,resource_info,windows,MagickFalse,
15691 "Help Viewer - Image Pan",ImagePanHelp);
15692 else
15693 XTranslateImage(display,windows,*image,key_symbol);
15694 }
15695 delay=display_image->delay/MagickMax(
15696 display_image->ticks_per_second,1L);
15697 timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15698 break;
15699 }
15700 case KeyRelease:
15701 {
15702 /*
15703 Respond to a user key release.
15704 */
15705 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15706 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15707 if (resource_info->debug != MagickFalse)
15708 (void) LogMagickEvent(X11Event,GetMagickModule(),
15709 "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15710 break;
15711 }
15712 case LeaveNotify:
15713 {
15714 /*
15715 Selectively uninstall colormap.
15716 */
15717 if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15718 if (event.xcrossing.mode != NotifyUngrab)
15719 XUninstallColormap(display,map_info->colormap);
15720 break;
15721 }
15722 case MapNotify:
15723 {
15724 if (resource_info->debug != MagickFalse)
15725 (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15726 event.xmap.window);
15727 if (event.xmap.window == windows->backdrop.id)
15728 {
15729 (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15730 CurrentTime);
15731 windows->backdrop.mapped=MagickTrue;
15732 break;
15733 }
15734 if (event.xmap.window == windows->image.id)
15735 {
15736 if (windows->backdrop.id != (Window) NULL)
15737 (void) XInstallColormap(display,map_info->colormap);
15738 if (LocaleCompare(display_image->magick,"LOGO") == 0)
15739 {
15740 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15741 nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15742 }
15743 if (((int) windows->image.width < windows->image.ximage->width) ||
15744 ((int) windows->image.height < windows->image.ximage->height))
15745 (void) XMapRaised(display,windows->pan.id);
15746 windows->image.mapped=MagickTrue;
15747 break;
15748 }
15749 if (event.xmap.window == windows->magnify.id)
15750 {
15751 XMakeMagnifyImage(display,windows);
15752 windows->magnify.mapped=MagickTrue;
15753 (void) XWithdrawWindow(display,windows->info.id,
15754 windows->info.screen);
15755 break;
15756 }
15757 if (event.xmap.window == windows->pan.id)
15758 {
15759 XMakePanImage(display,resource_info,windows,display_image);
15760 windows->pan.mapped=MagickTrue;
15761 break;
15762 }
15763 if (event.xmap.window == windows->info.id)
15764 {
15765 windows->info.mapped=MagickTrue;
15766 break;
15767 }
15768 if (event.xmap.window == windows->icon.id)
15769 {
15770 MagickBooleanType
15771 taint;
15772
15773 /*
15774 Create an icon image.
15775 */
15776 taint=display_image->taint;
15777 XMakeStandardColormap(display,icon_visual,icon_resources,
15778 display_image,icon_map,icon_pixel);
15779 (void) XMakeImage(display,icon_resources,&windows->icon,
15780 display_image,windows->icon.width,windows->icon.height);
15781 display_image->taint=taint;
15782 (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15783 windows->icon.pixmap);
15784 (void) XClearWindow(display,windows->icon.id);
15785 (void) XWithdrawWindow(display,windows->info.id,
15786 windows->info.screen);
15787 windows->icon.mapped=MagickTrue;
15788 break;
15789 }
15790 if (event.xmap.window == windows->command.id)
15791 {
15792 windows->command.mapped=MagickTrue;
15793 break;
15794 }
15795 if (event.xmap.window == windows->popup.id)
15796 {
15797 windows->popup.mapped=MagickTrue;
15798 break;
15799 }
15800 if (event.xmap.window == windows->widget.id)
15801 {
15802 windows->widget.mapped=MagickTrue;
15803 break;
15804 }
15805 break;
15806 }
15807 case MappingNotify:
15808 {
15809 (void) XRefreshKeyboardMapping(&event.xmapping);
15810 break;
15811 }
15812 case NoExpose:
15813 break;
15814 case PropertyNotify:
15815 {
15816 Atom
15817 type;
15818
15819 int
15820 format,
15821 status;
15822
15823 unsigned char
15824 *data;
15825
15826 unsigned long
15827 after,
15828 length;
15829
15830 if (resource_info->debug != MagickFalse)
15831 (void) LogMagickEvent(X11Event,GetMagickModule(),
15832 "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15833 event.xproperty.atom,event.xproperty.state);
15834 if (event.xproperty.atom != windows->im_remote_command)
15835 break;
15836 /*
15837 Display image named by the remote command protocol.
15838 */
15839 status=XGetWindowProperty(display,event.xproperty.window,
15840 event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15841 AnyPropertyType,&type,&format,&length,&after,&data);
15842 if ((status != Success) || (length == 0))
15843 break;
15844 if (LocaleCompare((char *) data,"-quit") == 0)
15845 {
15846 XClientMessage(display,windows->image.id,windows->im_protocols,
15847 windows->im_exit,CurrentTime);
15848 (void) XFree((void *) data);
15849 break;
15850 }
15851 (void) CopyMagickString(resource_info->image_info->filename,
15852 (char *) data,MaxTextExtent);
15853 (void) XFree((void *) data);
15854 nexus=ReadImage(resource_info->image_info,&display_image->exception);
15855 CatchException(&display_image->exception);
15856 if (nexus != (Image *) NULL)
15857 *state|=NextImageState | ExitState;
15858 break;
15859 }
15860 case ReparentNotify:
15861 {
15862 if (resource_info->debug != MagickFalse)
15863 (void) LogMagickEvent(X11Event,GetMagickModule(),
15864 "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15865 event.xreparent.window);
15866 break;
15867 }
15868 case UnmapNotify:
15869 {
15870 if (resource_info->debug != MagickFalse)
15871 (void) LogMagickEvent(X11Event,GetMagickModule(),
15872 "Unmap Notify: 0x%lx",event.xunmap.window);
15873 if (event.xunmap.window == windows->backdrop.id)
15874 {
15875 windows->backdrop.mapped=MagickFalse;
15876 break;
15877 }
15878 if (event.xunmap.window == windows->image.id)
15879 {
15880 windows->image.mapped=MagickFalse;
15881 break;
15882 }
15883 if (event.xunmap.window == windows->magnify.id)
15884 {
15885 windows->magnify.mapped=MagickFalse;
15886 break;
15887 }
15888 if (event.xunmap.window == windows->pan.id)
15889 {
15890 windows->pan.mapped=MagickFalse;
15891 break;
15892 }
15893 if (event.xunmap.window == windows->info.id)
15894 {
15895 windows->info.mapped=MagickFalse;
15896 break;
15897 }
15898 if (event.xunmap.window == windows->icon.id)
15899 {
15900 if (map_info->colormap == icon_map->colormap)
15901 XConfigureImageColormap(display,resource_info,windows,
15902 display_image);
15903 (void) XFreeStandardColormap(display,icon_visual,icon_map,
15904 icon_pixel);
15905 windows->icon.mapped=MagickFalse;
15906 break;
15907 }
15908 if (event.xunmap.window == windows->command.id)
15909 {
15910 windows->command.mapped=MagickFalse;
15911 break;
15912 }
15913 if (event.xunmap.window == windows->popup.id)
15914 {
15915 if (windows->backdrop.id != (Window) NULL)
15916 (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15917 CurrentTime);
15918 windows->popup.mapped=MagickFalse;
15919 break;
15920 }
15921 if (event.xunmap.window == windows->widget.id)
15922 {
15923 if (windows->backdrop.id != (Window) NULL)
15924 (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15925 CurrentTime);
15926 windows->widget.mapped=MagickFalse;
15927 break;
15928 }
15929 break;
15930 }
15931 default:
15932 {
15933 if (resource_info->debug != MagickFalse)
15934 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
15935 event.type);
15936 break;
15937 }
15938 }
15939 } while (!(*state & ExitState));
15940 if ((*state & ExitState) == 0)
15941 (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
15942 &display_image);
15943 else
15944 if (resource_info->confirm_edit != MagickFalse)
15945 {
15946 /*
15947 Query user if image has changed.
15948 */
15949 if ((resource_info->immutable == MagickFalse) &&
15950 (display_image->taint != MagickFalse))
15951 {
15952 int
15953 status;
15954
15955 status=XConfirmWidget(display,windows,"Your image changed.",
15956 "Do you want to save it");
15957 if (status == 0)
15958 *state&=(~ExitState);
15959 else
15960 if (status > 0)
15961 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
15962 &display_image);
15963 }
15964 }
15965 if ((windows->visual_info->klass == GrayScale) ||
15966 (windows->visual_info->klass == PseudoColor) ||
15967 (windows->visual_info->klass == DirectColor))
15968 {
15969 /*
15970 Withdraw pan and Magnify window.
15971 */
15972 if (windows->info.mapped != MagickFalse)
15973 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15974 if (windows->magnify.mapped != MagickFalse)
15975 (void) XWithdrawWindow(display,windows->magnify.id,
15976 windows->magnify.screen);
15977 if (windows->command.mapped != MagickFalse)
15978 (void) XWithdrawWindow(display,windows->command.id,
15979 windows->command.screen);
15980 }
15981 if (windows->pan.mapped != MagickFalse)
15982 (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
15983 if (resource_info->backdrop == MagickFalse)
15984 if (windows->backdrop.mapped)
15985 {
15986 (void) XWithdrawWindow(display,windows->backdrop.id,
15987 windows->backdrop.screen);
15988 (void) XDestroyWindow(display,windows->backdrop.id);
15989 windows->backdrop.id=(Window) NULL;
15990 (void) XWithdrawWindow(display,windows->image.id,
15991 windows->image.screen);
15992 (void) XDestroyWindow(display,windows->image.id);
15993 windows->image.id=(Window) NULL;
15994 }
15995 XSetCursorState(display,windows,MagickTrue);
15996 XCheckRefreshWindows(display,windows);
15997 if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
15998 *state&=(~ExitState);
15999 if (*state & ExitState)
16000 {
16001 /*
16002 Free Standard Colormap.
16003 */
16004 (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16005 if (resource_info->map_type == (char *) NULL)
16006 (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16007 /*
16008 Free X resources.
16009 */
16010 if (resource_info->copy_image != (Image *) NULL)
16011 resource_info->copy_image=DestroyImage(resource_info->copy_image);
16012 DestroyXResources();
16013 }
16014 (void) XSync(display,MagickFalse);
16015 /*
16016 Restore our progress monitor and warning handlers.
16017 */
16018 (void) SetErrorHandler(warning_handler);
16019 (void) SetWarningHandler(warning_handler);
16020 /*
16021 Change to home directory.
16022 */
16023 directory=getcwd(working_directory,MaxTextExtent);
16024 (void) directory;
16025 {
16026 int
16027 status;
16028
16029 if (*resource_info->home_directory == '\0')
16030 (void) CopyMagickString(resource_info->home_directory,".",MaxTextExtent);
16031 status=chdir(resource_info->home_directory);
16032 if (status == -1)
16033 (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
16034 FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
16035 }
16036 *image=display_image;
16037 return(nexus);
16038}
16039#else
16040
16041/*
16042%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16043% %
16044% %
16045% %
16046+ D i s p l a y I m a g e s %
16047% %
16048% %
16049% %
16050%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16051%
16052% DisplayImages() displays an image sequence to any X window screen. It
16053% returns a value other than 0 if successful. Check the exception member
16054% of image to determine the reason for any failure.
16055%
16056% The format of the DisplayImages method is:
16057%
16058% MagickBooleanType DisplayImages(const ImageInfo *image_info,
16059% Image *images)
16060%
16061% A description of each parameter follows:
16062%
16063% o image_info: the image info.
16064%
16065% o image: the image.
16066%
16067*/
16068MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16069 Image *image)
16070{
16071 assert(image_info != (const ImageInfo *) NULL);
16072 assert(image_info->signature == MagickCoreSignature);
16073 assert(image != (Image *) NULL);
16074 assert(image->signature == MagickCoreSignature);
16075 if (IsEventLogging() != MagickFalse)
16076 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16077 (void) image_info;
16078 (void) ThrowMagickException(&image->exception,GetMagickModule(),
16079 MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
16080 image->filename);
16081 return(MagickFalse);
16082}
16083
16084/*
16085%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16086% %
16087% %
16088% %
16089+ R e m o t e D i s p l a y C o m m a n d %
16090% %
16091% %
16092% %
16093%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16094%
16095% RemoteDisplayCommand() encourages a remote display program to display the
16096% specified image filename.
16097%
16098% The format of the RemoteDisplayCommand method is:
16099%
16100% MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16101% const char *window,const char *filename,ExceptionInfo *exception)
16102%
16103% A description of each parameter follows:
16104%
16105% o image_info: the image info.
16106%
16107% o window: Specifies the name or id of an X window.
16108%
16109% o filename: the name of the image filename to display.
16110%
16111% o exception: return any errors or warnings in this structure.
16112%
16113*/
16114MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16115 const char *window,const char *filename,ExceptionInfo *exception)
16116{
16117 assert(image_info != (const ImageInfo *) NULL);
16118 assert(image_info->signature == MagickCoreSignature);
16119 assert(filename != (char *) NULL);
16120 if (IsEventLogging() != MagickFalse)
16121 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16122 (void) window;
16123 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16124 "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16125 return(MagickFalse);
16126}
16127#endif