MagickCore 6.9.13-17
Convert, Edit, Or Compose Bitmap Images
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+=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 return;
6395 }
6396 cache_image=undo_image;
6397 undo_image=GetPreviousImageInList(undo_image);
6398 windows->image.window_changes.width=(int) cache_image->columns;
6399 windows->image.window_changes.height=(int) cache_image->rows;
6400 (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6401 windows->image.ximage->width,windows->image.ximage->height);
6402 (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
6403 if (windows->image.crop_geometry != (char *) NULL)
6404 windows->image.crop_geometry=(char *)
6405 RelinquishMagickMemory(windows->image.crop_geometry);
6406 windows->image.crop_geometry=cache_image->geometry;
6407 if (redo_image != (Image *) NULL)
6408 redo_image=DestroyImage(redo_image);
6409 redo_image=(*image);
6410 *image=cache_image->list;
6411 cache_image=DestroyImage(cache_image);
6412 if (windows->image.orphan != MagickFalse)
6413 return;
6414 XConfigureImageColormap(display,resource_info,windows,*image);
6415 (void) XConfigureImage(display,resource_info,windows,*image);
6416 return;
6417 }
6418 case CutCommand:
6419 case PasteCommand:
6420 case ApplyCommand:
6421 case HalfSizeCommand:
6422 case OriginalSizeCommand:
6423 case DoubleSizeCommand:
6424 case ResizeCommand:
6425 case TrimCommand:
6426 case CropCommand:
6427 case ChopCommand:
6428 case FlipCommand:
6429 case FlopCommand:
6430 case RotateRightCommand:
6431 case RotateLeftCommand:
6432 case RotateCommand:
6433 case ShearCommand:
6434 case RollCommand:
6435 case NegateCommand:
6436 case ContrastStretchCommand:
6437 case SigmoidalContrastCommand:
6438 case NormalizeCommand:
6439 case EqualizeCommand:
6440 case HueCommand:
6441 case SaturationCommand:
6442 case BrightnessCommand:
6443 case GammaCommand:
6444 case SpiffCommand:
6445 case DullCommand:
6446 case GrayscaleCommand:
6447 case MapCommand:
6448 case QuantizeCommand:
6449 case DespeckleCommand:
6450 case EmbossCommand:
6451 case ReduceNoiseCommand:
6452 case AddNoiseCommand:
6453 case SharpenCommand:
6454 case BlurCommand:
6455 case ThresholdCommand:
6456 case EdgeDetectCommand:
6457 case SpreadCommand:
6458 case ShadeCommand:
6459 case RaiseCommand:
6460 case SegmentCommand:
6461 case SolarizeCommand:
6462 case SepiaToneCommand:
6463 case SwirlCommand:
6464 case ImplodeCommand:
6465 case VignetteCommand:
6466 case WaveCommand:
6467 case OilPaintCommand:
6468 case CharcoalDrawCommand:
6469 case AnnotateCommand:
6470 case AddBorderCommand:
6471 case AddFrameCommand:
6472 case CompositeCommand:
6473 case CommentCommand:
6474 case LaunchCommand:
6475 case RegionOfInterestCommand:
6476 case SaveToUndoBufferCommand:
6477 case RedoCommand:
6478 {
6479 Image
6480 *previous_image;
6481
6482 ssize_t
6483 bytes;
6484
6485 bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
6486 if (undo_image != (Image *) NULL)
6487 {
6488 /*
6489 Ensure the undo cache has enough memory available.
6490 */
6491 previous_image=undo_image;
6492 while (previous_image != (Image *) NULL)
6493 {
6494 bytes+=previous_image->list->columns*previous_image->list->rows*
6495 sizeof(PixelPacket);
6496 if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6497 {
6498 previous_image=GetPreviousImageInList(previous_image);
6499 continue;
6500 }
6501 bytes-=previous_image->list->columns*previous_image->list->rows*
6502 sizeof(PixelPacket);
6503 if (previous_image == undo_image)
6504 undo_image=NewImageList();
6505 else
6506 previous_image->next->previous=NewImageList();
6507 break;
6508 }
6509 while (previous_image != (Image *) NULL)
6510 {
6511 /*
6512 Delete any excess memory from undo cache.
6513 */
6514 cache_image=previous_image;
6515 previous_image=GetPreviousImageInList(previous_image);
6516 cache_image->list=DestroyImage(cache_image->list);
6517 cache_image=DestroyImage(cache_image);
6518 }
6519 }
6520 if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6521 break;
6522 /*
6523 Save image before transformations are applied.
6524 */
6525 cache_image=AcquireImage((ImageInfo *) NULL);
6526 if (cache_image == (Image *) NULL)
6527 break;
6528 XSetCursorState(display,windows,MagickTrue);
6529 XCheckRefreshWindows(display,windows);
6530 cache_image->list=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
6531 XSetCursorState(display,windows,MagickFalse);
6532 if (cache_image->list == (Image *) NULL)
6533 {
6534 cache_image=DestroyImage(cache_image);
6535 break;
6536 }
6537 cache_image->columns=(size_t) windows->image.ximage->width;
6538 cache_image->rows=(size_t) windows->image.ximage->height;
6539 cache_image->geometry=windows->image.crop_geometry;
6540 if (windows->image.crop_geometry != (char *) NULL)
6541 {
6542 cache_image->geometry=AcquireString((char *) NULL);
6543 (void) CopyMagickString(cache_image->geometry,
6544 windows->image.crop_geometry,MaxTextExtent);
6545 }
6546 if (undo_image == (Image *) NULL)
6547 {
6548 undo_image=cache_image;
6549 break;
6550 }
6551 undo_image->next=cache_image;
6552 undo_image->next->previous=undo_image;
6553 undo_image=undo_image->next;
6554 break;
6555 }
6556 default:
6557 break;
6558 }
6559 if (command == RedoCommand)
6560 {
6561 /*
6562 Redo the last image transformation.
6563 */
6564 if (redo_image == (Image *) NULL)
6565 {
6566 (void) XBell(display,0);
6567 return;
6568 }
6569 windows->image.window_changes.width=(int) redo_image->columns;
6570 windows->image.window_changes.height=(int) redo_image->rows;
6571 if (windows->image.crop_geometry != (char *) NULL)
6572 windows->image.crop_geometry=(char *)
6573 RelinquishMagickMemory(windows->image.crop_geometry);
6574 windows->image.crop_geometry=redo_image->geometry;
6575 *image=DestroyImage(*image);
6576 *image=redo_image;
6577 redo_image=NewImageList();
6578 if (windows->image.orphan != MagickFalse)
6579 return;
6580 XConfigureImageColormap(display,resource_info,windows,*image);
6581 (void) XConfigureImage(display,resource_info,windows,*image);
6582 return;
6583 }
6584 if (command != InfoCommand)
6585 return;
6586 /*
6587 Display image info.
6588 */
6589 XSetCursorState(display,windows,MagickTrue);
6590 XCheckRefreshWindows(display,windows);
6591 XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6592 XSetCursorState(display,windows,MagickFalse);
6593}
6594
6595/*
6596%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6597% %
6598% %
6599% %
6600+ X I m a g e W i n d o w C o m m a n d %
6601% %
6602% %
6603% %
6604%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6605%
6606% XImageWindowCommand() makes a transform to the image or Image window as
6607% specified by a user menu button or keyboard command.
6608%
6609% The format of the XMagickCommand method is:
6610%
6611% DisplayCommand XImageWindowCommand(Display *display,
6612% XResourceInfo *resource_info,XWindows *windows,
6613% const MagickStatusType state,KeySym key_symbol,Image **image)
6614%
6615% A description of each parameter follows:
6616%
6617% o nexus: Method XImageWindowCommand returns an image when the
6618% user chooses 'Open Image' from the command menu. Otherwise a null
6619% image is returned.
6620%
6621% o display: Specifies a connection to an X server; returned from
6622% XOpenDisplay.
6623%
6624% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6625%
6626% o windows: Specifies a pointer to a XWindows structure.
6627%
6628% o state: key mask.
6629%
6630% o key_symbol: Specifies a command to perform.
6631%
6632% o image: the image; XImageWIndowCommand
6633% may transform the image and return a new image pointer.
6634%
6635*/
6636static DisplayCommand XImageWindowCommand(Display *display,
6637 XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6638 KeySym key_symbol,Image **image)
6639{
6640 static char
6641 delta[MaxTextExtent] = "";
6642
6643 static const char
6644 Digits[] = "01234567890";
6645
6646 static KeySym
6647 last_symbol = XK_0;
6648
6649 if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6650 {
6651 if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6652 {
6653 *delta='\0';
6654 resource_info->quantum=1;
6655 }
6656 last_symbol=key_symbol;
6657 delta[strlen(delta)+1]='\0';
6658 delta[strlen(delta)]=Digits[key_symbol-XK_0];
6659 resource_info->quantum=StringToLong(delta);
6660 return(NullCommand);
6661 }
6662 last_symbol=key_symbol;
6663 if (resource_info->immutable)
6664 {
6665 /*
6666 Virtual image window has a restricted command set.
6667 */
6668 switch (key_symbol)
6669 {
6670 case XK_question:
6671 return(InfoCommand);
6672 case XK_p:
6673 case XK_Print:
6674 return(PrintCommand);
6675 case XK_space:
6676 return(NextCommand);
6677 case XK_q:
6678 case XK_Escape:
6679 return(QuitCommand);
6680 default:
6681 break;
6682 }
6683 return(NullCommand);
6684 }
6685 switch ((int) key_symbol)
6686 {
6687 case XK_o:
6688 {
6689 if ((state & ControlMask) == 0)
6690 break;
6691 return(OpenCommand);
6692 }
6693 case XK_space:
6694 return(NextCommand);
6695 case XK_BackSpace:
6696 return(FormerCommand);
6697 case XK_s:
6698 {
6699 if ((state & Mod1Mask) != 0)
6700 return(SwirlCommand);
6701 if ((state & ControlMask) == 0)
6702 return(ShearCommand);
6703 return(SaveCommand);
6704 }
6705 case XK_p:
6706 case XK_Print:
6707 {
6708 if ((state & Mod1Mask) != 0)
6709 return(OilPaintCommand);
6710 if ((state & Mod4Mask) != 0)
6711 return(ColorCommand);
6712 if ((state & ControlMask) == 0)
6713 return(NullCommand);
6714 return(PrintCommand);
6715 }
6716 case XK_d:
6717 {
6718 if ((state & Mod4Mask) != 0)
6719 return(DrawCommand);
6720 if ((state & ControlMask) == 0)
6721 return(NullCommand);
6722 return(DeleteCommand);
6723 }
6724 case XK_Select:
6725 {
6726 if ((state & ControlMask) == 0)
6727 return(NullCommand);
6728 return(SelectCommand);
6729 }
6730 case XK_n:
6731 {
6732 if ((state & ControlMask) == 0)
6733 return(NullCommand);
6734 return(NewCommand);
6735 }
6736 case XK_q:
6737 case XK_Escape:
6738 return(QuitCommand);
6739 case XK_z:
6740 case XK_Undo:
6741 {
6742 if ((state & ControlMask) == 0)
6743 return(NullCommand);
6744 return(UndoCommand);
6745 }
6746 case XK_r:
6747 case XK_Redo:
6748 {
6749 if ((state & ControlMask) == 0)
6750 return(RollCommand);
6751 return(RedoCommand);
6752 }
6753 case XK_x:
6754 {
6755 if ((state & ControlMask) == 0)
6756 return(NullCommand);
6757 return(CutCommand);
6758 }
6759 case XK_c:
6760 {
6761 if ((state & Mod1Mask) != 0)
6762 return(CharcoalDrawCommand);
6763 if ((state & ControlMask) == 0)
6764 return(CropCommand);
6765 return(CopyCommand);
6766 }
6767 case XK_v:
6768 case XK_Insert:
6769 {
6770 if ((state & Mod4Mask) != 0)
6771 return(CompositeCommand);
6772 if ((state & ControlMask) == 0)
6773 return(FlipCommand);
6774 return(PasteCommand);
6775 }
6776 case XK_less:
6777 return(HalfSizeCommand);
6778 case XK_minus:
6779 return(OriginalSizeCommand);
6780 case XK_greater:
6781 return(DoubleSizeCommand);
6782 case XK_percent:
6783 return(ResizeCommand);
6784 case XK_at:
6785 return(RefreshCommand);
6786 case XK_bracketleft:
6787 return(ChopCommand);
6788 case XK_h:
6789 return(FlopCommand);
6790 case XK_slash:
6791 return(RotateRightCommand);
6792 case XK_backslash:
6793 return(RotateLeftCommand);
6794 case XK_asterisk:
6795 return(RotateCommand);
6796 case XK_t:
6797 return(TrimCommand);
6798 case XK_H:
6799 return(HueCommand);
6800 case XK_S:
6801 return(SaturationCommand);
6802 case XK_L:
6803 return(BrightnessCommand);
6804 case XK_G:
6805 return(GammaCommand);
6806 case XK_C:
6807 return(SpiffCommand);
6808 case XK_Z:
6809 return(DullCommand);
6810 case XK_N:
6811 return(NormalizeCommand);
6812 case XK_equal:
6813 return(EqualizeCommand);
6814 case XK_asciitilde:
6815 return(NegateCommand);
6816 case XK_period:
6817 return(GrayscaleCommand);
6818 case XK_numbersign:
6819 return(QuantizeCommand);
6820 case XK_F2:
6821 return(DespeckleCommand);
6822 case XK_F3:
6823 return(EmbossCommand);
6824 case XK_F4:
6825 return(ReduceNoiseCommand);
6826 case XK_F5:
6827 return(AddNoiseCommand);
6828 case XK_F6:
6829 return(SharpenCommand);
6830 case XK_F7:
6831 return(BlurCommand);
6832 case XK_F8:
6833 return(ThresholdCommand);
6834 case XK_F9:
6835 return(EdgeDetectCommand);
6836 case XK_F10:
6837 return(SpreadCommand);
6838 case XK_F11:
6839 return(ShadeCommand);
6840 case XK_F12:
6841 return(RaiseCommand);
6842 case XK_F13:
6843 return(SegmentCommand);
6844 case XK_i:
6845 {
6846 if ((state & Mod1Mask) == 0)
6847 return(NullCommand);
6848 return(ImplodeCommand);
6849 }
6850 case XK_w:
6851 {
6852 if ((state & Mod1Mask) == 0)
6853 return(NullCommand);
6854 return(WaveCommand);
6855 }
6856 case XK_m:
6857 {
6858 if ((state & Mod4Mask) == 0)
6859 return(NullCommand);
6860 return(MatteCommand);
6861 }
6862 case XK_b:
6863 {
6864 if ((state & Mod4Mask) == 0)
6865 return(NullCommand);
6866 return(AddBorderCommand);
6867 }
6868 case XK_f:
6869 {
6870 if ((state & Mod4Mask) == 0)
6871 return(NullCommand);
6872 return(AddFrameCommand);
6873 }
6874 case XK_exclam:
6875 {
6876 if ((state & Mod4Mask) == 0)
6877 return(NullCommand);
6878 return(CommentCommand);
6879 }
6880 case XK_a:
6881 {
6882 if ((state & Mod1Mask) != 0)
6883 return(ApplyCommand);
6884 if ((state & Mod4Mask) != 0)
6885 return(AnnotateCommand);
6886 if ((state & ControlMask) == 0)
6887 return(NullCommand);
6888 return(RegionOfInterestCommand);
6889 }
6890 case XK_question:
6891 return(InfoCommand);
6892 case XK_plus:
6893 return(ZoomCommand);
6894 case XK_P:
6895 {
6896 if ((state & ShiftMask) == 0)
6897 return(NullCommand);
6898 return(ShowPreviewCommand);
6899 }
6900 case XK_Execute:
6901 return(LaunchCommand);
6902 case XK_F1:
6903 return(HelpCommand);
6904 case XK_Find:
6905 return(BrowseDocumentationCommand);
6906 case XK_Menu:
6907 {
6908 (void) XMapRaised(display,windows->command.id);
6909 return(NullCommand);
6910 }
6911 case XK_Next:
6912 case XK_Prior:
6913 case XK_Home:
6914 case XK_KP_Home:
6915 {
6916 XTranslateImage(display,windows,*image,key_symbol);
6917 return(NullCommand);
6918 }
6919 case XK_Up:
6920 case XK_KP_Up:
6921 case XK_Down:
6922 case XK_KP_Down:
6923 case XK_Left:
6924 case XK_KP_Left:
6925 case XK_Right:
6926 case XK_KP_Right:
6927 {
6928 if ((state & Mod1Mask) != 0)
6929 {
6931 crop_info;
6932
6933 /*
6934 Trim one pixel from edge of image.
6935 */
6936 crop_info.x=0;
6937 crop_info.y=0;
6938 crop_info.width=(size_t) windows->image.ximage->width;
6939 crop_info.height=(size_t) windows->image.ximage->height;
6940 if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
6941 {
6942 if (resource_info->quantum >= (int) crop_info.height)
6943 resource_info->quantum=(int) crop_info.height-1;
6944 crop_info.height-=resource_info->quantum;
6945 }
6946 if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
6947 {
6948 if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
6949 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
6950 crop_info.y+=resource_info->quantum;
6951 crop_info.height-=resource_info->quantum;
6952 }
6953 if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
6954 {
6955 if (resource_info->quantum >= (int) crop_info.width)
6956 resource_info->quantum=(int) crop_info.width-1;
6957 crop_info.width-=resource_info->quantum;
6958 }
6959 if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
6960 {
6961 if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
6962 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
6963 crop_info.x+=resource_info->quantum;
6964 crop_info.width-=resource_info->quantum;
6965 }
6966 if ((int) (windows->image.x+windows->image.width) >
6967 (int) crop_info.width)
6968 windows->image.x=(int) (crop_info.width-windows->image.width);
6969 if ((int) (windows->image.y+windows->image.height) >
6970 (int) crop_info.height)
6971 windows->image.y=(int) (crop_info.height-windows->image.height);
6972 XSetCropGeometry(display,windows,&crop_info,*image);
6973 windows->image.window_changes.width=(int) crop_info.width;
6974 windows->image.window_changes.height=(int) crop_info.height;
6975 (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
6976 (void) XConfigureImage(display,resource_info,windows,*image);
6977 return(NullCommand);
6978 }
6979 XTranslateImage(display,windows,*image,key_symbol);
6980 return(NullCommand);
6981 }
6982 default:
6983 return(NullCommand);
6984 }
6985 return(NullCommand);
6986}
6987
6988/*
6989%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6990% %
6991% %
6992% %
6993+ X M a g i c k C o m m a n d %
6994% %
6995% %
6996% %
6997%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6998%
6999% XMagickCommand() makes a transform to the image or Image window as
7000% specified by a user menu button or keyboard command.
7001%
7002% The format of the XMagickCommand method is:
7003%
7004% Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7005% XWindows *windows,const DisplayCommand command,Image **image)
7006%
7007% A description of each parameter follows:
7008%
7009% o nexus: Method XMagickCommand returns an image when the
7010% user chooses 'Load Image' from the command menu. Otherwise a null
7011% image is returned.
7012%
7013% o display: Specifies a connection to an X server; returned from
7014% XOpenDisplay.
7015%
7016% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7017%
7018% o windows: Specifies a pointer to a XWindows structure.
7019%
7020% o command: Specifies a command to perform.
7021%
7022% o image: the image; XMagickCommand
7023% may transform the image and return a new image pointer.
7024%
7025*/
7026static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7027 XWindows *windows,const DisplayCommand command,Image **image)
7028{
7029 char
7030 filename[MaxTextExtent],
7031 geometry[MaxTextExtent],
7032 modulate_factors[MaxTextExtent];
7033
7035 geometry_info;
7036
7037 Image
7038 *nexus;
7039
7040 ImageInfo
7041 *image_info;
7042
7043 int
7044 x,
7045 y;
7046
7047 MagickStatusType
7048 flags,
7049 status;
7050
7052 quantize_info;
7053
7055 page_geometry;
7056
7057 int
7058 i;
7059
7060 static char
7061 color[MaxTextExtent] = "gray";
7062
7063 unsigned int
7064 height,
7065 width;
7066
7067 /*
7068 Process user command.
7069 */
7070 XCheckRefreshWindows(display,windows);
7071 XImageCache(display,resource_info,windows,command,image);
7072 nexus=NewImageList();
7073 windows->image.window_changes.width=windows->image.ximage->width;
7074 windows->image.window_changes.height=windows->image.ximage->height;
7075 image_info=CloneImageInfo(resource_info->image_info);
7076 SetGeometryInfo(&geometry_info);
7077 GetQuantizeInfo(&quantize_info);
7078 switch (command)
7079 {
7080 case OpenCommand:
7081 {
7082 /*
7083 Load image.
7084 */
7085 nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7086 break;
7087 }
7088 case NextCommand:
7089 {
7090 /*
7091 Display next image.
7092 */
7093 for (i=0; i < resource_info->quantum; i++)
7094 XClientMessage(display,windows->image.id,windows->im_protocols,
7095 windows->im_next_image,CurrentTime);
7096 break;
7097 }
7098 case FormerCommand:
7099 {
7100 /*
7101 Display former image.
7102 */
7103 for (i=0; i < resource_info->quantum; i++)
7104 XClientMessage(display,windows->image.id,windows->im_protocols,
7105 windows->im_former_image,CurrentTime);
7106 break;
7107 }
7108 case SelectCommand:
7109 {
7110 int
7111 status;
7112
7113 /*
7114 Select image.
7115 */
7116 if (*resource_info->home_directory == '\0')
7117 (void) CopyMagickString(resource_info->home_directory,".",
7118 MaxTextExtent);
7119 status=chdir(resource_info->home_directory);
7120 if (status == -1)
7121 (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
7122 FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
7123 nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7124 break;
7125 }
7126 case SaveCommand:
7127 {
7128 /*
7129 Save image.
7130 */
7131 status=XSaveImage(display,resource_info,windows,*image);
7132 if (status == MagickFalse)
7133 {
7134 char
7135 message[MaxTextExtent];
7136
7137 (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7138 (*image)->exception.reason != (char *) NULL ?
7139 (*image)->exception.reason : "",
7140 (*image)->exception.description != (char *) NULL ?
7141 (*image)->exception.description : "");
7142 XNoticeWidget(display,windows,"Unable to save file:",message);
7143 break;
7144 }
7145 break;
7146 }
7147 case PrintCommand:
7148 {
7149 /*
7150 Print image.
7151 */
7152 status=XPrintImage(display,resource_info,windows,*image);
7153 if (status == MagickFalse)
7154 {
7155 char
7156 message[MaxTextExtent];
7157
7158 (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7159 (*image)->exception.reason != (char *) NULL ?
7160 (*image)->exception.reason : "",
7161 (*image)->exception.description != (char *) NULL ?
7162 (*image)->exception.description : "");
7163 XNoticeWidget(display,windows,"Unable to print file:",message);
7164 break;
7165 }
7166 break;
7167 }
7168 case DeleteCommand:
7169 {
7170 static char
7171 filename[MaxTextExtent] = "\0";
7172
7173 /*
7174 Delete image file.
7175 */
7176 XFileBrowserWidget(display,windows,"Delete",filename);
7177 if (*filename == '\0')
7178 break;
7179 status=ShredFile(filename);
7180 status|=remove_utf8(filename);
7181 if (status != MagickFalse)
7182 XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7183 break;
7184 }
7185 case NewCommand:
7186 {
7187 int
7188 status;
7189
7190 static char
7191 color[MaxTextExtent] = "gray",
7192 geometry[MaxTextExtent] = "640x480";
7193
7194 static const char
7195 *format = "gradient";
7196
7197 /*
7198 Query user for canvas geometry.
7199 */
7200 status=XDialogWidget(display,windows,"New","Enter image geometry:",
7201 geometry);
7202 if (*geometry == '\0')
7203 break;
7204 if (status == 0)
7205 format="xc";
7206 XColorBrowserWidget(display,windows,"Select",color);
7207 if (*color == '\0')
7208 break;
7209 /*
7210 Create canvas.
7211 */
7212 (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7213 "%s:%s",format,color);
7214 (void) CloneString(&image_info->size,geometry);
7215 nexus=ReadImage(image_info,&(*image)->exception);
7216 CatchException(&(*image)->exception);
7217 XClientMessage(display,windows->image.id,windows->im_protocols,
7218 windows->im_next_image,CurrentTime);
7219 break;
7220 }
7221 case VisualDirectoryCommand:
7222 {
7223 /*
7224 Visual Image directory.
7225 */
7226 nexus=XVisualDirectoryImage(display,resource_info,windows);
7227 break;
7228 }
7229 case QuitCommand:
7230 {
7231 /*
7232 exit program.
7233 */
7234 if (resource_info->confirm_exit == MagickFalse)
7235 XClientMessage(display,windows->image.id,windows->im_protocols,
7236 windows->im_exit,CurrentTime);
7237 else
7238 {
7239 int
7240 status;
7241
7242 /*
7243 Confirm program exit.
7244 */
7245 status=XConfirmWidget(display,windows,"Do you really want to exit",
7246 resource_info->client_name);
7247 if (status > 0)
7248 XClientMessage(display,windows->image.id,windows->im_protocols,
7249 windows->im_exit,CurrentTime);
7250 }
7251 break;
7252 }
7253 case CutCommand:
7254 {
7255 /*
7256 Cut image.
7257 */
7258 (void) XCropImage(display,resource_info,windows,*image,CutMode);
7259 break;
7260 }
7261 case CopyCommand:
7262 {
7263 /*
7264 Copy image.
7265 */
7266 (void) XCropImage(display,resource_info,windows,*image,CopyMode);
7267 break;
7268 }
7269 case PasteCommand:
7270 {
7271 /*
7272 Paste image.
7273 */
7274 status=XPasteImage(display,resource_info,windows,*image);
7275 if (status == MagickFalse)
7276 {
7277 XNoticeWidget(display,windows,"Unable to paste X image",
7278 (*image)->filename);
7279 break;
7280 }
7281 break;
7282 }
7283 case HalfSizeCommand:
7284 {
7285 /*
7286 Half image size.
7287 */
7288 windows->image.window_changes.width=windows->image.ximage->width/2;
7289 windows->image.window_changes.height=windows->image.ximage->height/2;
7290 (void) XConfigureImage(display,resource_info,windows,*image);
7291 break;
7292 }
7293 case OriginalSizeCommand:
7294 {
7295 /*
7296 Original image size.
7297 */
7298 windows->image.window_changes.width=(int) (*image)->columns;
7299 windows->image.window_changes.height=(int) (*image)->rows;
7300 (void) XConfigureImage(display,resource_info,windows,*image);
7301 break;
7302 }
7303 case DoubleSizeCommand:
7304 {
7305 /*
7306 Double the image size.
7307 */
7308 windows->image.window_changes.width=windows->image.ximage->width << 1;
7309 windows->image.window_changes.height=windows->image.ximage->height << 1;
7310 (void) XConfigureImage(display,resource_info,windows,*image);
7311 break;
7312 }
7313 case ResizeCommand:
7314 {
7315 int
7316 status;
7317
7318 size_t
7319 height,
7320 width;
7321
7322 ssize_t
7323 x,
7324 y;
7325
7326 /*
7327 Resize image.
7328 */
7329 width=(size_t) windows->image.ximage->width;
7330 height=(size_t) windows->image.ximage->height;
7331 x=0;
7332 y=0;
7333 (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7334 (double) width,(double) height);
7335 status=XDialogWidget(display,windows,"Resize",
7336 "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7337 if (*geometry == '\0')
7338 break;
7339 if (status == 0)
7340 (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7341 (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7342 windows->image.window_changes.width=(int) width;
7343 windows->image.window_changes.height=(int) height;
7344 (void) XConfigureImage(display,resource_info,windows,*image);
7345 break;
7346 }
7347 case ApplyCommand:
7348 {
7349 char
7350 image_geometry[MaxTextExtent];
7351
7352 if ((windows->image.crop_geometry == (char *) NULL) &&
7353 ((int) (*image)->columns == windows->image.ximage->width) &&
7354 ((int) (*image)->rows == windows->image.ximage->height))
7355 break;
7356 /*
7357 Apply size transforms to image.
7358 */
7359 XSetCursorState(display,windows,MagickTrue);
7360 XCheckRefreshWindows(display,windows);
7361 /*
7362 Crop and/or scale displayed image.
7363 */
7364 (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7365 windows->image.ximage->width,windows->image.ximage->height);
7366 (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7367 if (windows->image.crop_geometry != (char *) NULL)
7368 windows->image.crop_geometry=(char *)
7369 RelinquishMagickMemory(windows->image.crop_geometry);
7370 windows->image.x=0;
7371 windows->image.y=0;
7372 XConfigureImageColormap(display,resource_info,windows,*image);
7373 (void) XConfigureImage(display,resource_info,windows,*image);
7374 break;
7375 }
7376 case RefreshCommand:
7377 {
7378 (void) XConfigureImage(display,resource_info,windows,*image);
7379 break;
7380 }
7381 case RestoreCommand:
7382 {
7383 /*
7384 Restore Image window to its original size.
7385 */
7386 if ((windows->image.width == (unsigned int) (*image)->columns) &&
7387 (windows->image.height == (unsigned int) (*image)->rows) &&
7388 (windows->image.crop_geometry == (char *) NULL))
7389 {
7390 (void) XBell(display,0);
7391 break;
7392 }
7393 windows->image.window_changes.width=(int) (*image)->columns;
7394 windows->image.window_changes.height=(int) (*image)->rows;
7395 if (windows->image.crop_geometry != (char *) NULL)
7396 {
7397 windows->image.crop_geometry=(char *)
7398 RelinquishMagickMemory(windows->image.crop_geometry);
7399 windows->image.crop_geometry=(char *) NULL;
7400 windows->image.x=0;
7401 windows->image.y=0;
7402 }
7403 XConfigureImageColormap(display,resource_info,windows,*image);
7404 (void) XConfigureImage(display,resource_info,windows,*image);
7405 break;
7406 }
7407 case CropCommand:
7408 {
7409 /*
7410 Crop image.
7411 */
7412 (void) XCropImage(display,resource_info,windows,*image,CropMode);
7413 break;
7414 }
7415 case ChopCommand:
7416 {
7417 /*
7418 Chop image.
7419 */
7420 status=XChopImage(display,resource_info,windows,image);
7421 if (status == MagickFalse)
7422 {
7423 XNoticeWidget(display,windows,"Unable to cut X image",
7424 (*image)->filename);
7425 break;
7426 }
7427 break;
7428 }
7429 case FlopCommand:
7430 {
7431 Image
7432 *flop_image;
7433
7434 /*
7435 Flop image scanlines.
7436 */
7437 XSetCursorState(display,windows,MagickTrue);
7438 XCheckRefreshWindows(display,windows);
7439 flop_image=FlopImage(*image,&(*image)->exception);
7440 if (flop_image != (Image *) NULL)
7441 {
7442 *image=DestroyImage(*image);
7443 *image=flop_image;
7444 }
7445 CatchException(&(*image)->exception);
7446 XSetCursorState(display,windows,MagickFalse);
7447 if (windows->image.crop_geometry != (char *) NULL)
7448 {
7449 /*
7450 Flop crop geometry.
7451 */
7452 width=(unsigned int) (*image)->columns;
7453 height=(unsigned int) (*image)->rows;
7454 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7455 &width,&height);
7456 (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7457 "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7458 }
7459 if (windows->image.orphan != MagickFalse)
7460 break;
7461 (void) XConfigureImage(display,resource_info,windows,*image);
7462 break;
7463 }
7464 case FlipCommand:
7465 {
7466 Image
7467 *flip_image;
7468
7469 /*
7470 Flip image scanlines.
7471 */
7472 XSetCursorState(display,windows,MagickTrue);
7473 XCheckRefreshWindows(display,windows);
7474 flip_image=FlipImage(*image,&(*image)->exception);
7475 if (flip_image != (Image *) NULL)
7476 {
7477 *image=DestroyImage(*image);
7478 *image=flip_image;
7479 }
7480 CatchException(&(*image)->exception);
7481 XSetCursorState(display,windows,MagickFalse);
7482 if (windows->image.crop_geometry != (char *) NULL)
7483 {
7484 /*
7485 Flip crop geometry.
7486 */
7487 width=(unsigned int) (*image)->columns;
7488 height=(unsigned int) (*image)->rows;
7489 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7490 &width,&height);
7491 (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7492 "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7493 }
7494 if (windows->image.orphan != MagickFalse)
7495 break;
7496 (void) XConfigureImage(display,resource_info,windows,*image);
7497 break;
7498 }
7499 case RotateRightCommand:
7500 {
7501 /*
7502 Rotate image 90 degrees clockwise.
7503 */
7504 status=XRotateImage(display,resource_info,windows,90.0,image);
7505 if (status == MagickFalse)
7506 {
7507 XNoticeWidget(display,windows,"Unable to rotate X image",
7508 (*image)->filename);
7509 break;
7510 }
7511 break;
7512 }
7513 case RotateLeftCommand:
7514 {
7515 /*
7516 Rotate image 90 degrees counter-clockwise.
7517 */
7518 status=XRotateImage(display,resource_info,windows,-90.0,image);
7519 if (status == MagickFalse)
7520 {
7521 XNoticeWidget(display,windows,"Unable to rotate X image",
7522 (*image)->filename);
7523 break;
7524 }
7525 break;
7526 }
7527 case RotateCommand:
7528 {
7529 /*
7530 Rotate image.
7531 */
7532 status=XRotateImage(display,resource_info,windows,0.0,image);
7533 if (status == MagickFalse)
7534 {
7535 XNoticeWidget(display,windows,"Unable to rotate X image",
7536 (*image)->filename);
7537 break;
7538 }
7539 break;
7540 }
7541 case ShearCommand:
7542 {
7543 Image
7544 *shear_image;
7545
7546 static char
7547 geometry[MaxTextExtent] = "45.0x45.0";
7548
7549 /*
7550 Query user for shear color and geometry.
7551 */
7552 XColorBrowserWidget(display,windows,"Select",color);
7553 if (*color == '\0')
7554 break;
7555 (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7556 geometry);
7557 if (*geometry == '\0')
7558 break;
7559 /*
7560 Shear image.
7561 */
7562 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7563 XSetCursorState(display,windows,MagickTrue);
7564 XCheckRefreshWindows(display,windows);
7565 (void) QueryColorDatabase(color,&(*image)->background_color,
7566 &(*image)->exception);
7567 flags=ParseGeometry(geometry,&geometry_info);
7568 if ((flags & SigmaValue) == 0)
7569 geometry_info.sigma=geometry_info.rho;
7570 shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7571 &(*image)->exception);
7572 if (shear_image != (Image *) NULL)
7573 {
7574 *image=DestroyImage(*image);
7575 *image=shear_image;
7576 }
7577 CatchException(&(*image)->exception);
7578 XSetCursorState(display,windows,MagickFalse);
7579 if (windows->image.orphan != MagickFalse)
7580 break;
7581 windows->image.window_changes.width=(int) (*image)->columns;
7582 windows->image.window_changes.height=(int) (*image)->rows;
7583 XConfigureImageColormap(display,resource_info,windows,*image);
7584 (void) XConfigureImage(display,resource_info,windows,*image);
7585 break;
7586 }
7587 case RollCommand:
7588 {
7589 Image
7590 *roll_image;
7591
7592 static char
7593 geometry[MaxTextExtent] = "+2+2";
7594
7595 /*
7596 Query user for the roll geometry.
7597 */
7598 (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7599 geometry);
7600 if (*geometry == '\0')
7601 break;
7602 /*
7603 Roll image.
7604 */
7605 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7606 XSetCursorState(display,windows,MagickTrue);
7607 XCheckRefreshWindows(display,windows);
7608 (void) ParsePageGeometry(*image,geometry,&page_geometry,
7609 &(*image)->exception);
7610 roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7611 &(*image)->exception);
7612 if (roll_image != (Image *) NULL)
7613 {
7614 *image=DestroyImage(*image);
7615 *image=roll_image;
7616 }
7617 CatchException(&(*image)->exception);
7618 XSetCursorState(display,windows,MagickFalse);
7619 if (windows->image.orphan != MagickFalse)
7620 break;
7621 windows->image.window_changes.width=(int) (*image)->columns;
7622 windows->image.window_changes.height=(int) (*image)->rows;
7623 XConfigureImageColormap(display,resource_info,windows,*image);
7624 (void) XConfigureImage(display,resource_info,windows,*image);
7625 break;
7626 }
7627 case TrimCommand:
7628 {
7629 static char
7630 fuzz[MaxTextExtent];
7631
7632 /*
7633 Query user for the fuzz factor.
7634 */
7635 (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7636 (*image)->fuzz/((double) QuantumRange+1.0));
7637 (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7638 if (*fuzz == '\0')
7639 break;
7640 (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7641 /*
7642 Trim image.
7643 */
7644 status=XTrimImage(display,resource_info,windows,*image);
7645 if (status == MagickFalse)
7646 {
7647 XNoticeWidget(display,windows,"Unable to trim X image",
7648 (*image)->filename);
7649 break;
7650 }
7651 break;
7652 }
7653 case HueCommand:
7654 {
7655 static char
7656 hue_percent[MaxTextExtent] = "110";
7657
7658 /*
7659 Query user for percent hue change.
7660 */
7661 (void) XDialogWidget(display,windows,"Apply",
7662 "Enter percent change in image hue (0-200):",hue_percent);
7663 if (*hue_percent == '\0')
7664 break;
7665 /*
7666 Vary the image hue.
7667 */
7668 XSetCursorState(display,windows,MagickTrue);
7669 XCheckRefreshWindows(display,windows);
7670 (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7671 (void) ConcatenateMagickString(modulate_factors,hue_percent,
7672 MaxTextExtent);
7673 (void) ModulateImage(*image,modulate_factors);
7674 XSetCursorState(display,windows,MagickFalse);
7675 if (windows->image.orphan != MagickFalse)
7676 break;
7677 XConfigureImageColormap(display,resource_info,windows,*image);
7678 (void) XConfigureImage(display,resource_info,windows,*image);
7679 break;
7680 }
7681 case SaturationCommand:
7682 {
7683 static char
7684 saturation_percent[MaxTextExtent] = "110";
7685
7686 /*
7687 Query user for percent saturation change.
7688 */
7689 (void) XDialogWidget(display,windows,"Apply",
7690 "Enter percent change in color saturation (0-200):",saturation_percent);
7691 if (*saturation_percent == '\0')
7692 break;
7693 /*
7694 Vary color saturation.
7695 */
7696 XSetCursorState(display,windows,MagickTrue);
7697 XCheckRefreshWindows(display,windows);
7698 (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7699 (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7700 MaxTextExtent);
7701 (void) ModulateImage(*image,modulate_factors);
7702 XSetCursorState(display,windows,MagickFalse);
7703 if (windows->image.orphan != MagickFalse)
7704 break;
7705 XConfigureImageColormap(display,resource_info,windows,*image);
7706 (void) XConfigureImage(display,resource_info,windows,*image);
7707 break;
7708 }
7709 case BrightnessCommand:
7710 {
7711 static char
7712 brightness_percent[MaxTextExtent] = "110";
7713
7714 /*
7715 Query user for percent brightness change.
7716 */
7717 (void) XDialogWidget(display,windows,"Apply",
7718 "Enter percent change in color brightness (0-200):",brightness_percent);
7719 if (*brightness_percent == '\0')
7720 break;
7721 /*
7722 Vary the color brightness.
7723 */
7724 XSetCursorState(display,windows,MagickTrue);
7725 XCheckRefreshWindows(display,windows);
7726 (void) CopyMagickString(modulate_factors,brightness_percent,
7727 MaxTextExtent);
7728 (void) ModulateImage(*image,modulate_factors);
7729 XSetCursorState(display,windows,MagickFalse);
7730 if (windows->image.orphan != MagickFalse)
7731 break;
7732 XConfigureImageColormap(display,resource_info,windows,*image);
7733 (void) XConfigureImage(display,resource_info,windows,*image);
7734 break;
7735 }
7736 case GammaCommand:
7737 {
7738 static char
7739 factor[MaxTextExtent] = "1.6";
7740
7741 /*
7742 Query user for gamma value.
7743 */
7744 (void) XDialogWidget(display,windows,"Gamma",
7745 "Enter gamma value (e.g. 1.0,1.0,1.6):",factor);
7746 if (*factor == '\0')
7747 break;
7748 /*
7749 Gamma correct image.
7750 */
7751 XSetCursorState(display,windows,MagickTrue);
7752 XCheckRefreshWindows(display,windows);
7753 (void) GammaImage(*image,factor);
7754 XSetCursorState(display,windows,MagickFalse);
7755 if (windows->image.orphan != MagickFalse)
7756 break;
7757 XConfigureImageColormap(display,resource_info,windows,*image);
7758 (void) XConfigureImage(display,resource_info,windows,*image);
7759 break;
7760 }
7761 case SpiffCommand:
7762 {
7763 /*
7764 Sharpen the image contrast.
7765 */
7766 XSetCursorState(display,windows,MagickTrue);
7767 XCheckRefreshWindows(display,windows);
7768 (void) ContrastImage(*image,MagickTrue);
7769 XSetCursorState(display,windows,MagickFalse);
7770 if (windows->image.orphan != MagickFalse)
7771 break;
7772 XConfigureImageColormap(display,resource_info,windows,*image);
7773 (void) XConfigureImage(display,resource_info,windows,*image);
7774 break;
7775 }
7776 case DullCommand:
7777 {
7778 /*
7779 Dull the image contrast.
7780 */
7781 XSetCursorState(display,windows,MagickTrue);
7782 XCheckRefreshWindows(display,windows);
7783 (void) ContrastImage(*image,MagickFalse);
7784 XSetCursorState(display,windows,MagickFalse);
7785 if (windows->image.orphan != MagickFalse)
7786 break;
7787 XConfigureImageColormap(display,resource_info,windows,*image);
7788 (void) XConfigureImage(display,resource_info,windows,*image);
7789 break;
7790 }
7791 case ContrastStretchCommand:
7792 {
7793 double
7794 black_point,
7795 white_point;
7796
7797 static char
7798 levels[MaxTextExtent] = "1%";
7799
7800 /*
7801 Query user for gamma value.
7802 */
7803 (void) XDialogWidget(display,windows,"Contrast Stretch",
7804 "Enter black and white points:",levels);
7805 if (*levels == '\0')
7806 break;
7807 /*
7808 Contrast stretch image.
7809 */
7810 XSetCursorState(display,windows,MagickTrue);
7811 XCheckRefreshWindows(display,windows);
7812 flags=ParseGeometry(levels,&geometry_info);
7813 black_point=geometry_info.rho;
7814 white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7815 if ((flags & PercentValue) != 0)
7816 {
7817 black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7818 white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7819 }
7820 white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7821 (void) ContrastStretchImageChannel(*image,DefaultChannels,black_point,
7822 white_point);
7823 XSetCursorState(display,windows,MagickFalse);
7824 if (windows->image.orphan != MagickFalse)
7825 break;
7826 XConfigureImageColormap(display,resource_info,windows,*image);
7827 (void) XConfigureImage(display,resource_info,windows,*image);
7828 break;
7829 }
7830 case SigmoidalContrastCommand:
7831 {
7832 static char
7833 levels[MaxTextExtent] = "3x50%";
7834
7835 /*
7836 Query user for gamma value.
7837 */
7838 (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7839 "Enter contrast and midpoint:",levels);
7840 if (*levels == '\0')
7841 break;
7842 /*
7843 Contrast stretch image.
7844 */
7845 XSetCursorState(display,windows,MagickTrue);
7846 XCheckRefreshWindows(display,windows);
7847 (void) SigmoidalContrastImage(*image,MagickTrue,levels);
7848 XSetCursorState(display,windows,MagickFalse);
7849 if (windows->image.orphan != MagickFalse)
7850 break;
7851 XConfigureImageColormap(display,resource_info,windows,*image);
7852 (void) XConfigureImage(display,resource_info,windows,*image);
7853 break;
7854 }
7855 case NormalizeCommand:
7856 {
7857 /*
7858 Perform histogram normalization on the image.
7859 */
7860 XSetCursorState(display,windows,MagickTrue);
7861 XCheckRefreshWindows(display,windows);
7862 (void) NormalizeImage(*image);
7863 XSetCursorState(display,windows,MagickFalse);
7864 if (windows->image.orphan != MagickFalse)
7865 break;
7866 XConfigureImageColormap(display,resource_info,windows,*image);
7867 (void) XConfigureImage(display,resource_info,windows,*image);
7868 break;
7869 }
7870 case EqualizeCommand:
7871 {
7872 /*
7873 Perform histogram equalization on the image.
7874 */
7875 XSetCursorState(display,windows,MagickTrue);
7876 XCheckRefreshWindows(display,windows);
7877 (void) EqualizeImage(*image);
7878 XSetCursorState(display,windows,MagickFalse);
7879 if (windows->image.orphan != MagickFalse)
7880 break;
7881 XConfigureImageColormap(display,resource_info,windows,*image);
7882 (void) XConfigureImage(display,resource_info,windows,*image);
7883 break;
7884 }
7885 case NegateCommand:
7886 {
7887 /*
7888 Negate colors in image.
7889 */
7890 XSetCursorState(display,windows,MagickTrue);
7891 XCheckRefreshWindows(display,windows);
7892 (void) NegateImage(*image,MagickFalse);
7893 XSetCursorState(display,windows,MagickFalse);
7894 if (windows->image.orphan != MagickFalse)
7895 break;
7896 XConfigureImageColormap(display,resource_info,windows,*image);
7897 (void) XConfigureImage(display,resource_info,windows,*image);
7898 break;
7899 }
7900 case GrayscaleCommand:
7901 {
7902 /*
7903 Convert image to grayscale.
7904 */
7905 XSetCursorState(display,windows,MagickTrue);
7906 XCheckRefreshWindows(display,windows);
7907 (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7908 GrayscaleType : GrayscaleMatteType);
7909 XSetCursorState(display,windows,MagickFalse);
7910 if (windows->image.orphan != MagickFalse)
7911 break;
7912 XConfigureImageColormap(display,resource_info,windows,*image);
7913 (void) XConfigureImage(display,resource_info,windows,*image);
7914 break;
7915 }
7916 case MapCommand:
7917 {
7918 Image
7919 *affinity_image;
7920
7921 static char
7922 filename[MaxTextExtent] = "\0";
7923
7924 /*
7925 Request image file name from user.
7926 */
7927 XFileBrowserWidget(display,windows,"Map",filename);
7928 if (*filename == '\0')
7929 break;
7930 /*
7931 Map image.
7932 */
7933 XSetCursorState(display,windows,MagickTrue);
7934 XCheckRefreshWindows(display,windows);
7935 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
7936 affinity_image=ReadImage(image_info,&(*image)->exception);
7937 if (affinity_image != (Image *) NULL)
7938 {
7939 (void) RemapImage(&quantize_info,*image,affinity_image);
7940 affinity_image=DestroyImage(affinity_image);
7941 }
7942 CatchException(&(*image)->exception);
7943 XSetCursorState(display,windows,MagickFalse);
7944 if (windows->image.orphan != MagickFalse)
7945 break;
7946 XConfigureImageColormap(display,resource_info,windows,*image);
7947 (void) XConfigureImage(display,resource_info,windows,*image);
7948 break;
7949 }
7950 case QuantizeCommand:
7951 {
7952 int
7953 status;
7954
7955 static char
7956 colors[MaxTextExtent] = "256";
7957
7958 /*
7959 Query user for maximum number of colors.
7960 */
7961 status=XDialogWidget(display,windows,"Quantize",
7962 "Maximum number of colors:",colors);
7963 if (*colors == '\0')
7964 break;
7965 /*
7966 Color reduce the image.
7967 */
7968 XSetCursorState(display,windows,MagickTrue);
7969 XCheckRefreshWindows(display,windows);
7970 quantize_info.number_colors=StringToUnsignedLong(colors);
7971 quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
7972 (void) QuantizeImage(&quantize_info,*image);
7973 XSetCursorState(display,windows,MagickFalse);
7974 if (windows->image.orphan != MagickFalse)
7975 break;
7976 XConfigureImageColormap(display,resource_info,windows,*image);
7977 (void) XConfigureImage(display,resource_info,windows,*image);
7978 break;
7979 }
7980 case DespeckleCommand:
7981 {
7982 Image
7983 *despeckle_image;
7984
7985 /*
7986 Despeckle image.
7987 */
7988 XSetCursorState(display,windows,MagickTrue);
7989 XCheckRefreshWindows(display,windows);
7990 despeckle_image=DespeckleImage(*image,&(*image)->exception);
7991 if (despeckle_image != (Image *) NULL)
7992 {
7993 *image=DestroyImage(*image);
7994 *image=despeckle_image;
7995 }
7996 CatchException(&(*image)->exception);
7997 XSetCursorState(display,windows,MagickFalse);
7998 if (windows->image.orphan != MagickFalse)
7999 break;
8000 XConfigureImageColormap(display,resource_info,windows,*image);
8001 (void) XConfigureImage(display,resource_info,windows,*image);
8002 break;
8003 }
8004 case EmbossCommand:
8005 {
8006 Image
8007 *emboss_image;
8008
8009 static char
8010 radius[MaxTextExtent] = "0.0x1.0";
8011
8012 /*
8013 Query user for emboss radius.
8014 */
8015 (void) XDialogWidget(display,windows,"Emboss",
8016 "Enter the emboss radius and standard deviation:",radius);
8017 if (*radius == '\0')
8018 break;
8019 /*
8020 Reduce noise in the image.
8021 */
8022 XSetCursorState(display,windows,MagickTrue);
8023 XCheckRefreshWindows(display,windows);
8024 flags=ParseGeometry(radius,&geometry_info);
8025 if ((flags & SigmaValue) == 0)
8026 geometry_info.sigma=1.0;
8027 emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8028 &(*image)->exception);
8029 if (emboss_image != (Image *) NULL)
8030 {
8031 *image=DestroyImage(*image);
8032 *image=emboss_image;
8033 }
8034 CatchException(&(*image)->exception);
8035 XSetCursorState(display,windows,MagickFalse);
8036 if (windows->image.orphan != MagickFalse)
8037 break;
8038 XConfigureImageColormap(display,resource_info,windows,*image);
8039 (void) XConfigureImage(display,resource_info,windows,*image);
8040 break;
8041 }
8042 case ReduceNoiseCommand:
8043 {
8044 Image
8045 *noise_image;
8046
8047 static char
8048 radius[MaxTextExtent] = "0";
8049
8050 /*
8051 Query user for noise radius.
8052 */
8053 (void) XDialogWidget(display,windows,"Reduce Noise",
8054 "Enter the noise radius:",radius);
8055 if (*radius == '\0')
8056 break;
8057 /*
8058 Reduce noise in the image.
8059 */
8060 XSetCursorState(display,windows,MagickTrue);
8061 XCheckRefreshWindows(display,windows);
8062 flags=ParseGeometry(radius,&geometry_info);
8063 noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8064 geometry_info.rho,(size_t) geometry_info.rho,&(*image)->exception);
8065 if (noise_image != (Image *) NULL)
8066 {
8067 *image=DestroyImage(*image);
8068 *image=noise_image;
8069 }
8070 CatchException(&(*image)->exception);
8071 XSetCursorState(display,windows,MagickFalse);
8072 if (windows->image.orphan != MagickFalse)
8073 break;
8074 XConfigureImageColormap(display,resource_info,windows,*image);
8075 (void) XConfigureImage(display,resource_info,windows,*image);
8076 break;
8077 }
8078 case AddNoiseCommand:
8079 {
8080 char
8081 **noises;
8082
8083 Image
8084 *noise_image;
8085
8086 static char
8087 noise_type[MaxTextExtent] = "Gaussian";
8088
8089 /*
8090 Add noise to the image.
8091 */
8092 noises=GetCommandOptions(MagickNoiseOptions);
8093 if (noises == (char **) NULL)
8094 break;
8095 XListBrowserWidget(display,windows,&windows->widget,
8096 (const char **) noises,"Add Noise",
8097 "Select a type of noise to add to your image:",noise_type);
8098 noises=DestroyStringList(noises);
8099 if (*noise_type == '\0')
8100 break;
8101 XSetCursorState(display,windows,MagickTrue);
8102 XCheckRefreshWindows(display,windows);
8103 noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8104 MagickNoiseOptions,MagickFalse,noise_type),&(*image)->exception);
8105 if (noise_image != (Image *) NULL)
8106 {
8107 *image=DestroyImage(*image);
8108 *image=noise_image;
8109 }
8110 CatchException(&(*image)->exception);
8111 XSetCursorState(display,windows,MagickFalse);
8112 if (windows->image.orphan != MagickFalse)
8113 break;
8114 XConfigureImageColormap(display,resource_info,windows,*image);
8115 (void) XConfigureImage(display,resource_info,windows,*image);
8116 break;
8117 }
8118 case SharpenCommand:
8119 {
8120 Image
8121 *sharp_image;
8122
8123 static char
8124 radius[MaxTextExtent] = "0.0x1.0";
8125
8126 /*
8127 Query user for sharpen radius.
8128 */
8129 (void) XDialogWidget(display,windows,"Sharpen",
8130 "Enter the sharpen radius and standard deviation:",radius);
8131 if (*radius == '\0')
8132 break;
8133 /*
8134 Sharpen image scanlines.
8135 */
8136 XSetCursorState(display,windows,MagickTrue);
8137 XCheckRefreshWindows(display,windows);
8138 flags=ParseGeometry(radius,&geometry_info);
8139 sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8140 &(*image)->exception);
8141 if (sharp_image != (Image *) NULL)
8142 {
8143 *image=DestroyImage(*image);
8144 *image=sharp_image;
8145 }
8146 CatchException(&(*image)->exception);
8147 XSetCursorState(display,windows,MagickFalse);
8148 if (windows->image.orphan != MagickFalse)
8149 break;
8150 XConfigureImageColormap(display,resource_info,windows,*image);
8151 (void) XConfigureImage(display,resource_info,windows,*image);
8152 break;
8153 }
8154 case BlurCommand:
8155 {
8156 Image
8157 *blur_image;
8158
8159 static char
8160 radius[MaxTextExtent] = "0.0x1.0";
8161
8162 /*
8163 Query user for blur radius.
8164 */
8165 (void) XDialogWidget(display,windows,"Blur",
8166 "Enter the blur radius and standard deviation:",radius);
8167 if (*radius == '\0')
8168 break;
8169 /*
8170 Blur an image.
8171 */
8172 XSetCursorState(display,windows,MagickTrue);
8173 XCheckRefreshWindows(display,windows);
8174 flags=ParseGeometry(radius,&geometry_info);
8175 blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8176 &(*image)->exception);
8177 if (blur_image != (Image *) NULL)
8178 {
8179 *image=DestroyImage(*image);
8180 *image=blur_image;
8181 }
8182 CatchException(&(*image)->exception);
8183 XSetCursorState(display,windows,MagickFalse);
8184 if (windows->image.orphan != MagickFalse)
8185 break;
8186 XConfigureImageColormap(display,resource_info,windows,*image);
8187 (void) XConfigureImage(display,resource_info,windows,*image);
8188 break;
8189 }
8190 case ThresholdCommand:
8191 {
8192 double
8193 threshold;
8194
8195 static char
8196 factor[MaxTextExtent] = "128";
8197
8198 /*
8199 Query user for threshold value.
8200 */
8201 (void) XDialogWidget(display,windows,"Threshold",
8202 "Enter threshold value:",factor);
8203 if (*factor == '\0')
8204 break;
8205 /*
8206 Gamma correct image.
8207 */
8208 XSetCursorState(display,windows,MagickTrue);
8209 XCheckRefreshWindows(display,windows);
8210 threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8211 (void) BilevelImage(*image,threshold);
8212 XSetCursorState(display,windows,MagickFalse);
8213 if (windows->image.orphan != MagickFalse)
8214 break;
8215 XConfigureImageColormap(display,resource_info,windows,*image);
8216 (void) XConfigureImage(display,resource_info,windows,*image);
8217 break;
8218 }
8219 case EdgeDetectCommand:
8220 {
8221 Image
8222 *edge_image;
8223
8224 static char
8225 radius[MaxTextExtent] = "0";
8226
8227 /*
8228 Query user for edge factor.
8229 */
8230 (void) XDialogWidget(display,windows,"Detect Edges",
8231 "Enter the edge detect radius:",radius);
8232 if (*radius == '\0')
8233 break;
8234 /*
8235 Detect edge in image.
8236 */
8237 XSetCursorState(display,windows,MagickTrue);
8238 XCheckRefreshWindows(display,windows);
8239 flags=ParseGeometry(radius,&geometry_info);
8240 edge_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8241 if (edge_image != (Image *) NULL)
8242 {
8243 *image=DestroyImage(*image);
8244 *image=edge_image;
8245 }
8246 CatchException(&(*image)->exception);
8247 XSetCursorState(display,windows,MagickFalse);
8248 if (windows->image.orphan != MagickFalse)
8249 break;
8250 XConfigureImageColormap(display,resource_info,windows,*image);
8251 (void) XConfigureImage(display,resource_info,windows,*image);
8252 break;
8253 }
8254 case SpreadCommand:
8255 {
8256 Image
8257 *spread_image;
8258
8259 static char
8260 amount[MaxTextExtent] = "2";
8261
8262 /*
8263 Query user for spread amount.
8264 */
8265 (void) XDialogWidget(display,windows,"Spread",
8266 "Enter the displacement amount:",amount);
8267 if (*amount == '\0')
8268 break;
8269 /*
8270 Displace image pixels by a random amount.
8271 */
8272 XSetCursorState(display,windows,MagickTrue);
8273 XCheckRefreshWindows(display,windows);
8274 flags=ParseGeometry(amount,&geometry_info);
8275 spread_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8276 if (spread_image != (Image *) NULL)
8277 {
8278 *image=DestroyImage(*image);
8279 *image=spread_image;
8280 }
8281 CatchException(&(*image)->exception);
8282 XSetCursorState(display,windows,MagickFalse);
8283 if (windows->image.orphan != MagickFalse)
8284 break;
8285 XConfigureImageColormap(display,resource_info,windows,*image);
8286 (void) XConfigureImage(display,resource_info,windows,*image);
8287 break;
8288 }
8289 case ShadeCommand:
8290 {
8291 Image
8292 *shade_image;
8293
8294 int
8295 status;
8296
8297 static char
8298 geometry[MaxTextExtent] = "30x30";
8299
8300 /*
8301 Query user for the shade geometry.
8302 */
8303 status=XDialogWidget(display,windows,"Shade",
8304 "Enter the azimuth and elevation of the light source:",geometry);
8305 if (*geometry == '\0')
8306 break;
8307 /*
8308 Shade image pixels.
8309 */
8310 XSetCursorState(display,windows,MagickTrue);
8311 XCheckRefreshWindows(display,windows);
8312 flags=ParseGeometry(geometry,&geometry_info);
8313 if ((flags & SigmaValue) == 0)
8314 geometry_info.sigma=1.0;
8315 shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8316 geometry_info.rho,geometry_info.sigma,&(*image)->exception);
8317 if (shade_image != (Image *) NULL)
8318 {
8319 *image=DestroyImage(*image);
8320 *image=shade_image;
8321 }
8322 CatchException(&(*image)->exception);
8323 XSetCursorState(display,windows,MagickFalse);
8324 if (windows->image.orphan != MagickFalse)
8325 break;
8326 XConfigureImageColormap(display,resource_info,windows,*image);
8327 (void) XConfigureImage(display,resource_info,windows,*image);
8328 break;
8329 }
8330 case RaiseCommand:
8331 {
8332 static char
8333 bevel_width[MaxTextExtent] = "10";
8334
8335 /*
8336 Query user for bevel width.
8337 */
8338 (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8339 if (*bevel_width == '\0')
8340 break;
8341 /*
8342 Raise an image.
8343 */
8344 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8345 XSetCursorState(display,windows,MagickTrue);
8346 XCheckRefreshWindows(display,windows);
8347 (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8348 &(*image)->exception);
8349 (void) RaiseImage(*image,&page_geometry,MagickTrue);
8350 XSetCursorState(display,windows,MagickFalse);
8351 if (windows->image.orphan != MagickFalse)
8352 break;
8353 XConfigureImageColormap(display,resource_info,windows,*image);
8354 (void) XConfigureImage(display,resource_info,windows,*image);
8355 break;
8356 }
8357 case SegmentCommand:
8358 {
8359 static char
8360 threshold[MaxTextExtent] = "1.0x1.5";
8361
8362 /*
8363 Query user for smoothing threshold.
8364 */
8365 (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8366 threshold);
8367 if (*threshold == '\0')
8368 break;
8369 /*
8370 Segment an image.
8371 */
8372 XSetCursorState(display,windows,MagickTrue);
8373 XCheckRefreshWindows(display,windows);
8374 flags=ParseGeometry(threshold,&geometry_info);
8375 if ((flags & SigmaValue) == 0)
8376 geometry_info.sigma=1.0;
8377 (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8378 geometry_info.sigma);
8379 XSetCursorState(display,windows,MagickFalse);
8380 if (windows->image.orphan != MagickFalse)
8381 break;
8382 XConfigureImageColormap(display,resource_info,windows,*image);
8383 (void) XConfigureImage(display,resource_info,windows,*image);
8384 break;
8385 }
8386 case SepiaToneCommand:
8387 {
8388 double
8389 threshold;
8390
8391 Image
8392 *sepia_image;
8393
8394 static char
8395 factor[MaxTextExtent] = "80%";
8396
8397 /*
8398 Query user for sepia-tone factor.
8399 */
8400 (void) XDialogWidget(display,windows,"Sepia Tone",
8401 "Enter the sepia tone factor (0 - 99.9%):",factor);
8402 if (*factor == '\0')
8403 break;
8404 /*
8405 Sepia tone image pixels.
8406 */
8407 XSetCursorState(display,windows,MagickTrue);
8408 XCheckRefreshWindows(display,windows);
8409 threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8410 sepia_image=SepiaToneImage(*image,threshold,&(*image)->exception);
8411 if (sepia_image != (Image *) NULL)
8412 {
8413 *image=DestroyImage(*image);
8414 *image=sepia_image;
8415 }
8416 CatchException(&(*image)->exception);
8417 XSetCursorState(display,windows,MagickFalse);
8418 if (windows->image.orphan != MagickFalse)
8419 break;
8420 XConfigureImageColormap(display,resource_info,windows,*image);
8421 (void) XConfigureImage(display,resource_info,windows,*image);
8422 break;
8423 }
8424 case SolarizeCommand:
8425 {
8426 double
8427 threshold;
8428
8429 static char
8430 factor[MaxTextExtent] = "60%";
8431
8432 /*
8433 Query user for solarize factor.
8434 */
8435 (void) XDialogWidget(display,windows,"Solarize",
8436 "Enter the solarize factor (0 - 99.9%):",factor);
8437 if (*factor == '\0')
8438 break;
8439 /*
8440 Solarize image pixels.
8441 */
8442 XSetCursorState(display,windows,MagickTrue);
8443 XCheckRefreshWindows(display,windows);
8444 threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8445 (void) SolarizeImage(*image,threshold);
8446 XSetCursorState(display,windows,MagickFalse);
8447 if (windows->image.orphan != MagickFalse)
8448 break;
8449 XConfigureImageColormap(display,resource_info,windows,*image);
8450 (void) XConfigureImage(display,resource_info,windows,*image);
8451 break;
8452 }
8453 case SwirlCommand:
8454 {
8455 Image
8456 *swirl_image;
8457
8458 static char
8459 degrees[MaxTextExtent] = "60";
8460
8461 /*
8462 Query user for swirl angle.
8463 */
8464 (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8465 degrees);
8466 if (*degrees == '\0')
8467 break;
8468 /*
8469 Swirl image pixels about the center.
8470 */
8471 XSetCursorState(display,windows,MagickTrue);
8472 XCheckRefreshWindows(display,windows);
8473 flags=ParseGeometry(degrees,&geometry_info);
8474 swirl_image=SwirlImage(*image,geometry_info.rho,&(*image)->exception);
8475 if (swirl_image != (Image *) NULL)
8476 {
8477 *image=DestroyImage(*image);
8478 *image=swirl_image;
8479 }
8480 CatchException(&(*image)->exception);
8481 XSetCursorState(display,windows,MagickFalse);
8482 if (windows->image.orphan != MagickFalse)
8483 break;
8484 XConfigureImageColormap(display,resource_info,windows,*image);
8485 (void) XConfigureImage(display,resource_info,windows,*image);
8486 break;
8487 }
8488 case ImplodeCommand:
8489 {
8490 Image
8491 *implode_image;
8492
8493 static char
8494 factor[MaxTextExtent] = "0.3";
8495
8496 /*
8497 Query user for implode factor.
8498 */
8499 (void) XDialogWidget(display,windows,"Implode",
8500 "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8501 if (*factor == '\0')
8502 break;
8503 /*
8504 Implode image pixels about the center.
8505 */
8506 XSetCursorState(display,windows,MagickTrue);
8507 XCheckRefreshWindows(display,windows);
8508 flags=ParseGeometry(factor,&geometry_info);
8509 implode_image=ImplodeImage(*image,geometry_info.rho,&(*image)->exception);
8510 if (implode_image != (Image *) NULL)
8511 {
8512 *image=DestroyImage(*image);
8513 *image=implode_image;
8514 }
8515 CatchException(&(*image)->exception);
8516 XSetCursorState(display,windows,MagickFalse);
8517 if (windows->image.orphan != MagickFalse)
8518 break;
8519 XConfigureImageColormap(display,resource_info,windows,*image);
8520 (void) XConfigureImage(display,resource_info,windows,*image);
8521 break;
8522 }
8523 case VignetteCommand:
8524 {
8525 Image
8526 *vignette_image;
8527
8528 static char
8529 geometry[MaxTextExtent] = "0x20";
8530
8531 /*
8532 Query user for the vignette geometry.
8533 */
8534 (void) XDialogWidget(display,windows,"Vignette",
8535 "Enter the radius, sigma, and x and y offsets:",geometry);
8536 if (*geometry == '\0')
8537 break;
8538 /*
8539 Soften the edges of the image in vignette style
8540 */
8541 XSetCursorState(display,windows,MagickTrue);
8542 XCheckRefreshWindows(display,windows);
8543 flags=ParseGeometry(geometry,&geometry_info);
8544 if ((flags & SigmaValue) == 0)
8545 geometry_info.sigma=1.0;
8546 if ((flags & XiValue) == 0)
8547 geometry_info.xi=0.1*(*image)->columns;
8548 if ((flags & PsiValue) == 0)
8549 geometry_info.psi=0.1*(*image)->rows;
8550 vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8551 (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8552 0.5),&(*image)->exception);
8553 if (vignette_image != (Image *) NULL)
8554 {
8555 *image=DestroyImage(*image);
8556 *image=vignette_image;
8557 }
8558 CatchException(&(*image)->exception);
8559 XSetCursorState(display,windows,MagickFalse);
8560 if (windows->image.orphan != MagickFalse)
8561 break;
8562 XConfigureImageColormap(display,resource_info,windows,*image);
8563 (void) XConfigureImage(display,resource_info,windows,*image);
8564 break;
8565 }
8566 case WaveCommand:
8567 {
8568 Image
8569 *wave_image;
8570
8571 static char
8572 geometry[MaxTextExtent] = "25x150";
8573
8574 /*
8575 Query user for the wave geometry.
8576 */
8577 (void) XDialogWidget(display,windows,"Wave",
8578 "Enter the amplitude and length of the wave:",geometry);
8579 if (*geometry == '\0')
8580 break;
8581 /*
8582 Alter an image along a sine wave.
8583 */
8584 XSetCursorState(display,windows,MagickTrue);
8585 XCheckRefreshWindows(display,windows);
8586 flags=ParseGeometry(geometry,&geometry_info);
8587 if ((flags & SigmaValue) == 0)
8588 geometry_info.sigma=1.0;
8589 wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8590 &(*image)->exception);
8591 if (wave_image != (Image *) NULL)
8592 {
8593 *image=DestroyImage(*image);
8594 *image=wave_image;
8595 }
8596 CatchException(&(*image)->exception);
8597 XSetCursorState(display,windows,MagickFalse);
8598 if (windows->image.orphan != MagickFalse)
8599 break;
8600 XConfigureImageColormap(display,resource_info,windows,*image);
8601 (void) XConfigureImage(display,resource_info,windows,*image);
8602 break;
8603 }
8604 case OilPaintCommand:
8605 {
8606 Image
8607 *paint_image;
8608
8609 static char
8610 radius[MaxTextExtent] = "0";
8611
8612 /*
8613 Query user for circular neighborhood radius.
8614 */
8615 (void) XDialogWidget(display,windows,"Oil Paint",
8616 "Enter the mask radius:",radius);
8617 if (*radius == '\0')
8618 break;
8619 /*
8620 OilPaint image scanlines.
8621 */
8622 XSetCursorState(display,windows,MagickTrue);
8623 XCheckRefreshWindows(display,windows);
8624 flags=ParseGeometry(radius,&geometry_info);
8625 paint_image=OilPaintImage(*image,geometry_info.rho,&(*image)->exception);
8626 if (paint_image != (Image *) NULL)
8627 {
8628 *image=DestroyImage(*image);
8629 *image=paint_image;
8630 }
8631 CatchException(&(*image)->exception);
8632 XSetCursorState(display,windows,MagickFalse);
8633 if (windows->image.orphan != MagickFalse)
8634 break;
8635 XConfigureImageColormap(display,resource_info,windows,*image);
8636 (void) XConfigureImage(display,resource_info,windows,*image);
8637 break;
8638 }
8639 case CharcoalDrawCommand:
8640 {
8641 Image
8642 *charcoal_image;
8643
8644 static char
8645 radius[MaxTextExtent] = "0x1";
8646
8647 /*
8648 Query user for charcoal radius.
8649 */
8650 (void) XDialogWidget(display,windows,"Charcoal Draw",
8651 "Enter the charcoal radius and sigma:",radius);
8652 if (*radius == '\0')
8653 break;
8654 /*
8655 Charcoal the image.
8656 */
8657 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8658 XSetCursorState(display,windows,MagickTrue);
8659 XCheckRefreshWindows(display,windows);
8660 flags=ParseGeometry(radius,&geometry_info);
8661 if ((flags & SigmaValue) == 0)
8662 geometry_info.sigma=geometry_info.rho;
8663 charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8664 &(*image)->exception);
8665 if (charcoal_image != (Image *) NULL)
8666 {
8667 *image=DestroyImage(*image);
8668 *image=charcoal_image;
8669 }
8670 CatchException(&(*image)->exception);
8671 XSetCursorState(display,windows,MagickFalse);
8672 if (windows->image.orphan != MagickFalse)
8673 break;
8674 XConfigureImageColormap(display,resource_info,windows,*image);
8675 (void) XConfigureImage(display,resource_info,windows,*image);
8676 break;
8677 }
8678 case AnnotateCommand:
8679 {
8680 /*
8681 Annotate the image with text.
8682 */
8683 status=XAnnotateEditImage(display,resource_info,windows,*image);
8684 if (status == MagickFalse)
8685 {
8686 XNoticeWidget(display,windows,"Unable to annotate X image",
8687 (*image)->filename);
8688 break;
8689 }
8690 break;
8691 }
8692 case DrawCommand:
8693 {
8694 /*
8695 Draw image.
8696 */
8697 status=XDrawEditImage(display,resource_info,windows,image);
8698 if (status == MagickFalse)
8699 {
8700 XNoticeWidget(display,windows,"Unable to draw on the X image",
8701 (*image)->filename);
8702 break;
8703 }
8704 break;
8705 }
8706 case ColorCommand:
8707 {
8708 /*
8709 Color edit.
8710 */
8711 status=XColorEditImage(display,resource_info,windows,image);
8712 if (status == MagickFalse)
8713 {
8714 XNoticeWidget(display,windows,"Unable to pixel edit X image",
8715 (*image)->filename);
8716 break;
8717 }
8718 break;
8719 }
8720 case MatteCommand:
8721 {
8722 /*
8723 Matte edit.
8724 */
8725 status=XMatteEditImage(display,resource_info,windows,image);
8726 if (status == MagickFalse)
8727 {
8728 XNoticeWidget(display,windows,"Unable to matte edit X image",
8729 (*image)->filename);
8730 break;
8731 }
8732 break;
8733 }
8734 case CompositeCommand:
8735 {
8736 /*
8737 Composite image.
8738 */
8739 status=XCompositeImage(display,resource_info,windows,*image);
8740 if (status == MagickFalse)
8741 {
8742 XNoticeWidget(display,windows,"Unable to composite X image",
8743 (*image)->filename);
8744 break;
8745 }
8746 break;
8747 }
8748 case AddBorderCommand:
8749 {
8750 Image
8751 *border_image;
8752
8753 static char
8754 geometry[MaxTextExtent] = "6x6";
8755
8756 /*
8757 Query user for border color and geometry.
8758 */
8759 XColorBrowserWidget(display,windows,"Select",color);
8760 if (*color == '\0')
8761 break;
8762 (void) XDialogWidget(display,windows,"Add Border",
8763 "Enter border geometry:",geometry);
8764 if (*geometry == '\0')
8765 break;
8766 /*
8767 Add a border to the image.
8768 */
8769 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8770 XSetCursorState(display,windows,MagickTrue);
8771 XCheckRefreshWindows(display,windows);
8772 (void) QueryColorDatabase(color,&(*image)->border_color,
8773 &(*image)->exception);
8774 (void) ParsePageGeometry(*image,geometry,&page_geometry,
8775 &(*image)->exception);
8776 border_image=BorderImage(*image,&page_geometry,&(*image)->exception);
8777 if (border_image != (Image *) NULL)
8778 {
8779 *image=DestroyImage(*image);
8780 *image=border_image;
8781 }
8782 CatchException(&(*image)->exception);
8783 XSetCursorState(display,windows,MagickFalse);
8784 if (windows->image.orphan != MagickFalse)
8785 break;
8786 windows->image.window_changes.width=(int) (*image)->columns;
8787 windows->image.window_changes.height=(int) (*image)->rows;
8788 XConfigureImageColormap(display,resource_info,windows,*image);
8789 (void) XConfigureImage(display,resource_info,windows,*image);
8790 break;
8791 }
8792 case AddFrameCommand:
8793 {
8794 FrameInfo
8795 frame_info;
8796
8797 Image
8798 *frame_image;
8799
8800 static char
8801 geometry[MaxTextExtent] = "6x6";
8802
8803 /*
8804 Query user for frame color and geometry.
8805 */
8806 XColorBrowserWidget(display,windows,"Select",color);
8807 if (*color == '\0')
8808 break;
8809 (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8810 geometry);
8811 if (*geometry == '\0')
8812 break;
8813 /*
8814 Surround image with an ornamental border.
8815 */
8816 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8817 XSetCursorState(display,windows,MagickTrue);
8818 XCheckRefreshWindows(display,windows);
8819 (void) QueryColorDatabase(color,&(*image)->matte_color,
8820 &(*image)->exception);
8821 (void) ParsePageGeometry(*image,geometry,&page_geometry,
8822 &(*image)->exception);
8823 frame_info.width=page_geometry.width;
8824 frame_info.height=page_geometry.height;
8825 frame_info.outer_bevel=page_geometry.x;
8826 frame_info.inner_bevel=page_geometry.y;
8827 frame_info.x=(ssize_t) frame_info.width;
8828 frame_info.y=(ssize_t) frame_info.height;
8829 frame_info.width=(*image)->columns+2*frame_info.width;
8830 frame_info.height=(*image)->rows+2*frame_info.height;
8831 frame_image=FrameImage(*image,&frame_info,&(*image)->exception);
8832 if (frame_image != (Image *) NULL)
8833 {
8834 *image=DestroyImage(*image);
8835 *image=frame_image;
8836 }
8837 CatchException(&(*image)->exception);
8838 XSetCursorState(display,windows,MagickFalse);
8839 if (windows->image.orphan != MagickFalse)
8840 break;
8841 windows->image.window_changes.width=(int) (*image)->columns;
8842 windows->image.window_changes.height=(int) (*image)->rows;
8843 XConfigureImageColormap(display,resource_info,windows,*image);
8844 (void) XConfigureImage(display,resource_info,windows,*image);
8845 break;
8846 }
8847 case CommentCommand:
8848 {
8849 const char
8850 *value;
8851
8852 FILE
8853 *file;
8854
8855 int
8856 unique_file;
8857
8858 /*
8859 Edit image comment.
8860 */
8861 unique_file=AcquireUniqueFileResource(image_info->filename);
8862 if (unique_file == -1)
8863 {
8864 XNoticeWidget(display,windows,"Unable to edit image comment",
8865 image_info->filename);
8866 break;
8867 }
8868 value=GetImageProperty(*image,"comment");
8869 if (value == (char *) NULL)
8870 unique_file=close(unique_file)-1;
8871 else
8872 {
8873 const char
8874 *p;
8875
8876 file=fdopen(unique_file,"w");
8877 if (file == (FILE *) NULL)
8878 {
8879 XNoticeWidget(display,windows,"Unable to edit image comment",
8880 image_info->filename);
8881 break;
8882 }
8883 for (p=value; *p != '\0'; p++)
8884 (void) fputc((int) *p,file);
8885 (void) fputc('\n',file);
8886 (void) fclose(file);
8887 }
8888 XSetCursorState(display,windows,MagickTrue);
8889 XCheckRefreshWindows(display,windows);
8890 status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8891 &(*image)->exception);
8892 if (status == MagickFalse)
8893 XNoticeWidget(display,windows,"Unable to edit image comment",
8894 (char *) NULL);
8895 else
8896 {
8897 char
8898 *comment;
8899
8900 comment=FileToString(image_info->filename,~0UL,&(*image)->exception);
8901 if (comment != (char *) NULL)
8902 {
8903 (void) SetImageProperty(*image,"comment",comment);
8904 (*image)->taint=MagickTrue;
8905 }
8906 }
8907 (void) RelinquishUniqueFileResource(image_info->filename);
8908 XSetCursorState(display,windows,MagickFalse);
8909 break;
8910 }
8911 case LaunchCommand:
8912 {
8913 /*
8914 Launch program.
8915 */
8916 XSetCursorState(display,windows,MagickTrue);
8917 XCheckRefreshWindows(display,windows);
8918 (void) AcquireUniqueFilename(filename);
8919 (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
8920 filename);
8921 status=WriteImage(image_info,*image);
8922 if (status == MagickFalse)
8923 XNoticeWidget(display,windows,"Unable to launch image editor",
8924 (char *) NULL);
8925 else
8926 {
8927 nexus=ReadImage(resource_info->image_info,&(*image)->exception);
8928 CatchException(&(*image)->exception);
8929 XClientMessage(display,windows->image.id,windows->im_protocols,
8930 windows->im_next_image,CurrentTime);
8931 }
8932 (void) RelinquishUniqueFileResource(filename);
8933 XSetCursorState(display,windows,MagickFalse);
8934 break;
8935 }
8936 case RegionOfInterestCommand:
8937 {
8938 /*
8939 Apply an image processing technique to a region of interest.
8940 */
8941 (void) XROIImage(display,resource_info,windows,image);
8942 break;
8943 }
8944 case InfoCommand:
8945 break;
8946 case ZoomCommand:
8947 {
8948 /*
8949 Zoom image.
8950 */
8951 if (windows->magnify.mapped != MagickFalse)
8952 (void) XRaiseWindow(display,windows->magnify.id);
8953 else
8954 {
8955 /*
8956 Make magnify image.
8957 */
8958 XSetCursorState(display,windows,MagickTrue);
8959 (void) XMapRaised(display,windows->magnify.id);
8960 XSetCursorState(display,windows,MagickFalse);
8961 }
8962 break;
8963 }
8964 case ShowPreviewCommand:
8965 {
8966 char
8967 **previews;
8968
8969 Image
8970 *preview_image;
8971
8972 static char
8973 preview_type[MaxTextExtent] = "Gamma";
8974
8975 /*
8976 Select preview type from menu.
8977 */
8978 previews=GetCommandOptions(MagickPreviewOptions);
8979 if (previews == (char **) NULL)
8980 break;
8981 XListBrowserWidget(display,windows,&windows->widget,
8982 (const char **) previews,"Preview",
8983 "Select an enhancement, effect, or F/X:",preview_type);
8984 previews=DestroyStringList(previews);
8985 if (*preview_type == '\0')
8986 break;
8987 /*
8988 Show image preview.
8989 */
8990 XSetCursorState(display,windows,MagickTrue);
8991 XCheckRefreshWindows(display,windows);
8992 image_info->preview_type=(PreviewType)
8993 ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
8994 image_info->group=(ssize_t) windows->image.id;
8995 (void) DeleteImageProperty(*image,"label");
8996 (void) SetImageProperty(*image,"label","Preview");
8997 (void) AcquireUniqueFilename(filename);
8998 (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
8999 filename);
9000 status=WriteImage(image_info,*image);
9001 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9002 preview_image=ReadImage(image_info,&(*image)->exception);
9003 (void) RelinquishUniqueFileResource(filename);
9004 if (preview_image == (Image *) NULL)
9005 break;
9006 (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9007 filename);
9008 status=WriteImage(image_info,preview_image);
9009 preview_image=DestroyImage(preview_image);
9010 if (status == MagickFalse)
9011 XNoticeWidget(display,windows,"Unable to show image preview",
9012 (*image)->filename);
9013 XDelay(display,1500);
9014 XSetCursorState(display,windows,MagickFalse);
9015 break;
9016 }
9017 case ShowHistogramCommand:
9018 {
9019 Image
9020 *histogram_image;
9021
9022 /*
9023 Show image histogram.
9024 */
9025 XSetCursorState(display,windows,MagickTrue);
9026 XCheckRefreshWindows(display,windows);
9027 image_info->group=(ssize_t) windows->image.id;
9028 (void) DeleteImageProperty(*image,"label");
9029 (void) SetImageProperty(*image,"label","Histogram");
9030 (void) AcquireUniqueFilename(filename);
9031 (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9032 filename);
9033 status=WriteImage(image_info,*image);
9034 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9035 histogram_image=ReadImage(image_info,&(*image)->exception);
9036 (void) RelinquishUniqueFileResource(filename);
9037 if (histogram_image == (Image *) NULL)
9038 break;
9039 (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9040 "show:%s",filename);
9041 status=WriteImage(image_info,histogram_image);
9042 histogram_image=DestroyImage(histogram_image);
9043 if (status == MagickFalse)
9044 XNoticeWidget(display,windows,"Unable to show histogram",
9045 (*image)->filename);
9046 XDelay(display,1500);
9047 XSetCursorState(display,windows,MagickFalse);
9048 break;
9049 }
9050 case ShowMatteCommand:
9051 {
9052 Image
9053 *matte_image;
9054
9055 if ((*image)->matte == MagickFalse)
9056 {
9057 XNoticeWidget(display,windows,
9058 "Image does not have any matte information",(*image)->filename);
9059 break;
9060 }
9061 /*
9062 Show image matte.
9063 */
9064 XSetCursorState(display,windows,MagickTrue);
9065 XCheckRefreshWindows(display,windows);
9066 image_info->group=(ssize_t) windows->image.id;
9067 (void) DeleteImageProperty(*image,"label");
9068 (void) SetImageProperty(*image,"label","Matte");
9069 (void) AcquireUniqueFilename(filename);
9070 (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9071 filename);
9072 status=WriteImage(image_info,*image);
9073 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9074 matte_image=ReadImage(image_info,&(*image)->exception);
9075 (void) RelinquishUniqueFileResource(filename);
9076 if (matte_image == (Image *) NULL)
9077 break;
9078 (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9079 filename);
9080 status=WriteImage(image_info,matte_image);
9081 matte_image=DestroyImage(matte_image);
9082 if (status == MagickFalse)
9083 XNoticeWidget(display,windows,"Unable to show matte",
9084 (*image)->filename);
9085 XDelay(display,1500);
9086 XSetCursorState(display,windows,MagickFalse);
9087 break;
9088 }
9089 case BackgroundCommand:
9090 {
9091 /*
9092 Background image.
9093 */
9094 status=XBackgroundImage(display,resource_info,windows,image);
9095 if (status == MagickFalse)
9096 break;
9097 nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9098 if (nexus != (Image *) NULL)
9099 XClientMessage(display,windows->image.id,windows->im_protocols,
9100 windows->im_next_image,CurrentTime);
9101 break;
9102 }
9103 case SlideShowCommand:
9104 {
9105 static char
9106 delay[MaxTextExtent] = "5";
9107
9108 /*
9109 Display next image after pausing.
9110 */
9111 (void) XDialogWidget(display,windows,"Slide Show",
9112 "Pause how many 1/100ths of a second between images:",delay);
9113 if (*delay == '\0')
9114 break;
9115 resource_info->delay=StringToUnsignedLong(delay);
9116 XClientMessage(display,windows->image.id,windows->im_protocols,
9117 windows->im_next_image,CurrentTime);
9118 break;
9119 }
9120 case PreferencesCommand:
9121 {
9122 /*
9123 Set user preferences.
9124 */
9125 status=XPreferencesWidget(display,resource_info,windows);
9126 if (status == MagickFalse)
9127 break;
9128 nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9129 if (nexus != (Image *) NULL)
9130 XClientMessage(display,windows->image.id,windows->im_protocols,
9131 windows->im_next_image,CurrentTime);
9132 break;
9133 }
9134 case HelpCommand:
9135 {
9136 /*
9137 User requested help.
9138 */
9139 XTextViewHelp(display,resource_info,windows,MagickFalse,
9140 "Help Viewer - Display",DisplayHelp);
9141 break;
9142 }
9143 case BrowseDocumentationCommand:
9144 {
9145 Atom
9146 mozilla_atom;
9147
9148 Window
9149 mozilla_window,
9150 root_window;
9151
9152 /*
9153 Browse the ImageMagick documentation.
9154 */
9155 root_window=XRootWindow(display,XDefaultScreen(display));
9156 mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9157 mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9158 if (mozilla_window != (Window) NULL)
9159 {
9160 char
9161 command[MaxTextExtent];
9162
9163 /*
9164 Display documentation using Netscape remote control.
9165 */
9166 (void) FormatLocaleString(command,MaxTextExtent,
9167 "openurl(%s,new-tab)",MagickAuthoritativeURL);
9168 mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9169 (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9170 8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9171 XSetCursorState(display,windows,MagickFalse);
9172 break;
9173 }
9174 XSetCursorState(display,windows,MagickTrue);
9175 XCheckRefreshWindows(display,windows);
9176 status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9177 &(*image)->exception);
9178 if (status == MagickFalse)
9179 XNoticeWidget(display,windows,"Unable to browse documentation",
9180 (char *) NULL);
9181 XDelay(display,1500);
9182 XSetCursorState(display,windows,MagickFalse);
9183 break;
9184 }
9185 case VersionCommand:
9186 {
9187 XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9188 GetMagickCopyright());
9189 break;
9190 }
9191 case SaveToUndoBufferCommand:
9192 break;
9193 default:
9194 {
9195 (void) XBell(display,0);
9196 break;
9197 }
9198 }
9199 image_info=DestroyImageInfo(image_info);
9200 return(nexus);
9201}
9202
9203/*
9204%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9205% %
9206% %
9207% %
9208+ X M a g n i f y I m a g e %
9209% %
9210% %
9211% %
9212%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9213%
9214% XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9215% The magnified portion is displayed in a separate window.
9216%
9217% The format of the XMagnifyImage method is:
9218%
9219% void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9220%
9221% A description of each parameter follows:
9222%
9223% o display: Specifies a connection to an X server; returned from
9224% XOpenDisplay.
9225%
9226% o windows: Specifies a pointer to a XWindows structure.
9227%
9228% o event: Specifies a pointer to a XEvent structure. If it is NULL,
9229% the entire image is refreshed.
9230%
9231*/
9232static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9233{
9234 char
9235 text[MaxTextExtent];
9236
9237 int
9238 x,
9239 y;
9240
9241 size_t
9242 state;
9243
9244 /*
9245 Update magnified image until the mouse button is released.
9246 */
9247 (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9248 state=DefaultState;
9249 x=event->xbutton.x;
9250 y=event->xbutton.y;
9251 windows->magnify.x=(int) windows->image.x+x;
9252 windows->magnify.y=(int) windows->image.y+y;
9253 do
9254 {
9255 /*
9256 Map and unmap Info widget as text cursor crosses its boundaries.
9257 */
9258 if (windows->info.mapped != MagickFalse)
9259 {
9260 if ((x < (int) (windows->info.x+windows->info.width)) &&
9261 (y < (int) (windows->info.y+windows->info.height)))
9262 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9263 }
9264 else
9265 if ((x > (int) (windows->info.x+windows->info.width)) ||
9266 (y > (int) (windows->info.y+windows->info.height)))
9267 (void) XMapWindow(display,windows->info.id);
9268 if (windows->info.mapped != MagickFalse)
9269 {
9270 /*
9271 Display pointer position.
9272 */
9273 (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9274 windows->magnify.x,windows->magnify.y);
9275 XInfoWidget(display,windows,text);
9276 }
9277 /*
9278 Wait for next event.
9279 */
9280 XScreenEvent(display,windows,event);
9281 switch (event->type)
9282 {
9283 case ButtonPress:
9284 break;
9285 case ButtonRelease:
9286 {
9287 /*
9288 User has finished magnifying image.
9289 */
9290 x=event->xbutton.x;
9291 y=event->xbutton.y;
9292 state|=ExitState;
9293 break;
9294 }
9295 case Expose:
9296 break;
9297 case MotionNotify:
9298 {
9299 x=event->xmotion.x;
9300 y=event->xmotion.y;
9301 break;
9302 }
9303 default:
9304 break;
9305 }
9306 /*
9307 Check boundary conditions.
9308 */
9309 if (x < 0)
9310 x=0;
9311 else
9312 if (x >= (int) windows->image.width)
9313 x=(int) windows->image.width-1;
9314 if (y < 0)
9315 y=0;
9316 else
9317 if (y >= (int) windows->image.height)
9318 y=(int) windows->image.height-1;
9319 } while ((state & ExitState) == 0);
9320 /*
9321 Display magnified image.
9322 */
9323 XSetCursorState(display,windows,MagickFalse);
9324}
9325
9326/*
9327%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9328% %
9329% %
9330% %
9331+ X M a g n i f y W i n d o w C o m m a n d %
9332% %
9333% %
9334% %
9335%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9336%
9337% XMagnifyWindowCommand() moves the image within an Magnify window by one
9338% pixel as specified by the key symbol.
9339%
9340% The format of the XMagnifyWindowCommand method is:
9341%
9342% void XMagnifyWindowCommand(Display *display,XWindows *windows,
9343% const MagickStatusType state,const KeySym key_symbol)
9344%
9345% A description of each parameter follows:
9346%
9347% o display: Specifies a connection to an X server; returned from
9348% XOpenDisplay.
9349%
9350% o windows: Specifies a pointer to a XWindows structure.
9351%
9352% o state: key mask.
9353%
9354% o key_symbol: Specifies a KeySym which indicates which side of the image
9355% to trim.
9356%
9357*/
9358static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9359 const MagickStatusType state,const KeySym key_symbol)
9360{
9361 unsigned int
9362 quantum;
9363
9364 /*
9365 User specified a magnify factor or position.
9366 */
9367 quantum=1;
9368 if ((state & Mod1Mask) != 0)
9369 quantum=10;
9370 switch ((int) key_symbol)
9371 {
9372 case QuitCommand:
9373 {
9374 (void) XWithdrawWindow(display,windows->magnify.id,
9375 windows->magnify.screen);
9376 break;
9377 }
9378 case XK_Home:
9379 case XK_KP_Home:
9380 {
9381 windows->magnify.x=(int) windows->image.width/2;
9382 windows->magnify.y=(int) windows->image.height/2;
9383 break;
9384 }
9385 case XK_Left:
9386 case XK_KP_Left:
9387 {
9388 if (windows->magnify.x > 0)
9389 windows->magnify.x-=quantum;
9390 break;
9391 }
9392 case XK_Up:
9393 case XK_KP_Up:
9394 {
9395 if (windows->magnify.y > 0)
9396 windows->magnify.y-=quantum;
9397 break;
9398 }
9399 case XK_Right:
9400 case XK_KP_Right:
9401 {
9402 if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9403 windows->magnify.x+=quantum;
9404 break;
9405 }
9406 case XK_Down:
9407 case XK_KP_Down:
9408 {
9409 if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9410 windows->magnify.y+=quantum;
9411 break;
9412 }
9413 case XK_0:
9414 case XK_1:
9415 case XK_2:
9416 case XK_3:
9417 case XK_4:
9418 case XK_5:
9419 case XK_6:
9420 case XK_7:
9421 case XK_8:
9422 case XK_9:
9423 {
9424 windows->magnify.data=(key_symbol-XK_0);
9425 break;
9426 }
9427 case XK_KP_0:
9428 case XK_KP_1:
9429 case XK_KP_2:
9430 case XK_KP_3:
9431 case XK_KP_4:
9432 case XK_KP_5:
9433 case XK_KP_6:
9434 case XK_KP_7:
9435 case XK_KP_8:
9436 case XK_KP_9:
9437 {
9438 windows->magnify.data=(key_symbol-XK_KP_0);
9439 break;
9440 }
9441 default:
9442 break;
9443 }
9444 XMakeMagnifyImage(display,windows);
9445}
9446
9447/*
9448%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9449% %
9450% %
9451% %
9452+ X M a k e P a n I m a g e %
9453% %
9454% %
9455% %
9456%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9457%
9458% XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9459% icon window.
9460%
9461% The format of the XMakePanImage method is:
9462%
9463% void XMakePanImage(Display *display,XResourceInfo *resource_info,
9464% XWindows *windows,Image *image)
9465%
9466% A description of each parameter follows:
9467%
9468% o display: Specifies a connection to an X server; returned from
9469% XOpenDisplay.
9470%
9471% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9472%
9473% o windows: Specifies a pointer to a XWindows structure.
9474%
9475% o image: the image.
9476%
9477*/
9478static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9479 XWindows *windows,Image *image)
9480{
9481 MagickStatusType
9482 status;
9483
9484 /*
9485 Create and display image for panning icon.
9486 */
9487 XSetCursorState(display,windows,MagickTrue);
9488 XCheckRefreshWindows(display,windows);
9489 windows->pan.x=(int) windows->image.x;
9490 windows->pan.y=(int) windows->image.y;
9491 status=XMakeImage(display,resource_info,&windows->pan,image,
9492 windows->pan.width,windows->pan.height);
9493 if (status == MagickFalse)
9494 ThrowXWindowFatalException(XServerFatalError,image->exception.reason,
9495 image->exception.description);
9496 (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9497 windows->pan.pixmap);
9498 (void) XClearWindow(display,windows->pan.id);
9499 XDrawPanRectangle(display,windows);
9500 XSetCursorState(display,windows,MagickFalse);
9501}
9502
9503/*
9504%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9505% %
9506% %
9507% %
9508+ X M a t t a E d i t I m a g e %
9509% %
9510% %
9511% %
9512%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9513%
9514% XMatteEditImage() allows the user to interactively change the Matte channel
9515% of an image. If the image is PseudoClass it is promoted to DirectClass
9516% before the matte information is stored.
9517%
9518% The format of the XMatteEditImage method is:
9519%
9520% MagickBooleanType XMatteEditImage(Display *display,
9521% XResourceInfo *resource_info,XWindows *windows,Image **image)
9522%
9523% A description of each parameter follows:
9524%
9525% o display: Specifies a connection to an X server; returned from
9526% XOpenDisplay.
9527%
9528% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9529%
9530% o windows: Specifies a pointer to a XWindows structure.
9531%
9532% o image: the image; returned from ReadImage.
9533%
9534*/
9535static MagickBooleanType XMatteEditImage(Display *display,
9536 XResourceInfo *resource_info,XWindows *windows,Image **image)
9537{
9538 const char
9539 *const MatteEditMenu[] =
9540 {
9541 "Method",
9542 "Border Color",
9543 "Fuzz",
9544 "Matte Value",
9545 "Undo",
9546 "Help",
9547 "Dismiss",
9548 (char *) NULL
9549 };
9550
9551 static char
9552 matte[MaxTextExtent] = "0";
9553
9554 static const ModeType
9555 MatteEditCommands[] =
9556 {
9557 MatteEditMethod,
9558 MatteEditBorderCommand,
9559 MatteEditFuzzCommand,
9560 MatteEditValueCommand,
9561 MatteEditUndoCommand,
9562 MatteEditHelpCommand,
9563 MatteEditDismissCommand
9564 };
9565
9566 static PaintMethod
9567 method = PointMethod;
9568
9569 static XColor
9570 border_color = { 0, 0, 0, 0, 0, 0 };
9571
9572 char
9573 command[MaxTextExtent],
9574 text[MaxTextExtent] = "";
9575
9576 Cursor
9577 cursor;
9578
9579 int
9580 entry,
9581 id,
9582 x,
9583 x_offset,
9584 y,
9585 y_offset;
9586
9587 int
9588 i;
9589
9591 *q;
9592
9593 unsigned int
9594 height,
9595 width;
9596
9597 size_t
9598 state;
9599
9600 XEvent
9601 event;
9602
9603 /*
9604 Map Command widget.
9605 */
9606 (void) CloneString(&windows->command.name,"Matte Edit");
9607 windows->command.data=4;
9608 (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9609 (void) XMapRaised(display,windows->command.id);
9610 XClientMessage(display,windows->image.id,windows->im_protocols,
9611 windows->im_update_widget,CurrentTime);
9612 /*
9613 Make cursor.
9614 */
9615 cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9616 resource_info->background_color,resource_info->foreground_color);
9617 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9618 /*
9619 Track pointer until button 1 is pressed.
9620 */
9621 XQueryPosition(display,windows->image.id,&x,&y);
9622 (void) XSelectInput(display,windows->image.id,
9623 windows->image.attributes.event_mask | PointerMotionMask);
9624 state=DefaultState;
9625 do
9626 {
9627 if (windows->info.mapped != MagickFalse)
9628 {
9629 /*
9630 Display pointer position.
9631 */
9632 (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9633 x+windows->image.x,y+windows->image.y);
9634 XInfoWidget(display,windows,text);
9635 }
9636 /*
9637 Wait for next event.
9638 */
9639 XScreenEvent(display,windows,&event);
9640 if (event.xany.window == windows->command.id)
9641 {
9642 /*
9643 Select a command from the Command widget.
9644 */
9645 id=XCommandWidget(display,windows,MatteEditMenu,&event);
9646 if (id < 0)
9647 {
9648 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9649 continue;
9650 }
9651 switch (MatteEditCommands[id])
9652 {
9653 case MatteEditMethod:
9654 {
9655 char
9656 **methods;
9657
9658 /*
9659 Select a method from the pop-up menu.
9660 */
9661 methods=GetCommandOptions(MagickMethodOptions);
9662 if (methods == (char **) NULL)
9663 break;
9664 entry=XMenuWidget(display,windows,MatteEditMenu[id],
9665 (const char **) methods,command);
9666 if (entry >= 0)
9667 method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9668 MagickFalse,methods[entry]);
9669 methods=DestroyStringList(methods);
9670 break;
9671 }
9672 case MatteEditBorderCommand:
9673 {
9674 const char
9675 *ColorMenu[MaxNumberPens];
9676
9677 int
9678 pen_number;
9679
9680 /*
9681 Initialize menu selections.
9682 */
9683 for (i=0; i < (int) (MaxNumberPens-2); i++)
9684 ColorMenu[i]=resource_info->pen_colors[i];
9685 ColorMenu[MaxNumberPens-2]="Browser...";
9686 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9687 /*
9688 Select a pen color from the pop-up menu.
9689 */
9690 pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9691 (const char **) ColorMenu,command);
9692 if (pen_number < 0)
9693 break;
9694 if (pen_number == (MaxNumberPens-2))
9695 {
9696 static char
9697 color_name[MaxTextExtent] = "gray";
9698
9699 /*
9700 Select a pen color from a dialog.
9701 */
9702 resource_info->pen_colors[pen_number]=color_name;
9703 XColorBrowserWidget(display,windows,"Select",color_name);
9704 if (*color_name == '\0')
9705 break;
9706 }
9707 /*
9708 Set border color.
9709 */
9710 (void) XParseColor(display,windows->map_info->colormap,
9711 resource_info->pen_colors[pen_number],&border_color);
9712 break;
9713 }
9714 case MatteEditFuzzCommand:
9715 {
9716 const char
9717 *const FuzzMenu[] =
9718 {
9719 "0%",
9720 "2%",
9721 "5%",
9722 "10%",
9723 "15%",
9724 "Dialog...",
9725 (char *) NULL,
9726 };
9727
9728 static char
9729 fuzz[MaxTextExtent];
9730
9731 /*
9732 Select a command from the pop-up menu.
9733 */
9734 entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9735 command);
9736 if (entry < 0)
9737 break;
9738 if (entry != 5)
9739 {
9740 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9741 QuantumRange+1.0);
9742 break;
9743 }
9744 (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9745 (void) XDialogWidget(display,windows,"Ok",
9746 "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9747 if (*fuzz == '\0')
9748 break;
9749 (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9750 (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9751 1.0);
9752 break;
9753 }
9754 case MatteEditValueCommand:
9755 {
9756 const char
9757 *const MatteMenu[] =
9758 {
9759 "Opaque",
9760 "Transparent",
9761 "Dialog...",
9762 (char *) NULL,
9763 };
9764
9765 static char
9766 message[MaxTextExtent];
9767
9768 /*
9769 Select a command from the pop-up menu.
9770 */
9771 entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9772 command);
9773 if (entry < 0)
9774 break;
9775 if (entry != 2)
9776 {
9777 (void) FormatLocaleString(matte,MaxTextExtent,"%g",
9778 (double) OpaqueOpacity);
9779 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9780 (void) FormatLocaleString(matte,MaxTextExtent,"%g",
9781 (double) TransparentOpacity);
9782 break;
9783 }
9784 (void) FormatLocaleString(message,MaxTextExtent,
9785 "Enter matte value (0 - " "%g" "):",(double) QuantumRange);
9786 (void) XDialogWidget(display,windows,"Matte",message,matte);
9787 if (*matte == '\0')
9788 break;
9789 break;
9790 }
9791 case MatteEditUndoCommand:
9792 {
9793 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9794 image);
9795 break;
9796 }
9797 case MatteEditHelpCommand:
9798 {
9799 XTextViewHelp(display,resource_info,windows,MagickFalse,
9800 "Help Viewer - Matte Edit",ImageMatteEditHelp);
9801 break;
9802 }
9803 case MatteEditDismissCommand:
9804 {
9805 /*
9806 Prematurely exit.
9807 */
9808 state|=EscapeState;
9809 state|=ExitState;
9810 break;
9811 }
9812 default:
9813 break;
9814 }
9815 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9816 continue;
9817 }
9818 switch (event.type)
9819 {
9820 case ButtonPress:
9821 {
9822 if (event.xbutton.button != Button1)
9823 break;
9824 if ((event.xbutton.window != windows->image.id) &&
9825 (event.xbutton.window != windows->magnify.id))
9826 break;
9827 /*
9828 Update matte data.
9829 */
9830 x=event.xbutton.x;
9831 y=event.xbutton.y;
9832 (void) XMagickCommand(display,resource_info,windows,
9833 SaveToUndoBufferCommand,image);
9834 state|=UpdateConfigurationState;
9835 break;
9836 }
9837 case ButtonRelease:
9838 {
9839 if (event.xbutton.button != Button1)
9840 break;
9841 if ((event.xbutton.window != windows->image.id) &&
9842 (event.xbutton.window != windows->magnify.id))
9843 break;
9844 /*
9845 Update colormap information.
9846 */
9847 x=event.xbutton.x;
9848 y=event.xbutton.y;
9849 XConfigureImageColormap(display,resource_info,windows,*image);
9850 (void) XConfigureImage(display,resource_info,windows,*image);
9851 XInfoWidget(display,windows,text);
9852 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9853 state&=(~UpdateConfigurationState);
9854 break;
9855 }
9856 case Expose:
9857 break;
9858 case KeyPress:
9859 {
9860 char
9861 command[MaxTextExtent];
9862
9863 KeySym
9864 key_symbol;
9865
9866 if (event.xkey.window == windows->magnify.id)
9867 {
9868 Window
9869 window;
9870
9871 window=windows->magnify.id;
9872 while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9873 }
9874 if (event.xkey.window != windows->image.id)
9875 break;
9876 /*
9877 Respond to a user key press.
9878 */
9879 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9880 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9881 switch ((int) key_symbol)
9882 {
9883 case XK_Escape:
9884 case XK_F20:
9885 {
9886 /*
9887 Prematurely exit.
9888 */
9889 state|=ExitState;
9890 break;
9891 }
9892 case XK_F1:
9893 case XK_Help:
9894 {
9895 XTextViewHelp(display,resource_info,windows,MagickFalse,
9896 "Help Viewer - Matte Edit",ImageMatteEditHelp);
9897 break;
9898 }
9899 default:
9900 {
9901 (void) XBell(display,0);
9902 break;
9903 }
9904 }
9905 break;
9906 }
9907 case MotionNotify:
9908 {
9909 /*
9910 Map and unmap Info widget as cursor crosses its boundaries.
9911 */
9912 x=event.xmotion.x;
9913 y=event.xmotion.y;
9914 if (windows->info.mapped != MagickFalse)
9915 {
9916 if ((x < (int) (windows->info.x+windows->info.width)) &&
9917 (y < (int) (windows->info.y+windows->info.height)))
9918 (void) XWithdrawWindow(display,windows->info.id,
9919 windows->info.screen);
9920 }
9921 else
9922 if ((x > (int) (windows->info.x+windows->info.width)) ||
9923 (y > (int) (windows->info.y+windows->info.height)))
9924 (void) XMapWindow(display,windows->info.id);
9925 break;
9926 }
9927 default:
9928 break;
9929 }
9930 if (event.xany.window == windows->magnify.id)
9931 {
9932 x=windows->magnify.x-windows->image.x;
9933 y=windows->magnify.y-windows->image.y;
9934 }
9935 x_offset=x;
9936 y_offset=y;
9937 if ((state & UpdateConfigurationState) != 0)
9938 {
9939 CacheView
9940 *image_view;
9941
9943 *exception;
9944
9945 int
9946 x,
9947 y;
9948
9949 /*
9950 Matte edit is relative to image configuration.
9951 */
9952 (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
9953 MagickTrue);
9954 XPutPixel(windows->image.ximage,x_offset,y_offset,
9955 windows->pixel_info->background_color.pixel);
9956 width=(unsigned int) (*image)->columns;
9957 height=(unsigned int) (*image)->rows;
9958 x=0;
9959 y=0;
9960 if (windows->image.crop_geometry != (char *) NULL)
9961 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
9962 &width,&height);
9963 x_offset=(int)
9964 (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
9965 y_offset=(int)
9966 (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
9967 if ((x_offset < 0) || (y_offset < 0))
9968 continue;
9969 if ((x_offset >= (int) (*image)->columns) ||
9970 (y_offset >= (int) (*image)->rows))
9971 continue;
9972 if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
9973 return(MagickFalse);
9974 (*image)->matte=MagickTrue;
9975 exception=(&(*image)->exception);
9976 image_view=AcquireAuthenticCacheView(*image,exception);
9977 switch (method)
9978 {
9979 case PointMethod:
9980 default:
9981 {
9982 /*
9983 Update matte information using point algorithm.
9984 */
9985 q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
9986 (ssize_t) y_offset,1,1,exception);
9987 if (q == (PixelPacket *) NULL)
9988 break;
9989 q->opacity=(Quantum) StringToLong(matte);
9990 (void) SyncCacheViewAuthenticPixels(image_view,exception);
9991 break;
9992 }
9993 case ReplaceMethod:
9994 {
9996 target;
9997
9998 /*
9999 Update matte information using replace algorithm.
10000 */
10001 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10002 (ssize_t) y_offset,&target,exception);
10003 for (y=0; y < (int) (*image)->rows; y++)
10004 {
10005 q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10006 (*image)->columns,1,&(*image)->exception);
10007 if (q == (PixelPacket *) NULL)
10008 break;
10009 for (x=0; x < (int) (*image)->columns; x++)
10010 {
10011 if (IsColorSimilar(*image,q,&target))
10012 q->opacity=(Quantum) StringToLong(matte);
10013 q++;
10014 }
10015 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10016 break;
10017 }
10018 break;
10019 }
10020 case FloodfillMethod:
10021 case FillToBorderMethod:
10022 {
10023 DrawInfo
10024 *draw_info;
10025
10027 target;
10028
10029 /*
10030 Update matte information using floodfill algorithm.
10031 */
10032 (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10033 (ssize_t) y_offset,&target,exception);
10034 if (method == FillToBorderMethod)
10035 {
10036 target.red=(MagickRealType)
10037 ScaleShortToQuantum(border_color.red);
10038 target.green=(MagickRealType)
10039 ScaleShortToQuantum(border_color.green);
10040 target.blue=(MagickRealType)
10041 ScaleShortToQuantum(border_color.blue);
10042 }
10043 draw_info=CloneDrawInfo(resource_info->image_info,
10044 (DrawInfo *) NULL);
10045 draw_info->fill.opacity=ClampToQuantum(StringToDouble(matte,
10046 (char **) NULL));
10047 (void) FloodfillPaintImage(*image,OpacityChannel,draw_info,&target,
10048 (ssize_t) x_offset,(ssize_t) y_offset,
10049 method == FloodfillMethod ? MagickFalse : MagickTrue);
10050 draw_info=DestroyDrawInfo(draw_info);
10051 break;
10052 }
10053 case ResetMethod:
10054 {
10055 /*
10056 Update matte information using reset algorithm.
10057 */
10058 if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
10059 return(MagickFalse);
10060 for (y=0; y < (int) (*image)->rows; y++)
10061 {
10062 q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10063 (*image)->columns,1,exception);
10064 if (q == (PixelPacket *) NULL)
10065 break;
10066 for (x=0; x < (int) (*image)->columns; x++)
10067 {
10068 q->opacity=(Quantum) StringToLong(matte);
10069 q++;
10070 }
10071 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10072 break;
10073 }
10074 if (StringToLong(matte) == OpaqueOpacity)
10075 (*image)->matte=MagickFalse;
10076 break;
10077 }
10078 }
10079 image_view=DestroyCacheView(image_view);
10080 state&=(~UpdateConfigurationState);
10081 }
10082 } while ((state & ExitState) == 0);
10083 (void) XSelectInput(display,windows->image.id,
10084 windows->image.attributes.event_mask);
10085 XSetCursorState(display,windows,MagickFalse);
10086 (void) XFreeCursor(display,cursor);
10087 return(MagickTrue);
10088}
10089
10090/*
10091%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10092% %
10093% %
10094% %
10095+ X O p e n I m a g e %
10096% %
10097% %
10098% %
10099%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10100%
10101% XOpenImage() loads an image from a file.
10102%
10103% The format of the XOpenImage method is:
10104%
10105% Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10106% XWindows *windows,const unsigned int command)
10107%
10108% A description of each parameter follows:
10109%
10110% o display: Specifies a connection to an X server; returned from
10111% XOpenDisplay.
10112%
10113% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10114%
10115% o windows: Specifies a pointer to a XWindows structure.
10116%
10117% o command: A value other than zero indicates that the file is selected
10118% from the command line argument list.
10119%
10120*/
10121static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10122 XWindows *windows,const MagickBooleanType command)
10123{
10124 const MagickInfo
10125 *magick_info;
10126
10128 *exception;
10129
10130 Image
10131 *nexus;
10132
10133 ImageInfo
10134 *image_info;
10135
10136 static char
10137 filename[MaxTextExtent] = "\0";
10138
10139 /*
10140 Request file name from user.
10141 */
10142 if (command == MagickFalse)
10143 XFileBrowserWidget(display,windows,"Open",filename);
10144 else
10145 {
10146 char
10147 **filelist,
10148 **files;
10149
10150 int
10151 count,
10152 status;
10153
10154 int
10155 i,
10156 j;
10157
10158 /*
10159 Select next image from the command line.
10160 */
10161 status=XGetCommand(display,windows->image.id,&files,&count);
10162 if (status == 0)
10163 ThrowXWindowException(XServerError,"UnableToGetProperty","...");
10164 filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10165 if (filelist == (char **) NULL)
10166 {
10167 (void) XFreeStringList(files);
10168 ThrowXWindowException(ResourceLimitError,
10169 "MemoryAllocationFailed","...");
10170 return((Image *) NULL);
10171 }
10172 j=0;
10173 for (i=1; i < count; i++)
10174 if (*files[i] != '-')
10175 filelist[j++]=files[i];
10176 filelist[j]=(char *) NULL;
10177 XListBrowserWidget(display,windows,&windows->widget,
10178 (const char **) filelist,"Load","Select Image to Load:",filename);
10179 filelist=(char **) RelinquishMagickMemory(filelist);
10180 (void) XFreeStringList(files);
10181 }
10182 if (*filename == '\0')
10183 return((Image *) NULL);
10184 image_info=CloneImageInfo(resource_info->image_info);
10185 (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10186 (void *) NULL);
10187 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10188 exception=AcquireExceptionInfo();
10189 (void) SetImageInfo(image_info,0,exception);
10190 if (LocaleCompare(image_info->magick,"X") == 0)
10191 {
10192 char
10193 seconds[MaxTextExtent];
10194
10195 /*
10196 User may want to delay the X server screen grab.
10197 */
10198 (void) CopyMagickString(seconds,"0",MaxTextExtent);
10199 (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10200 seconds);
10201 if (*seconds == '\0')
10202 return((Image *) NULL);
10203 XDelay(display,(size_t) (1000*StringToLong(seconds)));
10204 }
10205 magick_info=GetMagickInfo(image_info->magick,exception);
10206 if ((magick_info != (const MagickInfo *) NULL) &&
10207 (magick_info->raw != MagickFalse))
10208 {
10209 char
10210 geometry[MaxTextExtent];
10211
10212 /*
10213 Request image size from the user.
10214 */
10215 (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10216 if (image_info->size != (char *) NULL)
10217 (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10218 (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10219 geometry);
10220 (void) CloneString(&image_info->size,geometry);
10221 }
10222 /*
10223 Load the image.
10224 */
10225 XSetCursorState(display,windows,MagickTrue);
10226 XCheckRefreshWindows(display,windows);
10227 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10228 nexus=ReadImage(image_info,exception);
10229 CatchException(exception);
10230 XSetCursorState(display,windows,MagickFalse);
10231 if (nexus != (Image *) NULL)
10232 XClientMessage(display,windows->image.id,windows->im_protocols,
10233 windows->im_next_image,CurrentTime);
10234 else
10235 {
10236 char
10237 *text,
10238 **textlist;
10239
10240 /*
10241 Unknown image format.
10242 */
10243 text=FileToString(filename,~0UL,exception);
10244 if (text == (char *) NULL)
10245 return((Image *) NULL);
10246 textlist=StringToList(text);
10247 if (textlist != (char **) NULL)
10248 {
10249 char
10250 title[MaxTextExtent];
10251
10252 int
10253 i;
10254
10255 (void) FormatLocaleString(title,MaxTextExtent,
10256 "Unknown format: %s",filename);
10257 XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10258 (const char **) textlist);
10259 for (i=0; textlist[i] != (char *) NULL; i++)
10260 textlist[i]=DestroyString(textlist[i]);
10261 textlist=(char **) RelinquishMagickMemory(textlist);
10262 }
10263 text=DestroyString(text);
10264 }
10265 exception=DestroyExceptionInfo(exception);
10266 image_info=DestroyImageInfo(image_info);
10267 return(nexus);
10268}
10269
10270/*
10271%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10272% %
10273% %
10274% %
10275+ X P a n I m a g e %
10276% %
10277% %
10278% %
10279%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10280%
10281% XPanImage() pans the image until the mouse button is released.
10282%
10283% The format of the XPanImage method is:
10284%
10285% void XPanImage(Display *display,XWindows *windows,XEvent *event)
10286%
10287% A description of each parameter follows:
10288%
10289% o display: Specifies a connection to an X server; returned from
10290% XOpenDisplay.
10291%
10292% o windows: Specifies a pointer to a XWindows structure.
10293%
10294% o event: Specifies a pointer to a XEvent structure. If it is NULL,
10295% the entire image is refreshed.
10296%
10297*/
10298static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10299{
10300 char
10301 text[MaxTextExtent];
10302
10303 Cursor
10304 cursor;
10305
10306 MagickRealType
10307 x_factor,
10308 y_factor;
10309
10311 pan_info;
10312
10313 size_t
10314 state;
10315
10316 /*
10317 Define cursor.
10318 */
10319 if ((windows->image.ximage->width > (int) windows->image.width) &&
10320 (windows->image.ximage->height > (int) windows->image.height))
10321 cursor=XCreateFontCursor(display,XC_fleur);
10322 else
10323 if (windows->image.ximage->width > (int) windows->image.width)
10324 cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10325 else
10326 if (windows->image.ximage->height > (int) windows->image.height)
10327 cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10328 else
10329 cursor=XCreateFontCursor(display,XC_arrow);
10330 (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10331 /*
10332 Pan image as pointer moves until the mouse button is released.
10333 */
10334 x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10335 y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10336 pan_info.width=windows->pan.width*windows->image.width/
10337 windows->image.ximage->width;
10338 pan_info.height=windows->pan.height*windows->image.height/
10339 windows->image.ximage->height;
10340 pan_info.x=0;
10341 pan_info.y=0;
10342 state=UpdateConfigurationState;
10343 do
10344 {
10345 switch (event->type)
10346 {
10347 case ButtonPress:
10348 {
10349 /*
10350 User choose an initial pan location.
10351 */
10352 pan_info.x=(ssize_t) event->xbutton.x;
10353 pan_info.y=(ssize_t) event->xbutton.y;
10354 state|=UpdateConfigurationState;
10355 break;
10356 }
10357 case ButtonRelease:
10358 {
10359 /*
10360 User has finished panning the image.
10361 */
10362 pan_info.x=(ssize_t) event->xbutton.x;
10363 pan_info.y=(ssize_t) event->xbutton.y;
10364 state|=UpdateConfigurationState | ExitState;
10365 break;
10366 }
10367 case MotionNotify:
10368 {
10369 pan_info.x=(ssize_t) event->xmotion.x;
10370 pan_info.y=(ssize_t) event->xmotion.y;
10371 state|=UpdateConfigurationState;
10372 }
10373 default:
10374 break;
10375 }
10376 if ((state & UpdateConfigurationState) != 0)
10377 {
10378 /*
10379 Check boundary conditions.
10380 */
10381 if (pan_info.x < (ssize_t) (pan_info.width/2))
10382 pan_info.x=0;
10383 else
10384 pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10385 if (pan_info.x < 0)
10386 pan_info.x=0;
10387 else
10388 if ((int) (pan_info.x+windows->image.width) >
10389 windows->image.ximage->width)
10390 pan_info.x=(ssize_t)
10391 (windows->image.ximage->width-windows->image.width);
10392 if (pan_info.y < (ssize_t) (pan_info.height/2))
10393 pan_info.y=0;
10394 else
10395 pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10396 if (pan_info.y < 0)
10397 pan_info.y=0;
10398 else
10399 if ((int) (pan_info.y+windows->image.height) >
10400 windows->image.ximage->height)
10401 pan_info.y=(ssize_t)
10402 (windows->image.ximage->height-windows->image.height);
10403 if ((windows->image.x != (int) pan_info.x) ||
10404 (windows->image.y != (int) pan_info.y))
10405 {
10406 /*
10407 Display image pan offset.
10408 */
10409 windows->image.x=(int) pan_info.x;
10410 windows->image.y=(int) pan_info.y;
10411 (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10412 windows->image.width,windows->image.height,windows->image.x,
10413 windows->image.y);
10414 XInfoWidget(display,windows,text);
10415 /*
10416 Refresh Image window.
10417 */
10418 XDrawPanRectangle(display,windows);
10419 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10420 }
10421 state&=(~UpdateConfigurationState);
10422 }
10423 /*
10424 Wait for next event.
10425 */
10426 if ((state & ExitState) == 0)
10427 XScreenEvent(display,windows,event);
10428 } while ((state & ExitState) == 0);
10429 /*
10430 Restore cursor.
10431 */
10432 (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10433 (void) XFreeCursor(display,cursor);
10434 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10435}
10436
10437/*
10438%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10439% %
10440% %
10441% %
10442+ X P a s t e I m a g e %
10443% %
10444% %
10445% %
10446%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10447%
10448% XPasteImage() pastes an image previously saved with XCropImage in the X
10449% window image at a location the user chooses with the pointer.
10450%
10451% The format of the XPasteImage method is:
10452%
10453% MagickBooleanType XPasteImage(Display *display,
10454% XResourceInfo *resource_info,XWindows *windows,Image *image)
10455%
10456% A description of each parameter follows:
10457%
10458% o display: Specifies a connection to an X server; returned from
10459% XOpenDisplay.
10460%
10461% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10462%
10463% o windows: Specifies a pointer to a XWindows structure.
10464%
10465% o image: the image; returned from ReadImage.
10466%
10467*/
10468static MagickBooleanType XPasteImage(Display *display,
10469 XResourceInfo *resource_info,XWindows *windows,Image *image)
10470{
10471 const char
10472 *const PasteMenu[] =
10473 {
10474 "Operator",
10475 "Help",
10476 "Dismiss",
10477 (char *) NULL
10478 };
10479
10480 static const ModeType
10481 PasteCommands[] =
10482 {
10483 PasteOperatorsCommand,
10484 PasteHelpCommand,
10485 PasteDismissCommand
10486 };
10487
10488 static CompositeOperator
10489 compose = CopyCompositeOp;
10490
10491 char
10492 text[MaxTextExtent];
10493
10494 Cursor
10495 cursor;
10496
10497 Image
10498 *paste_image;
10499
10500 int
10501 entry,
10502 id,
10503 x,
10504 y;
10505
10506 MagickRealType
10507 scale_factor;
10508
10510 highlight_info,
10511 paste_info;
10512
10513 unsigned int
10514 height,
10515 width;
10516
10517 size_t
10518 state;
10519
10520 XEvent
10521 event;
10522
10523 /*
10524 Copy image.
10525 */
10526 if (resource_info->copy_image == (Image *) NULL)
10527 return(MagickFalse);
10528 paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,
10529 &image->exception);
10530 if (paste_image == (Image *) NULL)
10531 return(MagickFalse);
10532 /*
10533 Map Command widget.
10534 */
10535 (void) CloneString(&windows->command.name,"Paste");
10536 windows->command.data=1;
10537 (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10538 (void) XMapRaised(display,windows->command.id);
10539 XClientMessage(display,windows->image.id,windows->im_protocols,
10540 windows->im_update_widget,CurrentTime);
10541 /*
10542 Track pointer until button 1 is pressed.
10543 */
10544 XSetCursorState(display,windows,MagickFalse);
10545 XQueryPosition(display,windows->image.id,&x,&y);
10546 (void) XSelectInput(display,windows->image.id,
10547 windows->image.attributes.event_mask | PointerMotionMask);
10548 paste_info.x=(ssize_t) windows->image.x+x;
10549 paste_info.y=(ssize_t) windows->image.y+y;
10550 paste_info.width=0;
10551 paste_info.height=0;
10552 cursor=XCreateFontCursor(display,XC_ul_angle);
10553 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10554 state=DefaultState;
10555 do
10556 {
10557 if (windows->info.mapped != MagickFalse)
10558 {
10559 /*
10560 Display pointer position.
10561 */
10562 (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10563 (long) paste_info.x,(long) paste_info.y);
10564 XInfoWidget(display,windows,text);
10565 }
10566 highlight_info=paste_info;
10567 highlight_info.x=paste_info.x-windows->image.x;
10568 highlight_info.y=paste_info.y-windows->image.y;
10569 XHighlightRectangle(display,windows->image.id,
10570 windows->image.highlight_context,&highlight_info);
10571 /*
10572 Wait for next event.
10573 */
10574 XScreenEvent(display,windows,&event);
10575 XHighlightRectangle(display,windows->image.id,
10576 windows->image.highlight_context,&highlight_info);
10577 if (event.xany.window == windows->command.id)
10578 {
10579 /*
10580 Select a command from the Command widget.
10581 */
10582 id=XCommandWidget(display,windows,PasteMenu,&event);
10583 if (id < 0)
10584 continue;
10585 switch (PasteCommands[id])
10586 {
10587 case PasteOperatorsCommand:
10588 {
10589 char
10590 command[MaxTextExtent],
10591 **operators;
10592
10593 /*
10594 Select a command from the pop-up menu.
10595 */
10596 operators=GetCommandOptions(MagickComposeOptions);
10597 if (operators == (char **) NULL)
10598 break;
10599 entry=XMenuWidget(display,windows,PasteMenu[id],
10600 (const char **) operators,command);
10601 if (entry >= 0)
10602 compose=(CompositeOperator) ParseCommandOption(
10603 MagickComposeOptions,MagickFalse,operators[entry]);
10604 operators=DestroyStringList(operators);
10605 break;
10606 }
10607 case PasteHelpCommand:
10608 {
10609 XTextViewHelp(display,resource_info,windows,MagickFalse,
10610 "Help Viewer - Image Composite",ImagePasteHelp);
10611 break;
10612 }
10613 case PasteDismissCommand:
10614 {
10615 /*
10616 Prematurely exit.
10617 */
10618 state|=EscapeState;
10619 state|=ExitState;
10620 break;
10621 }
10622 default:
10623 break;
10624 }
10625 continue;
10626 }
10627 switch (event.type)
10628 {
10629 case ButtonPress:
10630 {
10631 if (resource_info->debug != MagickFalse)
10632 (void) LogMagickEvent(X11Event,GetMagickModule(),
10633 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10634 event.xbutton.button,event.xbutton.x,event.xbutton.y);
10635 if (event.xbutton.button != Button1)
10636 break;
10637 if (event.xbutton.window != windows->image.id)
10638 break;
10639 /*
10640 Paste rectangle is relative to image configuration.
10641 */
10642 width=(unsigned int) image->columns;
10643 height=(unsigned int) image->rows;
10644 x=0;
10645 y=0;
10646 if (windows->image.crop_geometry != (char *) NULL)
10647 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10648 &width,&height);
10649 scale_factor=(MagickRealType) windows->image.ximage->width/width;
10650 paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10651 scale_factor=(MagickRealType) windows->image.ximage->height/height;
10652 paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10653 (void) XCheckDefineCursor(display,windows->image.id,cursor);
10654 paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10655 paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10656 break;
10657 }
10658 case ButtonRelease:
10659 {
10660 if (resource_info->debug != MagickFalse)
10661 (void) LogMagickEvent(X11Event,GetMagickModule(),
10662 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10663 event.xbutton.button,event.xbutton.x,event.xbutton.y);
10664 if (event.xbutton.button != Button1)
10665 break;
10666 if (event.xbutton.window != windows->image.id)
10667 break;
10668 if ((paste_info.width != 0) && (paste_info.height != 0))
10669 {
10670 /*
10671 User has selected the location of the paste image.
10672 */
10673 paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10674 paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10675 state|=ExitState;
10676 }
10677 break;
10678 }
10679 case Expose:
10680 break;
10681 case KeyPress:
10682 {
10683 char
10684 command[MaxTextExtent];
10685
10686 KeySym
10687 key_symbol;
10688
10689 int
10690 length;
10691
10692 if (event.xkey.window != windows->image.id)
10693 break;
10694 /*
10695 Respond to a user key press.
10696 */
10697 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10698 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10699 *(command+length)='\0';
10700 if (resource_info->debug != MagickFalse)
10701 (void) LogMagickEvent(X11Event,GetMagickModule(),
10702 "Key press: 0x%lx (%s)",(long) key_symbol,command);
10703 switch ((int) key_symbol)
10704 {
10705 case XK_Escape:
10706 case XK_F20:
10707 {
10708 /*
10709 Prematurely exit.
10710 */
10711 paste_image=DestroyImage(paste_image);
10712 state|=EscapeState;
10713 state|=ExitState;
10714 break;
10715 }
10716 case XK_F1:
10717 case XK_Help:
10718 {
10719 (void) XSetFunction(display,windows->image.highlight_context,
10720 GXcopy);
10721 XTextViewHelp(display,resource_info,windows,MagickFalse,
10722 "Help Viewer - Image Composite",ImagePasteHelp);
10723 (void) XSetFunction(display,windows->image.highlight_context,
10724 GXinvert);
10725 break;
10726 }
10727 default:
10728 {
10729 (void) XBell(display,0);
10730 break;
10731 }
10732 }
10733 break;
10734 }
10735 case MotionNotify:
10736 {
10737 /*
10738 Map and unmap Info widget as text cursor crosses its boundaries.
10739 */
10740 x=event.xmotion.x;
10741 y=event.xmotion.y;
10742 if (windows->info.mapped != MagickFalse)
10743 {
10744 if ((x < (int) (windows->info.x+windows->info.width)) &&
10745 (y < (int) (windows->info.y+windows->info.height)))
10746 (void) XWithdrawWindow(display,windows->info.id,
10747 windows->info.screen);
10748 }
10749 else
10750 if ((x > (int) (windows->info.x+windows->info.width)) ||
10751 (y > (int) (windows->info.y+windows->info.height)))
10752 (void) XMapWindow(display,windows->info.id);
10753 paste_info.x=(ssize_t) windows->image.x+x;
10754 paste_info.y=(ssize_t) windows->image.y+y;
10755 break;
10756 }
10757 default:
10758 {
10759 if (resource_info->debug != MagickFalse)
10760 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10761 event.type);
10762 break;
10763 }
10764 }
10765 } while ((state & ExitState) == 0);
10766 (void) XSelectInput(display,windows->image.id,
10767 windows->image.attributes.event_mask);
10768 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10769 XSetCursorState(display,windows,MagickFalse);
10770 (void) XFreeCursor(display,cursor);
10771 if ((state & EscapeState) != 0)
10772 return(MagickTrue);
10773 /*
10774 Image pasting is relative to image configuration.
10775 */
10776 XSetCursorState(display,windows,MagickTrue);
10777 XCheckRefreshWindows(display,windows);
10778 width=(unsigned int) image->columns;
10779 height=(unsigned int) image->rows;
10780 x=0;
10781 y=0;
10782 if (windows->image.crop_geometry != (char *) NULL)
10783 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10784 scale_factor=(MagickRealType) width/windows->image.ximage->width;
10785 paste_info.x+=x;
10786 paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10787 paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10788 scale_factor=(MagickRealType) height/windows->image.ximage->height;
10789 paste_info.y+=y;
10790 paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10791 paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10792 /*
10793 Paste image with X Image window.
10794 */
10795 (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10796 paste_image=DestroyImage(paste_image);
10797 XSetCursorState(display,windows,MagickFalse);
10798 /*
10799 Update image colormap.
10800 */
10801 XConfigureImageColormap(display,resource_info,windows,image);
10802 (void) XConfigureImage(display,resource_info,windows,image);
10803 return(MagickTrue);
10804}
10805
10806/*
10807%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10808% %
10809% %
10810% %
10811+ X P r i n t I m a g e %
10812% %
10813% %
10814% %
10815%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10816%
10817% XPrintImage() prints an image to a Postscript printer.
10818%
10819% The format of the XPrintImage method is:
10820%
10821% MagickBooleanType XPrintImage(Display *display,
10822% XResourceInfo *resource_info,XWindows *windows,Image *image)
10823%
10824% A description of each parameter follows:
10825%
10826% o display: Specifies a connection to an X server; returned from
10827% XOpenDisplay.
10828%
10829% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10830%
10831% o windows: Specifies a pointer to a XWindows structure.
10832%
10833% o image: the image.
10834%
10835*/
10836static MagickBooleanType XPrintImage(Display *display,
10837 XResourceInfo *resource_info,XWindows *windows,Image *image)
10838{
10839 char
10840 filename[MaxTextExtent],
10841 geometry[MaxTextExtent];
10842
10843 const char
10844 *const PageSizes[] =
10845 {
10846 "Letter",
10847 "Tabloid",
10848 "Ledger",
10849 "Legal",
10850 "Statement",
10851 "Executive",
10852 "A3",
10853 "A4",
10854 "A5",
10855 "B4",
10856 "B5",
10857 "Folio",
10858 "Quarto",
10859 "10x14",
10860 (char *) NULL
10861 };
10862
10863 Image
10864 *print_image;
10865
10866 ImageInfo
10867 *image_info;
10868
10869 MagickStatusType
10870 status;
10871
10872 /*
10873 Request Postscript page geometry from user.
10874 */
10875 image_info=CloneImageInfo(resource_info->image_info);
10876 (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10877 if (image_info->page != (char *) NULL)
10878 (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10879 XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10880 "Select Postscript Page Geometry:",geometry);
10881 if (*geometry == '\0')
10882 return(MagickTrue);
10883 image_info->page=GetPageGeometry(geometry);
10884 /*
10885 Apply image transforms.
10886 */
10887 XSetCursorState(display,windows,MagickTrue);
10888 XCheckRefreshWindows(display,windows);
10889 print_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10890 if (print_image == (Image *) NULL)
10891 return(MagickFalse);
10892 (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
10893 windows->image.ximage->width,windows->image.ximage->height);
10894 (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10895 /*
10896 Print image.
10897 */
10898 (void) AcquireUniqueFilename(filename);
10899 (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
10900 filename);
10901 status=WriteImage(image_info,print_image);
10902 (void) RelinquishUniqueFileResource(filename);
10903 print_image=DestroyImage(print_image);
10904 image_info=DestroyImageInfo(image_info);
10905 XSetCursorState(display,windows,MagickFalse);
10906 return(status != 0 ? MagickTrue : MagickFalse);
10907}
10908
10909/*
10910%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10911% %
10912% %
10913% %
10914+ X R O I I m a g e %
10915% %
10916% %
10917% %
10918%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10919%
10920% XROIImage() applies an image processing technique to a region of interest.
10921%
10922% The format of the XROIImage method is:
10923%
10924% MagickBooleanType XROIImage(Display *display,
10925% XResourceInfo *resource_info,XWindows *windows,Image **image)
10926%
10927% A description of each parameter follows:
10928%
10929% o display: Specifies a connection to an X server; returned from
10930% XOpenDisplay.
10931%
10932% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10933%
10934% o windows: Specifies a pointer to a XWindows structure.
10935%
10936% o image: the image; returned from ReadImage.
10937%
10938*/
10939static MagickBooleanType XROIImage(Display *display,
10940 XResourceInfo *resource_info,XWindows *windows,Image **image)
10941{
10942#define ApplyMenus 7
10943
10944 const char
10945 *const ROIMenu[] =
10946 {
10947 "Help",
10948 "Dismiss",
10949 (char *) NULL
10950 },
10951 *const ApplyMenu[] =
10952 {
10953 "File",
10954 "Edit",
10955 "Transform",
10956 "Enhance",
10957 "Effects",
10958 "F/X",
10959 "Miscellany",
10960 "Help",
10961 "Dismiss",
10962 (char *) NULL
10963 },
10964 *const FileMenu[] =
10965 {
10966 "Save...",
10967 "Print...",
10968 (char *) NULL
10969 },
10970 *const EditMenu[] =
10971 {
10972 "Undo",
10973 "Redo",
10974 (char *) NULL
10975 },
10976 *const TransformMenu[] =
10977 {
10978 "Flop",
10979 "Flip",
10980 "Rotate Right",
10981 "Rotate Left",
10982 (char *) NULL
10983 },
10984 *const EnhanceMenu[] =
10985 {
10986 "Hue...",
10987 "Saturation...",
10988 "Brightness...",
10989 "Gamma...",
10990 "Spiff",
10991 "Dull",
10992 "Contrast Stretch...",
10993 "Sigmoidal Contrast...",
10994 "Normalize",
10995 "Equalize",
10996 "Negate",
10997 "Grayscale",
10998 "Map...",
10999 "Quantize...",
11000 (char *) NULL
11001 },
11002 *const EffectsMenu[] =
11003 {
11004 "Despeckle",
11005 "Emboss",
11006 "Reduce Noise",
11007 "Add Noise",
11008 "Sharpen...",
11009 "Blur...",
11010 "Threshold...",
11011 "Edge Detect...",
11012 "Spread...",
11013 "Shade...",
11014 "Raise...",
11015 "Segment...",
11016 (char *) NULL
11017 },
11018 *const FXMenu[] =
11019 {
11020 "Solarize...",
11021 "Sepia Tone...",
11022 "Swirl...",
11023 "Implode...",
11024 "Vignette...",
11025 "Wave...",
11026 "Oil Paint...",
11027 "Charcoal Draw...",
11028 (char *) NULL
11029 },
11030 *const MiscellanyMenu[] =
11031 {
11032 "Image Info",
11033 "Zoom Image",
11034 "Show Preview...",
11035 "Show Histogram",
11036 "Show Matte",
11037 (char *) NULL
11038 };
11039
11040 const char
11041 *const *Menus[ApplyMenus] =
11042 {
11043 FileMenu,
11044 EditMenu,
11045 TransformMenu,
11046 EnhanceMenu,
11047 EffectsMenu,
11048 FXMenu,
11049 MiscellanyMenu
11050 };
11051
11052 static const DisplayCommand
11053 ApplyCommands[] =
11054 {
11055 NullCommand,
11056 NullCommand,
11057 NullCommand,
11058 NullCommand,
11059 NullCommand,
11060 NullCommand,
11061 NullCommand,
11062 HelpCommand,
11063 QuitCommand
11064 },
11065 FileCommands[] =
11066 {
11067 SaveCommand,
11068 PrintCommand
11069 },
11070 EditCommands[] =
11071 {
11072 UndoCommand,
11073 RedoCommand
11074 },
11075 TransformCommands[] =
11076 {
11077 FlopCommand,
11078 FlipCommand,
11079 RotateRightCommand,
11080 RotateLeftCommand
11081 },
11082 EnhanceCommands[] =
11083 {
11084 HueCommand,
11085 SaturationCommand,
11086 BrightnessCommand,
11087 GammaCommand,
11088 SpiffCommand,
11089 DullCommand,
11090 ContrastStretchCommand,
11091 SigmoidalContrastCommand,
11092 NormalizeCommand,
11093 EqualizeCommand,
11094 NegateCommand,
11095 GrayscaleCommand,
11096 MapCommand,
11097 QuantizeCommand
11098 },
11099 EffectsCommands[] =
11100 {
11101 DespeckleCommand,
11102 EmbossCommand,
11103 ReduceNoiseCommand,
11104 AddNoiseCommand,
11105 SharpenCommand,
11106 BlurCommand,
11107 ThresholdCommand,
11108 EdgeDetectCommand,
11109 SpreadCommand,
11110 ShadeCommand,
11111 RaiseCommand,
11112 SegmentCommand
11113 },
11114 FXCommands[] =
11115 {
11116 SolarizeCommand,
11117 SepiaToneCommand,
11118 SwirlCommand,
11119 ImplodeCommand,
11120 VignetteCommand,
11121 WaveCommand,
11122 OilPaintCommand,
11123 CharcoalDrawCommand
11124 },
11125 MiscellanyCommands[] =
11126 {
11127 InfoCommand,
11128 ZoomCommand,
11129 ShowPreviewCommand,
11130 ShowHistogramCommand,
11131 ShowMatteCommand
11132 },
11133 ROICommands[] =
11134 {
11135 ROIHelpCommand,
11136 ROIDismissCommand
11137 };
11138
11139 static const DisplayCommand
11140 *Commands[ApplyMenus] =
11141 {
11142 FileCommands,
11143 EditCommands,
11144 TransformCommands,
11145 EnhanceCommands,
11146 EffectsCommands,
11147 FXCommands,
11148 MiscellanyCommands
11149 };
11150
11151 char
11152 command[MaxTextExtent],
11153 text[MaxTextExtent];
11154
11155 DisplayCommand
11156 display_command;
11157
11158 Cursor
11159 cursor;
11160
11161 Image
11162 *roi_image;
11163
11164 int
11165 entry,
11166 id,
11167 x,
11168 y;
11169
11170 MagickRealType
11171 scale_factor;
11172
11173 MagickProgressMonitor
11174 progress_monitor;
11175
11177 crop_info,
11178 highlight_info,
11179 roi_info;
11180
11181 unsigned int
11182 height,
11183 width;
11184
11185 size_t
11186 state;
11187
11188 XEvent
11189 event;
11190
11191 /*
11192 Map Command widget.
11193 */
11194 (void) CloneString(&windows->command.name,"ROI");
11195 windows->command.data=0;
11196 (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11197 (void) XMapRaised(display,windows->command.id);
11198 XClientMessage(display,windows->image.id,windows->im_protocols,
11199 windows->im_update_widget,CurrentTime);
11200 /*
11201 Track pointer until button 1 is pressed.
11202 */
11203 XQueryPosition(display,windows->image.id,&x,&y);
11204 (void) XSelectInput(display,windows->image.id,
11205 windows->image.attributes.event_mask | PointerMotionMask);
11206 crop_info.width=0;
11207 crop_info.height=0;
11208 crop_info.x=0;
11209 crop_info.y=0;
11210 roi_info.x=(ssize_t) windows->image.x+x;
11211 roi_info.y=(ssize_t) windows->image.y+y;
11212 roi_info.width=0;
11213 roi_info.height=0;
11214 cursor=XCreateFontCursor(display,XC_fleur);
11215 state=DefaultState;
11216 do
11217 {
11218 if (windows->info.mapped != MagickFalse)
11219 {
11220 /*
11221 Display pointer position.
11222 */
11223 (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11224 (long) roi_info.x,(long) roi_info.y);
11225 XInfoWidget(display,windows,text);
11226 }
11227 /*
11228 Wait for next event.
11229 */
11230 XScreenEvent(display,windows,&event);
11231 if (event.xany.window == windows->command.id)
11232 {
11233 /*
11234 Select a command from the Command widget.
11235 */
11236 id=XCommandWidget(display,windows,ROIMenu,&event);
11237 if (id < 0)
11238 continue;
11239 switch (ROICommands[id])
11240 {
11241 case ROIHelpCommand:
11242 {
11243 XTextViewHelp(display,resource_info,windows,MagickFalse,
11244 "Help Viewer - Region of Interest",ImageROIHelp);
11245 break;
11246 }
11247 case ROIDismissCommand:
11248 {
11249 /*
11250 Prematurely exit.
11251 */
11252 state|=EscapeState;
11253 state|=ExitState;
11254 break;
11255 }
11256 default:
11257 break;
11258 }
11259 continue;
11260 }
11261 switch (event.type)
11262 {
11263 case ButtonPress:
11264 {
11265 if (event.xbutton.button != Button1)
11266 break;
11267 if (event.xbutton.window != windows->image.id)
11268 break;
11269 /*
11270 Note first corner of region of interest rectangle-- exit loop.
11271 */
11272 (void) XCheckDefineCursor(display,windows->image.id,cursor);
11273 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11274 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11275 state|=ExitState;
11276 break;
11277 }
11278 case ButtonRelease:
11279 break;
11280 case Expose:
11281 break;
11282 case KeyPress:
11283 {
11284 KeySym
11285 key_symbol;
11286
11287 if (event.xkey.window != windows->image.id)
11288 break;
11289 /*
11290 Respond to a user key press.
11291 */
11292 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11293 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11294 switch ((int) key_symbol)
11295 {
11296 case XK_Escape:
11297 case XK_F20:
11298 {
11299 /*
11300 Prematurely exit.
11301 */
11302 state|=EscapeState;
11303 state|=ExitState;
11304 break;
11305 }
11306 case XK_F1:
11307 case XK_Help:
11308 {
11309 XTextViewHelp(display,resource_info,windows,MagickFalse,
11310 "Help Viewer - Region of Interest",ImageROIHelp);
11311 break;
11312 }
11313 default:
11314 {
11315 (void) XBell(display,0);
11316 break;
11317 }
11318 }
11319 break;
11320 }
11321 case MotionNotify:
11322 {
11323 /*
11324 Map and unmap Info widget as text cursor crosses its boundaries.
11325 */
11326 x=event.xmotion.x;
11327 y=event.xmotion.y;
11328 if (windows->info.mapped != MagickFalse)
11329 {
11330 if ((x < (int) (windows->info.x+windows->info.width)) &&
11331 (y < (int) (windows->info.y+windows->info.height)))
11332 (void) XWithdrawWindow(display,windows->info.id,
11333 windows->info.screen);
11334 }
11335 else
11336 if ((x > (int) (windows->info.x+windows->info.width)) ||
11337 (y > (int) (windows->info.y+windows->info.height)))
11338 (void) XMapWindow(display,windows->info.id);
11339 roi_info.x=(ssize_t) windows->image.x+x;
11340 roi_info.y=(ssize_t) windows->image.y+y;
11341 break;
11342 }
11343 default:
11344 break;
11345 }
11346 } while ((state & ExitState) == 0);
11347 (void) XSelectInput(display,windows->image.id,
11348 windows->image.attributes.event_mask);
11349 if ((state & EscapeState) != 0)
11350 {
11351 /*
11352 User want to exit without region of interest.
11353 */
11354 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11355 (void) XFreeCursor(display,cursor);
11356 return(MagickTrue);
11357 }
11358 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11359 do
11360 {
11361 /*
11362 Size rectangle as pointer moves until the mouse button is released.
11363 */
11364 x=(int) roi_info.x;
11365 y=(int) roi_info.y;
11366 roi_info.width=0;
11367 roi_info.height=0;
11368 state=DefaultState;
11369 do
11370 {
11371 highlight_info=roi_info;
11372 highlight_info.x=roi_info.x-windows->image.x;
11373 highlight_info.y=roi_info.y-windows->image.y;
11374 if ((highlight_info.width > 3) && (highlight_info.height > 3))
11375 {
11376 /*
11377 Display info and draw region of interest rectangle.
11378 */
11379 if (windows->info.mapped == MagickFalse)
11380 (void) XMapWindow(display,windows->info.id);
11381 (void) FormatLocaleString(text,MaxTextExtent,
11382 " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11383 roi_info.height,(double) roi_info.x,(double) roi_info.y);
11384 XInfoWidget(display,windows,text);
11385 XHighlightRectangle(display,windows->image.id,
11386 windows->image.highlight_context,&highlight_info);
11387 }
11388 else
11389 if (windows->info.mapped != MagickFalse)
11390 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11391 /*
11392 Wait for next event.
11393 */
11394 XScreenEvent(display,windows,&event);
11395 if ((highlight_info.width > 3) && (highlight_info.height > 3))
11396 XHighlightRectangle(display,windows->image.id,
11397 windows->image.highlight_context,&highlight_info);
11398 switch (event.type)
11399 {
11400 case ButtonPress:
11401 {
11402 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11403 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11404 break;
11405 }
11406 case ButtonRelease:
11407 {
11408 /*
11409 User has committed to region of interest rectangle.
11410 */
11411 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11412 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11413 XSetCursorState(display,windows,MagickFalse);
11414 state|=ExitState;
11415 if (LocaleCompare(windows->command.name,"Apply") == 0)
11416 break;
11417 (void) CloneString(&windows->command.name,"Apply");
11418 windows->command.data=ApplyMenus;
11419 (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11420 break;
11421 }
11422 case Expose:
11423 break;
11424 case MotionNotify:
11425 {
11426 roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11427 roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11428 }
11429 default:
11430 break;
11431 }
11432 if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11433 ((state & ExitState) != 0))
11434 {
11435 /*
11436 Check boundary conditions.
11437 */
11438 if (roi_info.x < 0)
11439 roi_info.x=0;
11440 else
11441 if (roi_info.x > (ssize_t) windows->image.ximage->width)
11442 roi_info.x=(ssize_t) windows->image.ximage->width;
11443 if ((int) roi_info.x < x)
11444 roi_info.width=(unsigned int) (x-roi_info.x);
11445 else
11446 {
11447 roi_info.width=(unsigned int) (roi_info.x-x);
11448 roi_info.x=(ssize_t) x;
11449 }
11450 if (roi_info.y < 0)
11451 roi_info.y=0;
11452 else
11453 if (roi_info.y > (ssize_t) windows->image.ximage->height)
11454 roi_info.y=(ssize_t) windows->image.ximage->height;
11455 if ((int) roi_info.y < y)
11456 roi_info.height=(unsigned int) (y-roi_info.y);
11457 else
11458 {
11459 roi_info.height=(unsigned int) (roi_info.y-y);
11460 roi_info.y=(ssize_t) y;
11461 }
11462 }
11463 } while ((state & ExitState) == 0);
11464 /*
11465 Wait for user to grab a corner of the rectangle or press return.
11466 */
11467 state=DefaultState;
11468 display_command=NullCommand;
11469 (void) XMapWindow(display,windows->info.id);
11470 do
11471 {
11472 if (windows->info.mapped != MagickFalse)
11473 {
11474 /*
11475 Display pointer position.
11476 */
11477 (void) FormatLocaleString(text,MaxTextExtent,
11478 " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11479 roi_info.height,(double) roi_info.x,(double) roi_info.y);
11480 XInfoWidget(display,windows,text);
11481 }
11482 highlight_info=roi_info;
11483 highlight_info.x=roi_info.x-windows->image.x;
11484 highlight_info.y=roi_info.y-windows->image.y;
11485 if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11486 {
11487 state|=EscapeState;
11488 state|=ExitState;
11489 break;
11490 }
11491 if ((state & UpdateRegionState) != 0)
11492 {
11493 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11494 switch (display_command)
11495 {
11496 case UndoCommand:
11497 case RedoCommand:
11498 {
11499 (void) XMagickCommand(display,resource_info,windows,
11500 display_command,image);
11501 break;
11502 }
11503 default:
11504 {
11505 /*
11506 Region of interest is relative to image configuration.
11507 */
11508 progress_monitor=SetImageProgressMonitor(*image,
11509 (MagickProgressMonitor) NULL,(*image)->client_data);
11510 crop_info=roi_info;
11511 width=(unsigned int) (*image)->columns;
11512 height=(unsigned int) (*image)->rows;
11513 x=0;
11514 y=0;
11515 if (windows->image.crop_geometry != (char *) NULL)
11516 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11517 &width,&height);
11518 scale_factor=(MagickRealType) width/windows->image.ximage->width;
11519 crop_info.x+=x;
11520 crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11521 crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11522 scale_factor=(MagickRealType)
11523 height/windows->image.ximage->height;
11524 crop_info.y+=y;
11525 crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11526 crop_info.height=(unsigned int)
11527 (scale_factor*crop_info.height+0.5);
11528 roi_image=CropImage(*image,&crop_info,&(*image)->exception);
11529 (void) SetImageProgressMonitor(*image,progress_monitor,
11530 (*image)->client_data);
11531 if (roi_image == (Image *) NULL)
11532 continue;
11533 /*
11534 Apply image processing technique to the region of interest.
11535 */
11536 windows->image.orphan=MagickTrue;
11537 (void) XMagickCommand(display,resource_info,windows,
11538 display_command,&roi_image);
11539 progress_monitor=SetImageProgressMonitor(*image,
11540 (MagickProgressMonitor) NULL,(*image)->client_data);
11541 (void) XMagickCommand(display,resource_info,windows,
11542 SaveToUndoBufferCommand,image);
11543 windows->image.orphan=MagickFalse;
11544 (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11545 crop_info.x,crop_info.y);
11546 roi_image=DestroyImage(roi_image);
11547 (void) SetImageProgressMonitor(*image,progress_monitor,
11548 (*image)->client_data);
11549 break;
11550 }
11551 }
11552 if (display_command != InfoCommand)
11553 {
11554 XConfigureImageColormap(display,resource_info,windows,*image);
11555 (void) XConfigureImage(display,resource_info,windows,*image);
11556 }
11557 XCheckRefreshWindows(display,windows);
11558 XInfoWidget(display,windows,text);
11559 (void) XSetFunction(display,windows->image.highlight_context,
11560 GXinvert);
11561 state&=(~UpdateRegionState);
11562 }
11563 XHighlightRectangle(display,windows->image.id,
11564 windows->image.highlight_context,&highlight_info);
11565 XScreenEvent(display,windows,&event);
11566 if (event.xany.window == windows->command.id)
11567 {
11568 /*
11569 Select a command from the Command widget.
11570 */
11571 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11572 display_command=NullCommand;
11573 id=XCommandWidget(display,windows,ApplyMenu,&event);
11574 if (id >= 0)
11575 {
11576 (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11577 display_command=ApplyCommands[id];
11578 if (id < ApplyMenus)
11579 {
11580 /*
11581 Select a command from a pop-up menu.
11582 */
11583 entry=XMenuWidget(display,windows,ApplyMenu[id],
11584 (const char **) Menus[id],command);
11585 if (entry >= 0)
11586 {
11587 (void) CopyMagickString(command,Menus[id][entry],
11588 MaxTextExtent);
11589 display_command=Commands[id][entry];
11590 }
11591 }
11592 }
11593 (void) XSetFunction(display,windows->image.highlight_context,
11594 GXinvert);
11595 XHighlightRectangle(display,windows->image.id,
11596 windows->image.highlight_context,&highlight_info);
11597 if (display_command == HelpCommand)
11598 {
11599 (void) XSetFunction(display,windows->image.highlight_context,
11600 GXcopy);
11601 XTextViewHelp(display,resource_info,windows,MagickFalse,
11602 "Help Viewer - Region of Interest",ImageROIHelp);
11603 (void) XSetFunction(display,windows->image.highlight_context,
11604 GXinvert);
11605 continue;
11606 }
11607 if (display_command == QuitCommand)
11608 {
11609 /*
11610 exit.
11611 */
11612 state|=EscapeState;
11613 state|=ExitState;
11614 continue;
11615 }
11616 if (display_command != NullCommand)
11617 state|=UpdateRegionState;
11618 continue;
11619 }
11620 XHighlightRectangle(display,windows->image.id,
11621 windows->image.highlight_context,&highlight_info);
11622 switch (event.type)
11623 {
11624 case ButtonPress:
11625 {
11626 x=windows->image.x;
11627 y=windows->image.y;
11628 if (event.xbutton.button != Button1)
11629 break;
11630 if (event.xbutton.window != windows->image.id)
11631 break;
11632 x=windows->image.x+event.xbutton.x;
11633 y=windows->image.y+event.xbutton.y;
11634 if ((x < (int) (roi_info.x+RoiDelta)) &&
11635 (x > (int) (roi_info.x-RoiDelta)) &&
11636 (y < (int) (roi_info.y+RoiDelta)) &&
11637 (y > (int) (roi_info.y-RoiDelta)))
11638 {
11639 roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11640 roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11641 state|=UpdateConfigurationState;
11642 break;
11643 }
11644 if ((x < (int) (roi_info.x+RoiDelta)) &&
11645 (x > (int) (roi_info.x-RoiDelta)) &&
11646 (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11647 (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11648 {
11649 roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11650 state|=UpdateConfigurationState;
11651 break;
11652 }
11653 if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11654 (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11655 (y < (int) (roi_info.y+RoiDelta)) &&
11656 (y > (int) (roi_info.y-RoiDelta)))
11657 {
11658 roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11659 state|=UpdateConfigurationState;
11660 break;
11661 }
11662 if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11663 (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11664 (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11665 (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11666 {
11667 state|=UpdateConfigurationState;
11668 break;
11669 }
11670 magick_fallthrough;
11671 }
11672 case ButtonRelease:
11673 {
11674 if (event.xbutton.window == windows->pan.id)
11675 if ((highlight_info.x != crop_info.x-windows->image.x) ||
11676 (highlight_info.y != crop_info.y-windows->image.y))
11677 XHighlightRectangle(display,windows->image.id,
11678 windows->image.highlight_context,&highlight_info);
11679 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11680 event.xbutton.time);
11681 break;
11682 }
11683 case Expose:
11684 {
11685 if (event.xexpose.window == windows->image.id)
11686 if (event.xexpose.count == 0)
11687 {
11688 event.xexpose.x=(int) highlight_info.x;
11689 event.xexpose.y=(int) highlight_info.y;
11690 event.xexpose.width=(int) highlight_info.width;
11691 event.xexpose.height=(int) highlight_info.height;
11692 XRefreshWindow(display,&windows->image,&event);
11693 }
11694 if (event.xexpose.window == windows->info.id)
11695 if (event.xexpose.count == 0)
11696 XInfoWidget(display,windows,text);
11697 break;
11698 }
11699 case KeyPress:
11700 {
11701 KeySym
11702 key_symbol;
11703
11704 if (event.xkey.window != windows->image.id)
11705 break;
11706 /*
11707 Respond to a user key press.
11708 */
11709 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11710 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11711 switch ((int) key_symbol)
11712 {
11713 case XK_Shift_L:
11714 case XK_Shift_R:
11715 break;
11716 case XK_Escape:
11717 case XK_F20:
11718 {
11719 state|=EscapeState;
11720 magick_fallthrough;
11721 }
11722 case XK_Return:
11723 {
11724 state|=ExitState;
11725 break;
11726 }
11727 case XK_Home:
11728 case XK_KP_Home:
11729 {
11730 roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11731 roi_info.y=(ssize_t) (windows->image.height/2L-
11732 roi_info.height/2L);
11733 break;
11734 }
11735 case XK_Left:
11736 case XK_KP_Left:
11737 {
11738 roi_info.x--;
11739 break;
11740 }
11741 case XK_Up:
11742 case XK_KP_Up:
11743 case XK_Next:
11744 {
11745 roi_info.y--;
11746 break;
11747 }
11748 case XK_Right:
11749 case XK_KP_Right:
11750 {
11751 roi_info.x++;
11752 break;
11753 }
11754 case XK_Prior:
11755 case XK_Down:
11756 case XK_KP_Down:
11757 {
11758 roi_info.y++;
11759 break;
11760 }
11761 case XK_F1:
11762 case XK_Help:
11763 {
11764 (void) XSetFunction(display,windows->image.highlight_context,
11765 GXcopy);
11766 XTextViewHelp(display,resource_info,windows,MagickFalse,
11767 "Help Viewer - Region of Interest",ImageROIHelp);
11768 (void) XSetFunction(display,windows->image.highlight_context,
11769 GXinvert);
11770 break;
11771 }
11772 default:
11773 {
11774 display_command=XImageWindowCommand(display,resource_info,windows,
11775 event.xkey.state,key_symbol,image);
11776 if (display_command != NullCommand)
11777 state|=UpdateRegionState;
11778 break;
11779 }
11780 }
11781 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11782 event.xkey.time);
11783 break;
11784 }
11785 case KeyRelease:
11786 break;
11787 case MotionNotify:
11788 {
11789 if (event.xbutton.window != windows->image.id)
11790 break;
11791 /*
11792 Map and unmap Info widget as text cursor crosses its boundaries.
11793 */
11794 x=event.xmotion.x;
11795 y=event.xmotion.y;
11796 if (windows->info.mapped != MagickFalse)
11797 {
11798 if ((x < (int) (windows->info.x+windows->info.width)) &&
11799 (y < (int) (windows->info.y+windows->info.height)))
11800 (void) XWithdrawWindow(display,windows->info.id,
11801 windows->info.screen);
11802 }
11803 else
11804 if ((x > (int) (windows->info.x+windows->info.width)) ||
11805 (y > (int) (windows->info.y+windows->info.height)))
11806 (void) XMapWindow(display,windows->info.id);
11807 roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11808 roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11809 break;
11810 }
11811 case SelectionRequest:
11812 {
11813 XSelectionEvent
11814 notify;
11815
11816 XSelectionRequestEvent
11817 *request;
11818
11819 /*
11820 Set primary selection.
11821 */
11822 (void) FormatLocaleString(text,MaxTextExtent,
11823 "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11824 roi_info.height,(double) roi_info.x,(double) roi_info.y);
11825 request=(&(event.xselectionrequest));
11826 (void) XChangeProperty(request->display,request->requestor,
11827 request->property,request->target,8,PropModeReplace,
11828 (unsigned char *) text,(int) strlen(text));
11829 notify.type=SelectionNotify;
11830 notify.display=request->display;
11831 notify.requestor=request->requestor;
11832 notify.selection=request->selection;
11833 notify.target=request->target;
11834 notify.time=request->time;
11835 if (request->property == None)
11836 notify.property=request->target;
11837 else
11838 notify.property=request->property;
11839 (void) XSendEvent(request->display,request->requestor,False,0,
11840 (XEvent *) &notify);
11841 }
11842 default:
11843 break;
11844 }
11845 if ((state & UpdateConfigurationState) != 0)
11846 {
11847 (void) XPutBackEvent(display,&event);
11848 (void) XCheckDefineCursor(display,windows->image.id,cursor);
11849 break;
11850 }
11851 } while ((state & ExitState) == 0);
11852 } while ((state & ExitState) == 0);
11853 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11854 XSetCursorState(display,windows,MagickFalse);
11855 if ((state & EscapeState) != 0)
11856 return(MagickTrue);
11857 return(MagickTrue);
11858}
11859
11860/*
11861%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11862% %
11863% %
11864% %
11865+ X R o t a t e I m a g e %
11866% %
11867% %
11868% %
11869%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11870%
11871% XRotateImage() rotates the X image. If the degrees parameter if zero, the
11872% rotation angle is computed from the slope of a line drawn by the user.
11873%
11874% The format of the XRotateImage method is:
11875%
11876% MagickBooleanType XRotateImage(Display *display,
11877% XResourceInfo *resource_info,XWindows *windows,double degrees,
11878% Image **image)
11879%
11880% A description of each parameter follows:
11881%
11882% o display: Specifies a connection to an X server; returned from
11883% XOpenDisplay.
11884%
11885% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11886%
11887% o windows: Specifies a pointer to a XWindows structure.
11888%
11889% o degrees: Specifies the number of degrees to rotate the image.
11890%
11891% o image: the image.
11892%
11893*/
11894static MagickBooleanType XRotateImage(Display *display,
11895 XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image)
11896{
11897 const char
11898 *const RotateMenu[] =
11899 {
11900 "Pixel Color",
11901 "Direction",
11902 "Help",
11903 "Dismiss",
11904 (char *) NULL
11905 };
11906
11907 static ModeType
11908 direction = HorizontalRotateCommand;
11909
11910 static const ModeType
11911 DirectionCommands[] =
11912 {
11913 HorizontalRotateCommand,
11914 VerticalRotateCommand
11915 },
11916 RotateCommands[] =
11917 {
11918 RotateColorCommand,
11919 RotateDirectionCommand,
11920 RotateHelpCommand,
11921 RotateDismissCommand
11922 };
11923
11924 static unsigned int
11925 pen_id = 0;
11926
11927 char
11928 command[MaxTextExtent],
11929 text[MaxTextExtent];
11930
11931 Image
11932 *rotate_image;
11933
11934 int
11935 id,
11936 x,
11937 y;
11938
11939 MagickRealType
11940 normalized_degrees;
11941
11942 int
11943 i;
11944
11945 unsigned int
11946 height,
11947 rotations,
11948 width;
11949
11950 if (degrees == 0.0)
11951 {
11952 unsigned int
11953 distance;
11954
11955 size_t
11956 state;
11957
11958 XEvent
11959 event;
11960
11961 XSegment
11962 rotate_info;
11963
11964 /*
11965 Map Command widget.
11966 */
11967 (void) CloneString(&windows->command.name,"Rotate");
11968 windows->command.data=2;
11969 (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
11970 (void) XMapRaised(display,windows->command.id);
11971 XClientMessage(display,windows->image.id,windows->im_protocols,
11972 windows->im_update_widget,CurrentTime);
11973 /*
11974 Wait for first button press.
11975 */
11976 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11977 XQueryPosition(display,windows->image.id,&x,&y);
11978 rotate_info.x1=x;
11979 rotate_info.y1=y;
11980 rotate_info.x2=x;
11981 rotate_info.y2=y;
11982 state=DefaultState;
11983 do
11984 {
11985 XHighlightLine(display,windows->image.id,
11986 windows->image.highlight_context,&rotate_info);
11987 /*
11988 Wait for next event.
11989 */
11990 XScreenEvent(display,windows,&event);
11991 XHighlightLine(display,windows->image.id,
11992 windows->image.highlight_context,&rotate_info);
11993 if (event.xany.window == windows->command.id)
11994 {
11995 /*
11996 Select a command from the Command widget.
11997 */
11998 id=XCommandWidget(display,windows,RotateMenu,&event);
11999 if (id < 0)
12000 continue;
12001 (void) XSetFunction(display,windows->image.highlight_context,
12002 GXcopy);
12003 switch (RotateCommands[id])
12004 {
12005 case RotateColorCommand:
12006 {
12007 const char
12008 *ColorMenu[MaxNumberPens];
12009
12010 int
12011 pen_number;
12012
12013 XColor
12014 color;
12015
12016 /*
12017 Initialize menu selections.
12018 */
12019 for (i=0; i < (int) (MaxNumberPens-2); i++)
12020 ColorMenu[i]=resource_info->pen_colors[i];
12021 ColorMenu[MaxNumberPens-2]="Browser...";
12022 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12023 /*
12024 Select a pen color from the pop-up menu.
12025 */
12026 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12027 (const char **) ColorMenu,command);
12028 if (pen_number < 0)
12029 break;
12030 if (pen_number == (MaxNumberPens-2))
12031 {
12032 static char
12033 color_name[MaxTextExtent] = "gray";
12034
12035 /*
12036 Select a pen color from a dialog.
12037 */
12038 resource_info->pen_colors[pen_number]=color_name;
12039 XColorBrowserWidget(display,windows,"Select",color_name);
12040 if (*color_name == '\0')
12041 break;
12042 }
12043 /*
12044 Set pen color.
12045 */
12046 (void) XParseColor(display,windows->map_info->colormap,
12047 resource_info->pen_colors[pen_number],&color);
12048 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12049 (unsigned int) MaxColors,&color);
12050 windows->pixel_info->pen_colors[pen_number]=color;
12051 pen_id=(unsigned int) pen_number;
12052 break;
12053 }
12054 case RotateDirectionCommand:
12055 {
12056 const char
12057 *const Directions[] =
12058 {
12059 "horizontal",
12060 "vertical",
12061 (char *) NULL,
12062 };
12063
12064 /*
12065 Select a command from the pop-up menu.
12066 */
12067 id=XMenuWidget(display,windows,RotateMenu[id],
12068 Directions,command);
12069 if (id >= 0)
12070 direction=DirectionCommands[id];
12071 break;
12072 }
12073 case RotateHelpCommand:
12074 {
12075 XTextViewHelp(display,resource_info,windows,MagickFalse,
12076 "Help Viewer - Image Rotation",ImageRotateHelp);
12077 break;
12078 }
12079 case RotateDismissCommand:
12080 {
12081 /*
12082 Prematurely exit.
12083 */
12084 state|=EscapeState;
12085 state|=ExitState;
12086 break;
12087 }
12088 default:
12089 break;
12090 }
12091 (void) XSetFunction(display,windows->image.highlight_context,
12092 GXinvert);
12093 continue;
12094 }
12095 switch (event.type)
12096 {
12097 case ButtonPress:
12098 {
12099 if (event.xbutton.button != Button1)
12100 break;
12101 if (event.xbutton.window != windows->image.id)
12102 break;
12103 /*
12104 exit loop.
12105 */
12106 (void) XSetFunction(display,windows->image.highlight_context,
12107 GXcopy);
12108 rotate_info.x1=event.xbutton.x;
12109 rotate_info.y1=event.xbutton.y;
12110 state|=ExitState;
12111 break;
12112 }
12113 case ButtonRelease:
12114 break;
12115 case Expose:
12116 break;
12117 case KeyPress:
12118 {
12119 char
12120 command[MaxTextExtent];
12121
12122 KeySym
12123 key_symbol;
12124
12125 if (event.xkey.window != windows->image.id)
12126 break;
12127 /*
12128 Respond to a user key press.
12129 */
12130 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12131 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12132 switch ((int) key_symbol)
12133 {
12134 case XK_Escape:
12135 case XK_F20:
12136 {
12137 /*
12138 Prematurely exit.
12139 */
12140 state|=EscapeState;
12141 state|=ExitState;
12142 break;
12143 }
12144 case XK_F1:
12145 case XK_Help:
12146 {
12147 (void) XSetFunction(display,windows->image.highlight_context,
12148 GXcopy);
12149 XTextViewHelp(display,resource_info,windows,MagickFalse,
12150 "Help Viewer - Image Rotation",ImageRotateHelp);
12151 (void) XSetFunction(display,windows->image.highlight_context,
12152 GXinvert);
12153 break;
12154 }
12155 default:
12156 {
12157 (void) XBell(display,0);
12158 break;
12159 }
12160 }
12161 break;
12162 }
12163 case MotionNotify:
12164 {
12165 rotate_info.x1=event.xmotion.x;
12166 rotate_info.y1=event.xmotion.y;
12167 }
12168 }
12169 rotate_info.x2=rotate_info.x1;
12170 rotate_info.y2=rotate_info.y1;
12171 if (direction == HorizontalRotateCommand)
12172 rotate_info.x2+=32;
12173 else
12174 rotate_info.y2-=32;
12175 } while ((state & ExitState) == 0);
12176 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12177 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12178 if ((state & EscapeState) != 0)
12179 return(MagickTrue);
12180 /*
12181 Draw line as pointer moves until the mouse button is released.
12182 */
12183 distance=0;
12184 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12185 state=DefaultState;
12186 do
12187 {
12188 if (distance > 9)
12189 {
12190 /*
12191 Display info and draw rotation line.
12192 */
12193 if (windows->info.mapped == MagickFalse)
12194 (void) XMapWindow(display,windows->info.id);
12195 (void) FormatLocaleString(text,MaxTextExtent," %g",
12196 direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12197 XInfoWidget(display,windows,text);
12198 XHighlightLine(display,windows->image.id,
12199 windows->image.highlight_context,&rotate_info);
12200 }
12201 else
12202 if (windows->info.mapped != MagickFalse)
12203 (void) XWithdrawWindow(display,windows->info.id,
12204 windows->info.screen);
12205 /*
12206 Wait for next event.
12207 */
12208 XScreenEvent(display,windows,&event);
12209 if (distance > 9)
12210 XHighlightLine(display,windows->image.id,
12211 windows->image.highlight_context,&rotate_info);
12212 switch (event.type)
12213 {
12214 case ButtonPress:
12215 break;
12216 case ButtonRelease:
12217 {
12218 /*
12219 User has committed to rotation line.
12220 */
12221 rotate_info.x2=event.xbutton.x;
12222 rotate_info.y2=event.xbutton.y;
12223 state|=ExitState;
12224 break;
12225 }
12226 case Expose:
12227 break;
12228 case MotionNotify:
12229 {
12230 rotate_info.x2=event.xmotion.x;
12231 rotate_info.y2=event.xmotion.y;
12232 }
12233 default:
12234 break;
12235 }
12236 /*
12237 Check boundary conditions.
12238 */
12239 if (rotate_info.x2 < 0)
12240 rotate_info.x2=0;
12241 else
12242 if (rotate_info.x2 > (int) windows->image.width)
12243 rotate_info.x2=(short) windows->image.width;
12244 if (rotate_info.y2 < 0)
12245 rotate_info.y2=0;
12246 else
12247 if (rotate_info.y2 > (int) windows->image.height)
12248 rotate_info.y2=(short) windows->image.height;
12249 /*
12250 Compute rotation angle from the slope of the line.
12251 */
12252 degrees=0.0;
12253 distance=(unsigned int)
12254 ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12255 ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12256 if (distance > 9)
12257 degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12258 rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12259 } while ((state & ExitState) == 0);
12260 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12261 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12262 if (distance <= 9)
12263 return(MagickTrue);
12264 }
12265 if (direction == VerticalRotateCommand)
12266 degrees-=90.0;
12267 if (degrees == 0.0)
12268 return(MagickTrue);
12269 /*
12270 Rotate image.
12271 */
12272 normalized_degrees=degrees;
12273 while (normalized_degrees < -45.0)
12274 normalized_degrees+=360.0;
12275 for (rotations=0; normalized_degrees > 45.0; rotations++)
12276 normalized_degrees-=90.0;
12277 if (normalized_degrees != 0.0)
12278 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
12279 XSetCursorState(display,windows,MagickTrue);
12280 XCheckRefreshWindows(display,windows);
12281 (*image)->background_color.red=ScaleShortToQuantum(
12282 windows->pixel_info->pen_colors[pen_id].red);
12283 (*image)->background_color.green=ScaleShortToQuantum(
12284 windows->pixel_info->pen_colors[pen_id].green);
12285 (*image)->background_color.blue=ScaleShortToQuantum(
12286 windows->pixel_info->pen_colors[pen_id].blue);
12287 rotate_image=RotateImage(*image,degrees,&(*image)->exception);
12288 XSetCursorState(display,windows,MagickFalse);
12289 if (rotate_image == (Image *) NULL)
12290 return(MagickFalse);
12291 *image=DestroyImage(*image);
12292 *image=rotate_image;
12293 if (windows->image.crop_geometry != (char *) NULL)
12294 {
12295 /*
12296 Rotate crop geometry.
12297 */
12298 width=(unsigned int) (*image)->columns;
12299 height=(unsigned int) (*image)->rows;
12300 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12301 switch (rotations % 4)
12302 {
12303 default:
12304 case 0:
12305 break;
12306 case 1:
12307 {
12308 /*
12309 Rotate 90 degrees.
12310 */
12311 (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12312 "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12313 (int) height-y,x);
12314 break;
12315 }
12316 case 2:
12317 {
12318 /*
12319 Rotate 180 degrees.
12320 */
12321 (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12322 "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12323 break;
12324 }
12325 case 3:
12326 {
12327 /*
12328 Rotate 270 degrees.
12329 */
12330 (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12331 "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12332 break;
12333 }
12334 }
12335 }
12336 if (windows->image.orphan != MagickFalse)
12337 return(MagickTrue);
12338 if (normalized_degrees != 0.0)
12339 {
12340 /*
12341 Update image colormap.
12342 */
12343 windows->image.window_changes.width=(int) (*image)->columns;
12344 windows->image.window_changes.height=(int) (*image)->rows;
12345 if (windows->image.crop_geometry != (char *) NULL)
12346 {
12347 /*
12348 Obtain dimensions of image from crop geometry.
12349 */
12350 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12351 &width,&height);
12352 windows->image.window_changes.width=(int) width;
12353 windows->image.window_changes.height=(int) height;
12354 }
12355 XConfigureImageColormap(display,resource_info,windows,*image);
12356 }
12357 else
12358 if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12359 {
12360 windows->image.window_changes.width=windows->image.ximage->height;
12361 windows->image.window_changes.height=windows->image.ximage->width;
12362 }
12363 /*
12364 Update image configuration.
12365 */
12366 (void) XConfigureImage(display,resource_info,windows,*image);
12367 return(MagickTrue);
12368}
12369
12370/*
12371%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12372% %
12373% %
12374% %
12375+ X S a v e I m a g e %
12376% %
12377% %
12378% %
12379%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12380%
12381% XSaveImage() saves an image to a file.
12382%
12383% The format of the XSaveImage method is:
12384%
12385% MagickBooleanType XSaveImage(Display *display,
12386% XResourceInfo *resource_info,XWindows *windows,Image *image)
12387%
12388% A description of each parameter follows:
12389%
12390% o display: Specifies a connection to an X server; returned from
12391% XOpenDisplay.
12392%
12393% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12394%
12395% o windows: Specifies a pointer to a XWindows structure.
12396%
12397% o image: the image.
12398%
12399*/
12400static MagickBooleanType XSaveImage(Display *display,
12401 XResourceInfo *resource_info,XWindows *windows,Image *image)
12402{
12403 char
12404 filename[MaxTextExtent],
12405 geometry[MaxTextExtent];
12406
12407 Image
12408 *save_image;
12409
12410 ImageInfo
12411 *image_info;
12412
12413 MagickStatusType
12414 status;
12415
12416 /*
12417 Request file name from user.
12418 */
12419 if (resource_info->write_filename != (char *) NULL)
12420 (void) CopyMagickString(filename,resource_info->write_filename,
12421 MaxTextExtent);
12422 else
12423 {
12424 char
12425 path[MaxTextExtent];
12426
12427 int
12428 status;
12429
12430 GetPathComponent(image->filename,HeadPath,path);
12431 GetPathComponent(image->filename,TailPath,filename);
12432 if (*path != '\0')
12433 {
12434 status=chdir(path);
12435 if (status == -1)
12436 (void) ThrowMagickException(&image->exception,GetMagickModule(),
12437 FileOpenError,"UnableToOpenFile","%s",path);
12438 }
12439 }
12440 XFileBrowserWidget(display,windows,"Save",filename);
12441 if (*filename == '\0')
12442 return(MagickTrue);
12443 if (IsPathAccessible(filename) != MagickFalse)
12444 {
12445 int
12446 status;
12447
12448 /*
12449 File exists-- seek user's permission before overwriting.
12450 */
12451 status=XConfirmWidget(display,windows,"Overwrite",filename);
12452 if (status <= 0)
12453 return(MagickTrue);
12454 }
12455 image_info=CloneImageInfo(resource_info->image_info);
12456 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12457 (void) SetImageInfo(image_info,1,&image->exception);
12458 if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12459 (LocaleCompare(image_info->magick,"JPG") == 0))
12460 {
12461 char
12462 quality[MaxTextExtent];
12463
12464 int
12465 status;
12466
12467 /*
12468 Request JPEG quality from user.
12469 */
12470 (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12471 image->quality);
12472 status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12473 quality);
12474 if (*quality == '\0')
12475 return(MagickTrue);
12476 image->quality=StringToUnsignedLong(quality);
12477 image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12478 }
12479 if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12480 (LocaleCompare(image_info->magick,"PDF") == 0) ||
12481 (LocaleCompare(image_info->magick,"PS") == 0) ||
12482 (LocaleCompare(image_info->magick,"PS2") == 0))
12483 {
12484 char
12485 geometry[MaxTextExtent];
12486
12487 const char
12488 *const PageSizes[] =
12489 {
12490 "Letter",
12491 "Tabloid",
12492 "Ledger",
12493 "Legal",
12494 "Statement",
12495 "Executive",
12496 "A3",
12497 "A4",
12498 "A5",
12499 "B4",
12500 "B5",
12501 "Folio",
12502 "Quarto",
12503 "10x14",
12504 (char *) NULL
12505 };
12506
12507 /*
12508 Request page geometry from user.
12509 */
12510 (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12511 if (LocaleCompare(image_info->magick,"PDF") == 0)
12512 (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12513 if (image_info->page != (char *) NULL)
12514 (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12515 XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12516 "Select page geometry:",geometry);
12517 if (*geometry != '\0')
12518 image_info->page=GetPageGeometry(geometry);
12519 }
12520 /*
12521 Apply image transforms.
12522 */
12523 XSetCursorState(display,windows,MagickTrue);
12524 XCheckRefreshWindows(display,windows);
12525 save_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12526 if (save_image == (Image *) NULL)
12527 return(MagickFalse);
12528 (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12529 windows->image.ximage->width,windows->image.ximage->height);
12530 (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12531 /*
12532 Write image.
12533 */
12534 (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12535 status=WriteImage(image_info,save_image);
12536 if (status != MagickFalse)
12537 image->taint=MagickFalse;
12538 save_image=DestroyImage(save_image);
12539 image_info=DestroyImageInfo(image_info);
12540 XSetCursorState(display,windows,MagickFalse);
12541 return(status != 0 ? MagickTrue : MagickFalse);
12542}
12543
12544/*
12545%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12546% %
12547% %
12548% %
12549+ X S c r e e n E v e n t %
12550% %
12551% %
12552% %
12553%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12554%
12555% XScreenEvent() handles global events associated with the Pan and Magnify
12556% windows.
12557%
12558% The format of the XScreenEvent function is:
12559%
12560% void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12561%
12562% A description of each parameter follows:
12563%
12564% o display: Specifies a pointer to the Display structure; returned from
12565% XOpenDisplay.
12566%
12567% o windows: Specifies a pointer to a XWindows structure.
12568%
12569% o event: Specifies a pointer to a X11 XEvent structure.
12570%
12571%
12572*/
12573
12574#if defined(__cplusplus) || defined(c_plusplus)
12575extern "C" {
12576#endif
12577
12578static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12579{
12580 XWindows
12581 *windows;
12582
12583 magick_unreferenced(display);
12584
12585 windows=(XWindows *) data;
12586 if ((event->type == ClientMessage) &&
12587 (event->xclient.window == windows->image.id))
12588 return(MagickFalse);
12589 return(MagickTrue);
12590}
12591
12592#if defined(__cplusplus) || defined(c_plusplus)
12593}
12594#endif
12595
12596static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12597{
12598 int
12599 x,
12600 y;
12601
12602 (void) XIfEvent(display,event,XPredicate,(char *) windows);
12603 if (event->xany.window == windows->command.id)
12604 return;
12605 switch (event->type)
12606 {
12607 case ButtonPress:
12608 case ButtonRelease:
12609 {
12610 if ((event->xbutton.button == Button3) &&
12611 (event->xbutton.state & Mod1Mask))
12612 {
12613 /*
12614 Convert Alt-Button3 to Button2.
12615 */
12616 event->xbutton.button=Button2;
12617 event->xbutton.state&=(~Mod1Mask);
12618 }
12619 if (event->xbutton.window == windows->backdrop.id)
12620 {
12621 (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12622 event->xbutton.time);
12623 break;
12624 }
12625 if (event->xbutton.window == windows->pan.id)
12626 {
12627 XPanImage(display,windows,event);
12628 break;
12629 }
12630 if (event->xbutton.window == windows->image.id)
12631 if (event->xbutton.button == Button2)
12632 {
12633 /*
12634 Update magnified image.
12635 */
12636 x=event->xbutton.x;
12637 y=event->xbutton.y;
12638 if (x < 0)
12639 x=0;
12640 else
12641 if (x >= (int) windows->image.width)
12642 x=(int) (windows->image.width-1);
12643 windows->magnify.x=(int) windows->image.x+x;
12644 if (y < 0)
12645 y=0;
12646 else
12647 if (y >= (int) windows->image.height)
12648 y=(int) (windows->image.height-1);
12649 windows->magnify.y=windows->image.y+y;
12650 if (windows->magnify.mapped == MagickFalse)
12651 (void) XMapRaised(display,windows->magnify.id);
12652 XMakeMagnifyImage(display,windows);
12653 if (event->type == ButtonRelease)
12654 (void) XWithdrawWindow(display,windows->info.id,
12655 windows->info.screen);
12656 break;
12657 }
12658 break;
12659 }
12660 case ClientMessage:
12661 {
12662 /*
12663 If client window delete message, exit.
12664 */
12665 if (event->xclient.message_type != windows->wm_protocols)
12666 break;
12667 if (*event->xclient.data.l != (long) windows->wm_delete_window)
12668 break;
12669 if (event->xclient.window == windows->magnify.id)
12670 {
12671 (void) XWithdrawWindow(display,windows->magnify.id,
12672 windows->magnify.screen);
12673 break;
12674 }
12675 break;
12676 }
12677 case ConfigureNotify:
12678 {
12679 if (event->xconfigure.window == windows->magnify.id)
12680 {
12681 unsigned int
12682 magnify;
12683
12684 /*
12685 Magnify window has a new configuration.
12686 */
12687 windows->magnify.width=(unsigned int) event->xconfigure.width;
12688 windows->magnify.height=(unsigned int) event->xconfigure.height;
12689 if (windows->magnify.mapped == MagickFalse)
12690 break;
12691 magnify=1;
12692 while ((int) magnify <= event->xconfigure.width)
12693 magnify<<=1;
12694 while ((int) magnify <= event->xconfigure.height)
12695 magnify<<=1;
12696 magnify>>=1;
12697 if (((int) magnify != event->xconfigure.width) ||
12698 ((int) magnify != event->xconfigure.height))
12699 {
12700 XWindowChanges
12701 window_changes;
12702
12703 window_changes.width=(int) magnify;
12704 window_changes.height=(int) magnify;
12705 (void) XReconfigureWMWindow(display,windows->magnify.id,
12706 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12707 &window_changes);
12708 break;
12709 }
12710 XMakeMagnifyImage(display,windows);
12711 break;
12712 }
12713 break;
12714 }
12715 case Expose:
12716 {
12717 if (event->xexpose.window == windows->image.id)
12718 {
12719 XRefreshWindow(display,&windows->image,event);
12720 break;
12721 }
12722 if (event->xexpose.window == windows->pan.id)
12723 if (event->xexpose.count == 0)
12724 {
12725 XDrawPanRectangle(display,windows);
12726 break;
12727 }
12728 if (event->xexpose.window == windows->magnify.id)
12729 if (event->xexpose.count == 0)
12730 {
12731 XMakeMagnifyImage(display,windows);
12732 break;
12733 }
12734 break;
12735 }
12736 case KeyPress:
12737 {
12738 char
12739 command[MaxTextExtent];
12740
12741 KeySym
12742 key_symbol;
12743
12744 if (event->xkey.window != windows->magnify.id)
12745 break;
12746 /*
12747 Respond to a user key press.
12748 */
12749 (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12750 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12751 XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12752 break;
12753 }
12754 case MapNotify:
12755 {
12756 if (event->xmap.window == windows->magnify.id)
12757 {
12758 windows->magnify.mapped=MagickTrue;
12759 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12760 break;
12761 }
12762 if (event->xmap.window == windows->info.id)
12763 {
12764 windows->info.mapped=MagickTrue;
12765 break;
12766 }
12767 break;
12768 }
12769 case MotionNotify:
12770 {
12771 while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12772 if (event->xmotion.window == windows->image.id)
12773 if (windows->magnify.mapped != MagickFalse)
12774 {
12775 /*
12776 Update magnified image.
12777 */
12778 x=event->xmotion.x;
12779 y=event->xmotion.y;
12780 if (x < 0)
12781 x=0;
12782 else
12783 if (x >= (int) windows->image.width)
12784 x=(int) (windows->image.width-1);
12785 windows->magnify.x=(int) windows->image.x+x;
12786 if (y < 0)
12787 y=0;
12788 else
12789 if (y >= (int) windows->image.height)
12790 y=(int) (windows->image.height-1);
12791 windows->magnify.y=windows->image.y+y;
12792 XMakeMagnifyImage(display,windows);
12793 }
12794 break;
12795 }
12796 case UnmapNotify:
12797 {
12798 if (event->xunmap.window == windows->magnify.id)
12799 {
12800 windows->magnify.mapped=MagickFalse;
12801 break;
12802 }
12803 if (event->xunmap.window == windows->info.id)
12804 {
12805 windows->info.mapped=MagickFalse;
12806 break;
12807 }
12808 break;
12809 }
12810 default:
12811 break;
12812 }
12813}
12814
12815/*
12816%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12817% %
12818% %
12819% %
12820+ X S e t C r o p G e o m e t r y %
12821% %
12822% %
12823% %
12824%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12825%
12826% XSetCropGeometry() accepts a cropping geometry relative to the Image window
12827% and translates it to a cropping geometry relative to the image.
12828%
12829% The format of the XSetCropGeometry method is:
12830%
12831% void XSetCropGeometry(Display *display,XWindows *windows,
12832% RectangleInfo *crop_info,Image *image)
12833%
12834% A description of each parameter follows:
12835%
12836% o display: Specifies a connection to an X server; returned from
12837% XOpenDisplay.
12838%
12839% o windows: Specifies a pointer to a XWindows structure.
12840%
12841% o crop_info: A pointer to a RectangleInfo that defines a region of the
12842% Image window to crop.
12843%
12844% o image: the image.
12845%
12846*/
12847static void XSetCropGeometry(Display *display,XWindows *windows,
12848 RectangleInfo *crop_info,Image *image)
12849{
12850 char
12851 text[MaxTextExtent];
12852
12853 int
12854 x,
12855 y;
12856
12857 MagickRealType
12858 scale_factor;
12859
12860 unsigned int
12861 height,
12862 width;
12863
12864 if (windows->info.mapped != MagickFalse)
12865 {
12866 /*
12867 Display info on cropping rectangle.
12868 */
12869 (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12870 (double) crop_info->width,(double) crop_info->height,(double)
12871 crop_info->x,(double) crop_info->y);
12872 XInfoWidget(display,windows,text);
12873 }
12874 /*
12875 Cropping geometry is relative to any previous crop geometry.
12876 */
12877 x=0;
12878 y=0;
12879 width=(unsigned int) image->columns;
12880 height=(unsigned int) image->rows;
12881 if (windows->image.crop_geometry != (char *) NULL)
12882 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12883 else
12884 windows->image.crop_geometry=AcquireString((char *) NULL);
12885 /*
12886 Define the crop geometry string from the cropping rectangle.
12887 */
12888 scale_factor=(MagickRealType) width/windows->image.ximage->width;
12889 if (crop_info->x > 0)
12890 x+=(int) (scale_factor*crop_info->x+0.5);
12891 width=(unsigned int) (scale_factor*crop_info->width+0.5);
12892 if (width == 0)
12893 width=1;
12894 scale_factor=(MagickRealType) height/windows->image.ximage->height;
12895 if (crop_info->y > 0)
12896 y+=(int) (scale_factor*crop_info->y+0.5);
12897 height=(unsigned int) (scale_factor*crop_info->height+0.5);
12898 if (height == 0)
12899 height=1;
12900 (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12901 "%ux%u%+d%+d",width,height,x,y);
12902}
12903
12904/*
12905%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12906% %
12907% %
12908% %
12909+ X T i l e I m a g e %
12910% %
12911% %
12912% %
12913%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12914%
12915% XTileImage() loads or deletes a selected tile from a visual image directory.
12916% The load or delete command is chosen from a menu.
12917%
12918% The format of the XTileImage method is:
12919%
12920% Image *XTileImage(Display *display,XResourceInfo *resource_info,
12921% XWindows *windows,Image *image,XEvent *event)
12922%
12923% A description of each parameter follows:
12924%
12925% o tile_image: XTileImage reads or deletes the tile image
12926% and returns it. A null image is returned if an error occurs.
12927%
12928% o display: Specifies a connection to an X server; returned from
12929% XOpenDisplay.
12930%
12931% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12932%
12933% o windows: Specifies a pointer to a XWindows structure.
12934%
12935% o image: the image; returned from ReadImage.
12936%
12937% o event: Specifies a pointer to a XEvent structure. If it is NULL,
12938% the entire image is refreshed.
12939%
12940*/
12941static Image *XTileImage(Display *display,XResourceInfo *resource_info,
12942 XWindows *windows,Image *image,XEvent *event)
12943{
12944 const char
12945 *const VerbMenu[] =
12946 {
12947 "Load",
12948 "Next",
12949 "Former",
12950 "Delete",
12951 "Update",
12952 (char *) NULL,
12953 };
12954
12955 static const ModeType
12956 TileCommands[] =
12957 {
12958 TileLoadCommand,
12959 TileNextCommand,
12960 TileFormerCommand,
12961 TileDeleteCommand,
12962 TileUpdateCommand
12963 };
12964
12965 char
12966 command[MaxTextExtent],
12967 filename[MaxTextExtent];
12968
12969 Image
12970 *tile_image;
12971
12972 int
12973 id,
12974 status,
12975 tile,
12976 x,
12977 y;
12978
12979 MagickRealType
12980 scale_factor;
12981
12982 char
12983 *p,
12984 *q;
12985
12986 int
12987 i;
12988
12989 unsigned int
12990 height,
12991 width;
12992
12993 /*
12994 Tile image is relative to montage image configuration.
12995 */
12996 x=0;
12997 y=0;
12998 width=(unsigned int) image->columns;
12999 height=(unsigned int) image->rows;
13000 if (windows->image.crop_geometry != (char *) NULL)
13001 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13002 scale_factor=(MagickRealType) width/windows->image.ximage->width;
13003 event->xbutton.x+=windows->image.x;
13004 event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13005 scale_factor=(MagickRealType) height/windows->image.ximage->height;
13006 event->xbutton.y+=windows->image.y;
13007 event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13008 /*
13009 Determine size and location of each tile in the visual image directory.
13010 */
13011 width=(unsigned int) image->columns;
13012 height=(unsigned int) image->rows;
13013 x=0;
13014 y=0;
13015 (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13016 tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13017 (event->xbutton.x-x)/width;
13018 if (tile < 0)
13019 {
13020 /*
13021 Button press is outside any tile.
13022 */
13023 (void) XBell(display,0);
13024 return((Image *) NULL);
13025 }
13026 /*
13027 Determine file name from the tile directory.
13028 */
13029 p=image->directory;
13030 for (i=tile; (i != 0) && (*p != '\0'); )
13031 {
13032 if (*p == '\xff')
13033 i--;
13034 p++;
13035 }
13036 if (*p == '\0')
13037 {
13038 /*
13039 Button press is outside any tile.
13040 */
13041 (void) XBell(display,0);
13042 return((Image *) NULL);
13043 }
13044 /*
13045 Select a command from the pop-up menu.
13046 */
13047 id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13048 if (id < 0)
13049 return((Image *) NULL);
13050 q=p;
13051 while ((*q != '\xff') && (*q != '\0'))
13052 q++;
13053 (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13054 /*
13055 Perform command for the selected tile.
13056 */
13057 XSetCursorState(display,windows,MagickTrue);
13058 XCheckRefreshWindows(display,windows);
13059 tile_image=NewImageList();
13060 switch (TileCommands[id])
13061 {
13062 case TileLoadCommand:
13063 {
13064 /*
13065 Load tile image.
13066 */
13067 XCheckRefreshWindows(display,windows);
13068 (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13069 MaxTextExtent);
13070 (void) CopyMagickString(resource_info->image_info->filename,filename,
13071 MaxTextExtent);
13072 tile_image=ReadImage(resource_info->image_info,&image->exception);
13073 CatchException(&image->exception);
13074 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13075 break;
13076 }
13077 case TileNextCommand:
13078 {
13079 /*
13080 Display next image.
13081 */
13082 XClientMessage(display,windows->image.id,windows->im_protocols,
13083 windows->im_next_image,CurrentTime);
13084 break;
13085 }
13086 case TileFormerCommand:
13087 {
13088 /*
13089 Display former image.
13090 */
13091 XClientMessage(display,windows->image.id,windows->im_protocols,
13092 windows->im_former_image,CurrentTime);
13093 break;
13094 }
13095 case TileDeleteCommand:
13096 {
13097 /*
13098 Delete tile image.
13099 */
13100 if (IsPathAccessible(filename) == MagickFalse)
13101 {
13102 XNoticeWidget(display,windows,"Image file does not exist:",filename);
13103 break;
13104 }
13105 status=XConfirmWidget(display,windows,"Really delete tile",filename);
13106 if (status <= 0)
13107 break;
13108 status=ShredFile(filename);
13109 status|=remove_utf8(filename);
13110 if (status != MagickFalse)
13111 {
13112 XNoticeWidget(display,windows,"Unable to delete image file:",
13113 filename);
13114 break;
13115 }
13116 magick_fallthrough;
13117 }
13118 case TileUpdateCommand:
13119 {
13121 *exception;
13122
13123 int
13124 x_offset,
13125 y_offset;
13126
13128 pixel;
13129
13130 int
13131 j;
13132
13134 *s;
13135
13136 /*
13137 Ensure all the images exist.
13138 */
13139 tile=0;
13140 for (p=image->directory; *p != '\0'; p++)
13141 {
13142 CacheView
13143 *image_view;
13144
13145 q=p;
13146 while ((*q != '\xff') && (*q != '\0'))
13147 q++;
13148 (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13149 p=q;
13150 if (IsPathAccessible(filename) != MagickFalse)
13151 {
13152 tile++;
13153 continue;
13154 }
13155 /*
13156 Overwrite tile with background color.
13157 */
13158 x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13159 y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13160 exception=(&image->exception);
13161 image_view=AcquireAuthenticCacheView(image,exception);
13162 (void) GetOneCacheViewVirtualPixel(image_view,0,0,&pixel,exception);
13163 for (i=0; i < (int) height; i++)
13164 {
13165 s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13166 y_offset+i,width,1,exception);
13167 if (s == (PixelPacket *) NULL)
13168 break;
13169 for (j=0; j < (int) width; j++)
13170 *s++=pixel;
13171 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13172 break;
13173 }
13174 image_view=DestroyCacheView(image_view);
13175 tile++;
13176 }
13177 windows->image.window_changes.width=(int) image->columns;
13178 windows->image.window_changes.height=(int) image->rows;
13179 XConfigureImageColormap(display,resource_info,windows,image);
13180 (void) XConfigureImage(display,resource_info,windows,image);
13181 break;
13182 }
13183 default:
13184 break;
13185 }
13186 XSetCursorState(display,windows,MagickFalse);
13187 return(tile_image);
13188}
13189
13190/*
13191%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13192% %
13193% %
13194% %
13195+ X T r a n s l a t e I m a g e %
13196% %
13197% %
13198% %
13199%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13200%
13201% XTranslateImage() translates the image within an Image window by one pixel
13202% as specified by the key symbol. If the image has a `montage string the
13203% translation is respect to the width and height contained within the string.
13204%
13205% The format of the XTranslateImage method is:
13206%
13207% void XTranslateImage(Display *display,XWindows *windows,
13208% Image *image,const KeySym key_symbol)
13209%
13210% A description of each parameter follows:
13211%
13212% o display: Specifies a connection to an X server; returned from
13213% XOpenDisplay.
13214%
13215% o windows: Specifies a pointer to a XWindows structure.
13216%
13217% o image: the image.
13218%
13219% o key_symbol: Specifies a KeySym which indicates which side of the image
13220% to trim.
13221%
13222*/
13223static void XTranslateImage(Display *display,XWindows *windows,
13224 Image *image,const KeySym key_symbol)
13225{
13226 char
13227 text[MaxTextExtent];
13228
13229 int
13230 x,
13231 y;
13232
13233 unsigned int
13234 x_offset,
13235 y_offset;
13236
13237 /*
13238 User specified a pan position offset.
13239 */
13240 x_offset=windows->image.width;
13241 y_offset=windows->image.height;
13242 if (image->montage != (char *) NULL)
13243 (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13244 switch ((int) key_symbol)
13245 {
13246 case XK_Home:
13247 case XK_KP_Home:
13248 {
13249 windows->image.x=(int) windows->image.width/2;
13250 windows->image.y=(int) windows->image.height/2;
13251 break;
13252 }
13253 case XK_Left:
13254 case XK_KP_Left:
13255 {
13256 windows->image.x-=x_offset;
13257 break;
13258 }
13259 case XK_Next:
13260 case XK_Up:
13261 case XK_KP_Up:
13262 {
13263 windows->image.y-=y_offset;
13264 break;
13265 }
13266 case XK_Right:
13267 case XK_KP_Right:
13268 {
13269 windows->image.x+=x_offset;
13270 break;
13271 }
13272 case XK_Prior:
13273 case XK_Down:
13274 case XK_KP_Down:
13275 {
13276 windows->image.y+=y_offset;
13277 break;
13278 }
13279 default:
13280 return;
13281 }
13282 /*
13283 Check boundary conditions.
13284 */
13285 if (windows->image.x < 0)
13286 windows->image.x=0;
13287 else
13288 if ((int) (windows->image.x+windows->image.width) >
13289 windows->image.ximage->width)
13290 windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13291 if (windows->image.y < 0)
13292 windows->image.y=0;
13293 else
13294 if ((int) (windows->image.y+windows->image.height) >
13295 windows->image.ximage->height)
13296 windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13297 /*
13298 Refresh Image window.
13299 */
13300 (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13301 windows->image.width,windows->image.height,windows->image.x,
13302 windows->image.y);
13303 XInfoWidget(display,windows,text);
13304 XCheckRefreshWindows(display,windows);
13305 XDrawPanRectangle(display,windows);
13306 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13307 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13308}
13309
13310/*
13311%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13312% %
13313% %
13314% %
13315+ X T r i m I m a g e %
13316% %
13317% %
13318% %
13319%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13320%
13321% XTrimImage() trims the edges from the Image window.
13322%
13323% The format of the XTrimImage method is:
13324%
13325% MagickBooleanType XTrimImage(Display *display,
13326% XResourceInfo *resource_info,XWindows *windows,Image *image)
13327%
13328% A description of each parameter follows:
13329%
13330% o display: Specifies a connection to an X server; returned from
13331% XOpenDisplay.
13332%
13333% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13334%
13335% o windows: Specifies a pointer to a XWindows structure.
13336%
13337% o image: the image.
13338%
13339*/
13340static MagickBooleanType XTrimImage(Display *display,
13341 XResourceInfo *resource_info,XWindows *windows,Image *image)
13342{
13344 trim_info;
13345
13346 int
13347 x,
13348 y;
13349
13350 size_t
13351 background,
13352 pixel;
13353
13354 /*
13355 Trim edges from image.
13356 */
13357 XSetCursorState(display,windows,MagickTrue);
13358 XCheckRefreshWindows(display,windows);
13359 /*
13360 Crop the left edge.
13361 */
13362 background=XGetPixel(windows->image.ximage,0,0);
13363 trim_info.width=(size_t) windows->image.ximage->width;
13364 for (x=0; x < windows->image.ximage->width; x++)
13365 {
13366 for (y=0; y < windows->image.ximage->height; y++)
13367 {
13368 pixel=XGetPixel(windows->image.ximage,x,y);
13369 if (pixel != background)
13370 break;
13371 }
13372 if (y < windows->image.ximage->height)
13373 break;
13374 }
13375 trim_info.x=(ssize_t) x;
13376 if (trim_info.x == (ssize_t) windows->image.ximage->width)
13377 {
13378 XSetCursorState(display,windows,MagickFalse);
13379 return(MagickFalse);
13380 }
13381 /*
13382 Crop the right edge.
13383 */
13384 background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13385 for (x=windows->image.ximage->width-1; x != 0; x--)
13386 {
13387 for (y=0; y < windows->image.ximage->height; y++)
13388 {
13389 pixel=XGetPixel(windows->image.ximage,x,y);
13390 if (pixel != background)
13391 break;
13392 }
13393 if (y < windows->image.ximage->height)
13394 break;
13395 }
13396 trim_info.width=(size_t) (x-trim_info.x+1);
13397 /*
13398 Crop the top edge.
13399 */
13400 background=XGetPixel(windows->image.ximage,0,0);
13401 trim_info.height=(size_t) windows->image.ximage->height;
13402 for (y=0; y < windows->image.ximage->height; y++)
13403 {
13404 for (x=0; x < windows->image.ximage->width; x++)
13405 {
13406 pixel=XGetPixel(windows->image.ximage,x,y);
13407 if (pixel != background)
13408 break;
13409 }
13410 if (x < windows->image.ximage->width)
13411 break;
13412 }
13413 trim_info.y=(ssize_t) y;
13414 /*
13415 Crop the bottom edge.
13416 */
13417 background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13418 for (y=windows->image.ximage->height-1; y != 0; y--)
13419 {
13420 for (x=0; x < windows->image.ximage->width; x++)
13421 {
13422 pixel=XGetPixel(windows->image.ximage,x,y);
13423 if (pixel != background)
13424 break;
13425 }
13426 if (x < windows->image.ximage->width)
13427 break;
13428 }
13429 trim_info.height=(size_t) y-trim_info.y+1;
13430 if (((unsigned int) trim_info.width != windows->image.width) ||
13431 ((unsigned int) trim_info.height != windows->image.height))
13432 {
13433 /*
13434 Reconfigure Image window as defined by the trimming rectangle.
13435 */
13436 XSetCropGeometry(display,windows,&trim_info,image);
13437 windows->image.window_changes.width=(int) trim_info.width;
13438 windows->image.window_changes.height=(int) trim_info.height;
13439 (void) XConfigureImage(display,resource_info,windows,image);
13440 }
13441 XSetCursorState(display,windows,MagickFalse);
13442 return(MagickTrue);
13443}
13444
13445/*
13446%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13447% %
13448% %
13449% %
13450+ X V i s u a l D i r e c t o r y I m a g e %
13451% %
13452% %
13453% %
13454%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13455%
13456% XVisualDirectoryImage() creates a Visual Image Directory.
13457%
13458% The format of the XVisualDirectoryImage method is:
13459%
13460% Image *XVisualDirectoryImage(Display *display,
13461% XResourceInfo *resource_info,XWindows *windows)
13462%
13463% A description of each parameter follows:
13464%
13465% o nexus: Method XVisualDirectoryImage returns a visual image
13466% directory if it can be created successfully. Otherwise a null image
13467% is returned.
13468%
13469% o display: Specifies a connection to an X server; returned from
13470% XOpenDisplay.
13471%
13472% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13473%
13474% o windows: Specifies a pointer to a XWindows structure.
13475%
13476*/
13477static Image *XVisualDirectoryImage(Display *display,
13478 XResourceInfo *resource_info,XWindows *windows)
13479{
13480#define TileImageTag "Scale/Image"
13481#define XClientName "montage"
13482
13483 char
13484 **filelist;
13485
13487 *exception;
13488
13489 Image
13490 *images,
13491 *montage_image,
13492 *next_image,
13493 *thumbnail_image;
13494
13495 ImageInfo
13496 *read_info;
13497
13498 int
13499 number_files;
13500
13501 MagickBooleanType
13502 backdrop;
13503
13504 MagickStatusType
13505 status;
13506
13508 *montage_info;
13509
13511 geometry;
13512
13513 int
13514 i;
13515
13516 static char
13517 filename[MaxTextExtent] = "\0",
13518 filenames[MaxTextExtent] = "*";
13519
13520 XResourceInfo
13521 background_resources;
13522
13523 /*
13524 Request file name from user.
13525 */
13526 XFileBrowserWidget(display,windows,"Directory",filenames);
13527 if (*filenames == '\0')
13528 return((Image *) NULL);
13529 /*
13530 Expand the filenames.
13531 */
13532 filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13533 if (filelist == (char **) NULL)
13534 {
13535 ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13536 filenames);
13537 return((Image *) NULL);
13538 }
13539 number_files=1;
13540 filelist[0]=filenames;
13541 status=ExpandFilenames(&number_files,&filelist);
13542 if ((status == MagickFalse) || (number_files == 0))
13543 {
13544 if (number_files == 0)
13545 ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13546 else
13547 ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13548 filenames);
13549 return((Image *) NULL);
13550 }
13551 /*
13552 Set image background resources.
13553 */
13554 background_resources=(*resource_info);
13555 background_resources.window_id=AcquireString("");
13556 (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13557 "0x%lx",windows->image.id);
13558 background_resources.backdrop=MagickTrue;
13559 /*
13560 Read each image and convert them to a tile.
13561 */
13562 backdrop=(windows->visual_info->klass == TrueColor) ||
13563 (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13564 read_info=CloneImageInfo(resource_info->image_info);
13565 (void) SetImageOption(read_info,"jpeg:size","120x120");
13566 (void) CloneString(&read_info->size,DefaultTileGeometry);
13567 (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13568 (void *) NULL);
13569 images=NewImageList();
13570 exception=AcquireExceptionInfo();
13571 XSetCursorState(display,windows,MagickTrue);
13572 XCheckRefreshWindows(display,windows);
13573 for (i=0; i < (int) number_files; i++)
13574 {
13575 (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13576 filelist[i]=DestroyString(filelist[i]);
13577 *read_info->magick='\0';
13578 next_image=ReadImage(read_info,exception);
13579 CatchException(exception);
13580 if (next_image != (Image *) NULL)
13581 {
13582 (void) DeleteImageProperty(next_image,"label");
13583 (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13584 read_info,next_image,DefaultTileLabel));
13585 (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13586 exception);
13587 thumbnail_image=ThumbnailImage(next_image,geometry.width,
13588 geometry.height,exception);
13589 if (thumbnail_image != (Image *) NULL)
13590 {
13591 next_image=DestroyImage(next_image);
13592 next_image=thumbnail_image;
13593 }
13594 if (backdrop)
13595 {
13596 (void) XDisplayBackgroundImage(display,&background_resources,
13597 next_image);
13598 XSetCursorState(display,windows,MagickTrue);
13599 }
13600 AppendImageToList(&images,next_image);
13601 if (images->progress_monitor != (MagickProgressMonitor) NULL)
13602 {
13603 MagickBooleanType
13604 proceed;
13605
13606 proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13607 (MagickSizeType) number_files);
13608 if (proceed == MagickFalse)
13609 break;
13610 }
13611 }
13612 }
13613 exception=DestroyExceptionInfo(exception);
13614 filelist=(char **) RelinquishMagickMemory(filelist);
13615 if (images == (Image *) NULL)
13616 {
13617 read_info=DestroyImageInfo(read_info);
13618 XSetCursorState(display,windows,MagickFalse);
13619 ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13620 return((Image *) NULL);
13621 }
13622 /*
13623 Create the Visual Image Directory.
13624 */
13625 montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13626 montage_info->pointsize=10;
13627 if (resource_info->font != (char *) NULL)
13628 (void) CloneString(&montage_info->font,resource_info->font);
13629 (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13630 montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13631 images),&images->exception);
13632 images=DestroyImageList(images);
13633 montage_info=DestroyMontageInfo(montage_info);
13634 read_info=DestroyImageInfo(read_info);
13635 XSetCursorState(display,windows,MagickFalse);
13636 if (montage_image == (Image *) NULL)
13637 return(montage_image);
13638 XClientMessage(display,windows->image.id,windows->im_protocols,
13639 windows->im_next_image,CurrentTime);
13640 return(montage_image);
13641}
13642
13643/*
13644%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13645% %
13646% %
13647% %
13648% X D i s p l a y B a c k g r o u n d I m a g e %
13649% %
13650% %
13651% %
13652%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13653%
13654% XDisplayBackgroundImage() displays an image in the background of a window.
13655%
13656% The format of the XDisplayBackgroundImage method is:
13657%
13658% MagickBooleanType XDisplayBackgroundImage(Display *display,
13659% XResourceInfo *resource_info,Image *image)
13660%
13661% A description of each parameter follows:
13662%
13663% o display: Specifies a connection to an X server; returned from
13664% XOpenDisplay.
13665%
13666% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13667%
13668% o image: the image.
13669%
13670*/
13671MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13672 XResourceInfo *resource_info,Image *image)
13673{
13674 char
13675 geometry[MaxTextExtent],
13676 visual_type[MaxTextExtent];
13677
13678 int
13679 height,
13680 status,
13681 width;
13682
13684 geometry_info;
13685
13686 static XPixelInfo
13687 pixel;
13688
13689 static XStandardColormap
13690 *map_info;
13691
13692 static XVisualInfo
13693 *visual_info = (XVisualInfo *) NULL;
13694
13695 static XWindowInfo
13696 window_info;
13697
13698 size_t
13699 delay;
13700
13701 Window
13702 root_window;
13703
13704 XGCValues
13705 context_values;
13706
13707 XResourceInfo
13708 resources;
13709
13710 XWindowAttributes
13711 window_attributes;
13712
13713 /*
13714 Determine target window.
13715 */
13716 assert(image != (Image *) NULL);
13717 assert(image->signature == MagickCoreSignature);
13718 if (IsEventLogging() != MagickFalse)
13719 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13720 resources=(*resource_info);
13721 window_info.id=(Window) NULL;
13722 root_window=XRootWindow(display,XDefaultScreen(display));
13723 if (LocaleCompare(resources.window_id,"root") == 0)
13724 window_info.id=root_window;
13725 else
13726 {
13727 if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13728 window_info.id=XWindowByID(display,root_window,
13729 (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13730 if (window_info.id == (Window) NULL)
13731 window_info.id=XWindowByName(display,root_window,resources.window_id);
13732 }
13733 if (window_info.id == (Window) NULL)
13734 {
13735 ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13736 resources.window_id);
13737 }
13738 /*
13739 Determine window visual id.
13740 */
13741 window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13742 window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13743 (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13744 status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13745 if (status != 0)
13746 (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13747 XVisualIDFromVisual(window_attributes.visual));
13748 if (visual_info == (XVisualInfo *) NULL)
13749 {
13750 /*
13751 Allocate standard colormap.
13752 */
13753 map_info=XAllocStandardColormap();
13754 if (map_info == (XStandardColormap *) NULL)
13755 ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13756 image->filename);
13757 map_info->colormap=(Colormap) NULL;
13758 pixel.pixels=(unsigned long *) NULL;
13759 /*
13760 Initialize visual info.
13761 */
13762 resources.map_type=(char *) NULL;
13763 resources.visual_type=visual_type;
13764 visual_info=XBestVisualInfo(display,map_info,&resources);
13765 if (visual_info == (XVisualInfo *) NULL)
13766 ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13767 resources.visual_type);
13768 /*
13769 Initialize window info.
13770 */
13771 window_info.ximage=(XImage *) NULL;
13772 window_info.matte_image=(XImage *) NULL;
13773 window_info.pixmap=(Pixmap) NULL;
13774 window_info.matte_pixmap=(Pixmap) NULL;
13775 }
13776 /*
13777 Free previous root colors.
13778 */
13779 if (window_info.id == root_window)
13780 (void) XDestroyWindowColors(display,root_window);
13781 /*
13782 Initialize Standard Colormap.
13783 */
13784 resources.colormap=SharedColormap;
13785 XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13786 /*
13787 Graphic context superclass.
13788 */
13789 context_values.background=pixel.foreground_color.pixel;
13790 context_values.foreground=pixel.background_color.pixel;
13791 pixel.annotate_context=XCreateGC(display,window_info.id,
13792 (size_t) (GCBackground | GCForeground),&context_values);
13793 if (pixel.annotate_context == (GC) NULL)
13794 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13795 image->filename);
13796 /*
13797 Initialize Image window attributes.
13798 */
13799 window_info.name=AcquireString("\0");
13800 window_info.icon_name=AcquireString("\0");
13801 XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13802 &resources,&window_info);
13803 /*
13804 Create the X image.
13805 */
13806 window_info.width=(unsigned int) image->columns;
13807 window_info.height=(unsigned int) image->rows;
13808 if ((image->columns != window_info.width) ||
13809 (image->rows != window_info.height))
13810 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13811 image->filename);
13812 (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13813 window_attributes.width,window_attributes.height);
13814 geometry_info.width=window_info.width;
13815 geometry_info.height=window_info.height;
13816 geometry_info.x=(ssize_t) window_info.x;
13817 geometry_info.y=(ssize_t) window_info.y;
13818 (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13819 &geometry_info.width,&geometry_info.height);
13820 window_info.width=(unsigned int) geometry_info.width;
13821 window_info.height=(unsigned int) geometry_info.height;
13822 window_info.x=(int) geometry_info.x;
13823 window_info.y=(int) geometry_info.y;
13824 status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13825 window_info.height);
13826 if (status == MagickFalse)
13827 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13828 image->filename);
13829 window_info.x=0;
13830 window_info.y=0;
13831 if (resource_info->debug != MagickFalse)
13832 {
13833 (void) LogMagickEvent(X11Event,GetMagickModule(),
13834 "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13835 (double) image->columns,(double) image->rows);
13836 if (image->colors != 0)
13837 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13838 image->colors);
13839 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13840 }
13841 /*
13842 Adjust image dimensions as specified by backdrop or geometry options.
13843 */
13844 width=(int) window_info.width;
13845 height=(int) window_info.height;
13846 if (resources.backdrop != MagickFalse)
13847 {
13848 /*
13849 Center image on window.
13850 */
13851 window_info.x=(window_attributes.width/2)-(window_info.ximage->width/2);
13852 window_info.y=(window_attributes.height/2)-(window_info.ximage->height/2);
13853 width=window_attributes.width;
13854 height=window_attributes.height;
13855 }
13856 if ((resources.image_geometry != (char *) NULL) &&
13857 (*resources.image_geometry != '\0'))
13858 {
13859 char
13860 default_geometry[MaxTextExtent];
13861
13862 int
13863 flags,
13864 gravity;
13865
13866 XSizeHints
13867 *size_hints;
13868
13869 /*
13870 User specified geometry.
13871 */
13872 size_hints=XAllocSizeHints();
13873 if (size_hints == (XSizeHints *) NULL)
13874 ThrowXWindowFatalException(ResourceLimitFatalError,
13875 "MemoryAllocationFailed",image->filename);
13876 size_hints->flags=0L;
13877 (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13878 width,height);
13879 flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13880 default_geometry,window_info.border_width,size_hints,&window_info.x,
13881 &window_info.y,&width,&height,&gravity);
13882 if (flags & (XValue | YValue))
13883 {
13884 width=window_attributes.width;
13885 height=window_attributes.height;
13886 }
13887 (void) XFree((void *) size_hints);
13888 }
13889 /*
13890 Create the X pixmap.
13891 */
13892 window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13893 (unsigned int) height,window_info.depth);
13894 if (window_info.pixmap == (Pixmap) NULL)
13895 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13896 image->filename);
13897 /*
13898 Display pixmap on the window.
13899 */
13900 if (((unsigned int) width > window_info.width) ||
13901 ((unsigned int) height > window_info.height))
13902 (void) XFillRectangle(display,window_info.pixmap,
13903 window_info.annotate_context,0,0,(unsigned int) width,
13904 (unsigned int) height);
13905 (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13906 window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13907 window_info.width,(unsigned int) window_info.height);
13908 (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13909 (void) XClearWindow(display,window_info.id);
13910 delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13911 XDelay(display,delay == 0UL ? 10UL : delay);
13912 (void) XSync(display,MagickFalse);
13913 return(window_info.id == root_window ? MagickTrue : MagickFalse);
13914}
13915
13916/*
13917%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13918% %
13919% %
13920% %
13921+ X D i s p l a y I m a g e %
13922% %
13923% %
13924% %
13925%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13926%
13927% XDisplayImage() displays an image via X11. A new image is created and
13928% returned if the user interactively transforms the displayed image.
13929%
13930% The format of the XDisplayImage method is:
13931%
13932% Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13933% char **argv,int argc,Image **image,size_t *state)
13934%
13935% A description of each parameter follows:
13936%
13937% o nexus: Method XDisplayImage returns an image when the
13938% user chooses 'Open Image' from the command menu or picks a tile
13939% from the image directory. Otherwise a null image is returned.
13940%
13941% o display: Specifies a connection to an X server; returned from
13942% XOpenDisplay.
13943%
13944% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13945%
13946% o argv: Specifies the application's argument list.
13947%
13948% o argc: Specifies the number of arguments.
13949%
13950% o image: Specifies an address to an address of an Image structure;
13951%
13952*/
13953MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13954 char **argv,int argc,Image **image,size_t *state)
13955{
13956#define MagnifySize 256 /* must be a power of 2 */
13957#define MagickMenus 10
13958#define MagickTitle "Commands"
13959
13960 const char
13961 *const CommandMenu[] =
13962 {
13963 "File",
13964 "Edit",
13965 "View",
13966 "Transform",
13967 "Enhance",
13968 "Effects",
13969 "F/X",
13970 "Image Edit",
13971 "Miscellany",
13972 "Help",
13973 (char *) NULL
13974 },
13975 *const FileMenu[] =
13976 {
13977 "Open...",
13978 "Next",
13979 "Former",
13980 "Select...",
13981 "Save...",
13982 "Print...",
13983 "Delete...",
13984 "New...",
13985 "Visual Directory...",
13986 "Quit",
13987 (char *) NULL
13988 },
13989 *const EditMenu[] =
13990 {
13991 "Undo",
13992 "Redo",
13993 "Cut",
13994 "Copy",
13995 "Paste",
13996 (char *) NULL
13997 },
13998 *const ViewMenu[] =
13999 {
14000 "Half Size",
14001 "Original Size",
14002 "Double Size",
14003 "Resize...",
14004 "Apply",
14005 "Refresh",
14006 "Restore",
14007 (char *) NULL
14008 },
14009 *const TransformMenu[] =
14010 {
14011 "Crop",
14012 "Chop",
14013 "Flop",
14014 "Flip",
14015 "Rotate Right",
14016 "Rotate Left",
14017 "Rotate...",
14018 "Shear...",
14019 "Roll...",
14020 "Trim Edges",
14021 (char *) NULL
14022 },
14023 *const EnhanceMenu[] =
14024 {
14025 "Hue...",
14026 "Saturation...",
14027 "Brightness...",
14028 "Gamma...",
14029 "Spiff",
14030 "Dull",
14031 "Contrast Stretch...",
14032 "Sigmoidal Contrast...",
14033 "Normalize",
14034 "Equalize",
14035 "Negate",
14036 "Grayscale",
14037 "Map...",
14038 "Quantize...",
14039 (char *) NULL
14040 },
14041 *const EffectsMenu[] =
14042 {
14043 "Despeckle",
14044 "Emboss",
14045 "Reduce Noise",
14046 "Add Noise...",
14047 "Sharpen...",
14048 "Blur...",
14049 "Threshold...",
14050 "Edge Detect...",
14051 "Spread...",
14052 "Shade...",
14053 "Raise...",
14054 "Segment...",
14055 (char *) NULL
14056 },
14057 *const FXMenu[] =
14058 {
14059 "Solarize...",
14060 "Sepia Tone...",
14061 "Swirl...",
14062 "Implode...",
14063 "Vignette...",
14064 "Wave...",
14065 "Oil Paint...",
14066 "Charcoal Draw...",
14067 (char *) NULL
14068 },
14069 *const ImageEditMenu[] =
14070 {
14071 "Annotate...",
14072 "Draw...",
14073 "Color...",
14074 "Matte...",
14075 "Composite...",
14076 "Add Border...",
14077 "Add Frame...",
14078 "Comment...",
14079 "Launch...",
14080 "Region of Interest...",
14081 (char *) NULL
14082 },
14083 *const MiscellanyMenu[] =
14084 {
14085 "Image Info",
14086 "Zoom Image",
14087 "Show Preview...",
14088 "Show Histogram",
14089 "Show Matte",
14090 "Background...",
14091 "Slide Show...",
14092 "Preferences...",
14093 (char *) NULL
14094 },
14095 *const HelpMenu[] =
14096 {
14097 "Overview",
14098 "Browse Documentation",
14099 "About Display",
14100 (char *) NULL
14101 },
14102 *const ShortCutsMenu[] =
14103 {
14104 "Next",
14105 "Former",
14106 "Open...",
14107 "Save...",
14108 "Print...",
14109 "Undo",
14110 "Restore",
14111 "Image Info",
14112 "Quit",
14113 (char *) NULL
14114 },
14115 *const VirtualMenu[] =
14116 {
14117 "Image Info",
14118 "Print",
14119 "Next",
14120 "Quit",
14121 (char *) NULL
14122 };
14123
14124 const char
14125 *const *Menus[MagickMenus] =
14126 {
14127 FileMenu,
14128 EditMenu,
14129 ViewMenu,
14130 TransformMenu,
14131 EnhanceMenu,
14132 EffectsMenu,
14133 FXMenu,
14134 ImageEditMenu,
14135 MiscellanyMenu,
14136 HelpMenu
14137 };
14138
14139 static DisplayCommand
14140 CommandMenus[] =
14141 {
14142 NullCommand,
14143 NullCommand,
14144 NullCommand,
14145 NullCommand,
14146 NullCommand,
14147 NullCommand,
14148 NullCommand,
14149 NullCommand,
14150 NullCommand,
14151 NullCommand,
14152 },
14153 FileCommands[] =
14154 {
14155 OpenCommand,
14156 NextCommand,
14157 FormerCommand,
14158 SelectCommand,
14159 SaveCommand,
14160 PrintCommand,
14161 DeleteCommand,
14162 NewCommand,
14163 VisualDirectoryCommand,
14164 QuitCommand
14165 },
14166 EditCommands[] =
14167 {
14168 UndoCommand,
14169 RedoCommand,
14170 CutCommand,
14171 CopyCommand,
14172 PasteCommand
14173 },
14174 ViewCommands[] =
14175 {
14176 HalfSizeCommand,
14177 OriginalSizeCommand,
14178 DoubleSizeCommand,
14179 ResizeCommand,
14180 ApplyCommand,
14181 RefreshCommand,
14182 RestoreCommand
14183 },
14184 TransformCommands[] =
14185 {
14186 CropCommand,
14187 ChopCommand,
14188 FlopCommand,
14189 FlipCommand,
14190 RotateRightCommand,
14191 RotateLeftCommand,
14192 RotateCommand,
14193 ShearCommand,
14194 RollCommand,
14195 TrimCommand
14196 },
14197 EnhanceCommands[] =
14198 {
14199 HueCommand,
14200 SaturationCommand,
14201 BrightnessCommand,
14202 GammaCommand,
14203 SpiffCommand,
14204 DullCommand,
14205 ContrastStretchCommand,
14206 SigmoidalContrastCommand,
14207 NormalizeCommand,
14208 EqualizeCommand,
14209 NegateCommand,
14210 GrayscaleCommand,
14211 MapCommand,
14212 QuantizeCommand
14213 },
14214 EffectsCommands[] =
14215 {
14216 DespeckleCommand,
14217 EmbossCommand,
14218 ReduceNoiseCommand,
14219 AddNoiseCommand,
14220 SharpenCommand,
14221 BlurCommand,
14222 ThresholdCommand,
14223 EdgeDetectCommand,
14224 SpreadCommand,
14225 ShadeCommand,
14226 RaiseCommand,
14227 SegmentCommand
14228 },
14229 FXCommands[] =
14230 {
14231 SolarizeCommand,
14232 SepiaToneCommand,
14233 SwirlCommand,
14234 ImplodeCommand,
14235 VignetteCommand,
14236 WaveCommand,
14237 OilPaintCommand,
14238 CharcoalDrawCommand
14239 },
14240 ImageEditCommands[] =
14241 {
14242 AnnotateCommand,
14243 DrawCommand,
14244 ColorCommand,
14245 MatteCommand,
14246 CompositeCommand,
14247 AddBorderCommand,
14248 AddFrameCommand,
14249 CommentCommand,
14250 LaunchCommand,
14251 RegionOfInterestCommand
14252 },
14253 MiscellanyCommands[] =
14254 {
14255 InfoCommand,
14256 ZoomCommand,
14257 ShowPreviewCommand,
14258 ShowHistogramCommand,
14259 ShowMatteCommand,
14260 BackgroundCommand,
14261 SlideShowCommand,
14262 PreferencesCommand
14263 },
14264 HelpCommands[] =
14265 {
14266 HelpCommand,
14267 BrowseDocumentationCommand,
14268 VersionCommand
14269 },
14270 ShortCutsCommands[] =
14271 {
14272 NextCommand,
14273 FormerCommand,
14274 OpenCommand,
14275 SaveCommand,
14276 PrintCommand,
14277 UndoCommand,
14278 RestoreCommand,
14279 InfoCommand,
14280 QuitCommand
14281 },
14282 VirtualCommands[] =
14283 {
14284 InfoCommand,
14285 PrintCommand,
14286 NextCommand,
14287 QuitCommand
14288 };
14289
14290 static DisplayCommand
14291 *Commands[MagickMenus] =
14292 {
14293 FileCommands,
14294 EditCommands,
14295 ViewCommands,
14296 TransformCommands,
14297 EnhanceCommands,
14298 EffectsCommands,
14299 FXCommands,
14300 ImageEditCommands,
14301 MiscellanyCommands,
14302 HelpCommands
14303 };
14304
14305 char
14306 command[MaxTextExtent],
14307 *directory,
14308 geometry[MaxTextExtent],
14309 resource_name[MaxTextExtent];
14310
14311 DisplayCommand
14312 display_command;
14313
14314 Image
14315 *display_image,
14316 *nexus;
14317
14318 int
14319 entry,
14320 id;
14321
14322 KeySym
14323 key_symbol;
14324
14325 MagickStatusType
14326 context_mask,
14327 status;
14328
14330 geometry_info;
14331
14332 int
14333 i;
14334
14335 static char
14336 working_directory[MaxTextExtent];
14337
14338 static XPoint
14339 vid_info;
14340
14341 static XWindowInfo
14342 *magick_windows[MaxXWindows];
14343
14344 static unsigned int
14345 number_windows;
14346
14347 struct stat
14348 attributes;
14349
14350 time_t
14351 timer,
14352 timestamp,
14353 update_time;
14354
14355 unsigned int
14356 height,
14357 width;
14358
14359 size_t
14360 delay;
14361
14362 WarningHandler
14363 warning_handler;
14364
14365 Window
14366 root_window;
14367
14368 XClassHint
14369 *class_hints;
14370
14371 XEvent
14372 event;
14373
14374 XFontStruct
14375 *font_info;
14376
14377 XGCValues
14378 context_values;
14379
14380 XPixelInfo
14381 *icon_pixel,
14382 *pixel;
14383
14384 XResourceInfo
14385 *icon_resources;
14386
14387 XStandardColormap
14388 *icon_map,
14389 *map_info;
14390
14391 XVisualInfo
14392 *icon_visual,
14393 *visual_info;
14394
14395 XWindowChanges
14396 window_changes;
14397
14398 XWindows
14399 *windows;
14400
14401 XWMHints
14402 *manager_hints;
14403
14404 assert(image != (Image **) NULL);
14405 assert((*image)->signature == MagickCoreSignature);
14406 if (IsEventLogging() != MagickFalse)
14407 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14408 display_image=(*image);
14409 warning_handler=(WarningHandler) NULL;
14410 windows=XSetWindows((XWindows *) ~0);
14411 if (windows != (XWindows *) NULL)
14412 {
14413 int
14414 status;
14415
14416 if (*working_directory == '\0')
14417 (void) CopyMagickString(working_directory,".",MaxTextExtent);
14418 status=chdir(working_directory);
14419 if (status == -1)
14420 (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
14421 FileOpenError,"UnableToOpenFile","%s",working_directory);
14422 warning_handler=resource_info->display_warnings ?
14423 SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14424 warning_handler=resource_info->display_warnings ?
14425 SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14426 }
14427 else
14428 {
14429 /*
14430 Allocate windows structure.
14431 */
14432 resource_info->colors=display_image->colors;
14433 windows=XSetWindows(XInitializeWindows(display,resource_info));
14434 if (windows == (XWindows *) NULL)
14435 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14436 (*image)->filename);
14437 /*
14438 Initialize window id's.
14439 */
14440 number_windows=0;
14441 magick_windows[number_windows++]=(&windows->icon);
14442 magick_windows[number_windows++]=(&windows->backdrop);
14443 magick_windows[number_windows++]=(&windows->image);
14444 magick_windows[number_windows++]=(&windows->info);
14445 magick_windows[number_windows++]=(&windows->command);
14446 magick_windows[number_windows++]=(&windows->widget);
14447 magick_windows[number_windows++]=(&windows->popup);
14448 magick_windows[number_windows++]=(&windows->magnify);
14449 magick_windows[number_windows++]=(&windows->pan);
14450 for (i=0; i < (int) number_windows; i++)
14451 magick_windows[i]->id=(Window) NULL;
14452 vid_info.x=0;
14453 vid_info.y=0;
14454 }
14455 /*
14456 Initialize font info.
14457 */
14458 if (windows->font_info != (XFontStruct *) NULL)
14459 (void) XFreeFont(display,windows->font_info);
14460 windows->font_info=XBestFont(display,resource_info,MagickFalse);
14461 if (windows->font_info == (XFontStruct *) NULL)
14462 ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14463 resource_info->font);
14464 /*
14465 Initialize Standard Colormap.
14466 */
14467 map_info=windows->map_info;
14468 icon_map=windows->icon_map;
14469 visual_info=windows->visual_info;
14470 icon_visual=windows->icon_visual;
14471 pixel=windows->pixel_info;
14472 icon_pixel=windows->icon_pixel;
14473 font_info=windows->font_info;
14474 icon_resources=windows->icon_resources;
14475 class_hints=windows->class_hints;
14476 manager_hints=windows->manager_hints;
14477 root_window=XRootWindow(display,visual_info->screen);
14478 nexus=NewImageList();
14479 if (resource_info->debug != MagickFalse)
14480 {
14481 (void) LogMagickEvent(X11Event,GetMagickModule(),
14482 "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14483 (double) display_image->scene,(double) display_image->columns,
14484 (double) display_image->rows);
14485 if (display_image->colors != 0)
14486 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14487 display_image->colors);
14488 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14489 display_image->magick);
14490 }
14491 XMakeStandardColormap(display,visual_info,resource_info,display_image,
14492 map_info,pixel);
14493 display_image->taint=MagickFalse;
14494 /*
14495 Initialize graphic context.
14496 */
14497 windows->context.id=(Window) NULL;
14498 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14499 resource_info,&windows->context);
14500 (void) CloneString(&class_hints->res_name,resource_info->client_name);
14501 (void) CloneString(&class_hints->res_class,resource_info->client_name);
14502 class_hints->res_class[0]=(char) LocaleToUppercase((int)
14503 class_hints->res_class[0]);
14504 manager_hints->flags=InputHint | StateHint;
14505 manager_hints->input=MagickFalse;
14506 manager_hints->initial_state=WithdrawnState;
14507 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14508 &windows->context);
14509 if (resource_info->debug != MagickFalse)
14510 (void) LogMagickEvent(X11Event,GetMagickModule(),
14511 "Window id: 0x%lx (context)",windows->context.id);
14512 context_values.background=pixel->background_color.pixel;
14513 context_values.font=font_info->fid;
14514 context_values.foreground=pixel->foreground_color.pixel;
14515 context_values.graphics_exposures=MagickFalse;
14516 context_mask=(MagickStatusType)
14517 (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14518 if (pixel->annotate_context != (GC) NULL)
14519 (void) XFreeGC(display,pixel->annotate_context);
14520 pixel->annotate_context=XCreateGC(display,windows->context.id,
14521 context_mask,&context_values);
14522 if (pixel->annotate_context == (GC) NULL)
14523 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14524 display_image->filename);
14525 context_values.background=pixel->depth_color.pixel;
14526 if (pixel->widget_context != (GC) NULL)
14527 (void) XFreeGC(display,pixel->widget_context);
14528 pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14529 &context_values);
14530 if (pixel->widget_context == (GC) NULL)
14531 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14532 display_image->filename);
14533 context_values.background=pixel->foreground_color.pixel;
14534 context_values.foreground=pixel->background_color.pixel;
14535 context_values.plane_mask=context_values.background ^
14536 context_values.foreground;
14537 if (pixel->highlight_context != (GC) NULL)
14538 (void) XFreeGC(display,pixel->highlight_context);
14539 pixel->highlight_context=XCreateGC(display,windows->context.id,
14540 (size_t) (context_mask | GCPlaneMask),&context_values);
14541 if (pixel->highlight_context == (GC) NULL)
14542 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14543 display_image->filename);
14544 (void) XDestroyWindow(display,windows->context.id);
14545 /*
14546 Initialize icon window.
14547 */
14548 XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14549 icon_resources,&windows->icon);
14550 windows->icon.geometry=resource_info->icon_geometry;
14551 XBestIconSize(display,&windows->icon,display_image);
14552 windows->icon.attributes.colormap=XDefaultColormap(display,
14553 icon_visual->screen);
14554 windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14555 manager_hints->flags=InputHint | StateHint;
14556 manager_hints->input=MagickFalse;
14557 manager_hints->initial_state=IconicState;
14558 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14559 &windows->icon);
14560 if (resource_info->debug != MagickFalse)
14561 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14562 windows->icon.id);
14563 /*
14564 Initialize graphic context for icon window.
14565 */
14566 if (icon_pixel->annotate_context != (GC) NULL)
14567 (void) XFreeGC(display,icon_pixel->annotate_context);
14568 context_values.background=icon_pixel->background_color.pixel;
14569 context_values.foreground=icon_pixel->foreground_color.pixel;
14570 icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14571 (size_t) (GCBackground | GCForeground),&context_values);
14572 if (icon_pixel->annotate_context == (GC) NULL)
14573 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14574 display_image->filename);
14575 windows->icon.annotate_context=icon_pixel->annotate_context;
14576 /*
14577 Initialize Image window.
14578 */
14579 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14580 &windows->image);
14581 windows->image.shape=MagickTrue; /* non-rectangular shape hint */
14582 if (resource_info->use_shared_memory == MagickFalse)
14583 windows->image.shared_memory=MagickFalse;
14584 if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14585 {
14586 char
14587 *title;
14588
14589 title=InterpretImageProperties(resource_info->image_info,display_image,
14590 resource_info->title);
14591 (void) CloneString(&windows->image.name,title);
14592 (void) CloneString(&windows->image.icon_name,title);
14593 title=DestroyString(title);
14594 }
14595 else
14596 {
14597 char
14598 filename[MaxTextExtent],
14599 window_name[MaxTextExtent];
14600
14601 /*
14602 Window name is the base of the filename.
14603 */
14604 GetPathComponent(display_image->magick_filename,TailPath,filename);
14605 if (display_image->scene == 0)
14606 (void) FormatLocaleString(window_name,MaxTextExtent,"%s: %s",
14607 MagickPackageName,filename);
14608 else
14609 (void) FormatLocaleString(window_name,MaxTextExtent,
14610 "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14611 (double) display_image->scene,(double) GetImageListLength(
14612 display_image));
14613 (void) CloneString(&windows->image.name,window_name);
14614 (void) CloneString(&windows->image.icon_name,filename);
14615 }
14616 if (resource_info->immutable)
14617 windows->image.immutable=MagickTrue;
14618 windows->image.use_pixmap=resource_info->use_pixmap;
14619 windows->image.geometry=resource_info->image_geometry;
14620 (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14621 XDisplayWidth(display,visual_info->screen),
14622 XDisplayHeight(display,visual_info->screen));
14623 geometry_info.width=display_image->columns;
14624 geometry_info.height=display_image->rows;
14625 geometry_info.x=0;
14626 geometry_info.y=0;
14627 (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14628 &geometry_info.width,&geometry_info.height);
14629 windows->image.width=(unsigned int) geometry_info.width;
14630 windows->image.height=(unsigned int) geometry_info.height;
14631 windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14632 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14633 KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14634 PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14635 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14636 resource_info,&windows->backdrop);
14637 if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14638 {
14639 /*
14640 Initialize backdrop window.
14641 */
14642 windows->backdrop.x=0;
14643 windows->backdrop.y=0;
14644 (void) CloneString(&windows->backdrop.name,"Backdrop");
14645 windows->backdrop.flags=(size_t) (USSize | USPosition);
14646 windows->backdrop.width=(unsigned int)
14647 XDisplayWidth(display,visual_info->screen);
14648 windows->backdrop.height=(unsigned int)
14649 XDisplayHeight(display,visual_info->screen);
14650 windows->backdrop.border_width=0;
14651 windows->backdrop.immutable=MagickTrue;
14652 windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14653 ButtonReleaseMask;
14654 windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14655 StructureNotifyMask;
14656 manager_hints->flags=IconWindowHint | InputHint | StateHint;
14657 manager_hints->icon_window=windows->icon.id;
14658 manager_hints->input=MagickTrue;
14659 manager_hints->initial_state=resource_info->iconic ? IconicState :
14660 NormalState;
14661 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14662 &windows->backdrop);
14663 if (resource_info->debug != MagickFalse)
14664 (void) LogMagickEvent(X11Event,GetMagickModule(),
14665 "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14666 (void) XMapWindow(display,windows->backdrop.id);
14667 (void) XClearWindow(display,windows->backdrop.id);
14668 if (windows->image.id != (Window) NULL)
14669 {
14670 (void) XDestroyWindow(display,windows->image.id);
14671 windows->image.id=(Window) NULL;
14672 }
14673 /*
14674 Position image in the center the backdrop.
14675 */
14676 windows->image.flags|=USPosition;
14677 windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14678 (windows->image.width/2);
14679 windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14680 (windows->image.height/2);
14681 }
14682 manager_hints->flags=IconWindowHint | InputHint | StateHint;
14683 manager_hints->icon_window=windows->icon.id;
14684 manager_hints->input=MagickTrue;
14685 manager_hints->initial_state=resource_info->iconic ? IconicState :
14686 NormalState;
14687 if (windows->group_leader.id != (Window) NULL)
14688 {
14689 /*
14690 Follow the leader.
14691 */
14692 manager_hints->flags|=WindowGroupHint;
14693 manager_hints->window_group=windows->group_leader.id;
14694 (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14695 if (resource_info->debug != MagickFalse)
14696 (void) LogMagickEvent(X11Event,GetMagickModule(),
14697 "Window id: 0x%lx (group leader)",windows->group_leader.id);
14698 }
14699 XMakeWindow(display,
14700 (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14701 argv,argc,class_hints,manager_hints,&windows->image);
14702 (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14703 XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14704 if (windows->group_leader.id != (Window) NULL)
14705 (void) XSetTransientForHint(display,windows->image.id,
14706 windows->group_leader.id);
14707 if (resource_info->debug != MagickFalse)
14708 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14709 windows->image.id);
14710 /*
14711 Initialize Info widget.
14712 */
14713 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14714 &windows->info);
14715 (void) CloneString(&windows->info.name,"Info");
14716 (void) CloneString(&windows->info.icon_name,"Info");
14717 windows->info.border_width=1;
14718 windows->info.x=2;
14719 windows->info.y=2;
14720 windows->info.flags|=PPosition;
14721 windows->info.attributes.win_gravity=UnmapGravity;
14722 windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14723 StructureNotifyMask;
14724 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14725 manager_hints->input=MagickFalse;
14726 manager_hints->initial_state=NormalState;
14727 manager_hints->window_group=windows->image.id;
14728 XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14729 &windows->info);
14730 windows->info.highlight_stipple=XCreateBitmapFromData(display,
14731 windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14732 windows->info.shadow_stipple=XCreateBitmapFromData(display,
14733 windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14734 (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14735 if (windows->image.mapped != MagickFalse)
14736 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14737 if (resource_info->debug != MagickFalse)
14738 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14739 windows->info.id);
14740 /*
14741 Initialize Command widget.
14742 */
14743 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14744 resource_info,&windows->command);
14745 windows->command.data=MagickMenus;
14746 (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14747 (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14748 resource_info->client_name);
14749 windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14750 resource_name,"geometry",(char *) NULL);
14751 (void) CloneString(&windows->command.name,MagickTitle);
14752 windows->command.border_width=0;
14753 windows->command.flags|=PPosition;
14754 windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14755 ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14756 OwnerGrabButtonMask | StructureNotifyMask;
14757 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14758 manager_hints->input=MagickTrue;
14759 manager_hints->initial_state=NormalState;
14760 manager_hints->window_group=windows->image.id;
14761 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14762 &windows->command);
14763 windows->command.highlight_stipple=XCreateBitmapFromData(display,
14764 windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14765 HighlightHeight);
14766 windows->command.shadow_stipple=XCreateBitmapFromData(display,
14767 windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14768 (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14769 if (windows->command.mapped != MagickFalse)
14770 (void) XMapRaised(display,windows->command.id);
14771 if (resource_info->debug != MagickFalse)
14772 (void) LogMagickEvent(X11Event,GetMagickModule(),
14773 "Window id: 0x%lx (command)",windows->command.id);
14774 /*
14775 Initialize Widget window.
14776 */
14777 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14778 resource_info,&windows->widget);
14779 (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14780 resource_info->client_name);
14781 windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14782 resource_name,"geometry",(char *) NULL);
14783 windows->widget.border_width=0;
14784 windows->widget.flags|=PPosition;
14785 windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14786 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14787 KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14788 StructureNotifyMask;
14789 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14790 manager_hints->input=MagickTrue;
14791 manager_hints->initial_state=NormalState;
14792 manager_hints->window_group=windows->image.id;
14793 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14794 &windows->widget);
14795 windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14796 windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14797 windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14798 windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14799 (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14800 if (resource_info->debug != MagickFalse)
14801 (void) LogMagickEvent(X11Event,GetMagickModule(),
14802 "Window id: 0x%lx (widget)",windows->widget.id);
14803 /*
14804 Initialize popup window.
14805 */
14806 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14807 resource_info,&windows->popup);
14808 windows->popup.border_width=0;
14809 windows->popup.flags|=PPosition;
14810 windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14811 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14812 KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14813 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14814 manager_hints->input=MagickTrue;
14815 manager_hints->initial_state=NormalState;
14816 manager_hints->window_group=windows->image.id;
14817 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14818 &windows->popup);
14819 windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14820 windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14821 windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14822 windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14823 (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14824 if (resource_info->debug != MagickFalse)
14825 (void) LogMagickEvent(X11Event,GetMagickModule(),
14826 "Window id: 0x%lx (pop up)",windows->popup.id);
14827 /*
14828 Initialize Magnify window and cursor.
14829 */
14830 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14831 resource_info,&windows->magnify);
14832 if (resource_info->use_shared_memory == MagickFalse)
14833 windows->magnify.shared_memory=MagickFalse;
14834 (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14835 resource_info->client_name);
14836 windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14837 resource_name,"geometry",(char *) NULL);
14838 (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14839 resource_info->magnify);
14840 if (windows->magnify.cursor != (Cursor) NULL)
14841 (void) XFreeCursor(display,windows->magnify.cursor);
14842 windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14843 map_info->colormap,resource_info->background_color,
14844 resource_info->foreground_color);
14845 if (windows->magnify.cursor == (Cursor) NULL)
14846 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14847 display_image->filename);
14848 windows->magnify.width=MagnifySize;
14849 windows->magnify.height=MagnifySize;
14850 windows->magnify.flags|=PPosition;
14851 windows->magnify.min_width=MagnifySize;
14852 windows->magnify.min_height=MagnifySize;
14853 windows->magnify.width_inc=MagnifySize;
14854 windows->magnify.height_inc=MagnifySize;
14855 windows->magnify.data=resource_info->magnify;
14856 windows->magnify.attributes.cursor=windows->magnify.cursor;
14857 windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14858 ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14859 StructureNotifyMask;
14860 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14861 manager_hints->input=MagickTrue;
14862 manager_hints->initial_state=NormalState;
14863 manager_hints->window_group=windows->image.id;
14864 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14865 &windows->magnify);
14866 if (resource_info->debug != MagickFalse)
14867 (void) LogMagickEvent(X11Event,GetMagickModule(),
14868 "Window id: 0x%lx (magnify)",windows->magnify.id);
14869 (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14870 /*
14871 Initialize panning window.
14872 */
14873 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14874 resource_info,&windows->pan);
14875 (void) CloneString(&windows->pan.name,"Pan Icon");
14876 windows->pan.width=windows->icon.width;
14877 windows->pan.height=windows->icon.height;
14878 (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14879 resource_info->client_name);
14880 windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14881 resource_name,"geometry",(char *) NULL);
14882 (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14883 &windows->pan.width,&windows->pan.height);
14884 windows->pan.flags|=PPosition;
14885 windows->pan.immutable=MagickTrue;
14886 windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14887 ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14888 StructureNotifyMask;
14889 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14890 manager_hints->input=MagickFalse;
14891 manager_hints->initial_state=NormalState;
14892 manager_hints->window_group=windows->image.id;
14893 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14894 &windows->pan);
14895 if (resource_info->debug != MagickFalse)
14896 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14897 windows->pan.id);
14898 (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14899 if (windows->info.mapped != MagickFalse)
14900 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14901 if ((windows->image.mapped == MagickFalse) ||
14902 (windows->backdrop.id != (Window) NULL))
14903 (void) XMapWindow(display,windows->image.id);
14904 /*
14905 Set our progress monitor and warning handlers.
14906 */
14907 if (warning_handler == (WarningHandler) NULL)
14908 {
14909 warning_handler=resource_info->display_warnings ?
14910 SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14911 warning_handler=resource_info->display_warnings ?
14912 SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14913 }
14914 /*
14915 Initialize Image and Magnify X images.
14916 */
14917 windows->image.x=0;
14918 windows->image.y=0;
14919 windows->magnify.shape=MagickFalse;
14920 width=(unsigned int) display_image->columns;
14921 height=(unsigned int) display_image->rows;
14922 if ((display_image->columns != width) || (display_image->rows != height))
14923 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14924 display_image->filename);
14925 status=XMakeImage(display,resource_info,&windows->image,display_image,
14926 width,height);
14927 if (status == MagickFalse)
14928 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14929 display_image->filename);
14930 status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14931 windows->magnify.width,windows->magnify.height);
14932 if (status == MagickFalse)
14933 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14934 display_image->filename);
14935 if (windows->magnify.mapped != MagickFalse)
14936 (void) XMapRaised(display,windows->magnify.id);
14937 if (windows->pan.mapped != MagickFalse)
14938 (void) XMapRaised(display,windows->pan.id);
14939 windows->image.window_changes.width=(int) display_image->columns;
14940 windows->image.window_changes.height=(int) display_image->rows;
14941 (void) XConfigureImage(display,resource_info,windows,display_image);
14942 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14943 (void) XSync(display,MagickFalse);
14944 /*
14945 Respond to events.
14946 */
14947 delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
14948 timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
14949 update_time=0;
14950 if (resource_info->update != MagickFalse)
14951 {
14952 MagickBooleanType
14953 status;
14954
14955 /*
14956 Determine when file data was last modified.
14957 */
14958 status=GetPathAttributes(display_image->filename,&attributes);
14959 if (status != MagickFalse)
14960 update_time=attributes.st_mtime;
14961 }
14962 *state&=(~FormerImageState);
14963 *state&=(~MontageImageState);
14964 *state&=(~NextImageState);
14965 do
14966 {
14967 /*
14968 Handle a window event.
14969 */
14970 if (windows->image.mapped != MagickFalse)
14971 if ((display_image->delay != 0) || (resource_info->update != 0))
14972 {
14973 if (timer < GetMagickTime())
14974 {
14975 if (resource_info->update == MagickFalse)
14976 *state|=NextImageState | ExitState;
14977 else
14978 {
14979 MagickBooleanType
14980 status;
14981
14982 /*
14983 Determine if image file was modified.
14984 */
14985 status=GetPathAttributes(display_image->filename,&attributes);
14986 if (status != MagickFalse)
14987 if (update_time != attributes.st_mtime)
14988 {
14989 /*
14990 Redisplay image.
14991 */
14992 (void) FormatLocaleString(
14993 resource_info->image_info->filename,MaxTextExtent,
14994 "%s:%s",display_image->magick,
14995 display_image->filename);
14996 nexus=ReadImage(resource_info->image_info,
14997 &display_image->exception);
14998 if (nexus != (Image *) NULL)
14999 *state|=NextImageState | ExitState;
15000 }
15001 delay=display_image->delay/MagickMax(
15002 display_image->ticks_per_second,1L);
15003 timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15004 }
15005 }
15006 if (XEventsQueued(display,QueuedAfterFlush) == 0)
15007 {
15008 /*
15009 Do not block if delay > 0.
15010 */
15011 XDelay(display,SuspendTime << 2);
15012 continue;
15013 }
15014 }
15015 timestamp=GetMagickTime();
15016 (void) XNextEvent(display,&event);
15017 if (windows->image.stasis == MagickFalse)
15018 windows->image.stasis=(GetMagickTime()-timestamp) > 0 ?
15019 MagickTrue : MagickFalse;
15020 if (windows->magnify.stasis == MagickFalse)
15021 windows->magnify.stasis=(GetMagickTime()-timestamp) > 0 ?
15022 MagickTrue : MagickFalse;
15023 if (event.xany.window == windows->command.id)
15024 {
15025 /*
15026 Select a command from the Command widget.
15027 */
15028 id=XCommandWidget(display,windows,CommandMenu,&event);
15029 if (id < 0)
15030 continue;
15031 (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15032 display_command=CommandMenus[id];
15033 if (id < MagickMenus)
15034 {
15035 /*
15036 Select a command from a pop-up menu.
15037 */
15038 entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15039 command);
15040 if (entry < 0)
15041 continue;
15042 (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15043 display_command=Commands[id][entry];
15044 }
15045 if (display_command != NullCommand)
15046 nexus=XMagickCommand(display,resource_info,windows,display_command,
15047 &display_image);
15048 continue;
15049 }
15050 switch (event.type)
15051 {
15052 case ButtonPress:
15053 {
15054 if (resource_info->debug != MagickFalse)
15055 (void) LogMagickEvent(X11Event,GetMagickModule(),
15056 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15057 event.xbutton.button,event.xbutton.x,event.xbutton.y);
15058 if ((event.xbutton.button == Button3) &&
15059 (event.xbutton.state & Mod1Mask))
15060 {
15061 /*
15062 Convert Alt-Button3 to Button2.
15063 */
15064 event.xbutton.button=Button2;
15065 event.xbutton.state&=(~Mod1Mask);
15066 }
15067 if (event.xbutton.window == windows->backdrop.id)
15068 {
15069 (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15070 event.xbutton.time);
15071 break;
15072 }
15073 if (event.xbutton.window == windows->image.id)
15074 {
15075 switch (event.xbutton.button)
15076 {
15077 case Button1:
15078 {
15079 if (resource_info->immutable)
15080 {
15081 /*
15082 Select a command from the Virtual menu.
15083 */
15084 entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15085 command);
15086 if (entry >= 0)
15087 nexus=XMagickCommand(display,resource_info,windows,
15088 VirtualCommands[entry],&display_image);
15089 break;
15090 }
15091 /*
15092 Map/unmap Command widget.
15093 */
15094 if (windows->command.mapped != MagickFalse)
15095 (void) XWithdrawWindow(display,windows->command.id,
15096 windows->command.screen);
15097 else
15098 {
15099 (void) XCommandWidget(display,windows,CommandMenu,
15100 (XEvent *) NULL);
15101 (void) XMapRaised(display,windows->command.id);
15102 }
15103 break;
15104 }
15105 case Button2:
15106 {
15107 /*
15108 User pressed the image magnify button.
15109 */
15110 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15111 &display_image);
15112 XMagnifyImage(display,windows,&event);
15113 break;
15114 }
15115 case Button3:
15116 {
15117 if (resource_info->immutable)
15118 {
15119 /*
15120 Select a command from the Virtual menu.
15121 */
15122 entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15123 command);
15124 if (entry >= 0)
15125 nexus=XMagickCommand(display,resource_info,windows,
15126 VirtualCommands[entry],&display_image);
15127 break;
15128 }
15129 if (display_image->montage != (char *) NULL)
15130 {
15131 /*
15132 Open or delete a tile from a visual image directory.
15133 */
15134 nexus=XTileImage(display,resource_info,windows,
15135 display_image,&event);
15136 if (nexus != (Image *) NULL)
15137 *state|=MontageImageState | NextImageState | ExitState;
15138 vid_info.x=(short int) windows->image.x;
15139 vid_info.y=(short int) windows->image.y;
15140 break;
15141 }
15142 /*
15143 Select a command from the Short Cuts menu.
15144 */
15145 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15146 command);
15147 if (entry >= 0)
15148 nexus=XMagickCommand(display,resource_info,windows,
15149 ShortCutsCommands[entry],&display_image);
15150 break;
15151 }
15152 case Button4:
15153 {
15154 /*
15155 Wheel up.
15156 */
15157 XTranslateImage(display,windows,*image,XK_Up);
15158 break;
15159 }
15160 case Button5:
15161 {
15162 /*
15163 Wheel down.
15164 */
15165 XTranslateImage(display,windows,*image,XK_Down);
15166 break;
15167 }
15168 default:
15169 break;
15170 }
15171 break;
15172 }
15173 if (event.xbutton.window == windows->magnify.id)
15174 {
15175 const char
15176 *const MagnifyMenu[] =
15177 {
15178 "2",
15179 "4",
15180 "5",
15181 "6",
15182 "7",
15183 "8",
15184 "9",
15185 "3",
15186 (char *) NULL,
15187 };
15188
15189 int
15190 factor;
15191
15192 static KeySym
15193 MagnifyCommands[] =
15194 {
15195 XK_2,
15196 XK_4,
15197 XK_5,
15198 XK_6,
15199 XK_7,
15200 XK_8,
15201 XK_9,
15202 XK_3
15203 };
15204
15205 /*
15206 Select a magnify factor from the pop-up menu.
15207 */
15208 factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15209 if (factor >= 0)
15210 XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15211 break;
15212 }
15213 if (event.xbutton.window == windows->pan.id)
15214 {
15215 switch (event.xbutton.button)
15216 {
15217 case Button4:
15218 {
15219 /*
15220 Wheel up.
15221 */
15222 XTranslateImage(display,windows,*image,XK_Up);
15223 break;
15224 }
15225 case Button5:
15226 {
15227 /*
15228 Wheel down.
15229 */
15230 XTranslateImage(display,windows,*image,XK_Down);
15231 break;
15232 }
15233 default:
15234 {
15235 XPanImage(display,windows,&event);
15236 break;
15237 }
15238 }
15239 break;
15240 }
15241 delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15242 1L);
15243 timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15244 break;
15245 }
15246 case ButtonRelease:
15247 {
15248 if (resource_info->debug != MagickFalse)
15249 (void) LogMagickEvent(X11Event,GetMagickModule(),
15250 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15251 event.xbutton.button,event.xbutton.x,event.xbutton.y);
15252 break;
15253 }
15254 case ClientMessage:
15255 {
15256 if (resource_info->debug != MagickFalse)
15257 (void) LogMagickEvent(X11Event,GetMagickModule(),
15258 "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15259 event.xclient.message_type,event.xclient.format,(unsigned long)
15260 event.xclient.data.l[0]);
15261 if (event.xclient.message_type == windows->im_protocols)
15262 {
15263 if (*event.xclient.data.l == (long) windows->im_update_widget)
15264 {
15265 (void) CloneString(&windows->command.name,MagickTitle);
15266 windows->command.data=MagickMenus;
15267 (void) XCommandWidget(display,windows,CommandMenu,
15268 (XEvent *) NULL);
15269 break;
15270 }
15271 if (*event.xclient.data.l == (long) windows->im_update_colormap)
15272 {
15273 /*
15274 Update graphic context and window colormap.
15275 */
15276 for (i=0; i < (int) number_windows; i++)
15277 {
15278 if (magick_windows[i]->id == windows->icon.id)
15279 continue;
15280 context_values.background=pixel->background_color.pixel;
15281 context_values.foreground=pixel->foreground_color.pixel;
15282 (void) XChangeGC(display,magick_windows[i]->annotate_context,
15283 context_mask,&context_values);
15284 (void) XChangeGC(display,magick_windows[i]->widget_context,
15285 context_mask,&context_values);
15286 context_values.background=pixel->foreground_color.pixel;
15287 context_values.foreground=pixel->background_color.pixel;
15288 context_values.plane_mask=context_values.background ^
15289 context_values.foreground;
15290 (void) XChangeGC(display,magick_windows[i]->highlight_context,
15291 (size_t) (context_mask | GCPlaneMask),
15292 &context_values);
15293 magick_windows[i]->attributes.background_pixel=
15294 pixel->background_color.pixel;
15295 magick_windows[i]->attributes.border_pixel=
15296 pixel->border_color.pixel;
15297 magick_windows[i]->attributes.colormap=map_info->colormap;
15298 (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15299 (unsigned long) magick_windows[i]->mask,
15300 &magick_windows[i]->attributes);
15301 }
15302 if (windows->pan.mapped != MagickFalse)
15303 {
15304 (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15305 windows->pan.pixmap);
15306 (void) XClearWindow(display,windows->pan.id);
15307 XDrawPanRectangle(display,windows);
15308 }
15309 if (windows->backdrop.id != (Window) NULL)
15310 (void) XInstallColormap(display,map_info->colormap);
15311 break;
15312 }
15313 if (*event.xclient.data.l == (long) windows->im_former_image)
15314 {
15315 *state|=FormerImageState | ExitState;
15316 break;
15317 }
15318 if (*event.xclient.data.l == (long) windows->im_next_image)
15319 {
15320 *state|=NextImageState | ExitState;
15321 break;
15322 }
15323 if (*event.xclient.data.l == (long) windows->im_retain_colors)
15324 {
15325 *state|=RetainColorsState;
15326 break;
15327 }
15328 if (*event.xclient.data.l == (long) windows->im_exit)
15329 {
15330 *state|=ExitState;
15331 break;
15332 }
15333 break;
15334 }
15335 if (event.xclient.message_type == windows->dnd_protocols)
15336 {
15337 Atom
15338 selection,
15339 type;
15340
15341 int
15342 format,
15343 status;
15344
15345 unsigned char
15346 *data;
15347
15348 unsigned long
15349 after,
15350 length;
15351
15352 /*
15353 Display image named by the Drag-and-Drop selection.
15354 */
15355 if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15356 break;
15357 selection=XInternAtom(display,"DndSelection",MagickFalse);
15358 status=XGetWindowProperty(display,root_window,selection,0L,(long)
15359 MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15360 &length,&after,&data);
15361 if ((status != Success) || (length == 0))
15362 break;
15363 if (*event.xclient.data.l == 2)
15364 {
15365 /*
15366 Offix DND.
15367 */
15368 (void) CopyMagickString(resource_info->image_info->filename,
15369 (char *) data,MaxTextExtent);
15370 }
15371 else
15372 {
15373 /*
15374 XDND.
15375 */
15376 if (strncmp((char *) data, "file:", 5) != 0)
15377 {
15378 (void) XFree((void *) data);
15379 break;
15380 }
15381 (void) CopyMagickString(resource_info->image_info->filename,
15382 ((char *) data)+5,MaxTextExtent);
15383 }
15384 nexus=ReadImage(resource_info->image_info,
15385 &display_image->exception);
15386 CatchException(&display_image->exception);
15387 if (nexus != (Image *) NULL)
15388 *state|=NextImageState | ExitState;
15389 (void) XFree((void *) data);
15390 break;
15391 }
15392 /*
15393 If client window delete message, exit.
15394 */
15395 if (event.xclient.message_type != windows->wm_protocols)
15396 break;
15397 if (*event.xclient.data.l != (long) windows->wm_delete_window)
15398 break;
15399 (void) XWithdrawWindow(display,event.xclient.window,
15400 visual_info->screen);
15401 if (event.xclient.window == windows->image.id)
15402 {
15403 *state|=ExitState;
15404 break;
15405 }
15406 if (event.xclient.window == windows->pan.id)
15407 {
15408 /*
15409 Restore original image size when pan window is deleted.
15410 */
15411 windows->image.window_changes.width=windows->image.ximage->width;
15412 windows->image.window_changes.height=windows->image.ximage->height;
15413 (void) XConfigureImage(display,resource_info,windows,
15414 display_image);
15415 }
15416 break;
15417 }
15418 case ConfigureNotify:
15419 {
15420 if (resource_info->debug != MagickFalse)
15421 (void) LogMagickEvent(X11Event,GetMagickModule(),
15422 "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15423 event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15424 event.xconfigure.y,event.xconfigure.send_event);
15425 if (event.xconfigure.window == windows->image.id)
15426 {
15427 /*
15428 Image window has a new configuration.
15429 */
15430 if (event.xconfigure.send_event != 0)
15431 {
15432 XWindowChanges
15433 window_changes;
15434
15435 /*
15436 Position the transient windows relative of the Image window.
15437 */
15438 if (windows->command.geometry == (char *) NULL)
15439 if (windows->command.mapped == MagickFalse)
15440 {
15441 windows->command.x=event.xconfigure.x-
15442 windows->command.width-25;
15443 windows->command.y=event.xconfigure.y;
15444 XConstrainWindowPosition(display,&windows->command);
15445 window_changes.x=windows->command.x;
15446 window_changes.y=windows->command.y;
15447 (void) XReconfigureWMWindow(display,windows->command.id,
15448 windows->command.screen,(unsigned int) (CWX | CWY),
15449 &window_changes);
15450 }
15451 if (windows->widget.geometry == (char *) NULL)
15452 if (windows->widget.mapped == MagickFalse)
15453 {
15454 windows->widget.x=event.xconfigure.x+
15455 event.xconfigure.width/10;
15456 windows->widget.y=event.xconfigure.y+
15457 event.xconfigure.height/10;
15458 XConstrainWindowPosition(display,&windows->widget);
15459 window_changes.x=windows->widget.x;
15460 window_changes.y=windows->widget.y;
15461 (void) XReconfigureWMWindow(display,windows->widget.id,
15462 windows->widget.screen,(unsigned int) (CWX | CWY),
15463 &window_changes);
15464 }
15465 if (windows->magnify.geometry == (char *) NULL)
15466 if (windows->magnify.mapped == MagickFalse)
15467 {
15468 windows->magnify.x=event.xconfigure.x+
15469 event.xconfigure.width+25;
15470 windows->magnify.y=event.xconfigure.y;
15471 XConstrainWindowPosition(display,&windows->magnify);
15472 window_changes.x=windows->magnify.x;
15473 window_changes.y=windows->magnify.y;
15474 (void) XReconfigureWMWindow(display,windows->magnify.id,
15475 windows->magnify.screen,(unsigned int) (CWX | CWY),
15476 &window_changes);
15477 }
15478 if (windows->pan.geometry == (char *) NULL)
15479 if (windows->pan.mapped == MagickFalse)
15480 {
15481 windows->pan.x=event.xconfigure.x+
15482 event.xconfigure.width+25;
15483 windows->pan.y=event.xconfigure.y+
15484 windows->magnify.height+50;
15485 XConstrainWindowPosition(display,&windows->pan);
15486 window_changes.x=windows->pan.x;
15487 window_changes.y=windows->pan.y;
15488 (void) XReconfigureWMWindow(display,windows->pan.id,
15489 windows->pan.screen,(unsigned int) (CWX | CWY),
15490 &window_changes);
15491 }
15492 }
15493 if ((event.xconfigure.width == (int) windows->image.width) &&
15494 (event.xconfigure.height == (int) windows->image.height))
15495 break;
15496 windows->image.width=(unsigned int) event.xconfigure.width;
15497 windows->image.height=(unsigned int) event.xconfigure.height;
15498 windows->image.x=0;
15499 windows->image.y=0;
15500 if (display_image->montage != (char *) NULL)
15501 {
15502 windows->image.x=vid_info.x;
15503 windows->image.y=vid_info.y;
15504 }
15505 if ((windows->image.mapped != MagickFalse) &&
15506 (windows->image.stasis != MagickFalse))
15507 {
15508 /*
15509 Update image window configuration.
15510 */
15511 windows->image.window_changes.width=event.xconfigure.width;
15512 windows->image.window_changes.height=event.xconfigure.height;
15513 (void) XConfigureImage(display,resource_info,windows,
15514 display_image);
15515 }
15516 /*
15517 Update pan window configuration.
15518 */
15519 if ((event.xconfigure.width < windows->image.ximage->width) ||
15520 (event.xconfigure.height < windows->image.ximage->height))
15521 {
15522 (void) XMapRaised(display,windows->pan.id);
15523 XDrawPanRectangle(display,windows);
15524 }
15525 else
15526 if (windows->pan.mapped != MagickFalse)
15527 (void) XWithdrawWindow(display,windows->pan.id,
15528 windows->pan.screen);
15529 break;
15530 }
15531 if (event.xconfigure.window == windows->magnify.id)
15532 {
15533 unsigned int
15534 magnify;
15535
15536 /*
15537 Magnify window has a new configuration.
15538 */
15539 windows->magnify.width=(unsigned int) event.xconfigure.width;
15540 windows->magnify.height=(unsigned int) event.xconfigure.height;
15541 if (windows->magnify.mapped == MagickFalse)
15542 break;
15543 magnify=1;
15544 while ((int) magnify <= event.xconfigure.width)
15545 magnify<<=1;
15546 while ((int) magnify <= event.xconfigure.height)
15547 magnify<<=1;
15548 magnify>>=1;
15549 if (((int) magnify != event.xconfigure.width) ||
15550 ((int) magnify != event.xconfigure.height))
15551 {
15552 window_changes.width=(int) magnify;
15553 window_changes.height=(int) magnify;
15554 (void) XReconfigureWMWindow(display,windows->magnify.id,
15555 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15556 &window_changes);
15557 break;
15558 }
15559 if ((windows->magnify.mapped != MagickFalse) &&
15560 (windows->magnify.stasis != MagickFalse))
15561 {
15562 status=XMakeImage(display,resource_info,&windows->magnify,
15563 display_image,windows->magnify.width,windows->magnify.height);
15564 XMakeMagnifyImage(display,windows);
15565 }
15566 break;
15567 }
15568 if ((windows->magnify.mapped != MagickFalse) &&
15569 (event.xconfigure.window == windows->pan.id))
15570 {
15571 /*
15572 Pan icon window has a new configuration.
15573 */
15574 if (event.xconfigure.send_event != 0)
15575 {
15576 windows->pan.x=event.xconfigure.x;
15577 windows->pan.y=event.xconfigure.y;
15578 }
15579 windows->pan.width=(unsigned int) event.xconfigure.width;
15580 windows->pan.height=(unsigned int) event.xconfigure.height;
15581 break;
15582 }
15583 if (event.xconfigure.window == windows->icon.id)
15584 {
15585 /*
15586 Icon window has a new configuration.
15587 */
15588 windows->icon.width=(unsigned int) event.xconfigure.width;
15589 windows->icon.height=(unsigned int) event.xconfigure.height;
15590 break;
15591 }
15592 break;
15593 }
15594 case DestroyNotify:
15595 {
15596 /*
15597 Group leader has exited.
15598 */
15599 if (resource_info->debug != MagickFalse)
15600 (void) LogMagickEvent(X11Event,GetMagickModule(),
15601 "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15602 if (event.xdestroywindow.window == windows->group_leader.id)
15603 {
15604 *state|=ExitState;
15605 break;
15606 }
15607 break;
15608 }
15609 case EnterNotify:
15610 {
15611 /*
15612 Selectively install colormap.
15613 */
15614 if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15615 if (event.xcrossing.mode != NotifyUngrab)
15616 XInstallColormap(display,map_info->colormap);
15617 break;
15618 }
15619 case Expose:
15620 {
15621 if (resource_info->debug != MagickFalse)
15622 (void) LogMagickEvent(X11Event,GetMagickModule(),
15623 "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15624 event.xexpose.width,event.xexpose.height,event.xexpose.x,
15625 event.xexpose.y);
15626 /*
15627 Refresh windows that are now exposed.
15628 */
15629 if ((event.xexpose.window == windows->image.id) &&
15630 (windows->image.mapped != MagickFalse))
15631 {
15632 XRefreshWindow(display,&windows->image,&event);
15633 delay=display_image->delay/MagickMax(
15634 display_image->ticks_per_second,1L);
15635 timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15636 break;
15637 }
15638 if ((event.xexpose.window == windows->magnify.id) &&
15639 (windows->magnify.mapped != MagickFalse))
15640 {
15641 XMakeMagnifyImage(display,windows);
15642 break;
15643 }
15644 if (event.xexpose.window == windows->pan.id)
15645 {
15646 XDrawPanRectangle(display,windows);
15647 break;
15648 }
15649 if (event.xexpose.window == windows->icon.id)
15650 {
15651 XRefreshWindow(display,&windows->icon,&event);
15652 break;
15653 }
15654 break;
15655 }
15656 case KeyPress:
15657 {
15658 int
15659 length;
15660
15661 /*
15662 Respond to a user key press.
15663 */
15664 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15665 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15666 *(command+length)='\0';
15667 if (resource_info->debug != MagickFalse)
15668 (void) LogMagickEvent(X11Event,GetMagickModule(),
15669 "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15670 key_symbol,command);
15671 if (event.xkey.window == windows->image.id)
15672 {
15673 display_command=XImageWindowCommand(display,resource_info,windows,
15674 event.xkey.state,key_symbol,&display_image);
15675 if (display_command != NullCommand)
15676 nexus=XMagickCommand(display,resource_info,windows,display_command,
15677 &display_image);
15678 }
15679 if (event.xkey.window == windows->magnify.id)
15680 XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15681 if (event.xkey.window == windows->pan.id)
15682 {
15683 if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15684 (void) XWithdrawWindow(display,windows->pan.id,
15685 windows->pan.screen);
15686 else
15687 if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15688 XTextViewHelp(display,resource_info,windows,MagickFalse,
15689 "Help Viewer - Image Pan",ImagePanHelp);
15690 else
15691 XTranslateImage(display,windows,*image,key_symbol);
15692 }
15693 delay=display_image->delay/MagickMax(
15694 display_image->ticks_per_second,1L);
15695 timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15696 break;
15697 }
15698 case KeyRelease:
15699 {
15700 /*
15701 Respond to a user key release.
15702 */
15703 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15704 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15705 if (resource_info->debug != MagickFalse)
15706 (void) LogMagickEvent(X11Event,GetMagickModule(),
15707 "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15708 break;
15709 }
15710 case LeaveNotify:
15711 {
15712 /*
15713 Selectively uninstall colormap.
15714 */
15715 if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15716 if (event.xcrossing.mode != NotifyUngrab)
15717 XUninstallColormap(display,map_info->colormap);
15718 break;
15719 }
15720 case MapNotify:
15721 {
15722 if (resource_info->debug != MagickFalse)
15723 (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15724 event.xmap.window);
15725 if (event.xmap.window == windows->backdrop.id)
15726 {
15727 (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15728 CurrentTime);
15729 windows->backdrop.mapped=MagickTrue;
15730 break;
15731 }
15732 if (event.xmap.window == windows->image.id)
15733 {
15734 if (windows->backdrop.id != (Window) NULL)
15735 (void) XInstallColormap(display,map_info->colormap);
15736 if (LocaleCompare(display_image->magick,"LOGO") == 0)
15737 {
15738 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15739 nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15740 }
15741 if (((int) windows->image.width < windows->image.ximage->width) ||
15742 ((int) windows->image.height < windows->image.ximage->height))
15743 (void) XMapRaised(display,windows->pan.id);
15744 windows->image.mapped=MagickTrue;
15745 break;
15746 }
15747 if (event.xmap.window == windows->magnify.id)
15748 {
15749 XMakeMagnifyImage(display,windows);
15750 windows->magnify.mapped=MagickTrue;
15751 (void) XWithdrawWindow(display,windows->info.id,
15752 windows->info.screen);
15753 break;
15754 }
15755 if (event.xmap.window == windows->pan.id)
15756 {
15757 XMakePanImage(display,resource_info,windows,display_image);
15758 windows->pan.mapped=MagickTrue;
15759 break;
15760 }
15761 if (event.xmap.window == windows->info.id)
15762 {
15763 windows->info.mapped=MagickTrue;
15764 break;
15765 }
15766 if (event.xmap.window == windows->icon.id)
15767 {
15768 MagickBooleanType
15769 taint;
15770
15771 /*
15772 Create an icon image.
15773 */
15774 taint=display_image->taint;
15775 XMakeStandardColormap(display,icon_visual,icon_resources,
15776 display_image,icon_map,icon_pixel);
15777 (void) XMakeImage(display,icon_resources,&windows->icon,
15778 display_image,windows->icon.width,windows->icon.height);
15779 display_image->taint=taint;
15780 (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15781 windows->icon.pixmap);
15782 (void) XClearWindow(display,windows->icon.id);
15783 (void) XWithdrawWindow(display,windows->info.id,
15784 windows->info.screen);
15785 windows->icon.mapped=MagickTrue;
15786 break;
15787 }
15788 if (event.xmap.window == windows->command.id)
15789 {
15790 windows->command.mapped=MagickTrue;
15791 break;
15792 }
15793 if (event.xmap.window == windows->popup.id)
15794 {
15795 windows->popup.mapped=MagickTrue;
15796 break;
15797 }
15798 if (event.xmap.window == windows->widget.id)
15799 {
15800 windows->widget.mapped=MagickTrue;
15801 break;
15802 }
15803 break;
15804 }
15805 case MappingNotify:
15806 {
15807 (void) XRefreshKeyboardMapping(&event.xmapping);
15808 break;
15809 }
15810 case NoExpose:
15811 break;
15812 case PropertyNotify:
15813 {
15814 Atom
15815 type;
15816
15817 int
15818 format,
15819 status;
15820
15821 unsigned char
15822 *data;
15823
15824 unsigned long
15825 after,
15826 length;
15827
15828 if (resource_info->debug != MagickFalse)
15829 (void) LogMagickEvent(X11Event,GetMagickModule(),
15830 "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15831 event.xproperty.atom,event.xproperty.state);
15832 if (event.xproperty.atom != windows->im_remote_command)
15833 break;
15834 /*
15835 Display image named by the remote command protocol.
15836 */
15837 status=XGetWindowProperty(display,event.xproperty.window,
15838 event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15839 AnyPropertyType,&type,&format,&length,&after,&data);
15840 if ((status != Success) || (length == 0))
15841 break;
15842 if (LocaleCompare((char *) data,"-quit") == 0)
15843 {
15844 XClientMessage(display,windows->image.id,windows->im_protocols,
15845 windows->im_exit,CurrentTime);
15846 (void) XFree((void *) data);
15847 break;
15848 }
15849 (void) CopyMagickString(resource_info->image_info->filename,
15850 (char *) data,MaxTextExtent);
15851 (void) XFree((void *) data);
15852 nexus=ReadImage(resource_info->image_info,&display_image->exception);
15853 CatchException(&display_image->exception);
15854 if (nexus != (Image *) NULL)
15855 *state|=NextImageState | ExitState;
15856 break;
15857 }
15858 case ReparentNotify:
15859 {
15860 if (resource_info->debug != MagickFalse)
15861 (void) LogMagickEvent(X11Event,GetMagickModule(),
15862 "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15863 event.xreparent.window);
15864 break;
15865 }
15866 case UnmapNotify:
15867 {
15868 if (resource_info->debug != MagickFalse)
15869 (void) LogMagickEvent(X11Event,GetMagickModule(),
15870 "Unmap Notify: 0x%lx",event.xunmap.window);
15871 if (event.xunmap.window == windows->backdrop.id)
15872 {
15873 windows->backdrop.mapped=MagickFalse;
15874 break;
15875 }
15876 if (event.xunmap.window == windows->image.id)
15877 {
15878 windows->image.mapped=MagickFalse;
15879 break;
15880 }
15881 if (event.xunmap.window == windows->magnify.id)
15882 {
15883 windows->magnify.mapped=MagickFalse;
15884 break;
15885 }
15886 if (event.xunmap.window == windows->pan.id)
15887 {
15888 windows->pan.mapped=MagickFalse;
15889 break;
15890 }
15891 if (event.xunmap.window == windows->info.id)
15892 {
15893 windows->info.mapped=MagickFalse;
15894 break;
15895 }
15896 if (event.xunmap.window == windows->icon.id)
15897 {
15898 if (map_info->colormap == icon_map->colormap)
15899 XConfigureImageColormap(display,resource_info,windows,
15900 display_image);
15901 (void) XFreeStandardColormap(display,icon_visual,icon_map,
15902 icon_pixel);
15903 windows->icon.mapped=MagickFalse;
15904 break;
15905 }
15906 if (event.xunmap.window == windows->command.id)
15907 {
15908 windows->command.mapped=MagickFalse;
15909 break;
15910 }
15911 if (event.xunmap.window == windows->popup.id)
15912 {
15913 if (windows->backdrop.id != (Window) NULL)
15914 (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15915 CurrentTime);
15916 windows->popup.mapped=MagickFalse;
15917 break;
15918 }
15919 if (event.xunmap.window == windows->widget.id)
15920 {
15921 if (windows->backdrop.id != (Window) NULL)
15922 (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15923 CurrentTime);
15924 windows->widget.mapped=MagickFalse;
15925 break;
15926 }
15927 break;
15928 }
15929 default:
15930 {
15931 if (resource_info->debug != MagickFalse)
15932 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
15933 event.type);
15934 break;
15935 }
15936 }
15937 } while (!(*state & ExitState));
15938 if ((*state & ExitState) == 0)
15939 (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
15940 &display_image);
15941 else
15942 if (resource_info->confirm_edit != MagickFalse)
15943 {
15944 /*
15945 Query user if image has changed.
15946 */
15947 if ((resource_info->immutable == MagickFalse) &&
15948 (display_image->taint != MagickFalse))
15949 {
15950 int
15951 status;
15952
15953 status=XConfirmWidget(display,windows,"Your image changed.",
15954 "Do you want to save it");
15955 if (status == 0)
15956 *state&=(~ExitState);
15957 else
15958 if (status > 0)
15959 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
15960 &display_image);
15961 }
15962 }
15963 if ((windows->visual_info->klass == GrayScale) ||
15964 (windows->visual_info->klass == PseudoColor) ||
15965 (windows->visual_info->klass == DirectColor))
15966 {
15967 /*
15968 Withdraw pan and Magnify window.
15969 */
15970 if (windows->info.mapped != MagickFalse)
15971 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15972 if (windows->magnify.mapped != MagickFalse)
15973 (void) XWithdrawWindow(display,windows->magnify.id,
15974 windows->magnify.screen);
15975 if (windows->command.mapped != MagickFalse)
15976 (void) XWithdrawWindow(display,windows->command.id,
15977 windows->command.screen);
15978 }
15979 if (windows->pan.mapped != MagickFalse)
15980 (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
15981 if (resource_info->backdrop == MagickFalse)
15982 if (windows->backdrop.mapped)
15983 {
15984 (void) XWithdrawWindow(display,windows->backdrop.id,
15985 windows->backdrop.screen);
15986 (void) XDestroyWindow(display,windows->backdrop.id);
15987 windows->backdrop.id=(Window) NULL;
15988 (void) XWithdrawWindow(display,windows->image.id,
15989 windows->image.screen);
15990 (void) XDestroyWindow(display,windows->image.id);
15991 windows->image.id=(Window) NULL;
15992 }
15993 XSetCursorState(display,windows,MagickTrue);
15994 XCheckRefreshWindows(display,windows);
15995 if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
15996 *state&=(~ExitState);
15997 if (*state & ExitState)
15998 {
15999 /*
16000 Free Standard Colormap.
16001 */
16002 (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16003 if (resource_info->map_type == (char *) NULL)
16004 (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16005 /*
16006 Free X resources.
16007 */
16008 if (resource_info->copy_image != (Image *) NULL)
16009 resource_info->copy_image=DestroyImage(resource_info->copy_image);
16010 DestroyXResources();
16011 }
16012 (void) XSync(display,MagickFalse);
16013 /*
16014 Restore our progress monitor and warning handlers.
16015 */
16016 (void) SetErrorHandler(warning_handler);
16017 (void) SetWarningHandler(warning_handler);
16018 /*
16019 Change to home directory.
16020 */
16021 directory=getcwd(working_directory,MaxTextExtent);
16022 (void) directory;
16023 {
16024 int
16025 status;
16026
16027 if (*resource_info->home_directory == '\0')
16028 (void) CopyMagickString(resource_info->home_directory,".",MaxTextExtent);
16029 status=chdir(resource_info->home_directory);
16030 if (status == -1)
16031 (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
16032 FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
16033 }
16034 *image=display_image;
16035 return(nexus);
16036}
16037#else
16038
16039/*
16040%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16041% %
16042% %
16043% %
16044+ D i s p l a y I m a g e s %
16045% %
16046% %
16047% %
16048%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16049%
16050% DisplayImages() displays an image sequence to any X window screen. It
16051% returns a value other than 0 if successful. Check the exception member
16052% of image to determine the reason for any failure.
16053%
16054% The format of the DisplayImages method is:
16055%
16056% MagickBooleanType DisplayImages(const ImageInfo *image_info,
16057% Image *images)
16058%
16059% A description of each parameter follows:
16060%
16061% o image_info: the image info.
16062%
16063% o image: the image.
16064%
16065*/
16066MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16067 Image *image)
16068{
16069 assert(image_info != (const ImageInfo *) NULL);
16070 assert(image_info->signature == MagickCoreSignature);
16071 assert(image != (Image *) NULL);
16072 assert(image->signature == MagickCoreSignature);
16073 if (IsEventLogging() != MagickFalse)
16074 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16075 (void) image_info;
16076 (void) ThrowMagickException(&image->exception,GetMagickModule(),
16077 MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
16078 image->filename);
16079 return(MagickFalse);
16080}
16081
16082/*
16083%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16084% %
16085% %
16086% %
16087+ R e m o t e D i s p l a y C o m m a n d %
16088% %
16089% %
16090% %
16091%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16092%
16093% RemoteDisplayCommand() encourages a remote display program to display the
16094% specified image filename.
16095%
16096% The format of the RemoteDisplayCommand method is:
16097%
16098% MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16099% const char *window,const char *filename,ExceptionInfo *exception)
16100%
16101% A description of each parameter follows:
16102%
16103% o image_info: the image info.
16104%
16105% o window: Specifies the name or id of an X window.
16106%
16107% o filename: the name of the image filename to display.
16108%
16109% o exception: return any errors or warnings in this structure.
16110%
16111*/
16112MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16113 const char *window,const char *filename,ExceptionInfo *exception)
16114{
16115 assert(image_info != (const ImageInfo *) NULL);
16116 assert(image_info->signature == MagickCoreSignature);
16117 assert(filename != (char *) NULL);
16118 if (IsEventLogging() != MagickFalse)
16119 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16120 (void) window;
16121 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16122 "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16123 return(MagickFalse);
16124}
16125#endif