MagickCore 7.1.1
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 "MagickCore/studio.h"
43#include "MagickCore/artifact.h"
44#include "MagickCore/attribute.h"
45#include "MagickCore/blob.h"
46#include "MagickCore/cache.h"
47#include "MagickCore/cache-private.h"
48#include "MagickCore/channel.h"
49#include "MagickCore/client.h"
50#include "MagickCore/color.h"
51#include "MagickCore/colorspace.h"
52#include "MagickCore/composite.h"
53#include "MagickCore/constitute.h"
54#include "MagickCore/decorate.h"
55#include "MagickCore/delegate.h"
56#include "MagickCore/display.h"
57#include "MagickCore/display-private.h"
58#include "MagickCore/distort.h"
59#include "MagickCore/draw.h"
60#include "MagickCore/effect.h"
61#include "MagickCore/enhance.h"
62#include "MagickCore/exception.h"
63#include "MagickCore/exception-private.h"
64#include "MagickCore/fx.h"
65#include "MagickCore/geometry.h"
66#include "MagickCore/image.h"
67#include "MagickCore/image-private.h"
68#include "MagickCore/list.h"
69#include "MagickCore/locale-private.h"
70#include "MagickCore/log.h"
71#include "MagickCore/magick.h"
72#include "MagickCore/memory_.h"
73#include "MagickCore/monitor.h"
74#include "MagickCore/monitor-private.h"
75#include "MagickCore/montage.h"
76#include "MagickCore/nt-base-private.h"
77#include "MagickCore/option.h"
78#include "MagickCore/paint.h"
79#include "MagickCore/pixel.h"
80#include "MagickCore/pixel-accessor.h"
81#include "MagickCore/property.h"
82#include "MagickCore/quantum.h"
83#include "MagickCore/quantum-private.h"
84#include "MagickCore/resize.h"
85#include "MagickCore/resource_.h"
86#include "MagickCore/shear.h"
87#include "MagickCore/segment.h"
88#include "MagickCore/statistic.h"
89#include "MagickCore/string_.h"
90#include "MagickCore/string-private.h"
91#include "MagickCore/timer-private.h"
92#include "MagickCore/transform.h"
93#include "MagickCore/transform-private.h"
94#include "MagickCore/threshold.h"
95#include "MagickCore/utility.h"
96#include "MagickCore/utility-private.h"
97#include "MagickCore/version.h"
98#include "MagickCore/visual-effects.h"
99#include "MagickCore/widget.h"
100#include "MagickCore/widget-private.h"
101#include "MagickCore/xwindow.h"
102#include "MagickCore/xwindow-private.h"
103
104#if defined(MAGICKCORE_X11_DELEGATE)
105/*
106 Define declarations.
107*/
108#define MaxColors MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
109
110/*
111 Constant declarations.
112*/
113static const unsigned char
114 HighlightBitmap[8] =
115 {
116 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
117 },
118 OpaqueBitmap[8] =
119 {
120 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
121 },
122 ShadowBitmap[8] =
123 {
124 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
125 };
126
127/*
128 Help widget declarations.
129*/
130static const char
131 ImageAnnotateHelp[] =
132 {
133 "In annotate mode, the Command widget has these options:\n"
134 "\n"
135 " Font Name\n"
136 " fixed\n"
137 " variable\n"
138 " 5x8\n"
139 " 6x10\n"
140 " 7x13bold\n"
141 " 8x13bold\n"
142 " 9x15bold\n"
143 " 10x20\n"
144 " 12x24\n"
145 " Browser...\n"
146 " Font Color\n"
147 " black\n"
148 " blue\n"
149 " cyan\n"
150 " green\n"
151 " gray\n"
152 " red\n"
153 " magenta\n"
154 " yellow\n"
155 " white\n"
156 " transparent\n"
157 " Browser...\n"
158 " Font Color\n"
159 " black\n"
160 " blue\n"
161 " cyan\n"
162 " green\n"
163 " gray\n"
164 " red\n"
165 " magenta\n"
166 " yellow\n"
167 " white\n"
168 " transparent\n"
169 " Browser...\n"
170 " Rotate Text\n"
171 " -90\n"
172 " -45\n"
173 " -30\n"
174 " 0\n"
175 " 30\n"
176 " 45\n"
177 " 90\n"
178 " 180\n"
179 " Dialog...\n"
180 " Help\n"
181 " Dismiss\n"
182 "\n"
183 "Choose a font name from the Font Name sub-menu. Additional\n"
184 "font names can be specified with the font browser. You can\n"
185 "change the menu names by setting the X resources font1\n"
186 "through font9.\n"
187 "\n"
188 "Choose a font color from the Font Color sub-menu.\n"
189 "Additional font colors can be specified with the color\n"
190 "browser. You can change the menu colors by setting the X\n"
191 "resources pen1 through pen9.\n"
192 "\n"
193 "If you select the color browser and press Grab, you can\n"
194 "choose the font color by moving the pointer to the desired\n"
195 "color on the screen and press any button.\n"
196 "\n"
197 "If you choose to rotate the text, choose Rotate Text from the\n"
198 "menu and select an angle. Typically you will only want to\n"
199 "rotate one line of text at a time. Depending on the angle you\n"
200 "choose, subsequent lines may end up overwriting each other.\n"
201 "\n"
202 "Choosing a font and its color is optional. The default font\n"
203 "is fixed and the default color is black. However, you must\n"
204 "choose a location to begin entering text and press button 1.\n"
205 "An underscore character will appear at the location of the\n"
206 "pointer. The cursor changes to a pencil to indicate you are\n"
207 "in text mode. To exit immediately, press Dismiss.\n"
208 "\n"
209 "In text mode, any key presses will display the character at\n"
210 "the location of the underscore and advance the underscore\n"
211 "cursor. Enter your text and once completed press Apply to\n"
212 "finish your image annotation. To correct errors press BACK\n"
213 "SPACE. To delete an entire line of text, press DELETE. Any\n"
214 "text that exceeds the boundaries of the image window is\n"
215 "automagically continued onto the next line.\n"
216 "\n"
217 "The actual color you request for the font is saved in the\n"
218 "image. However, the color that appears in your image window\n"
219 "may be different. For example, on a monochrome screen the\n"
220 "text will appear black or white even if you choose the color\n"
221 "red as the font color. However, the image saved to a file\n"
222 "with -write is written with red lettering. To assure the\n"
223 "correct color text in the final image, any PseudoClass image\n"
224 "is promoted to DirectClass (see miff(5)). To force a\n"
225 "PseudoClass image to remain PseudoClass, use -colors.\n"
226 },
227 ImageChopHelp[] =
228 {
229 "In chop mode, the Command widget has these options:\n"
230 "\n"
231 " Direction\n"
232 " horizontal\n"
233 " vertical\n"
234 " Help\n"
235 " Dismiss\n"
236 "\n"
237 "If the you choose the horizontal direction (this the\n"
238 "default), the area of the image between the two horizontal\n"
239 "endpoints of the chop line is removed. Otherwise, the area\n"
240 "of the image between the two vertical endpoints of the chop\n"
241 "line is removed.\n"
242 "\n"
243 "Select a location within the image window to begin your chop,\n"
244 "press and hold any button. Next, move the pointer to\n"
245 "another location in the image. As you move a line will\n"
246 "connect the initial location and the pointer. When you\n"
247 "release the button, the area within the image to chop is\n"
248 "determined by which direction you choose from the Command\n"
249 "widget.\n"
250 "\n"
251 "To cancel the image chopping, move the pointer back to the\n"
252 "starting point of the line and release the button.\n"
253 },
254 ImageColorEditHelp[] =
255 {
256 "In color edit mode, the Command widget has these options:\n"
257 "\n"
258 " Method\n"
259 " point\n"
260 " replace\n"
261 " floodfill\n"
262 " filltoborder\n"
263 " reset\n"
264 " Pixel Color\n"
265 " black\n"
266 " blue\n"
267 " cyan\n"
268 " green\n"
269 " gray\n"
270 " red\n"
271 " magenta\n"
272 " yellow\n"
273 " white\n"
274 " Browser...\n"
275 " Border Color\n"
276 " black\n"
277 " blue\n"
278 " cyan\n"
279 " green\n"
280 " gray\n"
281 " red\n"
282 " magenta\n"
283 " yellow\n"
284 " white\n"
285 " Browser...\n"
286 " Fuzz\n"
287 " 0%\n"
288 " 2%\n"
289 " 5%\n"
290 " 10%\n"
291 " 15%\n"
292 " Dialog...\n"
293 " Undo\n"
294 " Help\n"
295 " Dismiss\n"
296 "\n"
297 "Choose a color editing method from the Method sub-menu\n"
298 "of the Command widget. The point method recolors any pixel\n"
299 "selected with the pointer until the button is released. The\n"
300 "replace method recolors any pixel that matches the color of\n"
301 "the pixel you select with a button press. Floodfill recolors\n"
302 "any pixel that matches the color of the pixel you select with\n"
303 "a button press and is a neighbor. Whereas filltoborder recolors\n"
304 "any neighbor pixel that is not the border color. Finally reset\n"
305 "changes the entire image to the designated color.\n"
306 "\n"
307 "Next, choose a pixel color from the Pixel Color sub-menu.\n"
308 "Additional pixel colors can be specified with the color\n"
309 "browser. You can change the menu colors by setting the X\n"
310 "resources pen1 through pen9.\n"
311 "\n"
312 "Now press button 1 to select a pixel within the image window\n"
313 "to change its color. Additional pixels may be recolored as\n"
314 "prescribed by the method you choose.\n"
315 "\n"
316 "If the Magnify widget is mapped, it can be helpful in positioning\n"
317 "your pointer within the image (refer to button 2).\n"
318 "\n"
319 "The actual color you request for the pixels is saved in the\n"
320 "image. However, the color that appears in your image window\n"
321 "may be different. For example, on a monochrome screen the\n"
322 "pixel will appear black or white even if you choose the\n"
323 "color red as the pixel color. However, the image saved to a\n"
324 "file with -write is written with red pixels. To assure the\n"
325 "correct color text in the final image, any PseudoClass image\n"
326 "is promoted to DirectClass (see miff(5)). To force a\n"
327 "PseudoClass image to remain PseudoClass, use -colors.\n"
328 },
329 ImageCompositeHelp[] =
330 {
331 "First a widget window is displayed requesting you to enter an\n"
332 "image name. Press Composite, Grab or type a file name.\n"
333 "Press Cancel if you choose not to create a composite image.\n"
334 "When you choose Grab, move the pointer to the desired window\n"
335 "and press any button.\n"
336 "\n"
337 "If the Composite image does not have any matte information,\n"
338 "you are informed and the file browser is displayed again.\n"
339 "Enter the name of a mask image. The image is typically\n"
340 "grayscale and the same size as the composite image. If the\n"
341 "image is not grayscale, it is converted to grayscale and the\n"
342 "resulting intensities are used as matte information.\n"
343 "\n"
344 "A small window appears showing the location of the cursor in\n"
345 "the image window. You are now in composite mode. To exit\n"
346 "immediately, press Dismiss. In composite mode, the Command\n"
347 "widget has these options:\n"
348 "\n"
349 " Operators\n"
350 " Over\n"
351 " In\n"
352 " Out\n"
353 " Atop\n"
354 " Xor\n"
355 " Plus\n"
356 " Minus\n"
357 " Add\n"
358 " Subtract\n"
359 " Difference\n"
360 " Multiply\n"
361 " Bumpmap\n"
362 " Copy\n"
363 " CopyRed\n"
364 " CopyGreen\n"
365 " CopyBlue\n"
366 " CopyOpacity\n"
367 " Clear\n"
368 " Dissolve\n"
369 " Displace\n"
370 " Help\n"
371 " Dismiss\n"
372 "\n"
373 "Choose a composite operation from the Operators sub-menu of\n"
374 "the Command widget. How each operator behaves is described\n"
375 "below. Image window is the image currently displayed on\n"
376 "your X server and image is the image obtained with the File\n"
377 "Browser widget.\n"
378 "\n"
379 "Over The result is the union of the two image shapes,\n"
380 " with image obscuring image window in the region of\n"
381 " overlap.\n"
382 "\n"
383 "In The result is simply image cut by the shape of\n"
384 " image window. None of the image data of image\n"
385 " window is in the result.\n"
386 "\n"
387 "Out The resulting image is image with the shape of\n"
388 " image window cut out.\n"
389 "\n"
390 "Atop The result is the same shape as the image window,\n"
391 " with image obscuring image window where the image\n"
392 " shapes overlap. Note this differs from over\n"
393 " because the portion of image outside image window's\n"
394 " shape does not appear in the result.\n"
395 "\n"
396 "Xor The result is the image data from both image and\n"
397 " image window that is outside the overlap region.\n"
398 " The overlap region is blank.\n"
399 "\n"
400 "Plus The result is just the sum of the image data.\n"
401 " Output values are cropped to QuantumRange (no overflow).\n"
402 "\n"
403 "Minus The result of image - image window, with underflow\n"
404 " cropped to zero.\n"
405 "\n"
406 "Add The result of image + image window, with overflow\n"
407 " wrapping around (mod 256).\n"
408 "\n"
409 "Subtract The result of image - image window, with underflow\n"
410 " wrapping around (mod 256). The add and subtract\n"
411 " operators can be used to perform reversible\n"
412 " transformations.\n"
413 "\n"
414 "Difference\n"
415 " The result of abs(image - image window). This\n"
416 " useful for comparing two very similar images.\n"
417 "\n"
418 "Multiply\n"
419 " The result of image * image window. This\n"
420 " useful for the creation of drop-shadows.\n"
421 "\n"
422 "Bumpmap The result of surface normals from image * image\n"
423 " window.\n"
424 "\n"
425 "Copy The resulting image is image window replaced with\n"
426 " image. Here the matte information is ignored.\n"
427 "\n"
428 "CopyRed The red layer of the image window is replace with\n"
429 " the red layer of the image. The other layers are\n"
430 " untouched.\n"
431 "\n"
432 "CopyGreen\n"
433 " The green layer of the image window is replace with\n"
434 " the green layer of the image. The other layers are\n"
435 " untouched.\n"
436 "\n"
437 "CopyBlue The blue layer of the image window is replace with\n"
438 " the blue layer of the image. The other layers are\n"
439 " untouched.\n"
440 "\n"
441 "CopyOpacity\n"
442 " The matte layer of the image window is replace with\n"
443 " the matte layer of the image. The other layers are\n"
444 " untouched.\n"
445 "\n"
446 "The image compositor requires a matte, or alpha channel in\n"
447 "the image for some operations. This extra channel usually\n"
448 "defines a mask which represents a sort of a cookie-cutter\n"
449 "for the image. This the case when matte is opaque (full\n"
450 "coverage) for pixels inside the shape, zero outside, and\n"
451 "between 0 and QuantumRange on the boundary. If image does not\n"
452 "have a matte channel, it is initialized with 0 for any pixel\n"
453 "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
454 "\n"
455 "If you choose Dissolve, the composite operator becomes Over. The\n"
456 "image matte channel percent transparency is initialized to factor.\n"
457 "The image window is initialized to (100-factor). Where factor is the\n"
458 "value you specify in the Dialog widget.\n"
459 "\n"
460 "Displace shifts the image pixels as defined by a displacement\n"
461 "map. With this option, image is used as a displacement map.\n"
462 "Black, within the displacement map, is a maximum positive\n"
463 "displacement. White is a maximum negative displacement and\n"
464 "middle gray is neutral. The displacement is scaled to determine\n"
465 "the pixel shift. By default, the displacement applies in both the\n"
466 "horizontal and vertical directions. However, if you specify a mask,\n"
467 "image is the horizontal X displacement and mask the vertical Y\n"
468 "displacement.\n"
469 "\n"
470 "Note that matte information for image window is not retained\n"
471 "for colormapped X server visuals (e.g. StaticColor,\n"
472 "StaticColor, GrayScale, PseudoColor). Correct compositing\n"
473 "behavior may require a TrueColor or DirectColor visual or a\n"
474 "Standard Colormap.\n"
475 "\n"
476 "Choosing a composite operator is optional. The default\n"
477 "operator is replace. However, you must choose a location to\n"
478 "composite your image and press button 1. Press and hold the\n"
479 "button before releasing and an outline of the image will\n"
480 "appear to help you identify your location.\n"
481 "\n"
482 "The actual colors of the composite image is saved. However,\n"
483 "the color that appears in image window may be different.\n"
484 "For example, on a monochrome screen image window will appear\n"
485 "black or white even though your composited image may have\n"
486 "many colors. If the image is saved to a file it is written\n"
487 "with the correct colors. To assure the correct colors are\n"
488 "saved in the final image, any PseudoClass image is promoted\n"
489 "to DirectClass (see miff(5)). To force a PseudoClass image\n"
490 "to remain PseudoClass, use -colors.\n"
491 },
492 ImageCutHelp[] =
493 {
494 "In cut mode, the Command widget has these options:\n"
495 "\n"
496 " Help\n"
497 " Dismiss\n"
498 "\n"
499 "To define a cut region, press button 1 and drag. The\n"
500 "cut region is defined by a highlighted rectangle that\n"
501 "expands or contracts as it follows the pointer. Once you\n"
502 "are satisfied with the cut region, release the button.\n"
503 "You are now in rectify mode. In rectify mode, the Command\n"
504 "widget has these options:\n"
505 "\n"
506 " Cut\n"
507 " Help\n"
508 " Dismiss\n"
509 "\n"
510 "You can make adjustments by moving the pointer to one of the\n"
511 "cut rectangle corners, pressing a button, and dragging.\n"
512 "Finally, press Cut to commit your copy region. To\n"
513 "exit without cutting the image, press Dismiss.\n"
514 },
515 ImageCopyHelp[] =
516 {
517 "In copy mode, the Command widget has these options:\n"
518 "\n"
519 " Help\n"
520 " Dismiss\n"
521 "\n"
522 "To define a copy region, press button 1 and drag. The\n"
523 "copy region is defined by a highlighted rectangle that\n"
524 "expands or contracts as it follows the pointer. Once you\n"
525 "are satisfied with the copy region, release the button.\n"
526 "You are now in rectify mode. In rectify mode, the Command\n"
527 "widget has these options:\n"
528 "\n"
529 " Copy\n"
530 " Help\n"
531 " Dismiss\n"
532 "\n"
533 "You can make adjustments by moving the pointer to one of the\n"
534 "copy rectangle corners, pressing a button, and dragging.\n"
535 "Finally, press Copy to commit your copy region. To\n"
536 "exit without copying the image, press Dismiss.\n"
537 },
538 ImageCropHelp[] =
539 {
540 "In crop mode, the Command widget has these options:\n"
541 "\n"
542 " Help\n"
543 " Dismiss\n"
544 "\n"
545 "To define a cropping region, press button 1 and drag. The\n"
546 "cropping region is defined by a highlighted rectangle that\n"
547 "expands or contracts as it follows the pointer. Once you\n"
548 "are satisfied with the cropping region, release the button.\n"
549 "You are now in rectify mode. In rectify mode, the Command\n"
550 "widget has these options:\n"
551 "\n"
552 " Crop\n"
553 " Help\n"
554 " Dismiss\n"
555 "\n"
556 "You can make adjustments by moving the pointer to one of the\n"
557 "cropping rectangle corners, pressing a button, and dragging.\n"
558 "Finally, press Crop to commit your cropping region. To\n"
559 "exit without cropping the image, press Dismiss.\n"
560 },
561 ImageDrawHelp[] =
562 {
563 "The cursor changes to a crosshair to indicate you are in\n"
564 "draw mode. To exit immediately, press Dismiss. In draw mode,\n"
565 "the Command widget has these options:\n"
566 "\n"
567 " Element\n"
568 " point\n"
569 " line\n"
570 " rectangle\n"
571 " fill rectangle\n"
572 " circle\n"
573 " fill circle\n"
574 " ellipse\n"
575 " fill ellipse\n"
576 " polygon\n"
577 " fill polygon\n"
578 " Color\n"
579 " black\n"
580 " blue\n"
581 " cyan\n"
582 " green\n"
583 " gray\n"
584 " red\n"
585 " magenta\n"
586 " yellow\n"
587 " white\n"
588 " transparent\n"
589 " Browser...\n"
590 " Stipple\n"
591 " Brick\n"
592 " Diagonal\n"
593 " Scales\n"
594 " Vertical\n"
595 " Wavy\n"
596 " Translucent\n"
597 " Opaque\n"
598 " Open...\n"
599 " Width\n"
600 " 1\n"
601 " 2\n"
602 " 4\n"
603 " 8\n"
604 " 16\n"
605 " Dialog...\n"
606 " Undo\n"
607 " Help\n"
608 " Dismiss\n"
609 "\n"
610 "Choose a drawing primitive from the Element sub-menu.\n"
611 "\n"
612 "Choose a color from the Color sub-menu. Additional\n"
613 "colors can be specified with the color browser.\n"
614 "\n"
615 "If you choose the color browser and press Grab, you can\n"
616 "select the color by moving the pointer to the desired\n"
617 "color on the screen and press any button. The transparent\n"
618 "color updates the image matte channel and is useful for\n"
619 "image compositing.\n"
620 "\n"
621 "Choose a stipple, if appropriate, from the Stipple sub-menu.\n"
622 "Additional stipples can be specified with the file browser.\n"
623 "Stipples obtained from the file browser must be on disk in the\n"
624 "X11 bitmap format.\n"
625 "\n"
626 "Choose a width, if appropriate, from the Width sub-menu. To\n"
627 "choose a specific width select the Dialog widget.\n"
628 "\n"
629 "Choose a point in the Image window and press button 1 and\n"
630 "hold. Next, move the pointer to another location in the\n"
631 "image. As you move, a line connects the initial location and\n"
632 "the pointer. When you release the button, the image is\n"
633 "updated with the primitive you just drew. For polygons, the\n"
634 "image is updated when you press and release the button without\n"
635 "moving the pointer.\n"
636 "\n"
637 "To cancel image drawing, move the pointer back to the\n"
638 "starting point of the line and release the button.\n"
639 },
640 DisplayHelp[] =
641 {
642 "BUTTONS\n"
643 " The effects of each button press is described below. Three\n"
644 " buttons are required. If you have a two button mouse,\n"
645 " button 1 and 3 are returned. Press ALT and button 3 to\n"
646 " simulate button 2.\n"
647 "\n"
648 " 1 Press this button to map or unmap the Command widget.\n"
649 "\n"
650 " 2 Press and drag to define a region of the image to\n"
651 " magnify.\n"
652 "\n"
653 " 3 Press and drag to choose from a select set of commands.\n"
654 " This button behaves differently if the image being\n"
655 " displayed is a visual image directory. Here, choose a\n"
656 " particular tile of the directory and press this button and\n"
657 " drag to select a command from a pop-up menu. Choose from\n"
658 " these menu items:\n"
659 "\n"
660 " Open\n"
661 " Next\n"
662 " Former\n"
663 " Delete\n"
664 " Update\n"
665 "\n"
666 " If you choose Open, the image represented by the tile is\n"
667 " displayed. To return to the visual image directory, choose\n"
668 " Next from the Command widget. Next and Former moves to the\n"
669 " next or former image respectively. Choose Delete to delete\n"
670 " a particular image tile. Finally, choose Update to\n"
671 " synchronize all the image tiles with their respective\n"
672 " images.\n"
673 "\n"
674 "COMMAND WIDGET\n"
675 " The Command widget lists a number of sub-menus and commands.\n"
676 " They are\n"
677 "\n"
678 " File\n"
679 " Open...\n"
680 " Next\n"
681 " Former\n"
682 " Select...\n"
683 " Save...\n"
684 " Print...\n"
685 " Delete...\n"
686 " New...\n"
687 " Visual Directory...\n"
688 " Quit\n"
689 " Edit\n"
690 " Undo\n"
691 " Redo\n"
692 " Cut\n"
693 " Copy\n"
694 " Paste\n"
695 " View\n"
696 " Half Size\n"
697 " Original Size\n"
698 " Double Size\n"
699 " Resize...\n"
700 " Apply\n"
701 " Refresh\n"
702 " Restore\n"
703 " Transform\n"
704 " Crop\n"
705 " Chop\n"
706 " Flop\n"
707 " Flip\n"
708 " Rotate Right\n"
709 " Rotate Left\n"
710 " Rotate...\n"
711 " Shear...\n"
712 " Roll...\n"
713 " Trim Edges\n"
714 " Enhance\n"
715 " Brightness...\n"
716 " Saturation...\n"
717 " Hue...\n"
718 " Gamma...\n"
719 " Sharpen...\n"
720 " Dull\n"
721 " Contrast Stretch...\n"
722 " Sigmoidal Contrast...\n"
723 " Normalize\n"
724 " Equalize\n"
725 " Negate\n"
726 " Grayscale\n"
727 " Map...\n"
728 " Quantize...\n"
729 " Effects\n"
730 " Despeckle\n"
731 " Emboss\n"
732 " Reduce Noise\n"
733 " Add Noise\n"
734 " Sharpen...\n"
735 " Blur...\n"
736 " Threshold...\n"
737 " Edge Detect...\n"
738 " Spread...\n"
739 " Shade...\n"
740 " Painting...\n"
741 " Segment...\n"
742 " F/X\n"
743 " Solarize...\n"
744 " Sepia Tone...\n"
745 " Swirl...\n"
746 " Implode...\n"
747 " Vignette...\n"
748 " Wave...\n"
749 " Oil Painting...\n"
750 " Charcoal Drawing...\n"
751 " Image Edit\n"
752 " Annotate...\n"
753 " Draw...\n"
754 " Color...\n"
755 " Matte...\n"
756 " Composite...\n"
757 " Add Border...\n"
758 " Add Frame...\n"
759 " Comment...\n"
760 " Launch...\n"
761 " Region of Interest...\n"
762 " Miscellany\n"
763 " Image Info\n"
764 " Zoom Image\n"
765 " Show Preview...\n"
766 " Show Histogram\n"
767 " Show Matte\n"
768 " Background...\n"
769 " Slide Show\n"
770 " Preferences...\n"
771 " Help\n"
772 " Overview\n"
773 " Browse Documentation\n"
774 " About Display\n"
775 "\n"
776 " Menu items with a indented triangle have a sub-menu. They\n"
777 " are represented above as the indented items. To access a\n"
778 " sub-menu item, move the pointer to the appropriate menu and\n"
779 " press a button and drag. When you find the desired sub-menu\n"
780 " item, release the button and the command is executed. Move\n"
781 " the pointer away from the sub-menu if you decide not to\n"
782 " execute a particular command.\n"
783 "\n"
784 "KEYBOARD ACCELERATORS\n"
785 " Accelerators are one or two key presses that effect a\n"
786 " particular command. The keyboard accelerators that\n"
787 " display(1) understands is:\n"
788 "\n"
789 " Ctl+O Press to open an image from a file.\n"
790 "\n"
791 " space Press to display the next image.\n"
792 "\n"
793 " If the image is a multi-paged document such as a Postscript\n"
794 " document, you can skip ahead several pages by preceding\n"
795 " this command with a number. For example to display the\n"
796 " third page beyond the current page, press 3<space>.\n"
797 "\n"
798 " backspace Press to display the former image.\n"
799 "\n"
800 " If the image is a multi-paged document such as a Postscript\n"
801 " document, you can skip behind several pages by preceding\n"
802 " this command with a number. For example to display the\n"
803 " third page preceding the current page, press 3<backspace>.\n"
804 "\n"
805 " Ctl+S Press to write the image to a file.\n"
806 "\n"
807 " Ctl+P Press to print the image to a Postscript printer.\n"
808 "\n"
809 " Ctl+D Press to delete an image file.\n"
810 "\n"
811 " Ctl+N Press to create a blank canvas.\n"
812 "\n"
813 " Ctl+Q Press to discard all images and exit program.\n"
814 "\n"
815 " Ctl+Z Press to undo last image transformation.\n"
816 "\n"
817 " Ctl+R Press to redo last image transformation.\n"
818 "\n"
819 " Ctl+X Press to cut a region of the image.\n"
820 "\n"
821 " Ctl+C Press to copy a region of the image.\n"
822 "\n"
823 " Ctl+V Press to paste a region to the image.\n"
824 "\n"
825 " < Press to half the image size.\n"
826 "\n"
827 " - Press to return to the original image size.\n"
828 "\n"
829 " > Press to double the image size.\n"
830 "\n"
831 " % Press to resize the image to a width and height you\n"
832 " specify.\n"
833 "\n"
834 "Cmd-A Press to make any image transformations permanent."
835 "\n"
836 " By default, any image size transformations are applied\n"
837 " to the original image to create the image displayed on\n"
838 " the X server. However, the transformations are not\n"
839 " permanent (i.e. the original image does not change\n"
840 " size only the X image does). For example, if you\n"
841 " press > the X image will appear to double in size,\n"
842 " but the original image will in fact remain the same size.\n"
843 " To force the original image to double in size, press >\n"
844 " followed by Cmd-A.\n"
845 "\n"
846 " @ Press to refresh the image window.\n"
847 "\n"
848 " C Press to cut out a rectangular region of the image.\n"
849 "\n"
850 " [ Press to chop the image.\n"
851 "\n"
852 " H Press to flop image in the horizontal direction.\n"
853 "\n"
854 " V Press to flip image in the vertical direction.\n"
855 "\n"
856 " / Press to rotate the image 90 degrees clockwise.\n"
857 "\n"
858 " \\ Press to rotate the image 90 degrees counter-clockwise.\n"
859 "\n"
860 " * Press to rotate the image the number of degrees you\n"
861 " specify.\n"
862 "\n"
863 " S Press to shear the image the number of degrees you\n"
864 " specify.\n"
865 "\n"
866 " R Press to roll the image.\n"
867 "\n"
868 " T Press to trim the image edges.\n"
869 "\n"
870 " Shft-H Press to vary the image hue.\n"
871 "\n"
872 " Shft-S Press to vary the color saturation.\n"
873 "\n"
874 " Shft-L Press to vary the color brightness.\n"
875 "\n"
876 " Shft-G Press to gamma correct the image.\n"
877 "\n"
878 " Shft-C Press to sharpen the image contrast.\n"
879 "\n"
880 " Shft-Z Press to dull the image contrast.\n"
881 "\n"
882 " = Press to perform histogram equalization on the image.\n"
883 "\n"
884 " Shft-N Press to perform histogram normalization on the image.\n"
885 "\n"
886 " Shft-~ Press to negate the colors of the image.\n"
887 "\n"
888 " . Press to convert the image colors to gray.\n"
889 "\n"
890 " Shft-# Press to set the maximum number of unique colors in the\n"
891 " image.\n"
892 "\n"
893 " F2 Press to reduce the speckles in an image.\n"
894 "\n"
895 " F3 Press to eliminate peak noise from an image.\n"
896 "\n"
897 " F4 Press to add noise to an image.\n"
898 "\n"
899 " F5 Press to sharpen an image.\n"
900 "\n"
901 " F6 Press to delete an image file.\n"
902 "\n"
903 " F7 Press to threshold the image.\n"
904 "\n"
905 " F8 Press to detect edges within an image.\n"
906 "\n"
907 " F9 Press to emboss an image.\n"
908 "\n"
909 " F10 Press to displace pixels by a random amount.\n"
910 "\n"
911 " F11 Press to negate all pixels above the threshold level.\n"
912 "\n"
913 " F12 Press to shade the image using a distant light source.\n"
914 "\n"
915 " F13 Press to lighten or darken image edges to create a 3-D effect.\n"
916 "\n"
917 " F14 Press to segment the image by color.\n"
918 "\n"
919 " Meta-S Press to swirl image pixels about the center.\n"
920 "\n"
921 " Meta-I Press to implode image pixels about the center.\n"
922 "\n"
923 " Meta-W Press to alter an image along a sine wave.\n"
924 "\n"
925 " Meta-P Press to simulate an oil painting.\n"
926 "\n"
927 " Meta-C Press to simulate a charcoal drawing.\n"
928 "\n"
929 " Alt-A Press to annotate the image with text.\n"
930 "\n"
931 " Alt-D Press to draw on an image.\n"
932 "\n"
933 " Alt-P Press to edit an image pixel color.\n"
934 "\n"
935 " Alt-M Press to edit the image matte information.\n"
936 "\n"
937 " Alt-V Press to composite the image with another.\n"
938 "\n"
939 " Alt-B Press to add a border to the image.\n"
940 "\n"
941 " Alt-F Press to add an ornamental border to the image.\n"
942 "\n"
943 " Alt-Shft-!\n"
944 " Press to add an image comment.\n"
945 "\n"
946 " Ctl-A Press to apply image processing techniques to a region\n"
947 " of interest.\n"
948 "\n"
949 " Shft-? Press to display information about the image.\n"
950 "\n"
951 " Shft-+ Press to map the zoom image window.\n"
952 "\n"
953 " Shft-P Press to preview an image enhancement, effect, or f/x.\n"
954 "\n"
955 " F1 Press to display helpful information about display(1).\n"
956 "\n"
957 " Find Press to browse documentation about ImageMagick.\n"
958 "\n"
959 " 1-9 Press to change the level of magnification.\n"
960 "\n"
961 " Use the arrow keys to move the image one pixel up, down,\n"
962 " left, or right within the magnify window. Be sure to first\n"
963 " map the magnify window by pressing button 2.\n"
964 "\n"
965 " Press ALT and one of the arrow keys to trim off one pixel\n"
966 " from any side of the image.\n"
967 },
968 ImageMatteEditHelp[] =
969 {
970 "Matte information within an image is useful for some\n"
971 "operations such as image compositing (See IMAGE\n"
972 "COMPOSITING). This extra channel usually defines a mask\n"
973 "which represents a sort of a cookie-cutter for the image.\n"
974 "This the case when matte is opaque (full coverage) for\n"
975 "pixels inside the shape, zero outside, and between 0 and\n"
976 "QuantumRange on the boundary.\n"
977 "\n"
978 "A small window appears showing the location of the cursor in\n"
979 "the image window. You are now in matte edit mode. To exit\n"
980 "immediately, press Dismiss. In matte edit mode, the Command\n"
981 "widget has these options:\n"
982 "\n"
983 " Method\n"
984 " point\n"
985 " replace\n"
986 " floodfill\n"
987 " filltoborder\n"
988 " reset\n"
989 " Border Color\n"
990 " black\n"
991 " blue\n"
992 " cyan\n"
993 " green\n"
994 " gray\n"
995 " red\n"
996 " magenta\n"
997 " yellow\n"
998 " white\n"
999 " Browser...\n"
1000 " Fuzz\n"
1001 " 0%\n"
1002 " 2%\n"
1003 " 5%\n"
1004 " 10%\n"
1005 " 15%\n"
1006 " Dialog...\n"
1007 " Matte\n"
1008 " Opaque\n"
1009 " Transparent\n"
1010 " Dialog...\n"
1011 " Undo\n"
1012 " Help\n"
1013 " Dismiss\n"
1014 "\n"
1015 "Choose a matte editing method from the Method sub-menu of\n"
1016 "the Command widget. The point method changes the matte value\n"
1017 "of any pixel selected with the pointer until the button is\n"
1018 "is released. The replace method changes the matte value of\n"
1019 "any pixel that matches the color of the pixel you select with\n"
1020 "a button press. Floodfill changes the matte value of any pixel\n"
1021 "that matches the color of the pixel you select with a button\n"
1022 "press and is a neighbor. Whereas filltoborder changes the matte\n"
1023 "value any neighbor pixel that is not the border color. Finally\n"
1024 "reset changes the entire image to the designated matte value.\n"
1025 "\n"
1026 "Choose Matte Value and pick Opaque or Transparent. For other values\n"
1027 "select the Dialog entry. Here a dialog appears requesting a matte\n"
1028 "value. The value you select is assigned as the opacity value of the\n"
1029 "selected pixel or pixels.\n"
1030 "\n"
1031 "Now, press any button to select a pixel within the image\n"
1032 "window to change its matte value.\n"
1033 "\n"
1034 "If the Magnify widget is mapped, it can be helpful in positioning\n"
1035 "your pointer within the image (refer to button 2).\n"
1036 "\n"
1037 "Matte information is only valid in a DirectClass image.\n"
1038 "Therefore, any PseudoClass image is promoted to DirectClass\n"
1039 "(see miff(5)). Note that matte information for PseudoClass\n"
1040 "is not retained for colormapped X server visuals (e.g.\n"
1041 "StaticColor, StaticColor, GrayScale, PseudoColor) unless you\n"
1042 "immediately save your image to a file (refer to Write).\n"
1043 "Correct matte editing behavior may require a TrueColor or\n"
1044 "DirectColor visual or a Standard Colormap.\n"
1045 },
1046 ImagePanHelp[] =
1047 {
1048 "When an image exceeds the width or height of the X server\n"
1049 "screen, display maps a small panning icon. The rectangle\n"
1050 "within the panning icon shows the area that is currently\n"
1051 "displayed in the image window. To pan about the image,\n"
1052 "press any button and drag the pointer within the panning\n"
1053 "icon. The pan rectangle moves with the pointer and the\n"
1054 "image window is updated to reflect the location of the\n"
1055 "rectangle within the panning icon. When you have selected\n"
1056 "the area of the image you wish to view, release the button.\n"
1057 "\n"
1058 "Use the arrow keys to pan the image one pixel up, down,\n"
1059 "left, or right within the image window.\n"
1060 "\n"
1061 "The panning icon is withdrawn if the image becomes smaller\n"
1062 "than the dimensions of the X server screen.\n"
1063 },
1064 ImagePasteHelp[] =
1065 {
1066 "A small window appears showing the location of the cursor in\n"
1067 "the image window. You are now in paste mode. To exit\n"
1068 "immediately, press Dismiss. In paste mode, the Command\n"
1069 "widget has these options:\n"
1070 "\n"
1071 " Operators\n"
1072 " over\n"
1073 " in\n"
1074 " out\n"
1075 " atop\n"
1076 " xor\n"
1077 " plus\n"
1078 " minus\n"
1079 " add\n"
1080 " subtract\n"
1081 " difference\n"
1082 " replace\n"
1083 " Help\n"
1084 " Dismiss\n"
1085 "\n"
1086 "Choose a composite operation from the Operators sub-menu of\n"
1087 "the Command widget. How each operator behaves is described\n"
1088 "below. Image window is the image currently displayed on\n"
1089 "your X server and image is the image obtained with the File\n"
1090 "Browser widget.\n"
1091 "\n"
1092 "Over The result is the union of the two image shapes,\n"
1093 " with image obscuring image window in the region of\n"
1094 " overlap.\n"
1095 "\n"
1096 "In The result is simply image cut by the shape of\n"
1097 " image window. None of the image data of image\n"
1098 " window is in the result.\n"
1099 "\n"
1100 "Out The resulting image is image with the shape of\n"
1101 " image window cut out.\n"
1102 "\n"
1103 "Atop The result is the same shape as the image window,\n"
1104 " with image obscuring image window where the image\n"
1105 " shapes overlap. Note this differs from over\n"
1106 " because the portion of image outside image window's\n"
1107 " shape does not appear in the result.\n"
1108 "\n"
1109 "Xor The result is the image data from both image and\n"
1110 " image window that is outside the overlap region.\n"
1111 " The overlap region is blank.\n"
1112 "\n"
1113 "Plus The result is just the sum of the image data.\n"
1114 " Output values are cropped to QuantumRange (no overflow).\n"
1115 " This operation is independent of the matte\n"
1116 " channels.\n"
1117 "\n"
1118 "Minus The result of image - image window, with underflow\n"
1119 " cropped to zero.\n"
1120 "\n"
1121 "Add The result of image + image window, with overflow\n"
1122 " wrapping around (mod 256).\n"
1123 "\n"
1124 "Subtract The result of image - image window, with underflow\n"
1125 " wrapping around (mod 256). The add and subtract\n"
1126 " operators can be used to perform reversible\n"
1127 " transformations.\n"
1128 "\n"
1129 "Difference\n"
1130 " The result of abs(image - image window). This\n"
1131 " useful for comparing two very similar images.\n"
1132 "\n"
1133 "Copy The resulting image is image window replaced with\n"
1134 " image. Here the matte information is ignored.\n"
1135 "\n"
1136 "CopyRed The red layer of the image window is replace with\n"
1137 " the red layer of the image. The other layers are\n"
1138 " untouched.\n"
1139 "\n"
1140 "CopyGreen\n"
1141 " The green layer of the image window is replace with\n"
1142 " the green layer of the image. The other layers are\n"
1143 " untouched.\n"
1144 "\n"
1145 "CopyBlue The blue layer of the image window is replace with\n"
1146 " the blue layer of the image. The other layers are\n"
1147 " untouched.\n"
1148 "\n"
1149 "CopyOpacity\n"
1150 " The matte layer of the image window is replace with\n"
1151 " the matte layer of the image. The other layers are\n"
1152 " untouched.\n"
1153 "\n"
1154 "The image compositor requires a matte, or alpha channel in\n"
1155 "the image for some operations. This extra channel usually\n"
1156 "defines a mask which represents a sort of a cookie-cutter\n"
1157 "for the image. This the case when matte is opaque (full\n"
1158 "coverage) for pixels inside the shape, zero outside, and\n"
1159 "between 0 and QuantumRange on the boundary. If image does not\n"
1160 "have a matte channel, it is initialized with 0 for any pixel\n"
1161 "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
1162 "\n"
1163 "Note that matte information for image window is not retained\n"
1164 "for colormapped X server visuals (e.g. StaticColor,\n"
1165 "StaticColor, GrayScale, PseudoColor). Correct compositing\n"
1166 "behavior may require a TrueColor or DirectColor visual or a\n"
1167 "Standard Colormap.\n"
1168 "\n"
1169 "Choosing a composite operator is optional. The default\n"
1170 "operator is replace. However, you must choose a location to\n"
1171 "paste your image and press button 1. Press and hold the\n"
1172 "button before releasing and an outline of the image will\n"
1173 "appear to help you identify your location.\n"
1174 "\n"
1175 "The actual colors of the pasted image is saved. However,\n"
1176 "the color that appears in image window may be different.\n"
1177 "For example, on a monochrome screen image window will appear\n"
1178 "black or white even though your pasted image may have\n"
1179 "many colors. If the image is saved to a file it is written\n"
1180 "with the correct colors. To assure the correct colors are\n"
1181 "saved in the final image, any PseudoClass image is promoted\n"
1182 "to DirectClass (see miff(5)). To force a PseudoClass image\n"
1183 "to remain PseudoClass, use -colors.\n"
1184 },
1185 ImageROIHelp[] =
1186 {
1187 "In region of interest mode, the Command widget has these\n"
1188 "options:\n"
1189 "\n"
1190 " Help\n"
1191 " Dismiss\n"
1192 "\n"
1193 "To define a region of interest, press button 1 and drag.\n"
1194 "The region of interest is defined by a highlighted rectangle\n"
1195 "that expands or contracts as it follows the pointer. Once\n"
1196 "you are satisfied with the region of interest, release the\n"
1197 "button. You are now in apply mode. In apply mode the\n"
1198 "Command widget has these options:\n"
1199 "\n"
1200 " File\n"
1201 " Save...\n"
1202 " Print...\n"
1203 " Edit\n"
1204 " Undo\n"
1205 " Redo\n"
1206 " Transform\n"
1207 " Flop\n"
1208 " Flip\n"
1209 " Rotate Right\n"
1210 " Rotate Left\n"
1211 " Enhance\n"
1212 " Hue...\n"
1213 " Saturation...\n"
1214 " Brightness...\n"
1215 " Gamma...\n"
1216 " Spiff\n"
1217 " Dull\n"
1218 " Contrast Stretch\n"
1219 " Sigmoidal Contrast...\n"
1220 " Normalize\n"
1221 " Equalize\n"
1222 " Negate\n"
1223 " Grayscale\n"
1224 " Map...\n"
1225 " Quantize...\n"
1226 " Effects\n"
1227 " Despeckle\n"
1228 " Emboss\n"
1229 " Reduce Noise\n"
1230 " Sharpen...\n"
1231 " Blur...\n"
1232 " Threshold...\n"
1233 " Edge Detect...\n"
1234 " Spread...\n"
1235 " Shade...\n"
1236 " Raise...\n"
1237 " Segment...\n"
1238 " F/X\n"
1239 " Solarize...\n"
1240 " Sepia Tone...\n"
1241 " Swirl...\n"
1242 " Implode...\n"
1243 " Vignette...\n"
1244 " Wave...\n"
1245 " Oil Painting...\n"
1246 " Charcoal Drawing...\n"
1247 " Miscellany\n"
1248 " Image Info\n"
1249 " Zoom Image\n"
1250 " Show Preview...\n"
1251 " Show Histogram\n"
1252 " Show Matte\n"
1253 " Help\n"
1254 " Dismiss\n"
1255 "\n"
1256 "You can make adjustments to the region of interest by moving\n"
1257 "the pointer to one of the rectangle corners, pressing a\n"
1258 "button, and dragging. Finally, choose an image processing\n"
1259 "technique from the Command widget. You can choose more than\n"
1260 "one image processing technique to apply to an area.\n"
1261 "Alternatively, you can move the region of interest before\n"
1262 "applying another image processing technique. To exit, press\n"
1263 "Dismiss.\n"
1264 },
1265 ImageRotateHelp[] =
1266 {
1267 "In rotate mode, the Command widget has these options:\n"
1268 "\n"
1269 " Pixel Color\n"
1270 " black\n"
1271 " blue\n"
1272 " cyan\n"
1273 " green\n"
1274 " gray\n"
1275 " red\n"
1276 " magenta\n"
1277 " yellow\n"
1278 " white\n"
1279 " Browser...\n"
1280 " Direction\n"
1281 " horizontal\n"
1282 " vertical\n"
1283 " Help\n"
1284 " Dismiss\n"
1285 "\n"
1286 "Choose a background color from the Pixel Color sub-menu.\n"
1287 "Additional background colors can be specified with the color\n"
1288 "browser. You can change the menu colors by setting the X\n"
1289 "resources pen1 through pen9.\n"
1290 "\n"
1291 "If you choose the color browser and press Grab, you can\n"
1292 "select the background color by moving the pointer to the\n"
1293 "desired color on the screen and press any button.\n"
1294 "\n"
1295 "Choose a point in the image window and press this button and\n"
1296 "hold. Next, move the pointer to another location in the\n"
1297 "image. As you move a line connects the initial location and\n"
1298 "the pointer. When you release the button, the degree of\n"
1299 "image rotation is determined by the slope of the line you\n"
1300 "just drew. The slope is relative to the direction you\n"
1301 "choose from the Direction sub-menu of the Command widget.\n"
1302 "\n"
1303 "To cancel the image rotation, move the pointer back to the\n"
1304 "starting point of the line and release the button.\n"
1305 };
1306
1307/*
1308 Enumeration declarations.
1309*/
1310typedef enum
1311{
1312 CopyMode,
1313 CropMode,
1314 CutMode
1315} ClipboardMode;
1316
1317typedef enum
1318{
1319 OpenCommand,
1320 NextCommand,
1321 FormerCommand,
1322 SelectCommand,
1323 SaveCommand,
1324 PrintCommand,
1325 DeleteCommand,
1326 NewCommand,
1327 VisualDirectoryCommand,
1328 QuitCommand,
1329 UndoCommand,
1330 RedoCommand,
1331 CutCommand,
1332 CopyCommand,
1333 PasteCommand,
1334 HalfSizeCommand,
1335 OriginalSizeCommand,
1336 DoubleSizeCommand,
1337 ResizeCommand,
1338 ApplyCommand,
1339 RefreshCommand,
1340 RestoreCommand,
1341 CropCommand,
1342 ChopCommand,
1343 FlopCommand,
1344 FlipCommand,
1345 RotateRightCommand,
1346 RotateLeftCommand,
1347 RotateCommand,
1348 ShearCommand,
1349 RollCommand,
1350 TrimCommand,
1351 HueCommand,
1352 SaturationCommand,
1353 BrightnessCommand,
1354 GammaCommand,
1355 SpiffCommand,
1356 DullCommand,
1357 ContrastStretchCommand,
1358 SigmoidalContrastCommand,
1359 NormalizeCommand,
1360 EqualizeCommand,
1361 NegateCommand,
1362 GrayscaleCommand,
1363 MapCommand,
1364 QuantizeCommand,
1365 DespeckleCommand,
1366 EmbossCommand,
1367 ReduceNoiseCommand,
1368 AddNoiseCommand,
1369 SharpenCommand,
1370 BlurCommand,
1371 ThresholdCommand,
1372 EdgeDetectCommand,
1373 SpreadCommand,
1374 ShadeCommand,
1375 RaiseCommand,
1376 SegmentCommand,
1377 SolarizeCommand,
1378 SepiaToneCommand,
1379 SwirlCommand,
1380 ImplodeCommand,
1381 VignetteCommand,
1382 WaveCommand,
1383 OilPaintCommand,
1384 CharcoalDrawCommand,
1385 AnnotateCommand,
1386 DrawCommand,
1387 ColorCommand,
1388 MatteCommand,
1389 CompositeCommand,
1390 AddBorderCommand,
1391 AddFrameCommand,
1392 CommentCommand,
1393 LaunchCommand,
1394 RegionOfInterestCommand,
1395 ROIHelpCommand,
1396 ROIDismissCommand,
1397 InfoCommand,
1398 ZoomCommand,
1399 ShowPreviewCommand,
1400 ShowHistogramCommand,
1401 ShowMatteCommand,
1402 BackgroundCommand,
1403 SlideShowCommand,
1404 PreferencesCommand,
1405 HelpCommand,
1406 BrowseDocumentationCommand,
1407 VersionCommand,
1408 SaveToUndoBufferCommand,
1409 FreeBuffersCommand,
1410 NullCommand
1411} DisplayCommand;
1412
1413typedef enum
1414{
1415 AnnotateNameCommand,
1416 AnnotateFontColorCommand,
1417 AnnotateBackgroundColorCommand,
1418 AnnotateRotateCommand,
1419 AnnotateHelpCommand,
1420 AnnotateDismissCommand,
1421 TextHelpCommand,
1422 TextApplyCommand,
1423 ChopDirectionCommand,
1424 ChopHelpCommand,
1425 ChopDismissCommand,
1426 HorizontalChopCommand,
1427 VerticalChopCommand,
1428 ColorEditMethodCommand,
1429 ColorEditColorCommand,
1430 ColorEditBorderCommand,
1431 ColorEditFuzzCommand,
1432 ColorEditUndoCommand,
1433 ColorEditHelpCommand,
1434 ColorEditDismissCommand,
1435 CompositeOperatorsCommand,
1436 CompositeDissolveCommand,
1437 CompositeDisplaceCommand,
1438 CompositeHelpCommand,
1439 CompositeDismissCommand,
1440 CropHelpCommand,
1441 CropDismissCommand,
1442 RectifyCopyCommand,
1443 RectifyHelpCommand,
1444 RectifyDismissCommand,
1445 DrawElementCommand,
1446 DrawColorCommand,
1447 DrawStippleCommand,
1448 DrawWidthCommand,
1449 DrawUndoCommand,
1450 DrawHelpCommand,
1451 DrawDismissCommand,
1452 MatteEditMethod,
1453 MatteEditBorderCommand,
1454 MatteEditFuzzCommand,
1455 MatteEditValueCommand,
1456 MatteEditUndoCommand,
1457 MatteEditHelpCommand,
1458 MatteEditDismissCommand,
1459 PasteOperatorsCommand,
1460 PasteHelpCommand,
1461 PasteDismissCommand,
1462 RotateColorCommand,
1463 RotateDirectionCommand,
1464 RotateCropCommand,
1465 RotateSharpenCommand,
1466 RotateHelpCommand,
1467 RotateDismissCommand,
1468 HorizontalRotateCommand,
1469 VerticalRotateCommand,
1470 TileLoadCommand,
1471 TileNextCommand,
1472 TileFormerCommand,
1473 TileDeleteCommand,
1474 TileUpdateCommand
1475} ModeType;
1476
1477/*
1478 Stipples.
1479*/
1480#define BricksWidth 20
1481#define BricksHeight 20
1482#define DiagonalWidth 16
1483#define DiagonalHeight 16
1484#define HighlightWidth 8
1485#define HighlightHeight 8
1486#define OpaqueWidth 8
1487#define OpaqueHeight 8
1488#define ScalesWidth 16
1489#define ScalesHeight 16
1490#define ShadowWidth 8
1491#define ShadowHeight 8
1492#define VerticalWidth 16
1493#define VerticalHeight 16
1494#define WavyWidth 16
1495#define WavyHeight 16
1496
1497/*
1498 Constant declaration.
1499*/
1500static const int
1501 RoiDelta = 8;
1502
1503static const unsigned char
1504 BricksBitmap[] =
1505 {
1506 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1507 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1508 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1509 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1510 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1511 },
1512 DiagonalBitmap[] =
1513 {
1514 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1515 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1516 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1517 },
1518 ScalesBitmap[] =
1519 {
1520 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1521 0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1522 0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1523 },
1524 VerticalBitmap[] =
1525 {
1526 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1527 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1528 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1529 },
1530 WavyBitmap[] =
1531 {
1532 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1533 0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1534 0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1535 };
1536
1537/*
1538 Function prototypes.
1539*/
1540static DisplayCommand
1541 XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1542 const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1543
1544static Image
1545 *XMagickCommand(Display *,XResourceInfo *,XWindows *,const DisplayCommand,
1546 Image **,ExceptionInfo *),
1547 *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1548 *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1549 ExceptionInfo *),
1550 *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1551 ExceptionInfo *);
1552
1553static MagickBooleanType
1554 XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1555 ExceptionInfo *),
1556 XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1557 ExceptionInfo *),
1558 XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1559 ExceptionInfo *),
1560 XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1561 ExceptionInfo *),
1562 XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1563 ExceptionInfo *),
1564 XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1565 ExceptionInfo *),
1566 XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1567 XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1568 ExceptionInfo *),
1569 XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1570 ExceptionInfo *),
1571 XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1572 XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1573 XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1574 ExceptionInfo *),
1575 XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1576 XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1577 XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1578
1579static void
1580 XDrawPanRectangle(Display *,XWindows *),
1581 XImageCache(Display *,XResourceInfo *,XWindows *,const DisplayCommand,Image **,
1582 ExceptionInfo *),
1583 XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1584 XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1585 XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1586 XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1587 const KeySym,ExceptionInfo *),
1588 XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1589 XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1590 XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1591
1592/*
1593%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1594% %
1595% %
1596% %
1597% D i s p l a y I m a g e s %
1598% %
1599% %
1600% %
1601%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1602%
1603% DisplayImages() displays an image sequence to any X window screen. It
1604% returns a value other than 0 if successful. Check the exception member
1605% of image to determine the reason for any failure.
1606%
1607% The format of the DisplayImages method is:
1608%
1609% MagickBooleanType DisplayImages(const ImageInfo *image_info,
1610% Image *images,ExceptionInfo *exception)
1611%
1612% A description of each parameter follows:
1613%
1614% o image_info: the image info.
1615%
1616% o image: the image.
1617%
1618% o exception: return any errors or warnings in this structure.
1619%
1620*/
1621MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1622 Image *images,ExceptionInfo *exception)
1623{
1624 char
1625 *argv[1];
1626
1627 Display
1628 *display;
1629
1630 Image
1631 *image;
1632
1633 size_t
1634 state;
1635
1636 ssize_t
1637 i;
1638
1639 XrmDatabase
1640 resource_database;
1641
1642 XResourceInfo
1643 resource_info;
1644
1645 assert(image_info != (const ImageInfo *) NULL);
1646 assert(image_info->signature == MagickCoreSignature);
1647 assert(images != (Image *) NULL);
1648 assert(images->signature == MagickCoreSignature);
1649 if (IsEventLogging() != MagickFalse)
1650 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1651 display=XOpenDisplay(image_info->server_name);
1652 if (display == (Display *) NULL)
1653 {
1654 (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1655 "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1656 return(MagickFalse);
1657 }
1658 if (exception->severity != UndefinedException)
1659 CatchException(exception);
1660 (void) XSetErrorHandler(XError);
1661 resource_database=XGetResourceDatabase(display,GetClientName());
1662 (void) memset(&resource_info,0,sizeof(resource_info));
1663 XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1664 if (image_info->page != (char *) NULL)
1665 resource_info.image_geometry=AcquireString(image_info->page);
1666 resource_info.immutable=MagickTrue;
1667 argv[0]=AcquireString(GetClientName());
1668 state=DefaultState;
1669 for (i=0; (state & ExitState) == 0; i++)
1670 {
1671 if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1672 break;
1673 image=GetImageFromList(images,i % (ssize_t) GetImageListLength(images));
1674 (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1675 }
1676 (void) SetErrorHandler((ErrorHandler) NULL);
1677 (void) SetWarningHandler((WarningHandler) NULL);
1678 argv[0]=DestroyString(argv[0]);
1679 (void) XCloseDisplay(display);
1680 XDestroyResourceInfo(&resource_info);
1681 if (exception->severity != UndefinedException)
1682 return(MagickFalse);
1683 return(MagickTrue);
1684}
1685
1686/*
1687%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1688% %
1689% %
1690% %
1691% R e m o t e D i s p l a y C o m m a n d %
1692% %
1693% %
1694% %
1695%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1696%
1697% RemoteDisplayCommand() encourages a remote display program to display the
1698% specified image filename.
1699%
1700% The format of the RemoteDisplayCommand method is:
1701%
1702% MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1703% const char *window,const char *filename,ExceptionInfo *exception)
1704%
1705% A description of each parameter follows:
1706%
1707% o image_info: the image info.
1708%
1709% o window: Specifies the name or id of an X window.
1710%
1711% o filename: the name of the image filename to display.
1712%
1713% o exception: return any errors or warnings in this structure.
1714%
1715*/
1716MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1717 const char *window,const char *filename,ExceptionInfo *exception)
1718{
1719 Display
1720 *display;
1721
1722 MagickStatusType
1723 status;
1724
1725 assert(image_info != (const ImageInfo *) NULL);
1726 assert(image_info->signature == MagickCoreSignature);
1727 assert(filename != (char *) NULL);
1728 if (IsEventLogging() != MagickFalse)
1729 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1730 display=XOpenDisplay(image_info->server_name);
1731 if (display == (Display *) NULL)
1732 {
1733 (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1734 "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1735 return(MagickFalse);
1736 }
1737 (void) XSetErrorHandler(XError);
1738 status=XRemoteCommand(display,window,filename);
1739 (void) XCloseDisplay(display);
1740 return(status != 0 ? MagickTrue : MagickFalse);
1741}
1742
1743/*
1744%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1745% %
1746% %
1747% %
1748+ X A n n o t a t e E d i t I m a g e %
1749% %
1750% %
1751% %
1752%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1753%
1754% XAnnotateEditImage() annotates the image with text.
1755%
1756% The format of the XAnnotateEditImage method is:
1757%
1758% MagickBooleanType XAnnotateEditImage(Display *display,
1759% XResourceInfo *resource_info,XWindows *windows,Image *image,
1760% ExceptionInfo *exception)
1761%
1762% A description of each parameter follows:
1763%
1764% o display: Specifies a connection to an X server; returned from
1765% XOpenDisplay.
1766%
1767% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1768%
1769% o windows: Specifies a pointer to a XWindows structure.
1770%
1771% o image: the image; returned from ReadImage.
1772%
1773*/
1774
1775static MagickBooleanType XAnnotateEditImage(Display *display,
1776 XResourceInfo *resource_info,XWindows *windows,Image *image,
1777 ExceptionInfo *exception)
1778{
1779 const char
1780 *const AnnotateMenu[] =
1781 {
1782 "Font Name",
1783 "Font Color",
1784 "Box Color",
1785 "Rotate Text",
1786 "Help",
1787 "Dismiss",
1788 (char *) NULL
1789 },
1790 *const TextMenu[] =
1791 {
1792 "Help",
1793 "Apply",
1794 (char *) NULL
1795 };
1796
1797 static const ModeType
1798 AnnotateCommands[] =
1799 {
1800 AnnotateNameCommand,
1801 AnnotateFontColorCommand,
1802 AnnotateBackgroundColorCommand,
1803 AnnotateRotateCommand,
1804 AnnotateHelpCommand,
1805 AnnotateDismissCommand
1806 },
1807 TextCommands[] =
1808 {
1809 TextHelpCommand,
1810 TextApplyCommand
1811 };
1812
1813 static MagickBooleanType
1814 transparent_box = MagickTrue,
1815 transparent_pen = MagickFalse;
1816
1817 static double
1818 degrees = 0.0;
1819
1820 static unsigned int
1821 box_id = MaxNumberPens-2,
1822 font_id = 0,
1823 pen_id = 0;
1824
1825 char
1826 command[MagickPathExtent],
1827 *p,
1828 text[MagickPathExtent];
1829
1830 const char
1831 *ColorMenu[MaxNumberPens+1];
1832
1833 Cursor
1834 cursor;
1835
1836 GC
1837 annotate_context;
1838
1839 int
1840 id,
1841 pen_number,
1842 status,
1843 x,
1844 y;
1845
1846 KeySym
1847 key_symbol;
1848
1849 size_t
1850 state;
1851
1852 ssize_t
1853 i;
1854
1855 unsigned int
1856 height,
1857 width;
1858
1859 XAnnotateInfo
1860 *annotate_info,
1861 *previous_info;
1862
1863 XColor
1864 color;
1865
1866 XFontStruct
1867 *font_info;
1868
1869 XEvent
1870 event,
1871 text_event;
1872
1873 /*
1874 Map Command widget.
1875 */
1876 (void) CloneString(&windows->command.name,"Annotate");
1877 windows->command.data=4;
1878 (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1879 (void) XMapRaised(display,windows->command.id);
1880 XClientMessage(display,windows->image.id,windows->im_protocols,
1881 windows->im_update_widget,CurrentTime);
1882 /*
1883 Track pointer until button 1 is pressed.
1884 */
1885 XQueryPosition(display,windows->image.id,&x,&y);
1886 (void) XSelectInput(display,windows->image.id,
1887 windows->image.attributes.event_mask | PointerMotionMask);
1888 cursor=XCreateFontCursor(display,XC_left_side);
1889 (void) XCheckDefineCursor(display,windows->image.id,cursor);
1890 state=DefaultState;
1891 do
1892 {
1893 if (windows->info.mapped != MagickFalse)
1894 {
1895 /*
1896 Display pointer position.
1897 */
1898 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
1899 x+windows->image.x,y+windows->image.y);
1900 XInfoWidget(display,windows,text);
1901 }
1902 /*
1903 Wait for next event.
1904 */
1905 XScreenEvent(display,windows,&event,exception);
1906 if (event.xany.window == windows->command.id)
1907 {
1908 /*
1909 Select a command from the Command widget.
1910 */
1911 id=XCommandWidget(display,windows,AnnotateMenu,&event);
1912 (void) XCheckDefineCursor(display,windows->image.id,cursor);
1913 if (id < 0)
1914 continue;
1915 switch (AnnotateCommands[id])
1916 {
1917 case AnnotateNameCommand:
1918 {
1919 const char
1920 *FontMenu[MaxNumberFonts];
1921
1922 int
1923 font_number;
1924
1925 /*
1926 Initialize menu selections.
1927 */
1928 for (i=0; i < MaxNumberFonts; i++)
1929 FontMenu[i]=resource_info->font_name[i];
1930 FontMenu[MaxNumberFonts-2]="Browser...";
1931 FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1932 /*
1933 Select a font name from the pop-up menu.
1934 */
1935 font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1936 (const char **) FontMenu,command);
1937 if (font_number < 0)
1938 break;
1939 if (font_number == (MaxNumberFonts-2))
1940 {
1941 static char
1942 font_name[MagickPathExtent] = "fixed";
1943
1944 /*
1945 Select a font name from a browser.
1946 */
1947 resource_info->font_name[font_number]=font_name;
1948 XFontBrowserWidget(display,windows,"Select",font_name);
1949 if (*font_name == '\0')
1950 break;
1951 }
1952 /*
1953 Initialize font info.
1954 */
1955 font_info=XLoadQueryFont(display,resource_info->font_name[
1956 font_number]);
1957 if (font_info == (XFontStruct *) NULL)
1958 {
1959 XNoticeWidget(display,windows,"Unable to load font:",
1960 resource_info->font_name[font_number]);
1961 break;
1962 }
1963 font_id=(unsigned int) font_number;
1964 (void) XFreeFont(display,font_info);
1965 break;
1966 }
1967 case AnnotateFontColorCommand:
1968 {
1969 /*
1970 Initialize menu selections.
1971 */
1972 for (i=0; i < (int) (MaxNumberPens-2); i++)
1973 ColorMenu[i]=resource_info->pen_colors[i];
1974 ColorMenu[MaxNumberPens-2]="transparent";
1975 ColorMenu[MaxNumberPens-1]="Browser...";
1976 ColorMenu[MaxNumberPens]=(const char *) NULL;
1977 /*
1978 Select a pen color from the pop-up menu.
1979 */
1980 pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
1981 (const char **) ColorMenu,command);
1982 if (pen_number < 0)
1983 break;
1984 transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
1985 MagickFalse;
1986 if (transparent_pen != MagickFalse)
1987 break;
1988 if (pen_number == (MaxNumberPens-1))
1989 {
1990 static char
1991 color_name[MagickPathExtent] = "gray";
1992
1993 /*
1994 Select a pen color from a dialog.
1995 */
1996 resource_info->pen_colors[pen_number]=color_name;
1997 XColorBrowserWidget(display,windows,"Select",color_name);
1998 if (*color_name == '\0')
1999 break;
2000 }
2001 /*
2002 Set pen color.
2003 */
2004 (void) XParseColor(display,windows->map_info->colormap,
2005 resource_info->pen_colors[pen_number],&color);
2006 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2007 (unsigned int) MaxColors,&color);
2008 windows->pixel_info->pen_colors[pen_number]=color;
2009 pen_id=(unsigned int) pen_number;
2010 break;
2011 }
2012 case AnnotateBackgroundColorCommand:
2013 {
2014 /*
2015 Initialize menu selections.
2016 */
2017 for (i=0; i < (int) (MaxNumberPens-2); i++)
2018 ColorMenu[i]=resource_info->pen_colors[i];
2019 ColorMenu[MaxNumberPens-2]="transparent";
2020 ColorMenu[MaxNumberPens-1]="Browser...";
2021 ColorMenu[MaxNumberPens]=(const char *) NULL;
2022 /*
2023 Select a pen color from the pop-up menu.
2024 */
2025 pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2026 (const char **) ColorMenu,command);
2027 if (pen_number < 0)
2028 break;
2029 transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2030 MagickFalse;
2031 if (transparent_box != MagickFalse)
2032 break;
2033 if (pen_number == (MaxNumberPens-1))
2034 {
2035 static char
2036 color_name[MagickPathExtent] = "gray";
2037
2038 /*
2039 Select a pen color from a dialog.
2040 */
2041 resource_info->pen_colors[pen_number]=color_name;
2042 XColorBrowserWidget(display,windows,"Select",color_name);
2043 if (*color_name == '\0')
2044 break;
2045 }
2046 /*
2047 Set pen color.
2048 */
2049 (void) XParseColor(display,windows->map_info->colormap,
2050 resource_info->pen_colors[pen_number],&color);
2051 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2052 (unsigned int) MaxColors,&color);
2053 windows->pixel_info->pen_colors[pen_number]=color;
2054 box_id=(unsigned int) pen_number;
2055 break;
2056 }
2057 case AnnotateRotateCommand:
2058 {
2059 int
2060 entry;
2061
2062 const char
2063 *const RotateMenu[] =
2064 {
2065 "-90",
2066 "-45",
2067 "-30",
2068 "0",
2069 "30",
2070 "45",
2071 "90",
2072 "180",
2073 "Dialog...",
2074 (char *) NULL,
2075 };
2076
2077 static char
2078 angle[MagickPathExtent] = "30.0";
2079
2080 /*
2081 Select a command from the pop-up menu.
2082 */
2083 entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2084 command);
2085 if (entry < 0)
2086 break;
2087 if (entry != 8)
2088 {
2089 degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2090 break;
2091 }
2092 (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2093 angle);
2094 if (*angle == '\0')
2095 break;
2096 degrees=StringToDouble(angle,(char **) NULL);
2097 break;
2098 }
2099 case AnnotateHelpCommand:
2100 {
2101 XTextViewHelp(display,resource_info,windows,MagickFalse,
2102 "Help Viewer - Image Annotation",ImageAnnotateHelp);
2103 break;
2104 }
2105 case AnnotateDismissCommand:
2106 {
2107 /*
2108 Prematurely exit.
2109 */
2110 state|=EscapeState;
2111 state|=ExitState;
2112 break;
2113 }
2114 default:
2115 break;
2116 }
2117 continue;
2118 }
2119 switch (event.type)
2120 {
2121 case ButtonPress:
2122 {
2123 if (event.xbutton.button != Button1)
2124 break;
2125 if (event.xbutton.window != windows->image.id)
2126 break;
2127 /*
2128 Change to text entering mode.
2129 */
2130 x=event.xbutton.x;
2131 y=event.xbutton.y;
2132 state|=ExitState;
2133 break;
2134 }
2135 case ButtonRelease:
2136 break;
2137 case Expose:
2138 break;
2139 case KeyPress:
2140 {
2141 if (event.xkey.window != windows->image.id)
2142 break;
2143 /*
2144 Respond to a user key press.
2145 */
2146 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2147 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2148 switch ((int) key_symbol)
2149 {
2150 case XK_Escape:
2151 case XK_F20:
2152 {
2153 /*
2154 Prematurely exit.
2155 */
2156 state|=EscapeState;
2157 state|=ExitState;
2158 break;
2159 }
2160 case XK_F1:
2161 case XK_Help:
2162 {
2163 XTextViewHelp(display,resource_info,windows,MagickFalse,
2164 "Help Viewer - Image Annotation",ImageAnnotateHelp);
2165 break;
2166 }
2167 default:
2168 {
2169 (void) XBell(display,0);
2170 break;
2171 }
2172 }
2173 break;
2174 }
2175 case MotionNotify:
2176 {
2177 /*
2178 Map and unmap Info widget as cursor crosses its boundaries.
2179 */
2180 x=event.xmotion.x;
2181 y=event.xmotion.y;
2182 if (windows->info.mapped != MagickFalse)
2183 {
2184 if ((x < (windows->info.x+(int) windows->info.width)) &&
2185 (y < (windows->info.y+(int) windows->info.height)))
2186 (void) XWithdrawWindow(display,windows->info.id,
2187 windows->info.screen);
2188 }
2189 else
2190 if ((x > (windows->info.x+(int) windows->info.width)) ||
2191 (y > (windows->info.y+(int) windows->info.height)))
2192 (void) XMapWindow(display,windows->info.id);
2193 break;
2194 }
2195 default:
2196 break;
2197 }
2198 } while ((state & ExitState) == 0);
2199 (void) XSelectInput(display,windows->image.id,
2200 windows->image.attributes.event_mask);
2201 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2202 if ((state & EscapeState) != 0)
2203 return(MagickTrue);
2204 /*
2205 Set font info and check boundary conditions.
2206 */
2207 font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2208 if (font_info == (XFontStruct *) NULL)
2209 {
2210 XNoticeWidget(display,windows,"Unable to load font:",
2211 resource_info->font_name[font_id]);
2212 font_info=windows->font_info;
2213 }
2214 if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2215 x=(int) windows->image.width-font_info->max_bounds.width;
2216 if (y < (int) (font_info->ascent+font_info->descent))
2217 y=(int) font_info->ascent+font_info->descent;
2218 if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2219 ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2220 return(MagickFalse);
2221 /*
2222 Initialize annotate structure.
2223 */
2224 annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2225 if (annotate_info == (XAnnotateInfo *) NULL)
2226 return(MagickFalse);
2227 XGetAnnotateInfo(annotate_info);
2228 annotate_info->x=x;
2229 annotate_info->y=y;
2230 if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2231 annotate_info->stencil=OpaqueStencil;
2232 else
2233 if (transparent_box == MagickFalse)
2234 annotate_info->stencil=BackgroundStencil;
2235 else
2236 annotate_info->stencil=ForegroundStencil;
2237 annotate_info->height=(unsigned int) (font_info->ascent+font_info->descent);
2238 annotate_info->degrees=degrees;
2239 annotate_info->font_info=font_info;
2240 annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2241 windows->image.width/(size_t) MagickMax(font_info->min_bounds.width,1)+2UL,
2242 sizeof(*annotate_info->text));
2243 if (annotate_info->text == (char *) NULL)
2244 return(MagickFalse);
2245 /*
2246 Create cursor and set graphic context.
2247 */
2248 cursor=XCreateFontCursor(display,XC_pencil);
2249 (void) XCheckDefineCursor(display,windows->image.id,cursor);
2250 annotate_context=windows->image.annotate_context;
2251 (void) XSetFont(display,annotate_context,font_info->fid);
2252 (void) XSetBackground(display,annotate_context,
2253 windows->pixel_info->pen_colors[box_id].pixel);
2254 (void) XSetForeground(display,annotate_context,
2255 windows->pixel_info->pen_colors[pen_id].pixel);
2256 /*
2257 Begin annotating the image with text.
2258 */
2259 (void) CloneString(&windows->command.name,"Text");
2260 windows->command.data=0;
2261 (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2262 state=DefaultState;
2263 (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2264 text_event.xexpose.width=(int) font_info->max_bounds.width;
2265 text_event.xexpose.height=font_info->max_bounds.ascent+
2266 font_info->max_bounds.descent;
2267 p=annotate_info->text;
2268 do
2269 {
2270 /*
2271 Display text cursor.
2272 */
2273 *p='\0';
2274 (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2275 /*
2276 Wait for next event.
2277 */
2278 XScreenEvent(display,windows,&event,exception);
2279 if (event.xany.window == windows->command.id)
2280 {
2281 /*
2282 Select a command from the Command widget.
2283 */
2284 (void) XSetBackground(display,annotate_context,
2285 windows->pixel_info->background_color.pixel);
2286 (void) XSetForeground(display,annotate_context,
2287 windows->pixel_info->foreground_color.pixel);
2288 id=XCommandWidget(display,windows,AnnotateMenu,&event);
2289 (void) XSetBackground(display,annotate_context,
2290 windows->pixel_info->pen_colors[box_id].pixel);
2291 (void) XSetForeground(display,annotate_context,
2292 windows->pixel_info->pen_colors[pen_id].pixel);
2293 if (id < 0)
2294 continue;
2295 switch (TextCommands[id])
2296 {
2297 case TextHelpCommand:
2298 {
2299 XTextViewHelp(display,resource_info,windows,MagickFalse,
2300 "Help Viewer - Image Annotation",ImageAnnotateHelp);
2301 (void) XCheckDefineCursor(display,windows->image.id,cursor);
2302 break;
2303 }
2304 case TextApplyCommand:
2305 {
2306 /*
2307 Finished annotating.
2308 */
2309 annotate_info->width=(unsigned int) XTextWidth(font_info,
2310 annotate_info->text,(int) strlen(annotate_info->text));
2311 XRefreshWindow(display,&windows->image,&text_event);
2312 state|=ExitState;
2313 break;
2314 }
2315 default:
2316 break;
2317 }
2318 continue;
2319 }
2320 /*
2321 Erase text cursor.
2322 */
2323 text_event.xexpose.x=x;
2324 text_event.xexpose.y=y-font_info->max_bounds.ascent;
2325 (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2326 (unsigned int) text_event.xexpose.width,(unsigned int)
2327 text_event.xexpose.height,MagickFalse);
2328 XRefreshWindow(display,&windows->image,&text_event);
2329 switch (event.type)
2330 {
2331 case ButtonPress:
2332 {
2333 if (event.xbutton.window != windows->image.id)
2334 break;
2335 if (event.xbutton.button == Button2)
2336 {
2337 /*
2338 Request primary selection.
2339 */
2340 (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2341 windows->image.id,CurrentTime);
2342 break;
2343 }
2344 break;
2345 }
2346 case Expose:
2347 {
2348 if (event.xexpose.count == 0)
2349 {
2350 XAnnotateInfo
2351 *text_info;
2352
2353 /*
2354 Refresh Image window.
2355 */
2356 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2357 text_info=annotate_info;
2358 while (text_info != (XAnnotateInfo *) NULL)
2359 {
2360 if (annotate_info->stencil == ForegroundStencil)
2361 (void) XDrawString(display,windows->image.id,annotate_context,
2362 text_info->x,text_info->y,text_info->text,
2363 (int) strlen(text_info->text));
2364 else
2365 (void) XDrawImageString(display,windows->image.id,
2366 annotate_context,text_info->x,text_info->y,text_info->text,
2367 (int) strlen(text_info->text));
2368 text_info=text_info->previous;
2369 }
2370 (void) XDrawString(display,windows->image.id,annotate_context,
2371 x,y,"_",1);
2372 }
2373 break;
2374 }
2375 case KeyPress:
2376 {
2377 int
2378 length;
2379
2380 if (event.xkey.window != windows->image.id)
2381 break;
2382 /*
2383 Respond to a user key press.
2384 */
2385 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2386 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2387 *(command+length)='\0';
2388 if (((event.xkey.state & ControlMask) != 0) ||
2389 ((event.xkey.state & Mod1Mask) != 0))
2390 state|=ModifierState;
2391 if ((state & ModifierState) != 0)
2392 switch ((int) key_symbol)
2393 {
2394 case XK_u:
2395 case XK_U:
2396 {
2397 key_symbol=DeleteCommand;
2398 break;
2399 }
2400 default:
2401 break;
2402 }
2403 switch ((int) key_symbol)
2404 {
2405 case XK_BackSpace:
2406 {
2407 /*
2408 Erase one character.
2409 */
2410 if (p == annotate_info->text)
2411 {
2412 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2413 break;
2414 else
2415 {
2416 /*
2417 Go to end of the previous line of text.
2418 */
2419 annotate_info=annotate_info->previous;
2420 p=annotate_info->text;
2421 x=annotate_info->x+(int) annotate_info->width;
2422 y=annotate_info->y;
2423 if (annotate_info->width != 0)
2424 p+=strlen(annotate_info->text);
2425 break;
2426 }
2427 }
2428 p--;
2429 x-=XTextWidth(font_info,p,1);
2430 text_event.xexpose.x=x;
2431 text_event.xexpose.y=y-font_info->max_bounds.ascent;
2432 XRefreshWindow(display,&windows->image,&text_event);
2433 break;
2434 }
2435 case XK_bracketleft:
2436 {
2437 key_symbol=XK_Escape;
2438 break;
2439 }
2440 case DeleteCommand:
2441 {
2442 /*
2443 Erase the entire line of text.
2444 */
2445 while (p != annotate_info->text)
2446 {
2447 p--;
2448 x-=XTextWidth(font_info,p,1);
2449 text_event.xexpose.x=x;
2450 XRefreshWindow(display,&windows->image,&text_event);
2451 }
2452 break;
2453 }
2454 case XK_Escape:
2455 case XK_F20:
2456 {
2457 /*
2458 Finished annotating.
2459 */
2460 annotate_info->width=(unsigned int) XTextWidth(font_info,
2461 annotate_info->text,(int) strlen(annotate_info->text));
2462 XRefreshWindow(display,&windows->image,&text_event);
2463 state|=ExitState;
2464 break;
2465 }
2466 default:
2467 {
2468 /*
2469 Draw a single character on the Image window.
2470 */
2471 if ((state & ModifierState) != 0)
2472 break;
2473 if (*command == '\0')
2474 break;
2475 *p=(*command);
2476 if (annotate_info->stencil == ForegroundStencil)
2477 (void) XDrawString(display,windows->image.id,annotate_context,
2478 x,y,p,1);
2479 else
2480 (void) XDrawImageString(display,windows->image.id,
2481 annotate_context,x,y,p,1);
2482 x+=XTextWidth(font_info,p,1);
2483 p++;
2484 if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2485 break;
2486 magick_fallthrough;
2487 }
2488 case XK_Return:
2489 case XK_KP_Enter:
2490 {
2491 /*
2492 Advance to the next line of text.
2493 */
2494 *p='\0';
2495 annotate_info->width=(unsigned int) XTextWidth(font_info,
2496 annotate_info->text,(int) strlen(annotate_info->text));
2497 if (annotate_info->next != (XAnnotateInfo *) NULL)
2498 {
2499 /*
2500 Line of text already exists.
2501 */
2502 annotate_info=annotate_info->next;
2503 x=annotate_info->x;
2504 y=annotate_info->y;
2505 p=annotate_info->text;
2506 break;
2507 }
2508 annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2509 sizeof(*annotate_info->next));
2510 if (annotate_info->next == (XAnnotateInfo *) NULL)
2511 return(MagickFalse);
2512 *annotate_info->next=(*annotate_info);
2513 annotate_info->next->previous=annotate_info;
2514 annotate_info=annotate_info->next;
2515 annotate_info->text=(char *) AcquireQuantumMemory((size_t) (
2516 (ssize_t) windows->image.width/MagickMax((ssize_t)
2517 font_info->min_bounds.width,1)+2L),sizeof(*annotate_info->text));
2518 if (annotate_info->text == (char *) NULL)
2519 return(MagickFalse);
2520 annotate_info->y+=(ssize_t) annotate_info->height;
2521 if (annotate_info->y > (int) windows->image.height)
2522 annotate_info->y=(int) annotate_info->height;
2523 annotate_info->next=(XAnnotateInfo *) NULL;
2524 x=annotate_info->x;
2525 y=annotate_info->y;
2526 p=annotate_info->text;
2527 break;
2528 }
2529 }
2530 break;
2531 }
2532 case KeyRelease:
2533 {
2534 /*
2535 Respond to a user key release.
2536 */
2537 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2538 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2539 state&=(size_t) (~ModifierState);
2540 break;
2541 }
2542 case SelectionNotify:
2543 {
2544 Atom
2545 type;
2546
2547 int
2548 format;
2549
2550 unsigned char
2551 *data;
2552
2553 unsigned long
2554 after,
2555 length;
2556
2557 /*
2558 Obtain response from primary selection.
2559 */
2560 if (event.xselection.property == (Atom) None)
2561 break;
2562 status=XGetWindowProperty(display,event.xselection.requestor,
2563 event.xselection.property,0L,(long) MagickPathExtent,True,XA_STRING,
2564 &type,&format,&length,&after,&data);
2565 if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2566 (length == 0))
2567 break;
2568 /*
2569 Annotate Image window with primary selection.
2570 */
2571 for (i=0; i < (ssize_t) length; i++)
2572 {
2573 if ((char) data[i] != '\n')
2574 {
2575 /*
2576 Draw a single character on the Image window.
2577 */
2578 *p=(char) data[i];
2579 (void) XDrawString(display,windows->image.id,annotate_context,
2580 x,y,p,1);
2581 x+=XTextWidth(font_info,p,1);
2582 p++;
2583 if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2584 continue;
2585 }
2586 /*
2587 Advance to the next line of text.
2588 */
2589 *p='\0';
2590 annotate_info->width=(unsigned int) XTextWidth(font_info,
2591 annotate_info->text,(int) strlen(annotate_info->text));
2592 if (annotate_info->next != (XAnnotateInfo *) NULL)
2593 {
2594 /*
2595 Line of text already exists.
2596 */
2597 annotate_info=annotate_info->next;
2598 x=annotate_info->x;
2599 y=annotate_info->y;
2600 p=annotate_info->text;
2601 continue;
2602 }
2603 annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2604 sizeof(*annotate_info->next));
2605 if (annotate_info->next == (XAnnotateInfo *) NULL)
2606 return(MagickFalse);
2607 *annotate_info->next=(*annotate_info);
2608 annotate_info->next->previous=annotate_info;
2609 annotate_info=annotate_info->next;
2610 annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2611 (windows->image.width/MagickMax((ssize_t)
2612 font_info->min_bounds.width,1)+2L),sizeof(*annotate_info->text));
2613 if (annotate_info->text == (char *) NULL)
2614 return(MagickFalse);
2615 annotate_info->y+=(ssize_t) annotate_info->height;
2616 if (annotate_info->y > (int) windows->image.height)
2617 annotate_info->y=(int) annotate_info->height;
2618 annotate_info->next=(XAnnotateInfo *) NULL;
2619 x=annotate_info->x;
2620 y=annotate_info->y;
2621 p=annotate_info->text;
2622 }
2623 (void) XFree((void *) data);
2624 break;
2625 }
2626 default:
2627 break;
2628 }
2629 } while ((state & ExitState) == 0);
2630 (void) XFreeCursor(display,cursor);
2631 /*
2632 Annotation is relative to image configuration.
2633 */
2634 width=(unsigned int) image->columns;
2635 height=(unsigned int) image->rows;
2636 x=0;
2637 y=0;
2638 if (windows->image.crop_geometry != (char *) NULL)
2639 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2640 /*
2641 Initialize annotated image.
2642 */
2643 XSetCursorState(display,windows,MagickTrue);
2644 XCheckRefreshWindows(display,windows);
2645 while (annotate_info != (XAnnotateInfo *) NULL)
2646 {
2647 if (annotate_info->width == 0)
2648 {
2649 /*
2650 No text on this line-- go to the next line of text.
2651 */
2652 previous_info=annotate_info->previous;
2653 annotate_info->text=(char *)
2654 RelinquishMagickMemory(annotate_info->text);
2655 annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2656 annotate_info=previous_info;
2657 continue;
2658 }
2659 /*
2660 Determine pixel index for box and pen color.
2661 */
2662 windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2663 if (windows->pixel_info->colors != 0)
2664 for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2665 if (windows->pixel_info->pixels[i] ==
2666 windows->pixel_info->pen_colors[box_id].pixel)
2667 {
2668 windows->pixel_info->box_index=(unsigned short) i;
2669 break;
2670 }
2671 windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2672 if (windows->pixel_info->colors != 0)
2673 for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2674 if (windows->pixel_info->pixels[i] ==
2675 windows->pixel_info->pen_colors[pen_id].pixel)
2676 {
2677 windows->pixel_info->pen_index=(unsigned short) i;
2678 break;
2679 }
2680 /*
2681 Define the annotate geometry string.
2682 */
2683 annotate_info->x=(int)
2684 width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2685 annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2686 windows->image.y)/windows->image.ximage->height;
2687 (void) FormatLocaleString(annotate_info->geometry,MagickPathExtent,
2688 "%gx%g%+g%+g",(double) width*annotate_info->width/
2689 windows->image.ximage->width,(double) height*annotate_info->height/
2690 windows->image.ximage->height,(double) annotate_info->x+x,(double)
2691 annotate_info->y+y);
2692 /*
2693 Annotate image with text.
2694 */
2695 status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2696 exception) == MagickFalse ? 0 : 1;
2697 if (status == 0)
2698 return(MagickFalse);
2699 /*
2700 Free up memory.
2701 */
2702 previous_info=annotate_info->previous;
2703 annotate_info->text=DestroyString(annotate_info->text);
2704 annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2705 annotate_info=previous_info;
2706 }
2707 (void) XSetForeground(display,annotate_context,
2708 windows->pixel_info->foreground_color.pixel);
2709 (void) XSetBackground(display,annotate_context,
2710 windows->pixel_info->background_color.pixel);
2711 (void) XSetFont(display,annotate_context,windows->font_info->fid);
2712 XSetCursorState(display,windows,MagickFalse);
2713 (void) XFreeFont(display,font_info);
2714 /*
2715 Update image configuration.
2716 */
2717 XConfigureImageColormap(display,resource_info,windows,image,exception);
2718 (void) XConfigureImage(display,resource_info,windows,image,exception);
2719 return(MagickTrue);
2720}
2721
2722/*
2723%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2724% %
2725% %
2726% %
2727+ X B a c k g r o u n d I m a g e %
2728% %
2729% %
2730% %
2731%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2732%
2733% XBackgroundImage() displays the image in the background of a window.
2734%
2735% The format of the XBackgroundImage method is:
2736%
2737% MagickBooleanType XBackgroundImage(Display *display,
2738% XResourceInfo *resource_info,XWindows *windows,Image **image,
2739% ExceptionInfo *exception)
2740%
2741% A description of each parameter follows:
2742%
2743% o display: Specifies a connection to an X server; returned from
2744% XOpenDisplay.
2745%
2746% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2747%
2748% o windows: Specifies a pointer to a XWindows structure.
2749%
2750% o image: the image.
2751%
2752% o exception: return any errors or warnings in this structure.
2753%
2754*/
2755static MagickBooleanType XBackgroundImage(Display *display,
2756 XResourceInfo *resource_info,XWindows *windows,Image **image,
2757 ExceptionInfo *exception)
2758{
2759#define BackgroundImageTag "Background/Image"
2760
2761 int
2762 status;
2763
2764 static char
2765 window_id[MagickPathExtent] = "root";
2766
2767 XResourceInfo
2768 background_resources;
2769
2770 /*
2771 Put image in background.
2772 */
2773 status=XDialogWidget(display,windows,"Background",
2774 "Enter window id (id 0x00 selects window with pointer):",window_id);
2775 if (*window_id == '\0')
2776 return(MagickFalse);
2777 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2778 exception);
2779 XInfoWidget(display,windows,BackgroundImageTag);
2780 XSetCursorState(display,windows,MagickTrue);
2781 XCheckRefreshWindows(display,windows);
2782 background_resources=(*resource_info);
2783 background_resources.window_id=window_id;
2784 background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2785 status=XDisplayBackgroundImage(display,&background_resources,*image,
2786 exception) == MagickFalse ? 0 : 1;
2787 if (status != MagickFalse)
2788 XClientMessage(display,windows->image.id,windows->im_protocols,
2789 windows->im_retain_colors,CurrentTime);
2790 XSetCursorState(display,windows,MagickFalse);
2791 (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2792 exception);
2793 return(MagickTrue);
2794}
2795
2796/*
2797%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2798% %
2799% %
2800% %
2801+ X C h o p I m a g e %
2802% %
2803% %
2804% %
2805%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2806%
2807% XChopImage() chops the X image.
2808%
2809% The format of the XChopImage method is:
2810%
2811% MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2812% XWindows *windows,Image **image,ExceptionInfo *exception)
2813%
2814% A description of each parameter follows:
2815%
2816% o display: Specifies a connection to an X server; returned from
2817% XOpenDisplay.
2818%
2819% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2820%
2821% o windows: Specifies a pointer to a XWindows structure.
2822%
2823% o image: the image.
2824%
2825% o exception: return any errors or warnings in this structure.
2826%
2827*/
2828static MagickBooleanType XChopImage(Display *display,
2829 XResourceInfo *resource_info,XWindows *windows,Image **image,
2830 ExceptionInfo *exception)
2831{
2832 const char
2833 *const ChopMenu[] =
2834 {
2835 "Direction",
2836 "Help",
2837 "Dismiss",
2838 (char *) NULL
2839 };
2840
2841 static ModeType
2842 direction = HorizontalChopCommand;
2843
2844 static const ModeType
2845 ChopCommands[] =
2846 {
2847 ChopDirectionCommand,
2848 ChopHelpCommand,
2849 ChopDismissCommand
2850 },
2851 DirectionCommands[] =
2852 {
2853 HorizontalChopCommand,
2854 VerticalChopCommand
2855 };
2856
2857 char
2858 text[MagickPathExtent];
2859
2860 double
2861 scale_factor;
2862
2863 Image
2864 *chop_image;
2865
2866 int
2867 id,
2868 x,
2869 y;
2870
2872 chop_info;
2873
2874 size_t
2875 state;
2876
2877 unsigned int
2878 distance,
2879 height,
2880 width;
2881
2882 XEvent
2883 event;
2884
2885 XSegment
2886 segment_info;
2887
2888 /*
2889 Map Command widget.
2890 */
2891 (void) CloneString(&windows->command.name,"Chop");
2892 windows->command.data=1;
2893 (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2894 (void) XMapRaised(display,windows->command.id);
2895 XClientMessage(display,windows->image.id,windows->im_protocols,
2896 windows->im_update_widget,CurrentTime);
2897 /*
2898 Track pointer until button 1 is pressed.
2899 */
2900 XQueryPosition(display,windows->image.id,&x,&y);
2901 (void) XSelectInput(display,windows->image.id,
2902 windows->image.attributes.event_mask | PointerMotionMask);
2903 state=DefaultState;
2904 (void) memset(&segment_info,0,sizeof(segment_info));
2905 do
2906 {
2907 if (windows->info.mapped != MagickFalse)
2908 {
2909 /*
2910 Display pointer position.
2911 */
2912 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
2913 x+windows->image.x,y+windows->image.y);
2914 XInfoWidget(display,windows,text);
2915 }
2916 /*
2917 Wait for next event.
2918 */
2919 XScreenEvent(display,windows,&event,exception);
2920 if (event.xany.window == windows->command.id)
2921 {
2922 /*
2923 Select a command from the Command widget.
2924 */
2925 id=XCommandWidget(display,windows,ChopMenu,&event);
2926 if (id < 0)
2927 continue;
2928 switch (ChopCommands[id])
2929 {
2930 case ChopDirectionCommand:
2931 {
2932 char
2933 command[MagickPathExtent];
2934
2935 const char
2936 *const Directions[] =
2937 {
2938 "horizontal",
2939 "vertical",
2940 (char *) NULL,
2941 };
2942
2943 /*
2944 Select a command from the pop-up menu.
2945 */
2946 id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2947 if (id >= 0)
2948 direction=DirectionCommands[id];
2949 break;
2950 }
2951 case ChopHelpCommand:
2952 {
2953 XTextViewHelp(display,resource_info,windows,MagickFalse,
2954 "Help Viewer - Image Chop",ImageChopHelp);
2955 break;
2956 }
2957 case ChopDismissCommand:
2958 {
2959 /*
2960 Prematurely exit.
2961 */
2962 state|=EscapeState;
2963 state|=ExitState;
2964 break;
2965 }
2966 default:
2967 break;
2968 }
2969 continue;
2970 }
2971 switch (event.type)
2972 {
2973 case ButtonPress:
2974 {
2975 if (event.xbutton.button != Button1)
2976 break;
2977 if (event.xbutton.window != windows->image.id)
2978 break;
2979 /*
2980 User has committed to start point of chopping line.
2981 */
2982 segment_info.x1=(short int) event.xbutton.x;
2983 segment_info.x2=(short int) event.xbutton.x;
2984 segment_info.y1=(short int) event.xbutton.y;
2985 segment_info.y2=(short int) event.xbutton.y;
2986 state|=ExitState;
2987 break;
2988 }
2989 case ButtonRelease:
2990 break;
2991 case Expose:
2992 break;
2993 case KeyPress:
2994 {
2995 char
2996 command[MagickPathExtent];
2997
2998 KeySym
2999 key_symbol;
3000
3001 if (event.xkey.window != windows->image.id)
3002 break;
3003 /*
3004 Respond to a user key press.
3005 */
3006 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3007 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3008 switch ((int) key_symbol)
3009 {
3010 case XK_Escape:
3011 case XK_F20:
3012 {
3013 /*
3014 Prematurely exit.
3015 */
3016 state|=EscapeState;
3017 state|=ExitState;
3018 break;
3019 }
3020 case XK_F1:
3021 case XK_Help:
3022 {
3023 (void) XSetFunction(display,windows->image.highlight_context,
3024 GXcopy);
3025 XTextViewHelp(display,resource_info,windows,MagickFalse,
3026 "Help Viewer - Image Chop",ImageChopHelp);
3027 (void) XSetFunction(display,windows->image.highlight_context,
3028 GXinvert);
3029 break;
3030 }
3031 default:
3032 {
3033 (void) XBell(display,0);
3034 break;
3035 }
3036 }
3037 break;
3038 }
3039 case MotionNotify:
3040 {
3041 /*
3042 Map and unmap Info widget as text cursor crosses its boundaries.
3043 */
3044 x=event.xmotion.x;
3045 y=event.xmotion.y;
3046 if (windows->info.mapped != MagickFalse)
3047 {
3048 if ((x < (windows->info.x+(int) windows->info.width)) &&
3049 (y < (windows->info.y+(int) windows->info.height)))
3050 (void) XWithdrawWindow(display,windows->info.id,
3051 windows->info.screen);
3052 }
3053 else
3054 if ((x > (windows->info.x+(int) windows->info.width)) ||
3055 (y > (windows->info.y+(int) windows->info.height)))
3056 (void) XMapWindow(display,windows->info.id);
3057 }
3058 }
3059 } while ((state & ExitState) == 0);
3060 (void) XSelectInput(display,windows->image.id,
3061 windows->image.attributes.event_mask);
3062 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3063 if ((state & EscapeState) != 0)
3064 return(MagickTrue);
3065 /*
3066 Draw line as pointer moves until the mouse button is released.
3067 */
3068 chop_info.width=0;
3069 chop_info.height=0;
3070 chop_info.x=0;
3071 chop_info.y=0;
3072 distance=0;
3073 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3074 state=DefaultState;
3075 do
3076 {
3077 if (distance > 9)
3078 {
3079 /*
3080 Display info and draw chopping line.
3081 */
3082 if (windows->info.mapped == MagickFalse)
3083 (void) XMapWindow(display,windows->info.id);
3084 (void) FormatLocaleString(text,MagickPathExtent,
3085 " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3086 chop_info.height,(double) chop_info.x,(double) chop_info.y);
3087 XInfoWidget(display,windows,text);
3088 XHighlightLine(display,windows->image.id,
3089 windows->image.highlight_context,&segment_info);
3090 }
3091 else
3092 if (windows->info.mapped != MagickFalse)
3093 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3094 /*
3095 Wait for next event.
3096 */
3097 XScreenEvent(display,windows,&event,exception);
3098 if (distance > 9)
3099 XHighlightLine(display,windows->image.id,
3100 windows->image.highlight_context,&segment_info);
3101 switch (event.type)
3102 {
3103 case ButtonPress:
3104 {
3105 segment_info.x2=(short int) event.xmotion.x;
3106 segment_info.y2=(short int) event.xmotion.y;
3107 break;
3108 }
3109 case ButtonRelease:
3110 {
3111 /*
3112 User has committed to chopping line.
3113 */
3114 segment_info.x2=(short int) event.xbutton.x;
3115 segment_info.y2=(short int) event.xbutton.y;
3116 state|=ExitState;
3117 break;
3118 }
3119 case Expose:
3120 break;
3121 case MotionNotify:
3122 {
3123 segment_info.x2=(short int) event.xmotion.x;
3124 segment_info.y2=(short int) event.xmotion.y;
3125 }
3126 default:
3127 break;
3128 }
3129 /*
3130 Check boundary conditions.
3131 */
3132 if (segment_info.x2 < 0)
3133 segment_info.x2=0;
3134 else
3135 if (segment_info.x2 > windows->image.ximage->width)
3136 segment_info.x2=windows->image.ximage->width;
3137 if (segment_info.y2 < 0)
3138 segment_info.y2=0;
3139 else
3140 if (segment_info.y2 > windows->image.ximage->height)
3141 segment_info.y2=windows->image.ximage->height;
3142 distance=(unsigned int)
3143 (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3144 ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3145 /*
3146 Compute chopping geometry.
3147 */
3148 if (direction == HorizontalChopCommand)
3149 {
3150 chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3151 chop_info.height=0;
3152 chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3153 chop_info.y=0;
3154 if (segment_info.x1 > segment_info.x2)
3155 {
3156 chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3157 chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3158 }
3159 }
3160 else
3161 {
3162 chop_info.width=0;
3163 chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3164 chop_info.x=0;
3165 chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3166 if (segment_info.y1 > segment_info.y2)
3167 {
3168 chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3169 chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3170 }
3171 }
3172 } while ((state & ExitState) == 0);
3173 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3174 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3175 if (distance <= 9)
3176 return(MagickTrue);
3177 /*
3178 Image chopping is relative to image configuration.
3179 */
3180 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3181 exception);
3182 XSetCursorState(display,windows,MagickTrue);
3183 XCheckRefreshWindows(display,windows);
3184 windows->image.window_changes.width=windows->image.ximage->width-
3185 (int) chop_info.width;
3186 windows->image.window_changes.height=windows->image.ximage->height-
3187 (int) chop_info.height;
3188 width=(unsigned int) (*image)->columns;
3189 height=(unsigned int) (*image)->rows;
3190 x=0;
3191 y=0;
3192 if (windows->image.crop_geometry != (char *) NULL)
3193 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3194 scale_factor=(double) width/windows->image.ximage->width;
3195 chop_info.x+=x;
3196 chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3197 chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3198 scale_factor=(double) height/windows->image.ximage->height;
3199 chop_info.y+=y;
3200 chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3201 chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3202 /*
3203 Chop image.
3204 */
3205 chop_image=ChopImage(*image,&chop_info,exception);
3206 XSetCursorState(display,windows,MagickFalse);
3207 if (chop_image == (Image *) NULL)
3208 return(MagickFalse);
3209 *image=DestroyImage(*image);
3210 *image=chop_image;
3211 /*
3212 Update image configuration.
3213 */
3214 XConfigureImageColormap(display,resource_info,windows,*image,exception);
3215 (void) XConfigureImage(display,resource_info,windows,*image,exception);
3216 return(MagickTrue);
3217}
3218
3219/*
3220%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3221% %
3222% %
3223% %
3224+ X C o l o r E d i t I m a g e %
3225% %
3226% %
3227% %
3228%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3229%
3230% XColorEditImage() allows the user to interactively change the color of one
3231% pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3232%
3233% The format of the XColorEditImage method is:
3234%
3235% MagickBooleanType XColorEditImage(Display *display,
3236% XResourceInfo *resource_info,XWindows *windows,Image **image,
3237% ExceptionInfo *exception)
3238%
3239% A description of each parameter follows:
3240%
3241% o display: Specifies a connection to an X server; returned from
3242% XOpenDisplay.
3243%
3244% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3245%
3246% o windows: Specifies a pointer to a XWindows structure.
3247%
3248% o image: the image; returned from ReadImage.
3249%
3250% o exception: return any errors or warnings in this structure.
3251%
3252*/
3253static MagickBooleanType XColorEditImage(Display *display,
3254 XResourceInfo *resource_info,XWindows *windows,Image **image,
3255 ExceptionInfo *exception)
3256{
3257 const char
3258 *const ColorEditMenu[] =
3259 {
3260 "Method",
3261 "Pixel Color",
3262 "Border Color",
3263 "Fuzz",
3264 "Undo",
3265 "Help",
3266 "Dismiss",
3267 (char *) NULL
3268 };
3269
3270 static const ModeType
3271 ColorEditCommands[] =
3272 {
3273 ColorEditMethodCommand,
3274 ColorEditColorCommand,
3275 ColorEditBorderCommand,
3276 ColorEditFuzzCommand,
3277 ColorEditUndoCommand,
3278 ColorEditHelpCommand,
3279 ColorEditDismissCommand
3280 };
3281
3282 static PaintMethod
3283 method = PointMethod;
3284
3285 static unsigned int
3286 pen_id = 0;
3287
3288 static XColor
3289 border_color = { 0, 0, 0, 0, 0, 0 };
3290
3291 char
3292 command[MagickPathExtent],
3293 text[MagickPathExtent];
3294
3295 Cursor
3296 cursor;
3297
3298 int
3299 entry,
3300 id,
3301 x,
3302 x_offset,
3303 y,
3304 y_offset;
3305
3306 Quantum
3307 *q;
3308
3309 size_t
3310 state;
3311
3312 ssize_t
3313 i;
3314
3315 unsigned int
3316 height,
3317 width;
3318
3319 XColor
3320 color;
3321
3322 XEvent
3323 event;
3324
3325 /*
3326 Map Command widget.
3327 */
3328 (void) CloneString(&windows->command.name,"Color Edit");
3329 windows->command.data=4;
3330 (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3331 (void) XMapRaised(display,windows->command.id);
3332 XClientMessage(display,windows->image.id,windows->im_protocols,
3333 windows->im_update_widget,CurrentTime);
3334 /*
3335 Make cursor.
3336 */
3337 cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3338 resource_info->background_color,resource_info->foreground_color);
3339 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3340 /*
3341 Track pointer until button 1 is pressed.
3342 */
3343 XQueryPosition(display,windows->image.id,&x,&y);
3344 (void) XSelectInput(display,windows->image.id,
3345 windows->image.attributes.event_mask | PointerMotionMask);
3346 state=DefaultState;
3347 do
3348 {
3349 if (windows->info.mapped != MagickFalse)
3350 {
3351 /*
3352 Display pointer position.
3353 */
3354 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
3355 x+windows->image.x,y+windows->image.y);
3356 XInfoWidget(display,windows,text);
3357 }
3358 /*
3359 Wait for next event.
3360 */
3361 XScreenEvent(display,windows,&event,exception);
3362 if (event.xany.window == windows->command.id)
3363 {
3364 /*
3365 Select a command from the Command widget.
3366 */
3367 id=XCommandWidget(display,windows,ColorEditMenu,&event);
3368 if (id < 0)
3369 {
3370 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3371 continue;
3372 }
3373 switch (ColorEditCommands[id])
3374 {
3375 case ColorEditMethodCommand:
3376 {
3377 char
3378 **methods;
3379
3380 /*
3381 Select a method from the pop-up menu.
3382 */
3383 methods=(char **) GetCommandOptions(MagickMethodOptions);
3384 if (methods == (char **) NULL)
3385 break;
3386 entry=XMenuWidget(display,windows,ColorEditMenu[id],
3387 (const char **) methods,command);
3388 if (entry >= 0)
3389 method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3390 MagickFalse,methods[entry]);
3391 methods=DestroyStringList(methods);
3392 break;
3393 }
3394 case ColorEditColorCommand:
3395 {
3396 const char
3397 *ColorMenu[MaxNumberPens];
3398
3399 int
3400 pen_number;
3401
3402 /*
3403 Initialize menu selections.
3404 */
3405 for (i=0; i < (int) (MaxNumberPens-2); i++)
3406 ColorMenu[i]=resource_info->pen_colors[i];
3407 ColorMenu[MaxNumberPens-2]="Browser...";
3408 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3409 /*
3410 Select a pen color from the pop-up menu.
3411 */
3412 pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3413 (const char **) ColorMenu,command);
3414 if (pen_number < 0)
3415 break;
3416 if (pen_number == (MaxNumberPens-2))
3417 {
3418 static char
3419 color_name[MagickPathExtent] = "gray";
3420
3421 /*
3422 Select a pen color from a dialog.
3423 */
3424 resource_info->pen_colors[pen_number]=color_name;
3425 XColorBrowserWidget(display,windows,"Select",color_name);
3426 if (*color_name == '\0')
3427 break;
3428 }
3429 /*
3430 Set pen color.
3431 */
3432 (void) XParseColor(display,windows->map_info->colormap,
3433 resource_info->pen_colors[pen_number],&color);
3434 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3435 (unsigned int) MaxColors,&color);
3436 windows->pixel_info->pen_colors[pen_number]=color;
3437 pen_id=(unsigned int) pen_number;
3438 break;
3439 }
3440 case ColorEditBorderCommand:
3441 {
3442 const char
3443 *ColorMenu[MaxNumberPens];
3444
3445 int
3446 pen_number;
3447
3448 /*
3449 Initialize menu selections.
3450 */
3451 for (i=0; i < (int) (MaxNumberPens-2); i++)
3452 ColorMenu[i]=resource_info->pen_colors[i];
3453 ColorMenu[MaxNumberPens-2]="Browser...";
3454 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3455 /*
3456 Select a pen color from the pop-up menu.
3457 */
3458 pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3459 (const char **) ColorMenu,command);
3460 if (pen_number < 0)
3461 break;
3462 if (pen_number == (MaxNumberPens-2))
3463 {
3464 static char
3465 color_name[MagickPathExtent] = "gray";
3466
3467 /*
3468 Select a pen color from a dialog.
3469 */
3470 resource_info->pen_colors[pen_number]=color_name;
3471 XColorBrowserWidget(display,windows,"Select",color_name);
3472 if (*color_name == '\0')
3473 break;
3474 }
3475 /*
3476 Set border color.
3477 */
3478 (void) XParseColor(display,windows->map_info->colormap,
3479 resource_info->pen_colors[pen_number],&border_color);
3480 break;
3481 }
3482 case ColorEditFuzzCommand:
3483 {
3484 const char
3485 *const FuzzMenu[] =
3486 {
3487 "0%",
3488 "2%",
3489 "5%",
3490 "10%",
3491 "15%",
3492 "Dialog...",
3493 (char *) NULL,
3494 };
3495
3496 static char
3497 fuzz[MagickPathExtent];
3498
3499 /*
3500 Select a command from the pop-up menu.
3501 */
3502 entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3503 command);
3504 if (entry < 0)
3505 break;
3506 if (entry != 5)
3507 {
3508 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3509 QuantumRange+1.0);
3510 break;
3511 }
3512 (void) (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
3513 (void) XDialogWidget(display,windows,"Ok",
3514 "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3515 if (*fuzz == '\0')
3516 break;
3517 (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
3518 (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3519 1.0);
3520 break;
3521 }
3522 case ColorEditUndoCommand:
3523 {
3524 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3525 image,exception);
3526 break;
3527 }
3528 case ColorEditHelpCommand:
3529 default:
3530 {
3531 XTextViewHelp(display,resource_info,windows,MagickFalse,
3532 "Help Viewer - Image Annotation",ImageColorEditHelp);
3533 break;
3534 }
3535 case ColorEditDismissCommand:
3536 {
3537 /*
3538 Prematurely exit.
3539 */
3540 state|=EscapeState;
3541 state|=ExitState;
3542 break;
3543 }
3544 }
3545 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3546 continue;
3547 }
3548 switch (event.type)
3549 {
3550 case ButtonPress:
3551 {
3552 if (event.xbutton.button != Button1)
3553 break;
3554 if ((event.xbutton.window != windows->image.id) &&
3555 (event.xbutton.window != windows->magnify.id))
3556 break;
3557 /*
3558 exit loop.
3559 */
3560 x=event.xbutton.x;
3561 y=event.xbutton.y;
3562 (void) XMagickCommand(display,resource_info,windows,
3563 SaveToUndoBufferCommand,image,exception);
3564 state|=UpdateConfigurationState;
3565 break;
3566 }
3567 case ButtonRelease:
3568 {
3569 if (event.xbutton.button != Button1)
3570 break;
3571 if ((event.xbutton.window != windows->image.id) &&
3572 (event.xbutton.window != windows->magnify.id))
3573 break;
3574 /*
3575 Update colormap information.
3576 */
3577 x=event.xbutton.x;
3578 y=event.xbutton.y;
3579 XConfigureImageColormap(display,resource_info,windows,*image,exception);
3580 (void) XConfigureImage(display,resource_info,windows,*image,exception);
3581 XInfoWidget(display,windows,text);
3582 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3583 state&=(size_t) (~UpdateConfigurationState);
3584 break;
3585 }
3586 case Expose:
3587 break;
3588 case KeyPress:
3589 {
3590 KeySym
3591 key_symbol;
3592
3593 if (event.xkey.window == windows->magnify.id)
3594 {
3595 Window
3596 window;
3597
3598 window=windows->magnify.id;
3599 while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3600 }
3601 if (event.xkey.window != windows->image.id)
3602 break;
3603 /*
3604 Respond to a user key press.
3605 */
3606 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3607 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3608 switch ((int) key_symbol)
3609 {
3610 case XK_Escape:
3611 case XK_F20:
3612 {
3613 /*
3614 Prematurely exit.
3615 */
3616 state|=ExitState;
3617 break;
3618 }
3619 case XK_F1:
3620 case XK_Help:
3621 {
3622 XTextViewHelp(display,resource_info,windows,MagickFalse,
3623 "Help Viewer - Image Annotation",ImageColorEditHelp);
3624 break;
3625 }
3626 default:
3627 {
3628 (void) XBell(display,0);
3629 break;
3630 }
3631 }
3632 break;
3633 }
3634 case MotionNotify:
3635 {
3636 /*
3637 Map and unmap Info widget as cursor crosses its boundaries.
3638 */
3639 x=event.xmotion.x;
3640 y=event.xmotion.y;
3641 if (windows->info.mapped != MagickFalse)
3642 {
3643 if ((x < (windows->info.x+(int) windows->info.width)) &&
3644 (y < (windows->info.y+(int) windows->info.height)))
3645 (void) XWithdrawWindow(display,windows->info.id,
3646 windows->info.screen);
3647 }
3648 else
3649 if ((x > (windows->info.x+(int) windows->info.width)) ||
3650 (y > (windows->info.y+(int) windows->info.height)))
3651 (void) XMapWindow(display,windows->info.id);
3652 break;
3653 }
3654 default:
3655 break;
3656 }
3657 if (event.xany.window == windows->magnify.id)
3658 {
3659 x=windows->magnify.x-windows->image.x;
3660 y=windows->magnify.y-windows->image.y;
3661 }
3662 x_offset=x;
3663 y_offset=y;
3664 if ((state & UpdateConfigurationState) != 0)
3665 {
3666 CacheView
3667 *image_view;
3668
3669 int
3670 x,
3671 y;
3672
3673 /*
3674 Pixel edit is relative to image configuration.
3675 */
3676 (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3677 MagickTrue);
3678 color=windows->pixel_info->pen_colors[pen_id];
3679 XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3680 width=(unsigned int) (*image)->columns;
3681 height=(unsigned int) (*image)->rows;
3682 x=0;
3683 y=0;
3684 if (windows->image.crop_geometry != (char *) NULL)
3685 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3686 &width,&height);
3687 x_offset=((int) width*(windows->image.x+x_offset)/(int)
3688 windows->image.ximage->width+x);
3689 y_offset=((int) height*(windows->image.y+y_offset)/(int)
3690 windows->image.ximage->height+y);
3691 if ((x_offset < 0) || (y_offset < 0))
3692 continue;
3693 if ((x_offset >= (int) (*image)->columns) ||
3694 (y_offset >= (int) (*image)->rows))
3695 continue;
3696 image_view=AcquireAuthenticCacheView(*image,exception);
3697 switch (method)
3698 {
3699 case PointMethod:
3700 default:
3701 {
3702 /*
3703 Update color information using point algorithm.
3704 */
3705 if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3706 return(MagickFalse);
3707 q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3708 (ssize_t) y_offset,1,1,exception);
3709 if (q == (Quantum *) NULL)
3710 break;
3711 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3712 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3713 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3714 (void) SyncCacheViewAuthenticPixels(image_view,exception);
3715 break;
3716 }
3717 case ReplaceMethod:
3718 {
3719 PixelInfo
3720 pixel,
3721 target;
3722
3723 /*
3724 Update color information using replace algorithm.
3725 */
3726 (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3727 x_offset,(ssize_t) y_offset,&target,exception);
3728 if ((*image)->storage_class == DirectClass)
3729 {
3730 for (y=0; y < (int) (*image)->rows; y++)
3731 {
3732 q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3733 (*image)->columns,1,exception);
3734 if (q == (Quantum *) NULL)
3735 break;
3736 for (x=0; x < (int) (*image)->columns; x++)
3737 {
3738 GetPixelInfoPixel(*image,q,&pixel);
3739 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3740 {
3741 SetPixelRed(*image,ScaleShortToQuantum(
3742 color.red),q);
3743 SetPixelGreen(*image,ScaleShortToQuantum(
3744 color.green),q);
3745 SetPixelBlue(*image,ScaleShortToQuantum(
3746 color.blue),q);
3747 }
3748 q+=GetPixelChannels(*image);
3749 }
3750 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3751 break;
3752 }
3753 }
3754 else
3755 {
3756 for (i=0; i < (ssize_t) (*image)->colors; i++)
3757 if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3758 {
3759 (*image)->colormap[i].red=(double) ScaleShortToQuantum(
3760 color.red);
3761 (*image)->colormap[i].green=(double) ScaleShortToQuantum(
3762 color.green);
3763 (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
3764 color.blue);
3765 }
3766 (void) SyncImage(*image,exception);
3767 }
3768 break;
3769 }
3770 case FloodfillMethod:
3771 case FillToBorderMethod:
3772 {
3773 DrawInfo
3774 *draw_info;
3775
3776 PixelInfo
3777 target;
3778
3779 /*
3780 Update color information using floodfill algorithm.
3781 */
3782 (void) GetOneVirtualPixelInfo(*image,
3783 GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3784 y_offset,&target,exception);
3785 if (method == FillToBorderMethod)
3786 {
3787 target.red=(double)
3788 ScaleShortToQuantum(border_color.red);
3789 target.green=(double)
3790 ScaleShortToQuantum(border_color.green);
3791 target.blue=(double)
3792 ScaleShortToQuantum(border_color.blue);
3793 }
3794 draw_info=CloneDrawInfo(resource_info->image_info,
3795 (DrawInfo *) NULL);
3796 (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3797 AllCompliance,&draw_info->fill,exception);
3798 (void) FloodfillPaintImage(*image,draw_info,&target,
3799 (ssize_t)x_offset,(ssize_t)y_offset,
3800 method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
3801 draw_info=DestroyDrawInfo(draw_info);
3802 break;
3803 }
3804 case ResetMethod:
3805 {
3806 /*
3807 Update color information using reset algorithm.
3808 */
3809 if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3810 return(MagickFalse);
3811 for (y=0; y < (int) (*image)->rows; y++)
3812 {
3813 q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3814 (*image)->columns,1,exception);
3815 if (q == (Quantum *) NULL)
3816 break;
3817 for (x=0; x < (int) (*image)->columns; x++)
3818 {
3819 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3820 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3821 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3822 q+=GetPixelChannels(*image);
3823 }
3824 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3825 break;
3826 }
3827 break;
3828 }
3829 }
3830 image_view=DestroyCacheView(image_view);
3831 state&=(~0x0080U);
3832 }
3833 } while ((state & ExitState) == 0);
3834 (void) XSelectInput(display,windows->image.id,
3835 windows->image.attributes.event_mask);
3836 XSetCursorState(display,windows,MagickFalse);
3837 (void) XFreeCursor(display,cursor);
3838 return(MagickTrue);
3839}
3840
3841/*
3842%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3843% %
3844% %
3845% %
3846+ X C o m p o s i t e I m a g e %
3847% %
3848% %
3849% %
3850%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3851%
3852% XCompositeImage() requests an image name from the user, reads the image and
3853% composites it with the X window image at a location the user chooses with
3854% the pointer.
3855%
3856% The format of the XCompositeImage method is:
3857%
3858% MagickBooleanType XCompositeImage(Display *display,
3859% XResourceInfo *resource_info,XWindows *windows,Image *image,
3860% ExceptionInfo *exception)
3861%
3862% A description of each parameter follows:
3863%
3864% o display: Specifies a connection to an X server; returned from
3865% XOpenDisplay.
3866%
3867% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3868%
3869% o windows: Specifies a pointer to a XWindows structure.
3870%
3871% o image: the image; returned from ReadImage.
3872%
3873% o exception: return any errors or warnings in this structure.
3874%
3875*/
3876static MagickBooleanType XCompositeImage(Display *display,
3877 XResourceInfo *resource_info,XWindows *windows,Image *image,
3878 ExceptionInfo *exception)
3879{
3880 const char
3881 *const CompositeMenu[] =
3882 {
3883 "Operators",
3884 "Dissolve",
3885 "Displace",
3886 "Help",
3887 "Dismiss",
3888 (char *) NULL
3889 };
3890
3891 static char
3892 displacement_geometry[MagickPathExtent] = "30x30",
3893 filename[MagickPathExtent] = "\0";
3894
3895 static CompositeOperator
3896 compose = CopyCompositeOp;
3897
3898 static const ModeType
3899 CompositeCommands[] =
3900 {
3901 CompositeOperatorsCommand,
3902 CompositeDissolveCommand,
3903 CompositeDisplaceCommand,
3904 CompositeHelpCommand,
3905 CompositeDismissCommand
3906 };
3907
3908 char
3909 text[MagickPathExtent];
3910
3911 Cursor
3912 cursor;
3913
3914 Image
3915 *composite_image;
3916
3917 int
3918 entry,
3919 id,
3920 x,
3921 y;
3922
3923 double
3924 blend,
3925 scale_factor;
3926
3928 highlight_info,
3929 composite_info;
3930
3931 unsigned int
3932 height,
3933 width;
3934
3935 size_t
3936 state;
3937
3938 XEvent
3939 event;
3940
3941 /*
3942 Request image file name from user.
3943 */
3944 XFileBrowserWidget(display,windows,"Composite",filename);
3945 if (*filename == '\0')
3946 return(MagickTrue);
3947 /*
3948 Read image.
3949 */
3950 XSetCursorState(display,windows,MagickTrue);
3951 XCheckRefreshWindows(display,windows);
3952 (void) CopyMagickString(resource_info->image_info->filename,filename,
3953 MagickPathExtent);
3954 composite_image=ReadImage(resource_info->image_info,exception);
3955 CatchException(exception);
3956 XSetCursorState(display,windows,MagickFalse);
3957 if (composite_image == (Image *) NULL)
3958 return(MagickFalse);
3959 /*
3960 Map Command widget.
3961 */
3962 (void) CloneString(&windows->command.name,"Composite");
3963 windows->command.data=1;
3964 (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3965 (void) XMapRaised(display,windows->command.id);
3966 XClientMessage(display,windows->image.id,windows->im_protocols,
3967 windows->im_update_widget,CurrentTime);
3968 /*
3969 Track pointer until button 1 is pressed.
3970 */
3971 XQueryPosition(display,windows->image.id,&x,&y);
3972 (void) XSelectInput(display,windows->image.id,
3973 windows->image.attributes.event_mask | PointerMotionMask);
3974 composite_info.x=(ssize_t) windows->image.x+x;
3975 composite_info.y=(ssize_t) windows->image.y+y;
3976 composite_info.width=0;
3977 composite_info.height=0;
3978 cursor=XCreateFontCursor(display,XC_ul_angle);
3979 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3980 blend=0.0;
3981 state=DefaultState;
3982 do
3983 {
3984 if (windows->info.mapped != MagickFalse)
3985 {
3986 /*
3987 Display pointer position.
3988 */
3989 (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
3990 (long) composite_info.x,(long) composite_info.y);
3991 XInfoWidget(display,windows,text);
3992 }
3993 highlight_info=composite_info;
3994 highlight_info.x=composite_info.x-windows->image.x;
3995 highlight_info.y=composite_info.y-windows->image.y;
3996 XHighlightRectangle(display,windows->image.id,
3997 windows->image.highlight_context,&highlight_info);
3998 /*
3999 Wait for next event.
4000 */
4001 XScreenEvent(display,windows,&event,exception);
4002 XHighlightRectangle(display,windows->image.id,
4003 windows->image.highlight_context,&highlight_info);
4004 if (event.xany.window == windows->command.id)
4005 {
4006 /*
4007 Select a command from the Command widget.
4008 */
4009 id=XCommandWidget(display,windows,CompositeMenu,&event);
4010 if (id < 0)
4011 continue;
4012 switch (CompositeCommands[id])
4013 {
4014 case CompositeOperatorsCommand:
4015 {
4016 char
4017 command[MagickPathExtent],
4018 **operators;
4019
4020 /*
4021 Select a command from the pop-up menu.
4022 */
4023 operators=GetCommandOptions(MagickComposeOptions);
4024 if (operators == (char **) NULL)
4025 break;
4026 entry=XMenuWidget(display,windows,CompositeMenu[id],
4027 (const char **) operators,command);
4028 if (entry >= 0)
4029 compose=(CompositeOperator) ParseCommandOption(
4030 MagickComposeOptions,MagickFalse,operators[entry]);
4031 operators=DestroyStringList(operators);
4032 break;
4033 }
4034 case CompositeDissolveCommand:
4035 {
4036 static char
4037 factor[MagickPathExtent] = "20.0";
4038
4039 /*
4040 Dissolve the two images a given percent.
4041 */
4042 (void) XSetFunction(display,windows->image.highlight_context,
4043 GXcopy);
4044 (void) XDialogWidget(display,windows,"Dissolve",
4045 "Enter the blend factor (0.0 - 99.9%):",factor);
4046 (void) XSetFunction(display,windows->image.highlight_context,
4047 GXinvert);
4048 if (*factor == '\0')
4049 break;
4050 blend=StringToDouble(factor,(char **) NULL);
4051 compose=DissolveCompositeOp;
4052 break;
4053 }
4054 case CompositeDisplaceCommand:
4055 {
4056 /*
4057 Get horizontal and vertical scale displacement geometry.
4058 */
4059 (void) XSetFunction(display,windows->image.highlight_context,
4060 GXcopy);
4061 (void) XDialogWidget(display,windows,"Displace",
4062 "Enter the horizontal and vertical scale:",displacement_geometry);
4063 (void) XSetFunction(display,windows->image.highlight_context,
4064 GXinvert);
4065 if (*displacement_geometry == '\0')
4066 break;
4067 compose=DisplaceCompositeOp;
4068 break;
4069 }
4070 case CompositeHelpCommand:
4071 {
4072 (void) XSetFunction(display,windows->image.highlight_context,
4073 GXcopy);
4074 XTextViewHelp(display,resource_info,windows,MagickFalse,
4075 "Help Viewer - Image Composite",ImageCompositeHelp);
4076 (void) XSetFunction(display,windows->image.highlight_context,
4077 GXinvert);
4078 break;
4079 }
4080 case CompositeDismissCommand:
4081 {
4082 /*
4083 Prematurely exit.
4084 */
4085 state|=EscapeState;
4086 state|=ExitState;
4087 break;
4088 }
4089 default:
4090 break;
4091 }
4092 continue;
4093 }
4094 switch (event.type)
4095 {
4096 case ButtonPress:
4097 {
4098 if (resource_info->debug != MagickFalse)
4099 (void) LogMagickEvent(X11Event,GetMagickModule(),
4100 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4101 event.xbutton.button,event.xbutton.x,event.xbutton.y);
4102 if (event.xbutton.button != Button1)
4103 break;
4104 if (event.xbutton.window != windows->image.id)
4105 break;
4106 /*
4107 Change cursor.
4108 */
4109 composite_info.width=composite_image->columns;
4110 composite_info.height=composite_image->rows;
4111 (void) XCheckDefineCursor(display,windows->image.id,cursor);
4112 composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4113 composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4114 break;
4115 }
4116 case ButtonRelease:
4117 {
4118 if (resource_info->debug != MagickFalse)
4119 (void) LogMagickEvent(X11Event,GetMagickModule(),
4120 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4121 event.xbutton.button,event.xbutton.x,event.xbutton.y);
4122 if (event.xbutton.button != Button1)
4123 break;
4124 if (event.xbutton.window != windows->image.id)
4125 break;
4126 if ((composite_info.width != 0) && (composite_info.height != 0))
4127 {
4128 /*
4129 User has selected the location of the composite image.
4130 */
4131 composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4132 composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4133 state|=ExitState;
4134 }
4135 break;
4136 }
4137 case Expose:
4138 break;
4139 case KeyPress:
4140 {
4141 char
4142 command[MagickPathExtent];
4143
4144 KeySym
4145 key_symbol;
4146
4147 int
4148 length;
4149
4150 if (event.xkey.window != windows->image.id)
4151 break;
4152 /*
4153 Respond to a user key press.
4154 */
4155 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4156 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4157 *(command+length)='\0';
4158 if (resource_info->debug != MagickFalse)
4159 (void) LogMagickEvent(X11Event,GetMagickModule(),
4160 "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4161 switch ((int) key_symbol)
4162 {
4163 case XK_Escape:
4164 case XK_F20:
4165 {
4166 /*
4167 Prematurely exit.
4168 */
4169 composite_image=DestroyImage(composite_image);
4170 state|=EscapeState;
4171 state|=ExitState;
4172 break;
4173 }
4174 case XK_F1:
4175 case XK_Help:
4176 {
4177 (void) XSetFunction(display,windows->image.highlight_context,
4178 GXcopy);
4179 XTextViewHelp(display,resource_info,windows,MagickFalse,
4180 "Help Viewer - Image Composite",ImageCompositeHelp);
4181 (void) XSetFunction(display,windows->image.highlight_context,
4182 GXinvert);
4183 break;
4184 }
4185 default:
4186 {
4187 (void) XBell(display,0);
4188 break;
4189 }
4190 }
4191 break;
4192 }
4193 case MotionNotify:
4194 {
4195 /*
4196 Map and unmap Info widget as text cursor crosses its boundaries.
4197 */
4198 x=event.xmotion.x;
4199 y=event.xmotion.y;
4200 if (windows->info.mapped != MagickFalse)
4201 {
4202 if ((x < (windows->info.x+(int) windows->info.width)) &&
4203 (y < (windows->info.y+(int) windows->info.height)))
4204 (void) XWithdrawWindow(display,windows->info.id,
4205 windows->info.screen);
4206 }
4207 else
4208 if ((x > (windows->info.x+(int) windows->info.width)) ||
4209 (y > (windows->info.y+(int) windows->info.height)))
4210 (void) XMapWindow(display,windows->info.id);
4211 composite_info.x=(ssize_t) windows->image.x+x;
4212 composite_info.y=(ssize_t) windows->image.y+y;
4213 break;
4214 }
4215 default:
4216 {
4217 if (resource_info->debug != MagickFalse)
4218 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4219 event.type);
4220 break;
4221 }
4222 }
4223 } while ((state & ExitState) == 0);
4224 (void) XSelectInput(display,windows->image.id,
4225 windows->image.attributes.event_mask);
4226 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4227 XSetCursorState(display,windows,MagickFalse);
4228 (void) XFreeCursor(display,cursor);
4229 if ((state & EscapeState) != 0)
4230 return(MagickTrue);
4231 /*
4232 Image compositing is relative to image configuration.
4233 */
4234 XSetCursorState(display,windows,MagickTrue);
4235 XCheckRefreshWindows(display,windows);
4236 width=(unsigned int) image->columns;
4237 height=(unsigned int) image->rows;
4238 x=0;
4239 y=0;
4240 if (windows->image.crop_geometry != (char *) NULL)
4241 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4242 scale_factor=(double) width/windows->image.ximage->width;
4243 composite_info.x+=x;
4244 composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4245 composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4246 scale_factor=(double) height/windows->image.ximage->height;
4247 composite_info.y+=y;
4248 composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4249 composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4250 if ((composite_info.width != composite_image->columns) ||
4251 (composite_info.height != composite_image->rows))
4252 {
4253 Image
4254 *resize_image;
4255
4256 /*
4257 Scale composite image.
4258 */
4259 resize_image=ResizeImage(composite_image,composite_info.width,
4260 composite_info.height,composite_image->filter,exception);
4261 composite_image=DestroyImage(composite_image);
4262 if (resize_image == (Image *) NULL)
4263 {
4264 XSetCursorState(display,windows,MagickFalse);
4265 return(MagickFalse);
4266 }
4267 composite_image=resize_image;
4268 }
4269 if (compose == DisplaceCompositeOp)
4270 (void) SetImageArtifact(composite_image,"compose:args",
4271 displacement_geometry);
4272 if (blend != 0.0)
4273 {
4274 CacheView
4275 *image_view;
4276
4277 int
4278 y;
4279
4280 Quantum
4281 opacity;
4282
4283 int
4284 x;
4285
4286 Quantum
4287 *q;
4288
4289 /*
4290 Create mattes for blending.
4291 */
4292 (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4293 opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4294 ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4295 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4296 return(MagickFalse);
4297 image->alpha_trait=BlendPixelTrait;
4298 image_view=AcquireAuthenticCacheView(image,exception);
4299 for (y=0; y < (int) image->rows; y++)
4300 {
4301 q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4302 exception);
4303 if (q == (Quantum *) NULL)
4304 break;
4305 for (x=0; x < (int) image->columns; x++)
4306 {
4307 SetPixelAlpha(image,opacity,q);
4308 q+=GetPixelChannels(image);
4309 }
4310 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4311 break;
4312 }
4313 image_view=DestroyCacheView(image_view);
4314 }
4315 /*
4316 Composite image with X Image window.
4317 */
4318 (void) CompositeImage(image,composite_image,compose,MagickTrue,
4319 composite_info.x,composite_info.y,exception);
4320 composite_image=DestroyImage(composite_image);
4321 XSetCursorState(display,windows,MagickFalse);
4322 /*
4323 Update image configuration.
4324 */
4325 XConfigureImageColormap(display,resource_info,windows,image,exception);
4326 (void) XConfigureImage(display,resource_info,windows,image,exception);
4327 return(MagickTrue);
4328}
4329
4330/*
4331%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4332% %
4333% %
4334% %
4335+ X C o n f i g u r e I m a g e %
4336% %
4337% %
4338% %
4339%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4340%
4341% XConfigureImage() creates a new X image. It also notifies the window
4342% manager of the new image size and configures the transient widows.
4343%
4344% The format of the XConfigureImage method is:
4345%
4346% MagickBooleanType XConfigureImage(Display *display,
4347% XResourceInfo *resource_info,XWindows *windows,Image *image,
4348% ExceptionInfo *exception)
4349%
4350% A description of each parameter follows:
4351%
4352% o display: Specifies a connection to an X server; returned from
4353% XOpenDisplay.
4354%
4355% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4356%
4357% o windows: Specifies a pointer to a XWindows structure.
4358%
4359% o image: the image.
4360%
4361% o exception: return any errors or warnings in this structure.
4362%
4363% o exception: return any errors or warnings in this structure.
4364%
4365*/
4366static MagickBooleanType XConfigureImage(Display *display,
4367 XResourceInfo *resource_info,XWindows *windows,Image *image,
4368 ExceptionInfo *exception)
4369{
4370 char
4371 geometry[MagickPathExtent];
4372
4373 MagickStatusType
4374 status;
4375
4376 size_t
4377 mask,
4378 height,
4379 width;
4380
4381 ssize_t
4382 x,
4383 y;
4384
4385 XSizeHints
4386 *size_hints;
4387
4388 XWindowChanges
4389 window_changes;
4390
4391 /*
4392 Dismiss if window dimensions are zero.
4393 */
4394 width=(unsigned int) windows->image.window_changes.width;
4395 height=(unsigned int) windows->image.window_changes.height;
4396 if (resource_info->debug != MagickFalse)
4397 (void) LogMagickEvent(X11Event,GetMagickModule(),
4398 "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4399 windows->image.ximage->height,(double) width,(double) height);
4400 if ((width*height) == 0)
4401 return(MagickTrue);
4402 x=0;
4403 y=0;
4404 /*
4405 Resize image to fit Image window dimensions.
4406 */
4407 XSetCursorState(display,windows,MagickTrue);
4408 (void) XFlush(display);
4409 if (((int) width != windows->image.ximage->width) ||
4410 ((int) height != windows->image.ximage->height))
4411 image->taint=MagickTrue;
4412 windows->magnify.x=(int)
4413 width*windows->magnify.x/windows->image.ximage->width;
4414 windows->magnify.y=(int)
4415 height*windows->magnify.y/windows->image.ximage->height;
4416 windows->image.x=((int) width*windows->image.x/windows->image.ximage->width);
4417 windows->image.y=((int) height*windows->image.y/
4418 windows->image.ximage->height);
4419 status=XMakeImage(display,resource_info,&windows->image,image,
4420 (unsigned int) width,(unsigned int) height,exception);
4421 if (status == MagickFalse)
4422 XNoticeWidget(display,windows,"Unable to configure X image:",
4423 windows->image.name);
4424 /*
4425 Notify window manager of the new configuration.
4426 */
4427 if (resource_info->image_geometry != (char *) NULL)
4428 (void) FormatLocaleString(geometry,MagickPathExtent,"%s>!",
4429 resource_info->image_geometry);
4430 else
4431 (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
4432 XDisplayWidth(display,windows->image.screen),
4433 XDisplayHeight(display,windows->image.screen));
4434 (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4435 window_changes.width=(int) width;
4436 if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4437 window_changes.width=XDisplayWidth(display,windows->image.screen);
4438 window_changes.height=(int) height;
4439 if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4440 window_changes.height=XDisplayHeight(display,windows->image.screen);
4441 mask=(size_t) (CWWidth | CWHeight);
4442 if (resource_info->backdrop)
4443 {
4444 mask|=CWX | CWY;
4445 window_changes.x=((XDisplayWidth(display,windows->image.screen)/2)-
4446 ((int) width/2));
4447 window_changes.y=((XDisplayHeight(display,windows->image.screen)/2)-
4448 ((int) height/2));
4449 }
4450 (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4451 (unsigned int) mask,&window_changes);
4452 (void) XClearWindow(display,windows->image.id);
4453 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4454 /*
4455 Update Magnify window configuration.
4456 */
4457 if (windows->magnify.mapped != MagickFalse)
4458 XMakeMagnifyImage(display,windows,exception);
4459 windows->pan.crop_geometry=windows->image.crop_geometry;
4460 XBestIconSize(display,&windows->pan,image);
4461 while (((windows->pan.width << 1) < MaxIconSize) &&
4462 ((windows->pan.height << 1) < MaxIconSize))
4463 {
4464 windows->pan.width<<=1;
4465 windows->pan.height<<=1;
4466 }
4467 if (windows->pan.geometry != (char *) NULL)
4468 (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4469 &windows->pan.width,&windows->pan.height);
4470 window_changes.width=(int) windows->pan.width;
4471 window_changes.height=(int) windows->pan.height;
4472 size_hints=XAllocSizeHints();
4473 if (size_hints != (XSizeHints *) NULL)
4474 {
4475 /*
4476 Set new size hints.
4477 */
4478 size_hints->flags=PSize | PMinSize | PMaxSize;
4479 size_hints->width=window_changes.width;
4480 size_hints->height=window_changes.height;
4481 size_hints->min_width=size_hints->width;
4482 size_hints->min_height=size_hints->height;
4483 size_hints->max_width=size_hints->width;
4484 size_hints->max_height=size_hints->height;
4485 (void) XSetNormalHints(display,windows->pan.id,size_hints);
4486 (void) XFree((void *) size_hints);
4487 }
4488 (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4489 (unsigned int) (CWWidth | CWHeight),&window_changes);
4490 /*
4491 Update icon window configuration.
4492 */
4493 windows->icon.crop_geometry=windows->image.crop_geometry;
4494 XBestIconSize(display,&windows->icon,image);
4495 window_changes.width=(int) windows->icon.width;
4496 window_changes.height=(int) windows->icon.height;
4497 (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4498 (unsigned int) (CWWidth | CWHeight),&window_changes);
4499 XSetCursorState(display,windows,MagickFalse);
4500 return(status != 0 ? MagickTrue : MagickFalse);
4501}
4502
4503/*
4504%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4505% %
4506% %
4507% %
4508+ X C r o p I m a g e %
4509% %
4510% %
4511% %
4512%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4513%
4514% XCropImage() allows the user to select a region of the image and crop, copy,
4515% or cut it. For copy or cut, the image can subsequently be composited onto
4516% the image with XPasteImage.
4517%
4518% The format of the XCropImage method is:
4519%
4520% MagickBooleanType XCropImage(Display *display,
4521% XResourceInfo *resource_info,XWindows *windows,Image *image,
4522% const ClipboardMode mode,ExceptionInfo *exception)
4523%
4524% A description of each parameter follows:
4525%
4526% o display: Specifies a connection to an X server; returned from
4527% XOpenDisplay.
4528%
4529% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4530%
4531% o windows: Specifies a pointer to a XWindows structure.
4532%
4533% o image: the image; returned from ReadImage.
4534%
4535% o mode: This unsigned value specified whether the image should be
4536% cropped, copied, or cut.
4537%
4538% o exception: return any errors or warnings in this structure.
4539%
4540*/
4541static MagickBooleanType XCropImage(Display *display,
4542 XResourceInfo *resource_info,XWindows *windows,Image *image,
4543 const ClipboardMode mode,ExceptionInfo *exception)
4544{
4545 const char
4546 *const CropModeMenu[] =
4547 {
4548 "Help",
4549 "Dismiss",
4550 (char *) NULL
4551 },
4552 *RectifyModeMenu[] =
4553 {
4554 "Crop",
4555 "Help",
4556 "Dismiss",
4557 (char *) NULL
4558 };
4559
4560 static const ModeType
4561 CropCommands[] =
4562 {
4563 CropHelpCommand,
4564 CropDismissCommand
4565 },
4566 RectifyCommands[] =
4567 {
4568 RectifyCopyCommand,
4569 RectifyHelpCommand,
4570 RectifyDismissCommand
4571 };
4572
4573 CacheView
4574 *image_view;
4575
4576 char
4577 command[MagickPathExtent],
4578 text[MagickPathExtent];
4579
4580 Cursor
4581 cursor;
4582
4583 int
4584 id,
4585 x,
4586 y;
4587
4588 KeySym
4589 key_symbol;
4590
4591 Image
4592 *crop_image;
4593
4594 double
4595 scale_factor;
4596
4598 crop_info,
4599 highlight_info;
4600
4601 Quantum
4602 *q;
4603
4604 unsigned int
4605 height,
4606 width;
4607
4608 size_t
4609 state;
4610
4611 XEvent
4612 event;
4613
4614 /*
4615 Map Command widget.
4616 */
4617 switch (mode)
4618 {
4619 case CopyMode:
4620 {
4621 (void) CloneString(&windows->command.name,"Copy");
4622 break;
4623 }
4624 case CropMode:
4625 {
4626 (void) CloneString(&windows->command.name,"Crop");
4627 break;
4628 }
4629 case CutMode:
4630 {
4631 (void) CloneString(&windows->command.name,"Cut");
4632 break;
4633 }
4634 }
4635 RectifyModeMenu[0]=windows->command.name;
4636 windows->command.data=0;
4637 (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4638 (void) XMapRaised(display,windows->command.id);
4639 XClientMessage(display,windows->image.id,windows->im_protocols,
4640 windows->im_update_widget,CurrentTime);
4641 /*
4642 Track pointer until button 1 is pressed.
4643 */
4644 XQueryPosition(display,windows->image.id,&x,&y);
4645 (void) XSelectInput(display,windows->image.id,
4646 windows->image.attributes.event_mask | PointerMotionMask);
4647 crop_info.x=(ssize_t) windows->image.x+x;
4648 crop_info.y=(ssize_t) windows->image.y+y;
4649 crop_info.width=0;
4650 crop_info.height=0;
4651 cursor=XCreateFontCursor(display,XC_fleur);
4652 state=DefaultState;
4653 do
4654 {
4655 if (windows->info.mapped != MagickFalse)
4656 {
4657 /*
4658 Display pointer position.
4659 */
4660 (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
4661 (long) crop_info.x,(long) crop_info.y);
4662 XInfoWidget(display,windows,text);
4663 }
4664 /*
4665 Wait for next event.
4666 */
4667 XScreenEvent(display,windows,&event,exception);
4668 if (event.xany.window == windows->command.id)
4669 {
4670 /*
4671 Select a command from the Command widget.
4672 */
4673 id=XCommandWidget(display,windows,CropModeMenu,&event);
4674 if (id < 0)
4675 continue;
4676 switch (CropCommands[id])
4677 {
4678 case CropHelpCommand:
4679 {
4680 switch (mode)
4681 {
4682 case CopyMode:
4683 {
4684 XTextViewHelp(display,resource_info,windows,MagickFalse,
4685 "Help Viewer - Image Copy",ImageCopyHelp);
4686 break;
4687 }
4688 case CropMode:
4689 {
4690 XTextViewHelp(display,resource_info,windows,MagickFalse,
4691 "Help Viewer - Image Crop",ImageCropHelp);
4692 break;
4693 }
4694 case CutMode:
4695 {
4696 XTextViewHelp(display,resource_info,windows,MagickFalse,
4697 "Help Viewer - Image Cut",ImageCutHelp);
4698 break;
4699 }
4700 }
4701 break;
4702 }
4703 case CropDismissCommand:
4704 {
4705 /*
4706 Prematurely exit.
4707 */
4708 state|=EscapeState;
4709 state|=ExitState;
4710 break;
4711 }
4712 default:
4713 break;
4714 }
4715 continue;
4716 }
4717 switch (event.type)
4718 {
4719 case ButtonPress:
4720 {
4721 if (event.xbutton.button != Button1)
4722 break;
4723 if (event.xbutton.window != windows->image.id)
4724 break;
4725 /*
4726 Note first corner of cropping rectangle-- exit loop.
4727 */
4728 (void) XCheckDefineCursor(display,windows->image.id,cursor);
4729 crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4730 crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4731 state|=ExitState;
4732 break;
4733 }
4734 case ButtonRelease:
4735 break;
4736 case Expose:
4737 break;
4738 case KeyPress:
4739 {
4740 if (event.xkey.window != windows->image.id)
4741 break;
4742 /*
4743 Respond to a user key press.
4744 */
4745 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4746 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4747 switch ((int) key_symbol)
4748 {
4749 case XK_Escape:
4750 case XK_F20:
4751 {
4752 /*
4753 Prematurely exit.
4754 */
4755 state|=EscapeState;
4756 state|=ExitState;
4757 break;
4758 }
4759 case XK_F1:
4760 case XK_Help:
4761 {
4762 switch (mode)
4763 {
4764 case CopyMode:
4765 {
4766 XTextViewHelp(display,resource_info,windows,MagickFalse,
4767 "Help Viewer - Image Copy",ImageCopyHelp);
4768 break;
4769 }
4770 case CropMode:
4771 {
4772 XTextViewHelp(display,resource_info,windows,MagickFalse,
4773 "Help Viewer - Image Crop",ImageCropHelp);
4774 break;
4775 }
4776 case CutMode:
4777 {
4778 XTextViewHelp(display,resource_info,windows,MagickFalse,
4779 "Help Viewer - Image Cut",ImageCutHelp);
4780 break;
4781 }
4782 }
4783 break;
4784 }
4785 default:
4786 {
4787 (void) XBell(display,0);
4788 break;
4789 }
4790 }
4791 break;
4792 }
4793 case MotionNotify:
4794 {
4795 if (event.xmotion.window != windows->image.id)
4796 break;
4797 /*
4798 Map and unmap Info widget as text cursor crosses its boundaries.
4799 */
4800 x=event.xmotion.x;
4801 y=event.xmotion.y;
4802 if (windows->info.mapped != MagickFalse)
4803 {
4804 if ((x < (windows->info.x+(int) windows->info.width)) &&
4805 (y < (windows->info.y+(int) windows->info.height)))
4806 (void) XWithdrawWindow(display,windows->info.id,
4807 windows->info.screen);
4808 }
4809 else
4810 if ((x > (windows->info.x+(int) windows->info.width)) ||
4811 (y > (windows->info.y+(int) windows->info.height)))
4812 (void) XMapWindow(display,windows->info.id);
4813 crop_info.x=(ssize_t) windows->image.x+x;
4814 crop_info.y=(ssize_t) windows->image.y+y;
4815 break;
4816 }
4817 default:
4818 break;
4819 }
4820 } while ((state & ExitState) == 0);
4821 (void) XSelectInput(display,windows->image.id,
4822 windows->image.attributes.event_mask);
4823 if ((state & EscapeState) != 0)
4824 {
4825 /*
4826 User want to exit without cropping.
4827 */
4828 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4829 (void) XFreeCursor(display,cursor);
4830 return(MagickTrue);
4831 }
4832 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4833 do
4834 {
4835 /*
4836 Size rectangle as pointer moves until the mouse button is released.
4837 */
4838 x=(int) crop_info.x;
4839 y=(int) crop_info.y;
4840 crop_info.width=0;
4841 crop_info.height=0;
4842 state=DefaultState;
4843 do
4844 {
4845 highlight_info=crop_info;
4846 highlight_info.x=crop_info.x-windows->image.x;
4847 highlight_info.y=crop_info.y-windows->image.y;
4848 if ((highlight_info.width > 3) && (highlight_info.height > 3))
4849 {
4850 /*
4851 Display info and draw cropping rectangle.
4852 */
4853 if (windows->info.mapped == MagickFalse)
4854 (void) XMapWindow(display,windows->info.id);
4855 (void) FormatLocaleString(text,MagickPathExtent,
4856 " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4857 crop_info.height,(double) crop_info.x,(double) crop_info.y);
4858 XInfoWidget(display,windows,text);
4859 XHighlightRectangle(display,windows->image.id,
4860 windows->image.highlight_context,&highlight_info);
4861 }
4862 else
4863 if (windows->info.mapped != MagickFalse)
4864 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4865 /*
4866 Wait for next event.
4867 */
4868 XScreenEvent(display,windows,&event,exception);
4869 if ((highlight_info.width > 3) && (highlight_info.height > 3))
4870 XHighlightRectangle(display,windows->image.id,
4871 windows->image.highlight_context,&highlight_info);
4872 switch (event.type)
4873 {
4874 case ButtonPress:
4875 {
4876 crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4877 crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4878 break;
4879 }
4880 case ButtonRelease:
4881 {
4882 /*
4883 User has committed to cropping rectangle.
4884 */
4885 crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4886 crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4887 XSetCursorState(display,windows,MagickFalse);
4888 state|=ExitState;
4889 windows->command.data=0;
4890 (void) XCommandWidget(display,windows,RectifyModeMenu,
4891 (XEvent *) NULL);
4892 break;
4893 }
4894 case Expose:
4895 break;
4896 case MotionNotify:
4897 {
4898 crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4899 crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4900 }
4901 default:
4902 break;
4903 }
4904 if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4905 ((state & ExitState) != 0))
4906 {
4907 /*
4908 Check boundary conditions.
4909 */
4910 if (crop_info.x < 0)
4911 crop_info.x=0;
4912 else
4913 if (crop_info.x > (ssize_t) windows->image.ximage->width)
4914 crop_info.x=(ssize_t) windows->image.ximage->width;
4915 if ((int) crop_info.x < x)
4916 crop_info.width=(unsigned int) (x-crop_info.x);
4917 else
4918 {
4919 crop_info.width=(unsigned int) (crop_info.x-x);
4920 crop_info.x=(ssize_t) x;
4921 }
4922 if (crop_info.y < 0)
4923 crop_info.y=0;
4924 else
4925 if (crop_info.y > (ssize_t) windows->image.ximage->height)
4926 crop_info.y=(ssize_t) windows->image.ximage->height;
4927 if ((int) crop_info.y < y)
4928 crop_info.height=(unsigned int) (y-crop_info.y);
4929 else
4930 {
4931 crop_info.height=(unsigned int) (crop_info.y-y);
4932 crop_info.y=(ssize_t) y;
4933 }
4934 }
4935 } while ((state & ExitState) == 0);
4936 /*
4937 Wait for user to grab a corner of the rectangle or press return.
4938 */
4939 state=DefaultState;
4940 (void) XMapWindow(display,windows->info.id);
4941 do
4942 {
4943 if (windows->info.mapped != MagickFalse)
4944 {
4945 /*
4946 Display pointer position.
4947 */
4948 (void) FormatLocaleString(text,MagickPathExtent,
4949 " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4950 crop_info.height,(double) crop_info.x,(double) crop_info.y);
4951 XInfoWidget(display,windows,text);
4952 }
4953 highlight_info=crop_info;
4954 highlight_info.x=crop_info.x-windows->image.x;
4955 highlight_info.y=crop_info.y-windows->image.y;
4956 if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4957 {
4958 state|=EscapeState;
4959 state|=ExitState;
4960 break;
4961 }
4962 XHighlightRectangle(display,windows->image.id,
4963 windows->image.highlight_context,&highlight_info);
4964 XScreenEvent(display,windows,&event,exception);
4965 if (event.xany.window == windows->command.id)
4966 {
4967 /*
4968 Select a command from the Command widget.
4969 */
4970 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4971 id=XCommandWidget(display,windows,RectifyModeMenu,&event);
4972 (void) XSetFunction(display,windows->image.highlight_context,
4973 GXinvert);
4974 XHighlightRectangle(display,windows->image.id,
4975 windows->image.highlight_context,&highlight_info);
4976 if (id >= 0)
4977 switch (RectifyCommands[id])
4978 {
4979 case RectifyCopyCommand:
4980 {
4981 state|=ExitState;
4982 break;
4983 }
4984 case RectifyHelpCommand:
4985 {
4986 (void) XSetFunction(display,windows->image.highlight_context,
4987 GXcopy);
4988 switch (mode)
4989 {
4990 case CopyMode:
4991 {
4992 XTextViewHelp(display,resource_info,windows,MagickFalse,
4993 "Help Viewer - Image Copy",ImageCopyHelp);
4994 break;
4995 }
4996 case CropMode:
4997 {
4998 XTextViewHelp(display,resource_info,windows,MagickFalse,
4999 "Help Viewer - Image Crop",ImageCropHelp);
5000 break;
5001 }
5002 case CutMode:
5003 {
5004 XTextViewHelp(display,resource_info,windows,MagickFalse,
5005 "Help Viewer - Image Cut",ImageCutHelp);
5006 break;
5007 }
5008 }
5009 (void) XSetFunction(display,windows->image.highlight_context,
5010 GXinvert);
5011 break;
5012 }
5013 case RectifyDismissCommand:
5014 {
5015 /*
5016 Prematurely exit.
5017 */
5018 state|=EscapeState;
5019 state|=ExitState;
5020 break;
5021 }
5022 default:
5023 break;
5024 }
5025 continue;
5026 }
5027 XHighlightRectangle(display,windows->image.id,
5028 windows->image.highlight_context,&highlight_info);
5029 switch (event.type)
5030 {
5031 case ButtonPress:
5032 {
5033 if (event.xbutton.button != Button1)
5034 break;
5035 if (event.xbutton.window != windows->image.id)
5036 break;
5037 x=windows->image.x+event.xbutton.x;
5038 y=windows->image.y+event.xbutton.y;
5039 if ((x < (int) (crop_info.x+RoiDelta)) &&
5040 (x > (int) (crop_info.x-RoiDelta)) &&
5041 (y < (int) (crop_info.y+RoiDelta)) &&
5042 (y > (int) (crop_info.y-RoiDelta)))
5043 {
5044 crop_info.x=crop_info.x+(ssize_t) crop_info.width;
5045 crop_info.y=crop_info.y+(ssize_t) crop_info.height;
5046 state|=UpdateConfigurationState;
5047 break;
5048 }
5049 if ((x < (int) (crop_info.x+RoiDelta)) &&
5050 (x > (int) (crop_info.x-RoiDelta)) &&
5051 (y < (crop_info.y+(int) crop_info.height+RoiDelta)) &&
5052 (y > (crop_info.y+(int) crop_info.height-RoiDelta)))
5053 {
5054 crop_info.x=(crop_info.x+(int) crop_info.width);
5055 state|=UpdateConfigurationState;
5056 break;
5057 }
5058 if ((x < (crop_info.x+(int) crop_info.width+RoiDelta)) &&
5059 (x > (crop_info.x+(int) crop_info.width-RoiDelta)) &&
5060 (y < (int) (crop_info.y+RoiDelta)) &&
5061 (y > (int) (crop_info.y-RoiDelta)))
5062 {
5063 crop_info.y=(crop_info.y+(ssize_t) crop_info.height);
5064 state|=UpdateConfigurationState;
5065 break;
5066 }
5067 if ((x < (crop_info.x+(int) crop_info.width+RoiDelta)) &&
5068 (x > (crop_info.x+(int) crop_info.width-RoiDelta)) &&
5069 (y < (crop_info.y+(int) crop_info.height+RoiDelta)) &&
5070 (y > (crop_info.y+(int) crop_info.height-RoiDelta)))
5071 {
5072 state|=UpdateConfigurationState;
5073 break;
5074 }
5075 magick_fallthrough;
5076 }
5077 case ButtonRelease:
5078 {
5079 if (event.xbutton.window == windows->pan.id)
5080 if ((highlight_info.x != crop_info.x-windows->image.x) ||
5081 (highlight_info.y != crop_info.y-windows->image.y))
5082 XHighlightRectangle(display,windows->image.id,
5083 windows->image.highlight_context,&highlight_info);
5084 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5085 event.xbutton.time);
5086 break;
5087 }
5088 case Expose:
5089 {
5090 if (event.xexpose.window == windows->image.id)
5091 if (event.xexpose.count == 0)
5092 {
5093 event.xexpose.x=(int) highlight_info.x;
5094 event.xexpose.y=(int) highlight_info.y;
5095 event.xexpose.width=(int) highlight_info.width;
5096 event.xexpose.height=(int) highlight_info.height;
5097 XRefreshWindow(display,&windows->image,&event);
5098 }
5099 if (event.xexpose.window == windows->info.id)
5100 if (event.xexpose.count == 0)
5101 XInfoWidget(display,windows,text);
5102 break;
5103 }
5104 case KeyPress:
5105 {
5106 if (event.xkey.window != windows->image.id)
5107 break;
5108 /*
5109 Respond to a user key press.
5110 */
5111 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5112 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5113 switch ((int) key_symbol)
5114 {
5115 case XK_Escape:
5116 case XK_F20:
5117 {
5118 state|=EscapeState;
5119 magick_fallthrough;
5120 }
5121 case XK_Return:
5122 {
5123 state|=ExitState;
5124 break;
5125 }
5126 case XK_Home:
5127 case XK_KP_Home:
5128 {
5129 crop_info.x=(ssize_t) (windows->image.width/2L-crop_info.width/
5130 2L);
5131 crop_info.y=(ssize_t) (windows->image.height/2L-crop_info.height/
5132 2L);
5133 break;
5134 }
5135 case XK_Left:
5136 case XK_KP_Left:
5137 {
5138 crop_info.x--;
5139 break;
5140 }
5141 case XK_Up:
5142 case XK_KP_Up:
5143 case XK_Next:
5144 {
5145 crop_info.y--;
5146 break;
5147 }
5148 case XK_Right:
5149 case XK_KP_Right:
5150 {
5151 crop_info.x++;
5152 break;
5153 }
5154 case XK_Prior:
5155 case XK_Down:
5156 case XK_KP_Down:
5157 {
5158 crop_info.y++;
5159 break;
5160 }
5161 case XK_F1:
5162 case XK_Help:
5163 {
5164 (void) XSetFunction(display,windows->image.highlight_context,
5165 GXcopy);
5166 switch (mode)
5167 {
5168 case CopyMode:
5169 {
5170 XTextViewHelp(display,resource_info,windows,MagickFalse,
5171 "Help Viewer - Image Copy",ImageCopyHelp);
5172 break;
5173 }
5174 case CropMode:
5175 {
5176 XTextViewHelp(display,resource_info,windows,MagickFalse,
5177 "Help Viewer - Image Cropg",ImageCropHelp);
5178 break;
5179 }
5180 case CutMode:
5181 {
5182 XTextViewHelp(display,resource_info,windows,MagickFalse,
5183 "Help Viewer - Image Cutg",ImageCutHelp);
5184 break;
5185 }
5186 }
5187 (void) XSetFunction(display,windows->image.highlight_context,
5188 GXinvert);
5189 break;
5190 }
5191 default:
5192 {
5193 (void) XBell(display,0);
5194 break;
5195 }
5196 }
5197 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5198 event.xkey.time);
5199 break;
5200 }
5201 case KeyRelease:
5202 break;
5203 case MotionNotify:
5204 {
5205 if (event.xmotion.window != windows->image.id)
5206 break;
5207 /*
5208 Map and unmap Info widget as text cursor crosses its boundaries.
5209 */
5210 x=event.xmotion.x;
5211 y=event.xmotion.y;
5212 if (windows->info.mapped != MagickFalse)
5213 {
5214 if ((x < (windows->info.x+(int) windows->info.width)) &&
5215 (y < (windows->info.y+(int) windows->info.height)))
5216 (void) XWithdrawWindow(display,windows->info.id,
5217 windows->info.screen);
5218 }
5219 else
5220 if ((x > (windows->info.x+(int) windows->info.width)) ||
5221 (y > (windows->info.y+(int) windows->info.height)))
5222 (void) XMapWindow(display,windows->info.id);
5223 crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5224 crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5225 break;
5226 }
5227 case SelectionRequest:
5228 {
5229 XSelectionEvent
5230 notify;
5231
5232 XSelectionRequestEvent
5233 *request;
5234
5235 /*
5236 Set primary selection.
5237 */
5238 (void) FormatLocaleString(text,MagickPathExtent,
5239 "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5240 crop_info.height,(double) crop_info.x,(double) crop_info.y);
5241 request=(&(event.xselectionrequest));
5242 (void) XChangeProperty(request->display,request->requestor,
5243 request->property,request->target,8,PropModeReplace,
5244 (unsigned char *) text,(int) strlen(text));
5245 notify.type=SelectionNotify;
5246 notify.display=request->display;
5247 notify.requestor=request->requestor;
5248 notify.selection=request->selection;
5249 notify.target=request->target;
5250 notify.time=request->time;
5251 if (request->property == None)
5252 notify.property=request->target;
5253 else
5254 notify.property=request->property;
5255 (void) XSendEvent(request->display,request->requestor,False,0,
5256 (XEvent *) &notify);
5257 }
5258 default:
5259 break;
5260 }
5261 if ((state & UpdateConfigurationState) != 0)
5262 {
5263 (void) XPutBackEvent(display,&event);
5264 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5265 break;
5266 }
5267 } while ((state & ExitState) == 0);
5268 } while ((state & ExitState) == 0);
5269 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5270 XSetCursorState(display,windows,MagickFalse);
5271 if ((state & EscapeState) != 0)
5272 return(MagickTrue);
5273 if (mode == CropMode)
5274 if (((int) crop_info.width != windows->image.ximage->width) ||
5275 ((int) crop_info.height != windows->image.ximage->height))
5276 {
5277 /*
5278 Reconfigure Image window as defined by cropping rectangle.
5279 */
5280 XSetCropGeometry(display,windows,&crop_info,image);
5281 windows->image.window_changes.width=(int) crop_info.width;
5282 windows->image.window_changes.height=(int) crop_info.height;
5283 (void) XConfigureImage(display,resource_info,windows,image,exception);
5284 return(MagickTrue);
5285 }
5286 /*
5287 Copy image before applying image transforms.
5288 */
5289 XSetCursorState(display,windows,MagickTrue);
5290 XCheckRefreshWindows(display,windows);
5291 width=(unsigned int) image->columns;
5292 height=(unsigned int) image->rows;
5293 x=0;
5294 y=0;
5295 if (windows->image.crop_geometry != (char *) NULL)
5296 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5297 scale_factor=(double) width/windows->image.ximage->width;
5298 crop_info.x+=x;
5299 crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5300 crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5301 scale_factor=(double) height/windows->image.ximage->height;
5302 crop_info.y+=y;
5303 crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5304 crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5305 crop_info.x+=image->page.x;
5306 crop_info.y+=image->page.y;
5307 crop_image=CropImage(image,&crop_info,exception);
5308 XSetCursorState(display,windows,MagickFalse);
5309 if (crop_image == (Image *) NULL)
5310 return(MagickFalse);
5311 if (resource_info->copy_image != (Image *) NULL)
5312 resource_info->copy_image=DestroyImage(resource_info->copy_image);
5313 resource_info->copy_image=crop_image;
5314 if (mode == CopyMode)
5315 {
5316 (void) XConfigureImage(display,resource_info,windows,image,exception);
5317 return(MagickTrue);
5318 }
5319 /*
5320 Cut image.
5321 */
5322 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5323 return(MagickFalse);
5324 image->alpha_trait=BlendPixelTrait;
5325 image_view=AcquireAuthenticCacheView(image,exception);
5326 for (y=0; y < (int) crop_info.height; y++)
5327 {
5328 q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5329 crop_info.width,1,exception);
5330 if (q == (Quantum *) NULL)
5331 break;
5332 for (x=0; x < (int) crop_info.width; x++)
5333 {
5334 SetPixelAlpha(image,TransparentAlpha,q);
5335 q+=GetPixelChannels(image);
5336 }
5337 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5338 break;
5339 }
5340 image_view=DestroyCacheView(image_view);
5341 /*
5342 Update image configuration.
5343 */
5344 XConfigureImageColormap(display,resource_info,windows,image,exception);
5345 (void) XConfigureImage(display,resource_info,windows,image,exception);
5346 return(MagickTrue);
5347}
5348
5349/*
5350%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5351% %
5352% %
5353% %
5354+ X D r a w I m a g e %
5355% %
5356% %
5357% %
5358%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5359%
5360% XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5361% the image.
5362%
5363% The format of the XDrawEditImage method is:
5364%
5365% MagickBooleanType XDrawEditImage(Display *display,
5366% XResourceInfo *resource_info,XWindows *windows,Image **image,
5367% ExceptionInfo *exception)
5368%
5369% A description of each parameter follows:
5370%
5371% o display: Specifies a connection to an X server; returned from
5372% XOpenDisplay.
5373%
5374% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5375%
5376% o windows: Specifies a pointer to a XWindows structure.
5377%
5378% o image: the image.
5379%
5380% o exception: return any errors or warnings in this structure.
5381%
5382*/
5383static MagickBooleanType XDrawEditImage(Display *display,
5384 XResourceInfo *resource_info,XWindows *windows,Image **image,
5385 ExceptionInfo *exception)
5386{
5387 const char
5388 *const DrawMenu[] =
5389 {
5390 "Element",
5391 "Color",
5392 "Stipple",
5393 "Width",
5394 "Undo",
5395 "Help",
5396 "Dismiss",
5397 (char *) NULL
5398 };
5399
5400 static ElementType
5401 element = PointElement;
5402
5403 static const ModeType
5404 DrawCommands[] =
5405 {
5406 DrawElementCommand,
5407 DrawColorCommand,
5408 DrawStippleCommand,
5409 DrawWidthCommand,
5410 DrawUndoCommand,
5411 DrawHelpCommand,
5412 DrawDismissCommand
5413 };
5414
5415 static Pixmap
5416 stipple = (Pixmap) NULL;
5417
5418 static unsigned int
5419 pen_id = 0,
5420 line_width = 1;
5421
5422 char
5423 command[MagickPathExtent],
5424 text[MagickPathExtent];
5425
5426 Cursor
5427 cursor;
5428
5429 int
5430 entry,
5431 id,
5432 number_coordinates,
5433 x,
5434 y;
5435
5436 double
5437 degrees;
5438
5439 MagickStatusType
5440 status;
5441
5443 rectangle_info;
5444
5445 int
5446 i;
5447
5448 unsigned int
5449 distance,
5450 height,
5451 max_coordinates,
5452 width;
5453
5454 size_t
5455 state;
5456
5457 Window
5458 root_window;
5459
5460 XDrawInfo
5461 draw_info;
5462
5463 XEvent
5464 event;
5465
5466 XPoint
5467 *coordinate_info;
5468
5469 XSegment
5470 line_info;
5471
5472 /*
5473 Allocate polygon info.
5474 */
5475 max_coordinates=2048;
5476 coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5477 sizeof(*coordinate_info));
5478 if (coordinate_info == (XPoint *) NULL)
5479 {
5480 (void) ThrowMagickException(exception,GetMagickModule(),
5481 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5482 return(MagickFalse);
5483 }
5484 /*
5485 Map Command widget.
5486 */
5487 (void) CloneString(&windows->command.name,"Draw");
5488 windows->command.data=4;
5489 (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5490 (void) XMapRaised(display,windows->command.id);
5491 XClientMessage(display,windows->image.id,windows->im_protocols,
5492 windows->im_update_widget,CurrentTime);
5493 /*
5494 Wait for first button press.
5495 */
5496 root_window=XRootWindow(display,XDefaultScreen(display));
5497 draw_info.stencil=OpaqueStencil;
5498 status=MagickTrue;
5499 cursor=XCreateFontCursor(display,XC_tcross);
5500 for ( ; ; )
5501 {
5502 XQueryPosition(display,windows->image.id,&x,&y);
5503 (void) XSelectInput(display,windows->image.id,
5504 windows->image.attributes.event_mask | PointerMotionMask);
5505 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5506 state=DefaultState;
5507 do
5508 {
5509 if (windows->info.mapped != MagickFalse)
5510 {
5511 /*
5512 Display pointer position.
5513 */
5514 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
5515 x+windows->image.x,y+windows->image.y);
5516 XInfoWidget(display,windows,text);
5517 }
5518 /*
5519 Wait for next event.
5520 */
5521 XScreenEvent(display,windows,&event,exception);
5522 if (event.xany.window == windows->command.id)
5523 {
5524 /*
5525 Select a command from the Command widget.
5526 */
5527 id=XCommandWidget(display,windows,DrawMenu,&event);
5528 if (id < 0)
5529 continue;
5530 switch (DrawCommands[id])
5531 {
5532 case DrawElementCommand:
5533 {
5534 const char
5535 *const Elements[] =
5536 {
5537 "point",
5538 "line",
5539 "rectangle",
5540 "fill rectangle",
5541 "circle",
5542 "fill circle",
5543 "ellipse",
5544 "fill ellipse",
5545 "polygon",
5546 "fill polygon",
5547 (char *) NULL,
5548 };
5549
5550 /*
5551 Select a command from the pop-up menu.
5552 */
5553 element=(ElementType) (XMenuWidget(display,windows,
5554 DrawMenu[id],Elements,command)+1);
5555 break;
5556 }
5557 case DrawColorCommand:
5558 {
5559 const char
5560 *ColorMenu[MaxNumberPens+1];
5561
5562 int
5563 pen_number;
5564
5565 MagickBooleanType
5566 transparent;
5567
5568 XColor
5569 color;
5570
5571 /*
5572 Initialize menu selections.
5573 */
5574 for (i=0; i < (int) (MaxNumberPens-2); i++)
5575 ColorMenu[i]=resource_info->pen_colors[i];
5576 ColorMenu[MaxNumberPens-2]="transparent";
5577 ColorMenu[MaxNumberPens-1]="Browser...";
5578 ColorMenu[MaxNumberPens]=(char *) NULL;
5579 /*
5580 Select a pen color from the pop-up menu.
5581 */
5582 pen_number=XMenuWidget(display,windows,DrawMenu[id],
5583 (const char **) ColorMenu,command);
5584 if (pen_number < 0)
5585 break;
5586 transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5587 MagickFalse;
5588 if (transparent != MagickFalse)
5589 {
5590 draw_info.stencil=TransparentStencil;
5591 break;
5592 }
5593 if (pen_number == (MaxNumberPens-1))
5594 {
5595 static char
5596 color_name[MagickPathExtent] = "gray";
5597
5598 /*
5599 Select a pen color from a dialog.
5600 */
5601 resource_info->pen_colors[pen_number]=color_name;
5602 XColorBrowserWidget(display,windows,"Select",color_name);
5603 if (*color_name == '\0')
5604 break;
5605 }
5606 /*
5607 Set pen color.
5608 */
5609 (void) XParseColor(display,windows->map_info->colormap,
5610 resource_info->pen_colors[pen_number],&color);
5611 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5612 (unsigned int) MaxColors,&color);
5613 windows->pixel_info->pen_colors[pen_number]=color;
5614 pen_id=(unsigned int) pen_number;
5615 draw_info.stencil=OpaqueStencil;
5616 break;
5617 }
5618 case DrawStippleCommand:
5619 {
5620 const char
5621 *StipplesMenu[] =
5622 {
5623 "Brick",
5624 "Diagonal",
5625 "Scales",
5626 "Vertical",
5627 "Wavy",
5628 "Translucent",
5629 "Opaque",
5630 (char *) NULL,
5631 (char *) NULL,
5632 };
5633
5634 Image
5635 *stipple_image;
5636
5637 ImageInfo
5638 *image_info;
5639
5640 int
5641 status;
5642
5643 static char
5644 filename[MagickPathExtent] = "\0";
5645
5646 /*
5647 Select a command from the pop-up menu.
5648 */
5649 StipplesMenu[7]="Open...";
5650 entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5651 command);
5652 if (entry < 0)
5653 break;
5654 if (stipple != (Pixmap) NULL)
5655 (void) XFreePixmap(display,stipple);
5656 stipple=(Pixmap) NULL;
5657 if (entry != 7)
5658 {
5659 switch (entry)
5660 {
5661 case 0:
5662 {
5663 stipple=XCreateBitmapFromData(display,root_window,
5664 (char *) BricksBitmap,BricksWidth,BricksHeight);
5665 break;
5666 }
5667 case 1:
5668 {
5669 stipple=XCreateBitmapFromData(display,root_window,
5670 (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5671 break;
5672 }
5673 case 2:
5674 {
5675 stipple=XCreateBitmapFromData(display,root_window,
5676 (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5677 break;
5678 }
5679 case 3:
5680 {
5681 stipple=XCreateBitmapFromData(display,root_window,
5682 (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5683 break;
5684 }
5685 case 4:
5686 {
5687 stipple=XCreateBitmapFromData(display,root_window,
5688 (char *) WavyBitmap,WavyWidth,WavyHeight);
5689 break;
5690 }
5691 case 5:
5692 {
5693 stipple=XCreateBitmapFromData(display,root_window,
5694 (char *) HighlightBitmap,HighlightWidth,
5695 HighlightHeight);
5696 break;
5697 }
5698 case 6:
5699 default:
5700 {
5701 stipple=XCreateBitmapFromData(display,root_window,
5702 (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5703 break;
5704 }
5705 }
5706 break;
5707 }
5708 XFileBrowserWidget(display,windows,"Stipple",filename);
5709 if (*filename == '\0')
5710 break;
5711 /*
5712 Read image.
5713 */
5714 XSetCursorState(display,windows,MagickTrue);
5715 XCheckRefreshWindows(display,windows);
5716 image_info=AcquireImageInfo();
5717 (void) CopyMagickString(image_info->filename,filename,
5718 MagickPathExtent);
5719 stipple_image=ReadImage(image_info,exception);
5720 CatchException(exception);
5721 XSetCursorState(display,windows,MagickFalse);
5722 if (stipple_image == (Image *) NULL)
5723 break;
5724 (void) AcquireUniqueFileResource(filename);
5725 (void) FormatLocaleString(stipple_image->filename,
5726 MagickPathExtent,"xbm:%s",filename);
5727 (void) WriteImage(image_info,stipple_image,exception);
5728 stipple_image=DestroyImage(stipple_image);
5729 image_info=DestroyImageInfo(image_info);
5730 status=XReadBitmapFile(display,root_window,filename,&width,
5731 &height,&stipple,&x,&y);
5732 (void) RelinquishUniqueFileResource(filename);
5733 if ((status != BitmapSuccess) != 0)
5734 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5735 filename);
5736 break;
5737 }
5738 case DrawWidthCommand:
5739 {
5740 const char
5741 *const WidthsMenu[] =
5742 {
5743 "1",
5744 "2",
5745 "4",
5746 "8",
5747 "16",
5748 "Dialog...",
5749 (char *) NULL,
5750 };
5751
5752 static char
5753 width[MagickPathExtent] = "0";
5754
5755 /*
5756 Select a command from the pop-up menu.
5757 */
5758 entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5759 command);
5760 if (entry < 0)
5761 break;
5762 if (entry != 5)
5763 {
5764 line_width=(unsigned int) StringToUnsignedLong(
5765 WidthsMenu[entry]);
5766 break;
5767 }
5768 (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5769 width);
5770 if (*width == '\0')
5771 break;
5772 line_width=(unsigned int) StringToUnsignedLong(width);
5773 break;
5774 }
5775 case DrawUndoCommand:
5776 {
5777 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5778 image,exception);
5779 break;
5780 }
5781 case DrawHelpCommand:
5782 {
5783 XTextViewHelp(display,resource_info,windows,MagickFalse,
5784 "Help Viewer - Image Rotation",ImageDrawHelp);
5785 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5786 break;
5787 }
5788 case DrawDismissCommand:
5789 {
5790 /*
5791 Prematurely exit.
5792 */
5793 state|=EscapeState;
5794 state|=ExitState;
5795 break;
5796 }
5797 default:
5798 break;
5799 }
5800 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5801 continue;
5802 }
5803 switch (event.type)
5804 {
5805 case ButtonPress:
5806 {
5807 if (event.xbutton.button != Button1)
5808 break;
5809 if (event.xbutton.window != windows->image.id)
5810 break;
5811 /*
5812 exit loop.
5813 */
5814 x=event.xbutton.x;
5815 y=event.xbutton.y;
5816 state|=ExitState;
5817 break;
5818 }
5819 case ButtonRelease:
5820 break;
5821 case Expose:
5822 break;
5823 case KeyPress:
5824 {
5825 KeySym
5826 key_symbol;
5827
5828 if (event.xkey.window != windows->image.id)
5829 break;
5830 /*
5831 Respond to a user key press.
5832 */
5833 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5834 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5835 switch ((int) key_symbol)
5836 {
5837 case XK_Escape:
5838 case XK_F20:
5839 {
5840 /*
5841 Prematurely exit.
5842 */
5843 state|=EscapeState;
5844 state|=ExitState;
5845 break;
5846 }
5847 case XK_F1:
5848 case XK_Help:
5849 {
5850 XTextViewHelp(display,resource_info,windows,MagickFalse,
5851 "Help Viewer - Image Rotation",ImageDrawHelp);
5852 break;
5853 }
5854 default:
5855 {
5856 (void) XBell(display,0);
5857 break;
5858 }
5859 }
5860 break;
5861 }
5862 case MotionNotify:
5863 {
5864 /*
5865 Map and unmap Info widget as text cursor crosses its boundaries.
5866 */
5867 x=event.xmotion.x;
5868 y=event.xmotion.y;
5869 if (windows->info.mapped != MagickFalse)
5870 {
5871 if ((x < (windows->info.x+(int) windows->info.width)) &&
5872 (y < (windows->info.y+(int) windows->info.height)))
5873 (void) XWithdrawWindow(display,windows->info.id,
5874 windows->info.screen);
5875 }
5876 else
5877 if ((x > (windows->info.x+(int) windows->info.width)) ||
5878 (y > (windows->info.y+(int) windows->info.height)))
5879 (void) XMapWindow(display,windows->info.id);
5880 break;
5881 }
5882 }
5883 } while ((state & ExitState) == 0);
5884 (void) XSelectInput(display,windows->image.id,
5885 windows->image.attributes.event_mask);
5886 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5887 if ((state & EscapeState) != 0)
5888 break;
5889 /*
5890 Draw element as pointer moves until the button is released.
5891 */
5892 distance=0;
5893 degrees=0.0;
5894 line_info.x1=x;
5895 line_info.y1=y;
5896 line_info.x2=x;
5897 line_info.y2=y;
5898 rectangle_info.x=(ssize_t) x;
5899 rectangle_info.y=(ssize_t) y;
5900 rectangle_info.width=0;
5901 rectangle_info.height=0;
5902 number_coordinates=1;
5903 coordinate_info->x=x;
5904 coordinate_info->y=y;
5905 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5906 state=DefaultState;
5907 do
5908 {
5909 switch (element)
5910 {
5911 case PointElement:
5912 default:
5913 {
5914 if (number_coordinates > 1)
5915 {
5916 (void) XDrawLines(display,windows->image.id,
5917 windows->image.highlight_context,coordinate_info,
5918 number_coordinates,CoordModeOrigin);
5919 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d",
5920 coordinate_info[number_coordinates-1].x,
5921 coordinate_info[number_coordinates-1].y);
5922 XInfoWidget(display,windows,text);
5923 }
5924 break;
5925 }
5926 case LineElement:
5927 {
5928 if (distance > 9)
5929 {
5930 /*
5931 Display angle of the line.
5932 */
5933 degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5934 line_info.y1),(double) (line_info.x2-line_info.x1)));
5935 (void) FormatLocaleString(text,MagickPathExtent," %g",
5936 (double) degrees);
5937 XInfoWidget(display,windows,text);
5938 XHighlightLine(display,windows->image.id,
5939 windows->image.highlight_context,&line_info);
5940 }
5941 else
5942 if (windows->info.mapped != MagickFalse)
5943 (void) XWithdrawWindow(display,windows->info.id,
5944 windows->info.screen);
5945 break;
5946 }
5947 case RectangleElement:
5948 case FillRectangleElement:
5949 {
5950 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5951 {
5952 /*
5953 Display info and draw drawing rectangle.
5954 */
5955 (void) FormatLocaleString(text,MagickPathExtent,
5956 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5957 (double) rectangle_info.height,(double) rectangle_info.x,
5958 (double) rectangle_info.y);
5959 XInfoWidget(display,windows,text);
5960 XHighlightRectangle(display,windows->image.id,
5961 windows->image.highlight_context,&rectangle_info);
5962 }
5963 else
5964 if (windows->info.mapped != MagickFalse)
5965 (void) XWithdrawWindow(display,windows->info.id,
5966 windows->info.screen);
5967 break;
5968 }
5969 case CircleElement:
5970 case FillCircleElement:
5971 case EllipseElement:
5972 case FillEllipseElement:
5973 {
5974 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5975 {
5976 /*
5977 Display info and draw drawing rectangle.
5978 */
5979 (void) FormatLocaleString(text,MagickPathExtent,
5980 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5981 (double) rectangle_info.height,(double) rectangle_info.x,
5982 (double) rectangle_info.y);
5983 XInfoWidget(display,windows,text);
5984 XHighlightEllipse(display,windows->image.id,
5985 windows->image.highlight_context,&rectangle_info);
5986 }
5987 else
5988 if (windows->info.mapped != MagickFalse)
5989 (void) XWithdrawWindow(display,windows->info.id,
5990 windows->info.screen);
5991 break;
5992 }
5993 case PolygonElement:
5994 case FillPolygonElement:
5995 {
5996 if (number_coordinates > 1)
5997 (void) XDrawLines(display,windows->image.id,
5998 windows->image.highlight_context,coordinate_info,
5999 number_coordinates,CoordModeOrigin);
6000 if (distance > 9)
6001 {
6002 /*
6003 Display angle of the line.
6004 */
6005 degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6006 line_info.y1),(double) (line_info.x2-line_info.x1)));
6007 (void) FormatLocaleString(text,MagickPathExtent," %g",
6008 (double) degrees);
6009 XInfoWidget(display,windows,text);
6010 XHighlightLine(display,windows->image.id,
6011 windows->image.highlight_context,&line_info);
6012 }
6013 else
6014 if (windows->info.mapped != MagickFalse)
6015 (void) XWithdrawWindow(display,windows->info.id,
6016 windows->info.screen);
6017 break;
6018 }
6019 }
6020 /*
6021 Wait for next event.
6022 */
6023 XScreenEvent(display,windows,&event,exception);
6024 switch (element)
6025 {
6026 case PointElement:
6027 default:
6028 {
6029 if (number_coordinates > 1)
6030 (void) XDrawLines(display,windows->image.id,
6031 windows->image.highlight_context,coordinate_info,
6032 number_coordinates,CoordModeOrigin);
6033 break;
6034 }
6035 case LineElement:
6036 {
6037 if (distance > 9)
6038 XHighlightLine(display,windows->image.id,
6039 windows->image.highlight_context,&line_info);
6040 break;
6041 }
6042 case RectangleElement:
6043 case FillRectangleElement:
6044 {
6045 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6046 XHighlightRectangle(display,windows->image.id,
6047 windows->image.highlight_context,&rectangle_info);
6048 break;
6049 }
6050 case CircleElement:
6051 case FillCircleElement:
6052 case EllipseElement:
6053 case FillEllipseElement:
6054 {
6055 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6056 XHighlightEllipse(display,windows->image.id,
6057 windows->image.highlight_context,&rectangle_info);
6058 break;
6059 }
6060 case PolygonElement:
6061 case FillPolygonElement:
6062 {
6063 if (number_coordinates > 1)
6064 (void) XDrawLines(display,windows->image.id,
6065 windows->image.highlight_context,coordinate_info,
6066 number_coordinates,CoordModeOrigin);
6067 if (distance > 9)
6068 XHighlightLine(display,windows->image.id,
6069 windows->image.highlight_context,&line_info);
6070 break;
6071 }
6072 }
6073 switch (event.type)
6074 {
6075 case ButtonPress:
6076 break;
6077 case ButtonRelease:
6078 {
6079 /*
6080 User has committed to element.
6081 */
6082 line_info.x2=event.xbutton.x;
6083 line_info.y2=event.xbutton.y;
6084 rectangle_info.x=(ssize_t) event.xbutton.x;
6085 rectangle_info.y=(ssize_t) event.xbutton.y;
6086 coordinate_info[number_coordinates].x=event.xbutton.x;
6087 coordinate_info[number_coordinates].y=event.xbutton.y;
6088 if (((element != PolygonElement) &&
6089 (element != FillPolygonElement)) || (distance <= 9))
6090 {
6091 state|=ExitState;
6092 break;
6093 }
6094 number_coordinates++;
6095 if (number_coordinates < (int) max_coordinates)
6096 {
6097 line_info.x1=event.xbutton.x;
6098 line_info.y1=event.xbutton.y;
6099 break;
6100 }
6101 max_coordinates<<=1;
6102 coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6103 max_coordinates,sizeof(*coordinate_info));
6104 if (coordinate_info == (XPoint *) NULL)
6105 (void) ThrowMagickException(exception,GetMagickModule(),
6106 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6107 break;
6108 }
6109 case Expose:
6110 break;
6111 case MotionNotify:
6112 {
6113 if (event.xmotion.window != windows->image.id)
6114 break;
6115 if (element != PointElement)
6116 {
6117 line_info.x2=event.xmotion.x;
6118 line_info.y2=event.xmotion.y;
6119 rectangle_info.x=(ssize_t) event.xmotion.x;
6120 rectangle_info.y=(ssize_t) event.xmotion.y;
6121 break;
6122 }
6123 coordinate_info[number_coordinates].x=event.xbutton.x;
6124 coordinate_info[number_coordinates].y=event.xbutton.y;
6125 number_coordinates++;
6126 if (number_coordinates < (int) max_coordinates)
6127 break;
6128 max_coordinates<<=1;
6129 coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6130 max_coordinates,sizeof(*coordinate_info));
6131 if (coordinate_info == (XPoint *) NULL)
6132 (void) ThrowMagickException(exception,GetMagickModule(),
6133 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6134 break;
6135 }
6136 default:
6137 break;
6138 }
6139 /*
6140 Check boundary conditions.
6141 */
6142 if (line_info.x2 < 0)
6143 line_info.x2=0;
6144 else
6145 if (line_info.x2 > (int) windows->image.width)
6146 line_info.x2=(short) windows->image.width;
6147 if (line_info.y2 < 0)
6148 line_info.y2=0;
6149 else
6150 if (line_info.y2 > (int) windows->image.height)
6151 line_info.y2=(short) windows->image.height;
6152 distance=(unsigned int)
6153 (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6154 ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6155 if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6156 ((state & ExitState) != 0))
6157 {
6158 if (rectangle_info.x < 0)
6159 rectangle_info.x=0;
6160 else
6161 if (rectangle_info.x > (ssize_t) windows->image.width)
6162 rectangle_info.x=(ssize_t) windows->image.width;
6163 if ((int) rectangle_info.x < x)
6164 rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6165 else
6166 {
6167 rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6168 rectangle_info.x=(ssize_t) x;
6169 }
6170 if (rectangle_info.y < 0)
6171 rectangle_info.y=0;
6172 else
6173 if (rectangle_info.y > (ssize_t) windows->image.height)
6174 rectangle_info.y=(ssize_t) windows->image.height;
6175 if ((int) rectangle_info.y < y)
6176 rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6177 else
6178 {
6179 rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6180 rectangle_info.y=(ssize_t) y;
6181 }
6182 }
6183 } while ((state & ExitState) == 0);
6184 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6185 if ((element == PointElement) || (element == PolygonElement) ||
6186 (element == FillPolygonElement))
6187 {
6188 /*
6189 Determine polygon bounding box.
6190 */
6191 rectangle_info.x=(ssize_t) coordinate_info->x;
6192 rectangle_info.y=(ssize_t) coordinate_info->y;
6193 x=coordinate_info->x;
6194 y=coordinate_info->y;
6195 for (i=1; i < number_coordinates; i++)
6196 {
6197 if (coordinate_info[i].x > x)
6198 x=coordinate_info[i].x;
6199 if (coordinate_info[i].y > y)
6200 y=coordinate_info[i].y;
6201 if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6202 rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6203 if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6204 rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6205 }
6206 rectangle_info.width=(size_t) (x-rectangle_info.x);
6207 rectangle_info.height=(size_t) (y-rectangle_info.y);
6208 for (i=0; i < number_coordinates; i++)
6209 {
6210 coordinate_info[i].x-=rectangle_info.x;
6211 coordinate_info[i].y-=rectangle_info.y;
6212 }
6213 }
6214 else
6215 if (distance <= 9)
6216 continue;
6217 else
6218 if ((element == RectangleElement) ||
6219 (element == CircleElement) || (element == EllipseElement))
6220 {
6221 rectangle_info.width--;
6222 rectangle_info.height--;
6223 }
6224 /*
6225 Drawing is relative to image configuration.
6226 */
6227 draw_info.x=(int) rectangle_info.x;
6228 draw_info.y=(int) rectangle_info.y;
6229 (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6230 image,exception);
6231 width=(unsigned int) (*image)->columns;
6232 height=(unsigned int) (*image)->rows;
6233 x=0;
6234 y=0;
6235 if (windows->image.crop_geometry != (char *) NULL)
6236 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6237 draw_info.x+=windows->image.x-((int) line_width/2);
6238 if (draw_info.x < 0)
6239 draw_info.x=0;
6240 draw_info.x=(int) width*draw_info.x/windows->image.ximage->width;
6241 draw_info.y+=windows->image.y-((int) line_width/2);
6242 if (draw_info.y < 0)
6243 draw_info.y=0;
6244 draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6245 draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6246 if (draw_info.width > (unsigned int) (*image)->columns)
6247 draw_info.width=(unsigned int) (*image)->columns;
6248 draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6249 if (draw_info.height > (unsigned int) (*image)->rows)
6250 draw_info.height=(unsigned int) (*image)->rows;
6251 (void) FormatLocaleString(draw_info.geometry,MagickPathExtent,"%ux%u%+d%+d",
6252 width*draw_info.width/(unsigned int) windows->image.ximage->width,
6253 height*draw_info.height/(unsigned int) windows->image.ximage->height,
6254 draw_info.x+x,draw_info.y+y);
6255 /*
6256 Initialize drawing attributes.
6257 */
6258 draw_info.degrees=0.0;
6259 draw_info.element=element;
6260 draw_info.stipple=stipple;
6261 draw_info.line_width=line_width;
6262 draw_info.line_info=line_info;
6263 if (line_info.x1 > (int) (line_width/2))
6264 draw_info.line_info.x1=(short) line_width/2;
6265 if (line_info.y1 > (int) (line_width/2))
6266 draw_info.line_info.y1=(short) line_width/2;
6267 draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+
6268 ((int) line_width/2));
6269 draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+
6270 ((int) line_width/2));
6271 if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6272 {
6273 draw_info.line_info.x2=(-draw_info.line_info.x2);
6274 draw_info.line_info.y2=(-draw_info.line_info.y2);
6275 }
6276 if (draw_info.line_info.x2 < 0)
6277 {
6278 draw_info.line_info.x2=(-draw_info.line_info.x2);
6279 Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6280 }
6281 if (draw_info.line_info.y2 < 0)
6282 {
6283 draw_info.line_info.y2=(-draw_info.line_info.y2);
6284 Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6285 }
6286 draw_info.rectangle_info=rectangle_info;
6287 if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6288 draw_info.rectangle_info.x=(ssize_t) line_width/2;
6289 if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6290 draw_info.rectangle_info.y=(ssize_t) line_width/2;
6291 draw_info.number_coordinates=(unsigned int) number_coordinates;
6292 draw_info.coordinate_info=coordinate_info;
6293 windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6294 /*
6295 Draw element on image.
6296 */
6297 XSetCursorState(display,windows,MagickTrue);
6298 XCheckRefreshWindows(display,windows);
6299 status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6300 XSetCursorState(display,windows,MagickFalse);
6301 /*
6302 Update image colormap and return to image drawing.
6303 */
6304 XConfigureImageColormap(display,resource_info,windows,*image,exception);
6305 (void) XConfigureImage(display,resource_info,windows,*image,exception);
6306 }
6307 XSetCursorState(display,windows,MagickFalse);
6308 coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6309 return(status != 0 ? MagickTrue : MagickFalse);
6310}
6311
6312/*
6313%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6314% %
6315% %
6316% %
6317+ X D r a w P a n R e c t a n g l e %
6318% %
6319% %
6320% %
6321%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6322%
6323% XDrawPanRectangle() draws a rectangle in the pan window. The pan window
6324% displays a zoom image and the rectangle shows which portion of the image is
6325% displayed in the Image window.
6326%
6327% The format of the XDrawPanRectangle method is:
6328%
6329% XDrawPanRectangle(Display *display,XWindows *windows)
6330%
6331% A description of each parameter follows:
6332%
6333% o display: Specifies a connection to an X server; returned from
6334% XOpenDisplay.
6335%
6336% o windows: Specifies a pointer to a XWindows structure.
6337%
6338*/
6339static void XDrawPanRectangle(Display *display,XWindows *windows)
6340{
6341 double
6342 scale_factor;
6343
6345 highlight_info;
6346
6347 /*
6348 Determine dimensions of the panning rectangle.
6349 */
6350 scale_factor=(double) windows->pan.width/windows->image.ximage->width;
6351 highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6352 highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6353 scale_factor=(double)
6354 windows->pan.height/windows->image.ximage->height;
6355 highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6356 highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6357 /*
6358 Display the panning rectangle.
6359 */
6360 (void) XClearWindow(display,windows->pan.id);
6361 XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6362 &highlight_info);
6363}
6364
6365/*
6366%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6367% %
6368% %
6369% %
6370+ X I m a g e C a c h e %
6371% %
6372% %
6373% %
6374%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6375%
6376% XImageCache() handles the creation, manipulation, and destruction of the
6377% image cache (undo and redo buffers).
6378%
6379% The format of the XImageCache method is:
6380%
6381% void XImageCache(Display *display,XResourceInfo *resource_info,
6382% XWindows *windows,const DisplayCommand command,Image **image,
6383% ExceptionInfo *exception)
6384%
6385% A description of each parameter follows:
6386%
6387% o display: Specifies a connection to an X server; returned from
6388% XOpenDisplay.
6389%
6390% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6391%
6392% o windows: Specifies a pointer to a XWindows structure.
6393%
6394% o command: Specifies a command to perform.
6395%
6396% o image: the image; XImageCache may transform the image and return a new
6397% image pointer.
6398%
6399% o exception: return any errors or warnings in this structure.
6400%
6401*/
6402static void XImageCache(Display *display,XResourceInfo *resource_info,
6403 XWindows *windows,const DisplayCommand command,Image **image,
6404 ExceptionInfo *exception)
6405{
6406 Image
6407 *cache_image;
6408
6409 static Image
6410 *redo_image = (Image *) NULL,
6411 *undo_image = (Image *) NULL;
6412
6413 switch (command)
6414 {
6415 case FreeBuffersCommand:
6416 {
6417 /*
6418 Free memory from the undo and redo cache.
6419 */
6420 while (undo_image != (Image *) NULL)
6421 {
6422 cache_image=undo_image;
6423 undo_image=GetPreviousImageInList(undo_image);
6424 cache_image->list=DestroyImage(cache_image->list);
6425 cache_image=DestroyImage(cache_image);
6426 }
6427 undo_image=NewImageList();
6428 if (redo_image != (Image *) NULL)
6429 redo_image=DestroyImage(redo_image);
6430 redo_image=NewImageList();
6431 return;
6432 }
6433 case UndoCommand:
6434 {
6435 char
6436 image_geometry[MagickPathExtent];
6437
6438 /*
6439 Undo the last image transformation.
6440 */
6441 if (undo_image == (Image *) NULL)
6442 {
6443 (void) XBell(display,0);
6444 ThrowXWindowException(ImageError,"NoImagesWereFound",
6445 (*image)->filename);
6446 return;
6447 }
6448 cache_image=undo_image;
6449 undo_image=GetPreviousImageInList(undo_image);
6450 windows->image.window_changes.width=(int) cache_image->columns;
6451 windows->image.window_changes.height=(int) cache_image->rows;
6452 (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
6453 windows->image.ximage->width,windows->image.ximage->height);
6454 (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6455 exception);
6456 if (windows->image.crop_geometry != (char *) NULL)
6457 windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6458 windows->image.crop_geometry);
6459 windows->image.crop_geometry=cache_image->geometry;
6460 if (redo_image != (Image *) NULL)
6461 redo_image=DestroyImage(redo_image);
6462 redo_image=(*image);
6463 *image=cache_image->list;
6464 cache_image=DestroyImage(cache_image);
6465 if (windows->image.orphan != MagickFalse)
6466 return;
6467 XConfigureImageColormap(display,resource_info,windows,*image,exception);
6468 (void) XConfigureImage(display,resource_info,windows,*image,exception);
6469 return;
6470 }
6471 case CutCommand:
6472 case PasteCommand:
6473 case ApplyCommand:
6474 case HalfSizeCommand:
6475 case OriginalSizeCommand:
6476 case DoubleSizeCommand:
6477 case ResizeCommand:
6478 case TrimCommand:
6479 case CropCommand:
6480 case ChopCommand:
6481 case FlipCommand:
6482 case FlopCommand:
6483 case RotateRightCommand:
6484 case RotateLeftCommand:
6485 case RotateCommand:
6486 case ShearCommand:
6487 case RollCommand:
6488 case NegateCommand:
6489 case ContrastStretchCommand:
6490 case SigmoidalContrastCommand:
6491 case NormalizeCommand:
6492 case EqualizeCommand:
6493 case HueCommand:
6494 case SaturationCommand:
6495 case BrightnessCommand:
6496 case GammaCommand:
6497 case SpiffCommand:
6498 case DullCommand:
6499 case GrayscaleCommand:
6500 case MapCommand:
6501 case QuantizeCommand:
6502 case DespeckleCommand:
6503 case EmbossCommand:
6504 case ReduceNoiseCommand:
6505 case AddNoiseCommand:
6506 case SharpenCommand:
6507 case BlurCommand:
6508 case ThresholdCommand:
6509 case EdgeDetectCommand:
6510 case SpreadCommand:
6511 case ShadeCommand:
6512 case RaiseCommand:
6513 case SegmentCommand:
6514 case SolarizeCommand:
6515 case SepiaToneCommand:
6516 case SwirlCommand:
6517 case ImplodeCommand:
6518 case VignetteCommand:
6519 case WaveCommand:
6520 case OilPaintCommand:
6521 case CharcoalDrawCommand:
6522 case AnnotateCommand:
6523 case AddBorderCommand:
6524 case AddFrameCommand:
6525 case CompositeCommand:
6526 case CommentCommand:
6527 case LaunchCommand:
6528 case RegionOfInterestCommand:
6529 case SaveToUndoBufferCommand:
6530 case RedoCommand:
6531 {
6532 Image
6533 *previous_image;
6534
6535 size_t
6536 bytes;
6537
6538 bytes=(*image)->columns*(*image)->rows*sizeof(PixelInfo);
6539 if (undo_image != (Image *) NULL)
6540 {
6541 /*
6542 Ensure the undo cache has enough memory available.
6543 */
6544 previous_image=undo_image;
6545 while (previous_image != (Image *) NULL)
6546 {
6547 bytes+=previous_image->list->columns*previous_image->list->rows*
6548 sizeof(PixelInfo);
6549 if (bytes <= (resource_info->undo_cache << 20))
6550 {
6551 previous_image=GetPreviousImageInList(previous_image);
6552 continue;
6553 }
6554 bytes-=previous_image->list->columns*previous_image->list->rows*
6555 sizeof(PixelInfo);
6556 if (previous_image == undo_image)
6557 undo_image=NewImageList();
6558 else
6559 previous_image->next->previous=NewImageList();
6560 break;
6561 }
6562 while (previous_image != (Image *) NULL)
6563 {
6564 /*
6565 Delete any excess memory from undo cache.
6566 */
6567 cache_image=previous_image;
6568 previous_image=GetPreviousImageInList(previous_image);
6569 cache_image->list=DestroyImage(cache_image->list);
6570 cache_image=DestroyImage(cache_image);
6571 }
6572 }
6573 if (bytes > (resource_info->undo_cache << 20))
6574 break;
6575 /*
6576 Save image before transformations are applied.
6577 */
6578 cache_image=AcquireImage((ImageInfo *) NULL,exception);
6579 if (cache_image == (Image *) NULL)
6580 break;
6581 XSetCursorState(display,windows,MagickTrue);
6582 XCheckRefreshWindows(display,windows);
6583 cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6584 XSetCursorState(display,windows,MagickFalse);
6585 if (cache_image->list == (Image *) NULL)
6586 {
6587 cache_image=DestroyImage(cache_image);
6588 break;
6589 }
6590 cache_image->columns=(size_t) windows->image.ximage->width;
6591 cache_image->rows=(size_t) windows->image.ximage->height;
6592 cache_image->geometry=windows->image.crop_geometry;
6593 if (windows->image.crop_geometry != (char *) NULL)
6594 {
6595 cache_image->geometry=AcquireString((char *) NULL);
6596 (void) CopyMagickString(cache_image->geometry,
6597 windows->image.crop_geometry,MagickPathExtent);
6598 }
6599 if (undo_image == (Image *) NULL)
6600 {
6601 undo_image=cache_image;
6602 break;
6603 }
6604 undo_image->next=cache_image;
6605 undo_image->next->previous=undo_image;
6606 undo_image=undo_image->next;
6607 break;
6608 }
6609 default:
6610 break;
6611 }
6612 if (command == RedoCommand)
6613 {
6614 /*
6615 Redo the last image transformation.
6616 */
6617 if (redo_image == (Image *) NULL)
6618 {
6619 (void) XBell(display,0);
6620 return;
6621 }
6622 windows->image.window_changes.width=(int) redo_image->columns;
6623 windows->image.window_changes.height=(int) redo_image->rows;
6624 if (windows->image.crop_geometry != (char *) NULL)
6625 windows->image.crop_geometry=(char *)
6626 RelinquishMagickMemory(windows->image.crop_geometry);
6627 windows->image.crop_geometry=redo_image->geometry;
6628 *image=DestroyImage(*image);
6629 *image=redo_image;
6630 redo_image=NewImageList();
6631 if (windows->image.orphan != MagickFalse)
6632 return;
6633 XConfigureImageColormap(display,resource_info,windows,*image,exception);
6634 (void) XConfigureImage(display,resource_info,windows,*image,exception);
6635 return;
6636 }
6637 if (command != InfoCommand)
6638 return;
6639 /*
6640 Display image info.
6641 */
6642 XSetCursorState(display,windows,MagickTrue);
6643 XCheckRefreshWindows(display,windows);
6644 XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6645 XSetCursorState(display,windows,MagickFalse);
6646}
6647
6648/*
6649%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6650% %
6651% %
6652% %
6653+ X I m a g e W i n d o w C o m m a n d %
6654% %
6655% %
6656% %
6657%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6658%
6659% XImageWindowCommand() makes a transform to the image or Image window as
6660% specified by a user menu button or keyboard command.
6661%
6662% The format of the XImageWindowCommand method is:
6663%
6664% DisplayCommand XImageWindowCommand(Display *display,
6665% XResourceInfo *resource_info,XWindows *windows,
6666% const MagickStatusType state,KeySym key_symbol,Image **image,
6667% ExceptionInfo *exception)
6668%
6669% A description of each parameter follows:
6670%
6671% o nexus: Method XImageWindowCommand returns an image when the
6672% user chooses 'Open Image' from the command menu. Otherwise a null
6673% image is returned.
6674%
6675% o display: Specifies a connection to an X server; returned from
6676% XOpenDisplay.
6677%
6678% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6679%
6680% o windows: Specifies a pointer to a XWindows structure.
6681%
6682% o state: key mask.
6683%
6684% o key_symbol: Specifies a command to perform.
6685%
6686% o image: the image; XImageWIndowCommand may transform the image and
6687% return a new image pointer.
6688%
6689% o exception: return any errors or warnings in this structure.
6690%
6691*/
6692static DisplayCommand XImageWindowCommand(Display *display,
6693 XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6694 KeySym key_symbol,Image **image,ExceptionInfo *exception)
6695{
6696 static char
6697 delta[MagickPathExtent] = "";
6698
6699 static const char
6700 Digits[] = "01234567890";
6701
6702 static KeySym
6703 last_symbol = XK_0;
6704
6705 if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6706 {
6707 if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6708 {
6709 *delta='\0';
6710 resource_info->quantum=1;
6711 }
6712 last_symbol=key_symbol;
6713 delta[strlen(delta)+1]='\0';
6714 delta[strlen(delta)]=Digits[key_symbol-XK_0];
6715 resource_info->quantum=StringToLong(delta);
6716 return(NullCommand);
6717 }
6718 last_symbol=key_symbol;
6719 if (resource_info->immutable)
6720 {
6721 /*
6722 Virtual image window has a restricted command set.
6723 */
6724 switch (key_symbol)
6725 {
6726 case XK_question:
6727 return(InfoCommand);
6728 case XK_p:
6729 case XK_Print:
6730 return(PrintCommand);
6731 case XK_space:
6732 return(NextCommand);
6733 case XK_q:
6734 case XK_Escape:
6735 return(QuitCommand);
6736 default:
6737 break;
6738 }
6739 return(NullCommand);
6740 }
6741 switch ((int) key_symbol)
6742 {
6743 case XK_o:
6744 {
6745 if ((state & ControlMask) == 0)
6746 break;
6747 return(OpenCommand);
6748 }
6749 case XK_space:
6750 return(NextCommand);
6751 case XK_BackSpace:
6752 return(FormerCommand);
6753 case XK_s:
6754 {
6755 if ((state & Mod1Mask) != 0)
6756 return(SwirlCommand);
6757 if ((state & ControlMask) == 0)
6758 return(ShearCommand);
6759 return(SaveCommand);
6760 }
6761 case XK_p:
6762 case XK_Print:
6763 {
6764 if ((state & Mod1Mask) != 0)
6765 return(OilPaintCommand);
6766 if ((state & Mod4Mask) != 0)
6767 return(ColorCommand);
6768 if ((state & ControlMask) == 0)
6769 return(NullCommand);
6770 return(PrintCommand);
6771 }
6772 case XK_d:
6773 {
6774 if ((state & Mod4Mask) != 0)
6775 return(DrawCommand);
6776 if ((state & ControlMask) == 0)
6777 return(NullCommand);
6778 return(DeleteCommand);
6779 }
6780 case XK_Select:
6781 {
6782 if ((state & ControlMask) == 0)
6783 return(NullCommand);
6784 return(SelectCommand);
6785 }
6786 case XK_n:
6787 {
6788 if ((state & ControlMask) == 0)
6789 return(NullCommand);
6790 return(NewCommand);
6791 }
6792 case XK_q:
6793 case XK_Escape:
6794 return(QuitCommand);
6795 case XK_z:
6796 case XK_Undo:
6797 {
6798 if ((state & ControlMask) == 0)
6799 return(NullCommand);
6800 return(UndoCommand);
6801 }
6802 case XK_r:
6803 case XK_Redo:
6804 {
6805 if ((state & ControlMask) == 0)
6806 return(RollCommand);
6807 return(RedoCommand);
6808 }
6809 case XK_x:
6810 {
6811 if ((state & ControlMask) == 0)
6812 return(NullCommand);
6813 return(CutCommand);
6814 }
6815 case XK_c:
6816 {
6817 if ((state & Mod1Mask) != 0)
6818 return(CharcoalDrawCommand);
6819 if ((state & ControlMask) == 0)
6820 return(CropCommand);
6821 return(CopyCommand);
6822 }
6823 case XK_v:
6824 case XK_Insert:
6825 {
6826 if ((state & Mod4Mask) != 0)
6827 return(CompositeCommand);
6828 if ((state & ControlMask) == 0)
6829 return(FlipCommand);
6830 return(PasteCommand);
6831 }
6832 case XK_less:
6833 return(HalfSizeCommand);
6834 case XK_minus:
6835 return(OriginalSizeCommand);
6836 case XK_greater:
6837 return(DoubleSizeCommand);
6838 case XK_percent:
6839 return(ResizeCommand);
6840 case XK_at:
6841 return(RefreshCommand);
6842 case XK_bracketleft:
6843 return(ChopCommand);
6844 case XK_h:
6845 return(FlopCommand);
6846 case XK_slash:
6847 return(RotateRightCommand);
6848 case XK_backslash:
6849 return(RotateLeftCommand);
6850 case XK_asterisk:
6851 return(RotateCommand);
6852 case XK_t:
6853 return(TrimCommand);
6854 case XK_H:
6855 return(HueCommand);
6856 case XK_S:
6857 return(SaturationCommand);
6858 case XK_L:
6859 return(BrightnessCommand);
6860 case XK_G:
6861 return(GammaCommand);
6862 case XK_C:
6863 return(SpiffCommand);
6864 case XK_Z:
6865 return(DullCommand);
6866 case XK_N:
6867 return(NormalizeCommand);
6868 case XK_equal:
6869 return(EqualizeCommand);
6870 case XK_asciitilde:
6871 return(NegateCommand);
6872 case XK_period:
6873 return(GrayscaleCommand);
6874 case XK_numbersign:
6875 return(QuantizeCommand);
6876 case XK_F2:
6877 return(DespeckleCommand);
6878 case XK_F3:
6879 return(EmbossCommand);
6880 case XK_F4:
6881 return(ReduceNoiseCommand);
6882 case XK_F5:
6883 return(AddNoiseCommand);
6884 case XK_F6:
6885 return(SharpenCommand);
6886 case XK_F7:
6887 return(BlurCommand);
6888 case XK_F8:
6889 return(ThresholdCommand);
6890 case XK_F9:
6891 return(EdgeDetectCommand);
6892 case XK_F10:
6893 return(SpreadCommand);
6894 case XK_F11:
6895 return(ShadeCommand);
6896 case XK_F12:
6897 return(RaiseCommand);
6898 case XK_F13:
6899 return(SegmentCommand);
6900 case XK_i:
6901 {
6902 if ((state & Mod1Mask) == 0)
6903 return(NullCommand);
6904 return(ImplodeCommand);
6905 }
6906 case XK_w:
6907 {
6908 if ((state & Mod1Mask) == 0)
6909 return(NullCommand);
6910 return(WaveCommand);
6911 }
6912 case XK_m:
6913 {
6914 if ((state & Mod4Mask) == 0)
6915 return(NullCommand);
6916 return(MatteCommand);
6917 }
6918 case XK_b:
6919 {
6920 if ((state & Mod4Mask) == 0)
6921 return(NullCommand);
6922 return(AddBorderCommand);
6923 }
6924 case XK_f:
6925 {
6926 if ((state & Mod4Mask) == 0)
6927 return(NullCommand);
6928 return(AddFrameCommand);
6929 }
6930 case XK_exclam:
6931 {
6932 if ((state & Mod4Mask) == 0)
6933 return(NullCommand);
6934 return(CommentCommand);
6935 }
6936 case XK_a:
6937 {
6938 if ((state & Mod1Mask) != 0)
6939 return(ApplyCommand);
6940 if ((state & Mod4Mask) != 0)
6941 return(AnnotateCommand);
6942 if ((state & ControlMask) == 0)
6943 return(NullCommand);
6944 return(RegionOfInterestCommand);
6945 }
6946 case XK_question:
6947 return(InfoCommand);
6948 case XK_plus:
6949 return(ZoomCommand);
6950 case XK_P:
6951 {
6952 if ((state & ShiftMask) == 0)
6953 return(NullCommand);
6954 return(ShowPreviewCommand);
6955 }
6956 case XK_Execute:
6957 return(LaunchCommand);
6958 case XK_F1:
6959 return(HelpCommand);
6960 case XK_Find:
6961 return(BrowseDocumentationCommand);
6962 case XK_Menu:
6963 {
6964 (void) XMapRaised(display,windows->command.id);
6965 return(NullCommand);
6966 }
6967 case XK_Next:
6968 case XK_Prior:
6969 case XK_Home:
6970 case XK_KP_Home:
6971 {
6972 XTranslateImage(display,windows,*image,key_symbol);
6973 return(NullCommand);
6974 }
6975 case XK_Up:
6976 case XK_KP_Up:
6977 case XK_Down:
6978 case XK_KP_Down:
6979 case XK_Left:
6980 case XK_KP_Left:
6981 case XK_Right:
6982 case XK_KP_Right:
6983 {
6984 if ((state & Mod1Mask) != 0)
6985 {
6987 crop_info;
6988
6989 /*
6990 Trim one pixel from edge of image.
6991 */
6992 crop_info.x=0;
6993 crop_info.y=0;
6994 crop_info.width=(size_t) windows->image.ximage->width;
6995 crop_info.height=(size_t) windows->image.ximage->height;
6996 if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
6997 {
6998 if (resource_info->quantum >= (int) crop_info.height)
6999 resource_info->quantum=(int) crop_info.height-1;
7000 crop_info.height-=(size_t) resource_info->quantum;
7001 }
7002 if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7003 {
7004 if (resource_info->quantum >= ((int) crop_info.height-crop_info.y))
7005 resource_info->quantum=(int) crop_info.height-crop_info.y-1;
7006 crop_info.y+=resource_info->quantum;
7007 crop_info.height-=(size_t) resource_info->quantum;
7008 }
7009 if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7010 {
7011 if (resource_info->quantum >= (int) crop_info.width)
7012 resource_info->quantum=(int) crop_info.width-1;
7013 crop_info.width-=(size_t) resource_info->quantum;
7014 }
7015 if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7016 {
7017 if (resource_info->quantum >= ((int) crop_info.width-crop_info.x))
7018 resource_info->quantum=(int) crop_info.width-crop_info.x-1;
7019 crop_info.x+=resource_info->quantum;
7020 crop_info.width-=(size_t) resource_info->quantum;
7021 }
7022 if ((windows->image.x+(int) windows->image.width) > (int) crop_info.width)
7023 windows->image.x=(int) (crop_info.width-windows->image.width);
7024 if ((windows->image.y+(int) windows->image.height) > (int) crop_info.height)
7025 windows->image.y=(int) (crop_info.height-windows->image.height);
7026 XSetCropGeometry(display,windows,&crop_info,*image);
7027 windows->image.window_changes.width=(int) crop_info.width;
7028 windows->image.window_changes.height=(int) crop_info.height;
7029 (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7030 (void) XConfigureImage(display,resource_info,windows,*image,
7031 exception);
7032 return(NullCommand);
7033 }
7034 XTranslateImage(display,windows,*image,key_symbol);
7035 return(NullCommand);
7036 }
7037 default:
7038 return(NullCommand);
7039 }
7040 return(NullCommand);
7041}
7042
7043/*
7044%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7045% %
7046% %
7047% %
7048+ X M a g i c k C o m m a n d %
7049% %
7050% %
7051% %
7052%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7053%
7054% XMagickCommand() makes a transform to the image or Image window as
7055% specified by a user menu button or keyboard command.
7056%
7057% The format of the XMagickCommand method is:
7058%
7059% Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7060% XWindows *windows,const DisplayCommand command,Image **image,
7061% ExceptionInfo *exception)
7062%
7063% A description of each parameter follows:
7064%
7065% o display: Specifies a connection to an X server; returned from
7066% XOpenDisplay.
7067%
7068% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7069%
7070% o windows: Specifies a pointer to a XWindows structure.
7071%
7072% o command: Specifies a command to perform.
7073%
7074% o image: the image; XMagickCommand may transform the image and return a
7075% new image pointer.
7076%
7077% o exception: return any errors or warnings in this structure.
7078%
7079*/
7080static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7081 XWindows *windows,const DisplayCommand command,Image **image,
7082 ExceptionInfo *exception)
7083{
7084 char
7085 filename[MagickPathExtent],
7086 geometry[MagickPathExtent],
7087 modulate_factors[MagickPathExtent];
7088
7090 geometry_info;
7091
7092 Image
7093 *nexus;
7094
7095 ImageInfo
7096 *image_info;
7097
7098 int
7099 x,
7100 y;
7101
7102 MagickStatusType
7103 flags,
7104 status;
7105
7107 quantize_info;
7108
7110 page_geometry;
7111
7112 int
7113 i;
7114
7115 static char
7116 color[MagickPathExtent] = "gray";
7117
7118 unsigned int
7119 height,
7120 width;
7121
7122 /*
7123 Process user command.
7124 */
7125 XCheckRefreshWindows(display,windows);
7126 XImageCache(display,resource_info,windows,command,image,exception);
7127 nexus=NewImageList();
7128 windows->image.window_changes.width=windows->image.ximage->width;
7129 windows->image.window_changes.height=windows->image.ximage->height;
7130 image_info=CloneImageInfo(resource_info->image_info);
7131 SetGeometryInfo(&geometry_info);
7132 GetQuantizeInfo(&quantize_info);
7133 switch (command)
7134 {
7135 case OpenCommand:
7136 {
7137 /*
7138 Load image.
7139 */
7140 nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7141 break;
7142 }
7143 case NextCommand:
7144 {
7145 /*
7146 Display next image.
7147 */
7148 for (i=0; i < resource_info->quantum; i++)
7149 XClientMessage(display,windows->image.id,windows->im_protocols,
7150 windows->im_next_image,CurrentTime);
7151 break;
7152 }
7153 case FormerCommand:
7154 {
7155 /*
7156 Display former image.
7157 */
7158 for (i=0; i < resource_info->quantum; i++)
7159 XClientMessage(display,windows->image.id,windows->im_protocols,
7160 windows->im_former_image,CurrentTime);
7161 break;
7162 }
7163 case SelectCommand:
7164 {
7165 int
7166 status;
7167
7168 /*
7169 Select image.
7170 */
7171 if (*resource_info->home_directory == '\0')
7172 (void) CopyMagickString(resource_info->home_directory,".",
7173 MagickPathExtent);
7174 status=chdir(resource_info->home_directory);
7175 if (status == -1)
7176 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7177 "UnableToOpenFile","%s",resource_info->home_directory);
7178 nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7179 break;
7180 }
7181 case SaveCommand:
7182 {
7183 /*
7184 Save image.
7185 */
7186 status=XSaveImage(display,resource_info,windows,*image,exception);
7187 if (status == MagickFalse)
7188 {
7189 char
7190 message[MagickPathExtent];
7191
7192 (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
7193 exception->reason != (char *) NULL ? exception->reason : "",
7194 exception->description != (char *) NULL ? exception->description :
7195 "");
7196 XNoticeWidget(display,windows,"Unable to save file:",message);
7197 break;
7198 }
7199 break;
7200 }
7201 case PrintCommand:
7202 {
7203 /*
7204 Print image.
7205 */
7206 status=XPrintImage(display,resource_info,windows,*image,exception);
7207 if (status == MagickFalse)
7208 {
7209 char
7210 message[MagickPathExtent];
7211
7212 (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
7213 exception->reason != (char *) NULL ? exception->reason : "",
7214 exception->description != (char *) NULL ? exception->description :
7215 "");
7216 XNoticeWidget(display,windows,"Unable to print file:",message);
7217 break;
7218 }
7219 break;
7220 }
7221 case DeleteCommand:
7222 {
7223 static char
7224 filename[MagickPathExtent] = "\0";
7225
7226 /*
7227 Delete image file.
7228 */
7229 XFileBrowserWidget(display,windows,"Delete",filename);
7230 if (*filename == '\0')
7231 break;
7232 status=ShredFile(filename);
7233 if (remove_utf8(filename) < 0)
7234 status=MagickTrue;
7235 if (status != MagickFalse)
7236 XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7237 break;
7238 }
7239 case NewCommand:
7240 {
7241 int
7242 status;
7243
7244 static char
7245 color[MagickPathExtent] = "gray",
7246 geometry[MagickPathExtent] = "640x480";
7247
7248 static const char
7249 *format = "gradient";
7250
7251 /*
7252 Query user for canvas geometry.
7253 */
7254 status=XDialogWidget(display,windows,"New","Enter image geometry:",
7255 geometry);
7256 if (*geometry == '\0')
7257 break;
7258 if (status == 0)
7259 format="xc";
7260 XColorBrowserWidget(display,windows,"Select",color);
7261 if (*color == '\0')
7262 break;
7263 /*
7264 Create canvas.
7265 */
7266 (void) FormatLocaleString(image_info->filename,MagickPathExtent,
7267 "%s:%s",format,color);
7268 (void) CloneString(&image_info->size,geometry);
7269 nexus=ReadImage(image_info,exception);
7270 CatchException(exception);
7271 XClientMessage(display,windows->image.id,windows->im_protocols,
7272 windows->im_next_image,CurrentTime);
7273 break;
7274 }
7275 case VisualDirectoryCommand:
7276 {
7277 /*
7278 Visual Image directory.
7279 */
7280 nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7281 break;
7282 }
7283 case QuitCommand:
7284 {
7285 /*
7286 exit program.
7287 */
7288 if (resource_info->confirm_exit == MagickFalse)
7289 XClientMessage(display,windows->image.id,windows->im_protocols,
7290 windows->im_exit,CurrentTime);
7291 else
7292 {
7293 int
7294 status;
7295
7296 /*
7297 Confirm program exit.
7298 */
7299 status=XConfirmWidget(display,windows,"Do you really want to exit",
7300 resource_info->client_name);
7301 if (status > 0)
7302 XClientMessage(display,windows->image.id,windows->im_protocols,
7303 windows->im_exit,CurrentTime);
7304 }
7305 break;
7306 }
7307 case CutCommand:
7308 {
7309 /*
7310 Cut image.
7311 */
7312 (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7313 break;
7314 }
7315 case CopyCommand:
7316 {
7317 /*
7318 Copy image.
7319 */
7320 (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7321 exception);
7322 break;
7323 }
7324 case PasteCommand:
7325 {
7326 /*
7327 Paste image.
7328 */
7329 status=XPasteImage(display,resource_info,windows,*image,exception);
7330 if (status == MagickFalse)
7331 {
7332 XNoticeWidget(display,windows,"Unable to paste X image",
7333 (*image)->filename);
7334 break;
7335 }
7336 break;
7337 }
7338 case HalfSizeCommand:
7339 {
7340 /*
7341 Half image size.
7342 */
7343 windows->image.window_changes.width=windows->image.ximage->width/2;
7344 windows->image.window_changes.height=windows->image.ximage->height/2;
7345 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7346 break;
7347 }
7348 case OriginalSizeCommand:
7349 {
7350 /*
7351 Original image size.
7352 */
7353 windows->image.window_changes.width=(int) (*image)->columns;
7354 windows->image.window_changes.height=(int) (*image)->rows;
7355 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7356 break;
7357 }
7358 case DoubleSizeCommand:
7359 {
7360 /*
7361 Double the image size.
7362 */
7363 windows->image.window_changes.width=windows->image.ximage->width << 1;
7364 windows->image.window_changes.height=windows->image.ximage->height << 1;
7365 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7366 break;
7367 }
7368 case ResizeCommand:
7369 {
7370 int
7371 status;
7372
7373 size_t
7374 height,
7375 width;
7376
7377 ssize_t
7378 x,
7379 y;
7380
7381 /*
7382 Resize image.
7383 */
7384 width=(size_t) windows->image.ximage->width;
7385 height=(size_t) windows->image.ximage->height;
7386 x=0;
7387 y=0;
7388 (void) FormatLocaleString(geometry,MagickPathExtent,"%.20gx%.20g+0+0",
7389 (double) width,(double) height);
7390 status=XDialogWidget(display,windows,"Resize",
7391 "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7392 if (*geometry == '\0')
7393 break;
7394 if (status == 0)
7395 (void) ConcatenateMagickString(geometry,"!",MagickPathExtent);
7396 (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7397 windows->image.window_changes.width=(int) width;
7398 windows->image.window_changes.height=(int) height;
7399 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7400 break;
7401 }
7402 case ApplyCommand:
7403 {
7404 char
7405 image_geometry[MagickPathExtent];
7406
7407 if ((windows->image.crop_geometry == (char *) NULL) &&
7408 ((int) (*image)->columns == windows->image.ximage->width) &&
7409 ((int) (*image)->rows == windows->image.ximage->height))
7410 break;
7411 /*
7412 Apply size transforms to image.
7413 */
7414 XSetCursorState(display,windows,MagickTrue);
7415 XCheckRefreshWindows(display,windows);
7416 /*
7417 Crop and/or scale displayed image.
7418 */
7419 (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
7420 windows->image.ximage->width,windows->image.ximage->height);
7421 (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7422 exception);
7423 if (windows->image.crop_geometry != (char *) NULL)
7424 windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7425 windows->image.crop_geometry);
7426 windows->image.x=0;
7427 windows->image.y=0;
7428 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7429 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7430 break;
7431 }
7432 case RefreshCommand:
7433 {
7434 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7435 break;
7436 }
7437 case RestoreCommand:
7438 {
7439 /*
7440 Restore Image window to its original size.
7441 */
7442 if ((windows->image.width == (unsigned int) (*image)->columns) &&
7443 (windows->image.height == (unsigned int) (*image)->rows) &&
7444 (windows->image.crop_geometry == (char *) NULL))
7445 {
7446 (void) XBell(display,0);
7447 break;
7448 }
7449 windows->image.window_changes.width=(int) (*image)->columns;
7450 windows->image.window_changes.height=(int) (*image)->rows;
7451 if (windows->image.crop_geometry != (char *) NULL)
7452 {
7453 windows->image.crop_geometry=(char *)
7454 RelinquishMagickMemory(windows->image.crop_geometry);
7455 windows->image.crop_geometry=(char *) NULL;
7456 windows->image.x=0;
7457 windows->image.y=0;
7458 }
7459 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7460 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7461 break;
7462 }
7463 case CropCommand:
7464 {
7465 /*
7466 Crop image.
7467 */
7468 (void) XCropImage(display,resource_info,windows,*image,CropMode,
7469 exception);
7470 break;
7471 }
7472 case ChopCommand:
7473 {
7474 /*
7475 Chop image.
7476 */
7477 status=XChopImage(display,resource_info,windows,image,exception);
7478 if (status == MagickFalse)
7479 {
7480 XNoticeWidget(display,windows,"Unable to cut X image",
7481 (*image)->filename);
7482 break;
7483 }
7484 break;
7485 }
7486 case FlopCommand:
7487 {
7488 Image
7489 *flop_image;
7490
7491 /*
7492 Flop image scanlines.
7493 */
7494 XSetCursorState(display,windows,MagickTrue);
7495 XCheckRefreshWindows(display,windows);
7496 flop_image=FlopImage(*image,exception);
7497 if (flop_image != (Image *) NULL)
7498 {
7499 *image=DestroyImage(*image);
7500 *image=flop_image;
7501 }
7502 CatchException(exception);
7503 XSetCursorState(display,windows,MagickFalse);
7504 if (windows->image.crop_geometry != (char *) NULL)
7505 {
7506 /*
7507 Flop crop geometry.
7508 */
7509 width=(unsigned int) (*image)->columns;
7510 height=(unsigned int) (*image)->rows;
7511 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7512 &width,&height);
7513 (void) FormatLocaleString(windows->image.crop_geometry,
7514 MagickPathExtent,"%ux%u%+d%+d",width,height,(int) (*image)->columns-
7515 (int) width-x,y);
7516 }
7517 if (windows->image.orphan != MagickFalse)
7518 break;
7519 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7520 break;
7521 }
7522 case FlipCommand:
7523 {
7524 Image
7525 *flip_image;
7526
7527 /*
7528 Flip image scanlines.
7529 */
7530 XSetCursorState(display,windows,MagickTrue);
7531 XCheckRefreshWindows(display,windows);
7532 flip_image=FlipImage(*image,exception);
7533 if (flip_image != (Image *) NULL)
7534 {
7535 *image=DestroyImage(*image);
7536 *image=flip_image;
7537 }
7538 CatchException(exception);
7539 XSetCursorState(display,windows,MagickFalse);
7540 if (windows->image.crop_geometry != (char *) NULL)
7541 {
7542 /*
7543 Flip crop geometry.
7544 */
7545 width=(unsigned int) (*image)->columns;
7546 height=(unsigned int) (*image)->rows;
7547 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7548 &width,&height);
7549 (void) FormatLocaleString(windows->image.crop_geometry,
7550 MagickPathExtent,"%ux%u%+d%+d",width,height,x,(int) (*image)->rows-
7551 (int) height-y);
7552 }
7553 if (windows->image.orphan != MagickFalse)
7554 break;
7555 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7556 break;
7557 }
7558 case RotateRightCommand:
7559 {
7560 /*
7561 Rotate image 90 degrees clockwise.
7562 */
7563 status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7564 if (status == MagickFalse)
7565 {
7566 XNoticeWidget(display,windows,"Unable to rotate X image",
7567 (*image)->filename);
7568 break;
7569 }
7570 break;
7571 }
7572 case RotateLeftCommand:
7573 {
7574 /*
7575 Rotate image 90 degrees counter-clockwise.
7576 */
7577 status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7578 if (status == MagickFalse)
7579 {
7580 XNoticeWidget(display,windows,"Unable to rotate X image",
7581 (*image)->filename);
7582 break;
7583 }
7584 break;
7585 }
7586 case RotateCommand:
7587 {
7588 /*
7589 Rotate image.
7590 */
7591 status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7592 if (status == MagickFalse)
7593 {
7594 XNoticeWidget(display,windows,"Unable to rotate X image",
7595 (*image)->filename);
7596 break;
7597 }
7598 break;
7599 }
7600 case ShearCommand:
7601 {
7602 Image
7603 *shear_image;
7604
7605 static char
7606 geometry[MagickPathExtent] = "45.0x45.0";
7607
7608 /*
7609 Query user for shear color and geometry.
7610 */
7611 XColorBrowserWidget(display,windows,"Select",color);
7612 if (*color == '\0')
7613 break;
7614 (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7615 geometry);
7616 if (*geometry == '\0')
7617 break;
7618 /*
7619 Shear image.
7620 */
7621 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7622 exception);
7623 XSetCursorState(display,windows,MagickTrue);
7624 XCheckRefreshWindows(display,windows);
7625 (void) QueryColorCompliance(color,AllCompliance,
7626 &(*image)->background_color,exception);
7627 flags=ParseGeometry(geometry,&geometry_info);
7628 if ((flags & SigmaValue) == 0)
7629 geometry_info.sigma=geometry_info.rho;
7630 shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7631 exception);
7632 if (shear_image != (Image *) NULL)
7633 {
7634 *image=DestroyImage(*image);
7635 *image=shear_image;
7636 }
7637 CatchException(exception);
7638 XSetCursorState(display,windows,MagickFalse);
7639 if (windows->image.orphan != MagickFalse)
7640 break;
7641 windows->image.window_changes.width=(int) (*image)->columns;
7642 windows->image.window_changes.height=(int) (*image)->rows;
7643 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7644 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7645 break;
7646 }
7647 case RollCommand:
7648 {
7649 Image
7650 *roll_image;
7651
7652 static char
7653 geometry[MagickPathExtent] = "+2+2";
7654
7655 /*
7656 Query user for the roll geometry.
7657 */
7658 (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7659 geometry);
7660 if (*geometry == '\0')
7661 break;
7662 /*
7663 Roll image.
7664 */
7665 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7666 exception);
7667 XSetCursorState(display,windows,MagickTrue);
7668 XCheckRefreshWindows(display,windows);
7669 (void) ParsePageGeometry(*image,geometry,&page_geometry,
7670 exception);
7671 roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7672 exception);
7673 if (roll_image != (Image *) NULL)
7674 {
7675 *image=DestroyImage(*image);
7676 *image=roll_image;
7677 }
7678 CatchException(exception);
7679 XSetCursorState(display,windows,MagickFalse);
7680 if (windows->image.orphan != MagickFalse)
7681 break;
7682 windows->image.window_changes.width=(int) (*image)->columns;
7683 windows->image.window_changes.height=(int) (*image)->rows;
7684 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7685 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7686 break;
7687 }
7688 case TrimCommand:
7689 {
7690 static char
7691 fuzz[MagickPathExtent];
7692
7693 /*
7694 Query user for the fuzz factor.
7695 */
7696 (void) FormatLocaleString(fuzz,MagickPathExtent,"%g%%",100.0*
7697 (*image)->fuzz/((double) QuantumRange+1.0));
7698 (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7699 if (*fuzz == '\0')
7700 break;
7701 (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7702 /*
7703 Trim image.
7704 */
7705 status=XTrimImage(display,resource_info,windows,*image,exception);
7706 if (status == MagickFalse)
7707 {
7708 XNoticeWidget(display,windows,"Unable to trim X image",
7709 (*image)->filename);
7710 break;
7711 }
7712 break;
7713 }
7714 case HueCommand:
7715 {
7716 static char
7717 hue_percent[MagickPathExtent] = "110";
7718
7719 /*
7720 Query user for percent hue change.
7721 */
7722 (void) XDialogWidget(display,windows,"Apply",
7723 "Enter percent change in image hue (0-200):",hue_percent);
7724 if (*hue_percent == '\0')
7725 break;
7726 /*
7727 Vary the image hue.
7728 */
7729 XSetCursorState(display,windows,MagickTrue);
7730 XCheckRefreshWindows(display,windows);
7731 (void) CopyMagickString(modulate_factors,"100.0/100.0/",MagickPathExtent);
7732 (void) ConcatenateMagickString(modulate_factors,hue_percent,
7733 MagickPathExtent);
7734 (void) ModulateImage(*image,modulate_factors,exception);
7735 XSetCursorState(display,windows,MagickFalse);
7736 if (windows->image.orphan != MagickFalse)
7737 break;
7738 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7739 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7740 break;
7741 }
7742 case SaturationCommand:
7743 {
7744 static char
7745 saturation_percent[MagickPathExtent] = "110";
7746
7747 /*
7748 Query user for percent saturation change.
7749 */
7750 (void) XDialogWidget(display,windows,"Apply",
7751 "Enter percent change in color saturation (0-200):",saturation_percent);
7752 if (*saturation_percent == '\0')
7753 break;
7754 /*
7755 Vary color saturation.
7756 */
7757 XSetCursorState(display,windows,MagickTrue);
7758 XCheckRefreshWindows(display,windows);
7759 (void) CopyMagickString(modulate_factors,"100.0/",MagickPathExtent);
7760 (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7761 MagickPathExtent);
7762 (void) ModulateImage(*image,modulate_factors,exception);
7763 XSetCursorState(display,windows,MagickFalse);
7764 if (windows->image.orphan != MagickFalse)
7765 break;
7766 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7767 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7768 break;
7769 }
7770 case BrightnessCommand:
7771 {
7772 static char
7773 brightness_percent[MagickPathExtent] = "110";
7774
7775 /*
7776 Query user for percent brightness change.
7777 */
7778 (void) XDialogWidget(display,windows,"Apply",
7779 "Enter percent change in color brightness (0-200):",brightness_percent);
7780 if (*brightness_percent == '\0')
7781 break;
7782 /*
7783 Vary the color brightness.
7784 */
7785 XSetCursorState(display,windows,MagickTrue);
7786 XCheckRefreshWindows(display,windows);
7787 (void) CopyMagickString(modulate_factors,brightness_percent,
7788 MagickPathExtent);
7789 (void) ModulateImage(*image,modulate_factors,exception);
7790 XSetCursorState(display,windows,MagickFalse);
7791 if (windows->image.orphan != MagickFalse)
7792 break;
7793 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7794 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7795 break;
7796 }
7797 case GammaCommand:
7798 {
7799 static char
7800 factor[MagickPathExtent] = "1.6";
7801
7802 /*
7803 Query user for gamma value.
7804 */
7805 (void) XDialogWidget(display,windows,"Gamma",
7806 "Enter gamma value (e.g. 1.2):",factor);
7807 if (*factor == '\0')
7808 break;
7809 /*
7810 Gamma correct image.
7811 */
7812 XSetCursorState(display,windows,MagickTrue);
7813 XCheckRefreshWindows(display,windows);
7814 (void) GammaImage(*image,strtod(factor,(char **) NULL),exception);
7815 XSetCursorState(display,windows,MagickFalse);
7816 if (windows->image.orphan != MagickFalse)
7817 break;
7818 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7819 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7820 break;
7821 }
7822 case SpiffCommand:
7823 {
7824 /*
7825 Sharpen the image contrast.
7826 */
7827 XSetCursorState(display,windows,MagickTrue);
7828 XCheckRefreshWindows(display,windows);
7829 (void) ContrastImage(*image,MagickTrue,exception);
7830 XSetCursorState(display,windows,MagickFalse);
7831 if (windows->image.orphan != MagickFalse)
7832 break;
7833 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7834 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7835 break;
7836 }
7837 case DullCommand:
7838 {
7839 /*
7840 Dull the image contrast.
7841 */
7842 XSetCursorState(display,windows,MagickTrue);
7843 XCheckRefreshWindows(display,windows);
7844 (void) ContrastImage(*image,MagickFalse,exception);
7845 XSetCursorState(display,windows,MagickFalse);
7846 if (windows->image.orphan != MagickFalse)
7847 break;
7848 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7849 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7850 break;
7851 }
7852 case ContrastStretchCommand:
7853 {
7854 double
7855 black_point,
7856 white_point;
7857
7858 static char
7859 levels[MagickPathExtent] = "1%";
7860
7861 /*
7862 Query user for gamma value.
7863 */
7864 (void) XDialogWidget(display,windows,"Contrast Stretch",
7865 "Enter black and white points:",levels);
7866 if (*levels == '\0')
7867 break;
7868 /*
7869 Contrast stretch image.
7870 */
7871 XSetCursorState(display,windows,MagickTrue);
7872 XCheckRefreshWindows(display,windows);
7873 flags=ParseGeometry(levels,&geometry_info);
7874 black_point=geometry_info.rho;
7875 white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7876 if ((flags & PercentValue) != 0)
7877 {
7878 black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7879 white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7880 }
7881 white_point=(double) (*image)->columns*(*image)->rows-white_point;
7882 (void) ContrastStretchImage(*image,black_point,white_point,
7883 exception);
7884 XSetCursorState(display,windows,MagickFalse);
7885 if (windows->image.orphan != MagickFalse)
7886 break;
7887 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7888 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7889 break;
7890 }
7891 case SigmoidalContrastCommand:
7892 {
7894 geometry_info;
7895
7896 MagickStatusType
7897 flags;
7898
7899 static char
7900 levels[MagickPathExtent] = "3x50%";
7901
7902 /*
7903 Query user for gamma value.
7904 */
7905 (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7906 "Enter contrast and midpoint:",levels);
7907 if (*levels == '\0')
7908 break;
7909 /*
7910 Contrast stretch image.
7911 */
7912 XSetCursorState(display,windows,MagickTrue);
7913 XCheckRefreshWindows(display,windows);
7914 flags=ParseGeometry(levels,&geometry_info);
7915 if ((flags & SigmaValue) == 0)
7916 geometry_info.sigma=1.0*(double) QuantumRange/2.0;
7917 if ((flags & PercentValue) != 0)
7918 geometry_info.sigma=1.0*(double) QuantumRange*geometry_info.sigma/100.0;
7919 (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7920 geometry_info.sigma,exception);
7921 XSetCursorState(display,windows,MagickFalse);
7922 if (windows->image.orphan != MagickFalse)
7923 break;
7924 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7925 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7926 break;
7927 }
7928 case NormalizeCommand:
7929 {
7930 /*
7931 Perform histogram normalization on the image.
7932 */
7933 XSetCursorState(display,windows,MagickTrue);
7934 XCheckRefreshWindows(display,windows);
7935 (void) NormalizeImage(*image,exception);
7936 XSetCursorState(display,windows,MagickFalse);
7937 if (windows->image.orphan != MagickFalse)
7938 break;
7939 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7940 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7941 break;
7942 }
7943 case EqualizeCommand:
7944 {
7945 /*
7946 Perform histogram equalization on the image.
7947 */
7948 XSetCursorState(display,windows,MagickTrue);
7949 XCheckRefreshWindows(display,windows);
7950 (void) EqualizeImage(*image,exception);
7951 XSetCursorState(display,windows,MagickFalse);
7952 if (windows->image.orphan != MagickFalse)
7953 break;
7954 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7955 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7956 break;
7957 }
7958 case NegateCommand:
7959 {
7960 /*
7961 Negate colors in image.
7962 */
7963 XSetCursorState(display,windows,MagickTrue);
7964 XCheckRefreshWindows(display,windows);
7965 (void) NegateImage(*image,MagickFalse,exception);
7966 XSetCursorState(display,windows,MagickFalse);
7967 if (windows->image.orphan != MagickFalse)
7968 break;
7969 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7970 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7971 break;
7972 }
7973 case GrayscaleCommand:
7974 {
7975 /*
7976 Convert image to grayscale.
7977 */
7978 XSetCursorState(display,windows,MagickTrue);
7979 XCheckRefreshWindows(display,windows);
7980 (void) SetImageType(*image,(*image)->alpha_trait == UndefinedPixelTrait ?
7981 GrayscaleType : GrayscaleAlphaType,exception);
7982 XSetCursorState(display,windows,MagickFalse);
7983 if (windows->image.orphan != MagickFalse)
7984 break;
7985 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7986 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7987 break;
7988 }
7989 case MapCommand:
7990 {
7991 Image
7992 *affinity_image;
7993
7994 static char
7995 filename[MagickPathExtent] = "\0";
7996
7997 /*
7998 Request image file name from user.
7999 */
8000 XFileBrowserWidget(display,windows,"Map",filename);
8001 if (*filename == '\0')
8002 break;
8003 /*
8004 Map image.
8005 */
8006 XSetCursorState(display,windows,MagickTrue);
8007 XCheckRefreshWindows(display,windows);
8008 (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
8009 affinity_image=ReadImage(image_info,exception);
8010 if (affinity_image != (Image *) NULL)
8011 {
8012 (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8013 affinity_image=DestroyImage(affinity_image);
8014 }
8015 CatchException(exception);
8016 XSetCursorState(display,windows,MagickFalse);
8017 if (windows->image.orphan != MagickFalse)
8018 break;
8019 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8020 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8021 break;
8022 }
8023 case QuantizeCommand:
8024 {
8025 int
8026 status;
8027
8028 static char
8029 colors[MagickPathExtent] = "256";
8030
8031 /*
8032 Query user for maximum number of colors.
8033 */
8034 status=XDialogWidget(display,windows,"Quantize",
8035 "Maximum number of colors:",colors);
8036 if (*colors == '\0')
8037 break;
8038 /*
8039 Color reduce the image.
8040 */
8041 XSetCursorState(display,windows,MagickTrue);
8042 XCheckRefreshWindows(display,windows);
8043 quantize_info.number_colors=StringToUnsignedLong(colors);
8044 quantize_info.dither_method=status != 0 ? RiemersmaDitherMethod :
8045 NoDitherMethod;
8046 (void) QuantizeImage(&quantize_info,*image,exception);
8047 XSetCursorState(display,windows,MagickFalse);
8048 if (windows->image.orphan != MagickFalse)
8049 break;
8050 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8051 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8052 break;
8053 }
8054 case DespeckleCommand:
8055 {
8056 Image
8057 *despeckle_image;
8058
8059 /*
8060 Despeckle image.
8061 */
8062 XSetCursorState(display,windows,MagickTrue);
8063 XCheckRefreshWindows(display,windows);
8064 despeckle_image=DespeckleImage(*image,exception);
8065 if (despeckle_image != (Image *) NULL)
8066 {
8067 *image=DestroyImage(*image);
8068 *image=despeckle_image;
8069 }
8070 CatchException(exception);
8071 XSetCursorState(display,windows,MagickFalse);
8072 if (windows->image.orphan != MagickFalse)
8073 break;
8074 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8075 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8076 break;
8077 }
8078 case EmbossCommand:
8079 {
8080 Image
8081 *emboss_image;
8082
8083 static char
8084 radius[MagickPathExtent] = "0.0x1.0";
8085
8086 /*
8087 Query user for emboss radius.
8088 */
8089 (void) XDialogWidget(display,windows,"Emboss",
8090 "Enter the emboss radius and standard deviation:",radius);
8091 if (*radius == '\0')
8092 break;
8093 /*
8094 Reduce noise in the image.
8095 */
8096 XSetCursorState(display,windows,MagickTrue);
8097 XCheckRefreshWindows(display,windows);
8098 flags=ParseGeometry(radius,&geometry_info);
8099 if ((flags & SigmaValue) == 0)
8100 geometry_info.sigma=1.0;
8101 emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8102 exception);
8103 if (emboss_image != (Image *) NULL)
8104 {
8105 *image=DestroyImage(*image);
8106 *image=emboss_image;
8107 }
8108 CatchException(exception);
8109 XSetCursorState(display,windows,MagickFalse);
8110 if (windows->image.orphan != MagickFalse)
8111 break;
8112 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8113 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8114 break;
8115 }
8116 case ReduceNoiseCommand:
8117 {
8118 Image
8119 *noise_image;
8120
8121 static char
8122 radius[MagickPathExtent] = "0";
8123
8124 /*
8125 Query user for noise radius.
8126 */
8127 (void) XDialogWidget(display,windows,"Reduce Noise",
8128 "Enter the noise radius:",radius);
8129 if (*radius == '\0')
8130 break;
8131 /*
8132 Reduce noise in the image.
8133 */
8134 XSetCursorState(display,windows,MagickTrue);
8135 XCheckRefreshWindows(display,windows);
8136 flags=ParseGeometry(radius,&geometry_info);
8137 noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8138 geometry_info.rho,(size_t) geometry_info.rho,exception);
8139 if (noise_image != (Image *) NULL)
8140 {
8141 *image=DestroyImage(*image);
8142 *image=noise_image;
8143 }
8144 CatchException(exception);
8145 XSetCursorState(display,windows,MagickFalse);
8146 if (windows->image.orphan != MagickFalse)
8147 break;
8148 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8149 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8150 break;
8151 }
8152 case AddNoiseCommand:
8153 {
8154 char
8155 **noises;
8156
8157 Image
8158 *noise_image;
8159
8160 static char
8161 noise_type[MagickPathExtent] = "Gaussian";
8162
8163 /*
8164 Add noise to the image.
8165 */
8166 noises=GetCommandOptions(MagickNoiseOptions);
8167 if (noises == (char **) NULL)
8168 break;
8169 XListBrowserWidget(display,windows,&windows->widget,
8170 (const char **) noises,"Add Noise",
8171 "Select a type of noise to add to your image:",noise_type);
8172 noises=DestroyStringList(noises);
8173 if (*noise_type == '\0')
8174 break;
8175 XSetCursorState(display,windows,MagickTrue);
8176 XCheckRefreshWindows(display,windows);
8177 noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8178 MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8179 if (noise_image != (Image *) NULL)
8180 {
8181 *image=DestroyImage(*image);
8182 *image=noise_image;
8183 }
8184 CatchException(exception);
8185 XSetCursorState(display,windows,MagickFalse);
8186 if (windows->image.orphan != MagickFalse)
8187 break;
8188 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8189 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8190 break;
8191 }
8192 case SharpenCommand:
8193 {
8194 Image
8195 *sharp_image;
8196
8197 static char
8198 radius[MagickPathExtent] = "0.0x1.0";
8199
8200 /*
8201 Query user for sharpen radius.
8202 */
8203 (void) XDialogWidget(display,windows,"Sharpen",
8204 "Enter the sharpen radius and standard deviation:",radius);
8205 if (*radius == '\0')
8206 break;
8207 /*
8208 Sharpen image scanlines.
8209 */
8210 XSetCursorState(display,windows,MagickTrue);
8211 XCheckRefreshWindows(display,windows);
8212 flags=ParseGeometry(radius,&geometry_info);
8213 sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8214 exception);
8215 if (sharp_image != (Image *) NULL)
8216 {
8217 *image=DestroyImage(*image);
8218 *image=sharp_image;
8219 }
8220 CatchException(exception);
8221 XSetCursorState(display,windows,MagickFalse);
8222 if (windows->image.orphan != MagickFalse)
8223 break;
8224 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8225 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8226 break;
8227 }
8228 case BlurCommand:
8229 {
8230 Image
8231 *blur_image;
8232
8233 static char
8234 radius[MagickPathExtent] = "0.0x1.0";
8235
8236 /*
8237 Query user for blur radius.
8238 */
8239 (void) XDialogWidget(display,windows,"Blur",
8240 "Enter the blur radius and standard deviation:",radius);
8241 if (*radius == '\0')
8242 break;
8243 /*
8244 Blur an image.
8245 */
8246 XSetCursorState(display,windows,MagickTrue);
8247 XCheckRefreshWindows(display,windows);
8248 flags=ParseGeometry(radius,&geometry_info);
8249 blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8250 exception);
8251 if (blur_image != (Image *) NULL)
8252 {
8253 *image=DestroyImage(*image);
8254 *image=blur_image;
8255 }
8256 CatchException(exception);
8257 XSetCursorState(display,windows,MagickFalse);
8258 if (windows->image.orphan != MagickFalse)
8259 break;
8260 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8261 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8262 break;
8263 }
8264 case ThresholdCommand:
8265 {
8266 double
8267 threshold;
8268
8269 static char
8270 factor[MagickPathExtent] = "128";
8271
8272 /*
8273 Query user for threshold value.
8274 */
8275 (void) XDialogWidget(display,windows,"Threshold",
8276 "Enter threshold value:",factor);
8277 if (*factor == '\0')
8278 break;
8279 /*
8280 Gamma correct image.
8281 */
8282 XSetCursorState(display,windows,MagickTrue);
8283 XCheckRefreshWindows(display,windows);
8284 threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8285 (void) BilevelImage(*image,threshold,exception);
8286 XSetCursorState(display,windows,MagickFalse);
8287 if (windows->image.orphan != MagickFalse)
8288 break;
8289 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8290 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8291 break;
8292 }
8293 case EdgeDetectCommand:
8294 {
8295 Image
8296 *edge_image;
8297
8298 static char
8299 radius[MagickPathExtent] = "0";
8300
8301 /*
8302 Query user for edge factor.
8303 */
8304 (void) XDialogWidget(display,windows,"Detect Edges",
8305 "Enter the edge detect radius:",radius);
8306 if (*radius == '\0')
8307 break;
8308 /*
8309 Detect edge in image.
8310 */
8311 XSetCursorState(display,windows,MagickTrue);
8312 XCheckRefreshWindows(display,windows);
8313 flags=ParseGeometry(radius,&geometry_info);
8314 edge_image=EdgeImage(*image,geometry_info.rho,exception);
8315 if (edge_image != (Image *) NULL)
8316 {
8317 *image=DestroyImage(*image);
8318 *image=edge_image;
8319 }
8320 CatchException(exception);
8321 XSetCursorState(display,windows,MagickFalse);
8322 if (windows->image.orphan != MagickFalse)
8323 break;
8324 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8325 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8326 break;
8327 }
8328 case SpreadCommand:
8329 {
8330 Image
8331 *spread_image;
8332
8333 static char
8334 amount[MagickPathExtent] = "2";
8335
8336 /*
8337 Query user for spread amount.
8338 */
8339 (void) XDialogWidget(display,windows,"Spread",
8340 "Enter the displacement amount:",amount);
8341 if (*amount == '\0')
8342 break;
8343 /*
8344 Displace image pixels by a random amount.
8345 */
8346 XSetCursorState(display,windows,MagickTrue);
8347 XCheckRefreshWindows(display,windows);
8348 flags=ParseGeometry(amount,&geometry_info);
8349 spread_image=EdgeImage(*image,geometry_info.rho,exception);
8350 if (spread_image != (Image *) NULL)
8351 {
8352 *image=DestroyImage(*image);
8353 *image=spread_image;
8354 }
8355 CatchException(exception);
8356 XSetCursorState(display,windows,MagickFalse);
8357 if (windows->image.orphan != MagickFalse)
8358 break;
8359 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8360 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8361 break;
8362 }
8363 case ShadeCommand:
8364 {
8365 Image
8366 *shade_image;
8367
8368 int
8369 status;
8370
8371 static char
8372 geometry[MagickPathExtent] = "30x30";
8373
8374 /*
8375 Query user for the shade geometry.
8376 */
8377 status=XDialogWidget(display,windows,"Shade",
8378 "Enter the azimuth and elevation of the light source:",geometry);
8379 if (*geometry == '\0')
8380 break;
8381 /*
8382 Shade image pixels.
8383 */
8384 XSetCursorState(display,windows,MagickTrue);
8385 XCheckRefreshWindows(display,windows);
8386 flags=ParseGeometry(geometry,&geometry_info);
8387 if ((flags & SigmaValue) == 0)
8388 geometry_info.sigma=1.0;
8389 shade_image=ShadeImage(*image,status != 0 ? MagickTrue : MagickFalse,
8390 geometry_info.rho,geometry_info.sigma,exception);
8391 if (shade_image != (Image *) NULL)
8392 {
8393 *image=DestroyImage(*image);
8394 *image=shade_image;
8395 }
8396 CatchException(exception);
8397 XSetCursorState(display,windows,MagickFalse);
8398 if (windows->image.orphan != MagickFalse)
8399 break;
8400 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8401 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8402 break;
8403 }
8404 case RaiseCommand:
8405 {
8406 static char
8407 bevel_width[MagickPathExtent] = "10";
8408
8409 /*
8410 Query user for bevel width.
8411 */
8412 (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8413 if (*bevel_width == '\0')
8414 break;
8415 /*
8416 Raise an image.
8417 */
8418 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8419 exception);
8420 XSetCursorState(display,windows,MagickTrue);
8421 XCheckRefreshWindows(display,windows);
8422 (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8423 exception);
8424 (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8425 XSetCursorState(display,windows,MagickFalse);
8426 if (windows->image.orphan != MagickFalse)
8427 break;
8428 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8429 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8430 break;
8431 }
8432 case SegmentCommand:
8433 {
8434 static char
8435 threshold[MagickPathExtent] = "1.0x1.5";
8436
8437 /*
8438 Query user for smoothing threshold.
8439 */
8440 (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8441 threshold);
8442 if (*threshold == '\0')
8443 break;
8444 /*
8445 Segment an image.
8446 */
8447 XSetCursorState(display,windows,MagickTrue);
8448 XCheckRefreshWindows(display,windows);
8449 flags=ParseGeometry(threshold,&geometry_info);
8450 if ((flags & SigmaValue) == 0)
8451 geometry_info.sigma=1.0;
8452 (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8453 geometry_info.sigma,exception);
8454 XSetCursorState(display,windows,MagickFalse);
8455 if (windows->image.orphan != MagickFalse)
8456 break;
8457 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8458 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8459 break;
8460 }
8461 case SepiaToneCommand:
8462 {
8463 double
8464 threshold;
8465
8466 Image
8467 *sepia_image;
8468
8469 static char
8470 factor[MagickPathExtent] = "80%";
8471
8472 /*
8473 Query user for sepia-tone factor.
8474 */
8475 (void) XDialogWidget(display,windows,"Sepia Tone",
8476 "Enter the sepia tone factor (0 - 99.9%):",factor);
8477 if (*factor == '\0')
8478 break;
8479 /*
8480 Sepia tone image pixels.
8481 */
8482 XSetCursorState(display,windows,MagickTrue);
8483 XCheckRefreshWindows(display,windows);
8484 threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8485 sepia_image=SepiaToneImage(*image,threshold,exception);
8486 if (sepia_image != (Image *) NULL)
8487 {
8488 *image=DestroyImage(*image);
8489 *image=sepia_image;
8490 }
8491 CatchException(exception);
8492 XSetCursorState(display,windows,MagickFalse);
8493 if (windows->image.orphan != MagickFalse)
8494 break;
8495 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8496 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8497 break;
8498 }
8499 case SolarizeCommand:
8500 {
8501 double
8502 threshold;
8503
8504 static char
8505 factor[MagickPathExtent] = "60%";
8506
8507 /*
8508 Query user for solarize factor.
8509 */
8510 (void) XDialogWidget(display,windows,"Solarize",
8511 "Enter the solarize factor (0 - 99.9%):",factor);
8512 if (*factor == '\0')
8513 break;
8514 /*
8515 Solarize image pixels.
8516 */
8517 XSetCursorState(display,windows,MagickTrue);
8518 XCheckRefreshWindows(display,windows);
8519 threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8520 (void) SolarizeImage(*image,threshold,exception);
8521 XSetCursorState(display,windows,MagickFalse);
8522 if (windows->image.orphan != MagickFalse)
8523 break;
8524 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8525 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8526 break;
8527 }
8528 case SwirlCommand:
8529 {
8530 Image
8531 *swirl_image;
8532
8533 static char
8534 degrees[MagickPathExtent] = "60";
8535
8536 /*
8537 Query user for swirl angle.
8538 */
8539 (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8540 degrees);
8541 if (*degrees == '\0')
8542 break;
8543 /*
8544 Swirl image pixels about the center.
8545 */
8546 XSetCursorState(display,windows,MagickTrue);
8547 XCheckRefreshWindows(display,windows);
8548 flags=ParseGeometry(degrees,&geometry_info);
8549 swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8550 exception);
8551 if (swirl_image != (Image *) NULL)
8552 {
8553 *image=DestroyImage(*image);
8554 *image=swirl_image;
8555 }
8556 CatchException(exception);
8557 XSetCursorState(display,windows,MagickFalse);
8558 if (windows->image.orphan != MagickFalse)
8559 break;
8560 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8561 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8562 break;
8563 }
8564 case ImplodeCommand:
8565 {
8566 Image
8567 *implode_image;
8568
8569 static char
8570 factor[MagickPathExtent] = "0.3";
8571
8572 /*
8573 Query user for implode factor.
8574 */
8575 (void) XDialogWidget(display,windows,"Implode",
8576 "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8577 if (*factor == '\0')
8578 break;
8579 /*
8580 Implode image pixels about the center.
8581 */
8582 XSetCursorState(display,windows,MagickTrue);
8583 XCheckRefreshWindows(display,windows);
8584 flags=ParseGeometry(factor,&geometry_info);
8585 implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8586 exception);
8587 if (implode_image != (Image *) NULL)
8588 {
8589 *image=DestroyImage(*image);
8590 *image=implode_image;
8591 }
8592 CatchException(exception);
8593 XSetCursorState(display,windows,MagickFalse);
8594 if (windows->image.orphan != MagickFalse)
8595 break;
8596 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8597 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8598 break;
8599 }
8600 case VignetteCommand:
8601 {
8602 Image
8603 *vignette_image;
8604
8605 static char
8606 geometry[MagickPathExtent] = "0x20";
8607
8608 /*
8609 Query user for the vignette geometry.
8610 */
8611 (void) XDialogWidget(display,windows,"Vignette",
8612 "Enter the radius, sigma, and x and y offsets:",geometry);
8613 if (*geometry == '\0')
8614 break;
8615 /*
8616 Soften the edges of the image in vignette style
8617 */
8618 XSetCursorState(display,windows,MagickTrue);
8619 XCheckRefreshWindows(display,windows);
8620 flags=ParseGeometry(geometry,&geometry_info);
8621 if ((flags & SigmaValue) == 0)
8622 geometry_info.sigma=1.0;
8623 if ((flags & XiValue) == 0)
8624 geometry_info.xi=0.1*(*image)->columns;
8625 if ((flags & PsiValue) == 0)
8626 geometry_info.psi=0.1*(*image)->rows;
8627 vignette_image=VignetteImage(*image,geometry_info.rho,0.0,(ssize_t)
8628 ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-0.5),
8629 exception);
8630 if (vignette_image != (Image *) NULL)
8631 {
8632 *image=DestroyImage(*image);
8633 *image=vignette_image;
8634 }
8635 CatchException(exception);
8636 XSetCursorState(display,windows,MagickFalse);
8637 if (windows->image.orphan != MagickFalse)
8638 break;
8639 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8640 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8641 break;
8642 }
8643 case WaveCommand:
8644 {
8645 Image
8646 *wave_image;
8647
8648 static char
8649 geometry[MagickPathExtent] = "25x150";
8650
8651 /*
8652 Query user for the wave geometry.
8653 */
8654 (void) XDialogWidget(display,windows,"Wave",
8655 "Enter the amplitude and length of the wave:",geometry);
8656 if (*geometry == '\0')
8657 break;
8658 /*
8659 Alter an image along a sine wave.
8660 */
8661 XSetCursorState(display,windows,MagickTrue);
8662 XCheckRefreshWindows(display,windows);
8663 flags=ParseGeometry(geometry,&geometry_info);
8664 if ((flags & SigmaValue) == 0)
8665 geometry_info.sigma=1.0;
8666 wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8667 (*image)->interpolate,exception);
8668 if (wave_image != (Image *) NULL)
8669 {
8670 *image=DestroyImage(*image);
8671 *image=wave_image;
8672 }
8673 CatchException(exception);
8674 XSetCursorState(display,windows,MagickFalse);
8675 if (windows->image.orphan != MagickFalse)
8676 break;
8677 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8678 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8679 break;
8680 }
8681 case OilPaintCommand:
8682 {
8683 Image
8684 *paint_image;
8685
8686 static char
8687 radius[MagickPathExtent] = "0";
8688
8689 /*
8690 Query user for circular neighborhood radius.
8691 */
8692 (void) XDialogWidget(display,windows,"Oil Paint",
8693 "Enter the mask radius:",radius);
8694 if (*radius == '\0')
8695 break;
8696 /*
8697 OilPaint image scanlines.
8698 */
8699 XSetCursorState(display,windows,MagickTrue);
8700 XCheckRefreshWindows(display,windows);
8701 flags=ParseGeometry(radius,&geometry_info);
8702 paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8703 exception);
8704 if (paint_image != (Image *) NULL)
8705 {
8706 *image=DestroyImage(*image);
8707 *image=paint_image;
8708 }
8709 CatchException(exception);
8710 XSetCursorState(display,windows,MagickFalse);
8711 if (windows->image.orphan != MagickFalse)
8712 break;
8713 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8714 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8715 break;
8716 }
8717 case CharcoalDrawCommand:
8718 {
8719 Image
8720 *charcoal_image;
8721
8722 static char
8723 radius[MagickPathExtent] = "0x1";
8724
8725 /*
8726 Query user for charcoal radius.
8727 */
8728 (void) XDialogWidget(display,windows,"Charcoal Draw",
8729 "Enter the charcoal radius and sigma:",radius);
8730 if (*radius == '\0')
8731 break;
8732 /*
8733 Charcoal the image.
8734 */
8735 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8736 exception);
8737 XSetCursorState(display,windows,MagickTrue);
8738 XCheckRefreshWindows(display,windows);
8739 flags=ParseGeometry(radius,&geometry_info);
8740 if ((flags & SigmaValue) == 0)
8741 geometry_info.sigma=geometry_info.rho;
8742 charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8743 exception);
8744 if (charcoal_image != (Image *) NULL)
8745 {
8746 *image=DestroyImage(*image);
8747 *image=charcoal_image;
8748 }
8749 CatchException(exception);
8750 XSetCursorState(display,windows,MagickFalse);
8751 if (windows->image.orphan != MagickFalse)
8752 break;
8753 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8754 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8755 break;
8756 }
8757 case AnnotateCommand:
8758 {
8759 /*
8760 Annotate the image with text.
8761 */
8762 status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8763 if (status == MagickFalse)
8764 {
8765 XNoticeWidget(display,windows,"Unable to annotate X image",
8766 (*image)->filename);
8767 break;
8768 }
8769 break;
8770 }
8771 case DrawCommand:
8772 {
8773 /*
8774 Draw image.
8775 */
8776 status=XDrawEditImage(display,resource_info,windows,image,exception);
8777 if (status == MagickFalse)
8778 {
8779 XNoticeWidget(display,windows,"Unable to draw on the X image",
8780 (*image)->filename);
8781 break;
8782 }
8783 break;
8784 }
8785 case ColorCommand:
8786 {
8787 /*
8788 Color edit.
8789 */
8790 status=XColorEditImage(display,resource_info,windows,image,exception);
8791 if (status == MagickFalse)
8792 {
8793 XNoticeWidget(display,windows,"Unable to pixel edit X image",
8794 (*image)->filename);
8795 break;
8796 }
8797 break;
8798 }
8799 case MatteCommand:
8800 {
8801 /*
8802 Matte edit.
8803 */
8804 status=XMatteEditImage(display,resource_info,windows,image,exception);
8805 if (status == MagickFalse)
8806 {
8807 XNoticeWidget(display,windows,"Unable to matte edit X image",
8808 (*image)->filename);
8809 break;
8810 }
8811 break;
8812 }
8813 case CompositeCommand:
8814 {
8815 /*
8816 Composite image.
8817 */
8818 status=XCompositeImage(display,resource_info,windows,*image,
8819 exception);
8820 if (status == MagickFalse)
8821 {
8822 XNoticeWidget(display,windows,"Unable to composite X image",
8823 (*image)->filename);
8824 break;
8825 }
8826 break;
8827 }
8828 case AddBorderCommand:
8829 {
8830 Image
8831 *border_image;
8832
8833 static char
8834 geometry[MagickPathExtent] = "6x6";
8835
8836 /*
8837 Query user for border color and geometry.
8838 */
8839 XColorBrowserWidget(display,windows,"Select",color);
8840 if (*color == '\0')
8841 break;
8842 (void) XDialogWidget(display,windows,"Add Border",
8843 "Enter border geometry:",geometry);
8844 if (*geometry == '\0')
8845 break;
8846 /*
8847 Add a border to the image.
8848 */
8849 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8850 exception);
8851 XSetCursorState(display,windows,MagickTrue);
8852 XCheckRefreshWindows(display,windows);
8853 (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8854 exception);
8855 (void) ParsePageGeometry(*image,geometry,&page_geometry,
8856 exception);
8857 border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8858 exception);
8859 if (border_image != (Image *) NULL)
8860 {
8861 *image=DestroyImage(*image);
8862 *image=border_image;
8863 }
8864 CatchException(exception);
8865 XSetCursorState(display,windows,MagickFalse);
8866 if (windows->image.orphan != MagickFalse)
8867 break;
8868 windows->image.window_changes.width=(int) (*image)->columns;
8869 windows->image.window_changes.height=(int) (*image)->rows;
8870 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8871 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8872 break;
8873 }
8874 case AddFrameCommand:
8875 {
8876 FrameInfo
8877 frame_info;
8878
8879 Image
8880 *frame_image;
8881
8882 static char
8883 geometry[MagickPathExtent] = "6x6";
8884
8885 /*
8886 Query user for frame color and geometry.
8887 */
8888 XColorBrowserWidget(display,windows,"Select",color);
8889 if (*color == '\0')
8890 break;
8891 (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8892 geometry);
8893 if (*geometry == '\0')
8894 break;
8895 /*
8896 Surround image with an ornamental border.
8897 */
8898 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8899 exception);
8900 XSetCursorState(display,windows,MagickTrue);
8901 XCheckRefreshWindows(display,windows);
8902 (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8903 exception);
8904 (void) ParsePageGeometry(*image,geometry,&page_geometry,
8905 exception);
8906 frame_info.width=page_geometry.width;
8907 frame_info.height=page_geometry.height;
8908 frame_info.outer_bevel=page_geometry.x;
8909 frame_info.inner_bevel=page_geometry.y;
8910 frame_info.x=(ssize_t) frame_info.width;
8911 frame_info.y=(ssize_t) frame_info.height;
8912 frame_info.width=(*image)->columns+2*frame_info.width;
8913 frame_info.height=(*image)->rows+2*frame_info.height;
8914 frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8915 if (frame_image != (Image *) NULL)
8916 {
8917 *image=DestroyImage(*image);
8918 *image=frame_image;
8919 }
8920 CatchException(exception);
8921 XSetCursorState(display,windows,MagickFalse);
8922 if (windows->image.orphan != MagickFalse)
8923 break;
8924 windows->image.window_changes.width=(int) (*image)->columns;
8925 windows->image.window_changes.height=(int) (*image)->rows;
8926 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8927 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8928 break;
8929 }
8930 case CommentCommand:
8931 {
8932 const char
8933 *value;
8934
8935 FILE
8936 *file;
8937
8938 int
8939 unique_file;
8940
8941 /*
8942 Edit image comment.
8943 */
8944 unique_file=AcquireUniqueFileResource(image_info->filename);
8945 if (unique_file == -1)
8946 {
8947 XNoticeWidget(display,windows,"Unable to edit image comment",
8948 image_info->filename);
8949 break;
8950 }
8951 value=GetImageProperty(*image,"comment",exception);
8952 if (value == (char *) NULL)
8953 unique_file=close(unique_file)-1;
8954 else
8955 {
8956 const char
8957 *p;
8958
8959 file=fdopen(unique_file,"w");
8960 if (file == (FILE *) NULL)
8961 {
8962 XNoticeWidget(display,windows,"Unable to edit image comment",
8963 image_info->filename);
8964 break;
8965 }
8966 for (p=value; *p != '\0'; p++)
8967 (void) fputc((int) *p,file);
8968 (void) fputc('\n',file);
8969 (void) fclose(file);
8970 }
8971 XSetCursorState(display,windows,MagickTrue);
8972 XCheckRefreshWindows(display,windows);
8973 status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8974 exception);
8975 if (status == MagickFalse)
8976 XNoticeWidget(display,windows,"Unable to edit image comment",
8977 (char *) NULL);
8978 else
8979 {
8980 char
8981 *comment;
8982
8983 comment=FileToString(image_info->filename,~0UL,exception);
8984 if (comment != (char *) NULL)
8985 {
8986 (void) SetImageProperty(*image,"comment",comment,exception);
8987 (*image)->taint=MagickTrue;
8988 }
8989 }
8990 (void) RelinquishUniqueFileResource(image_info->filename);
8991 XSetCursorState(display,windows,MagickFalse);
8992 break;
8993 }
8994 case LaunchCommand:
8995 {
8996 /*
8997 Launch program.
8998 */
8999 XSetCursorState(display,windows,MagickTrue);
9000 XCheckRefreshWindows(display,windows);
9001 (void) AcquireUniqueFilename(filename);
9002 (void) FormatLocaleString((*image)->filename,MagickPathExtent,"launch:%s",
9003 filename);
9004 status=WriteImage(image_info,*image,exception);
9005 if (status == MagickFalse)
9006 XNoticeWidget(display,windows,"Unable to launch image editor",
9007 (char *) NULL);
9008 else
9009 {
9010 nexus=ReadImage(resource_info->image_info,exception);
9011 CatchException(exception);
9012 XClientMessage(display,windows->image.id,windows->im_protocols,
9013 windows->im_next_image,CurrentTime);
9014 }
9015 (void) RelinquishUniqueFileResource(filename);
9016 XSetCursorState(display,windows,MagickFalse);
9017 break;
9018 }
9019 case RegionOfInterestCommand:
9020 {
9021 /*
9022 Apply an image processing technique to a region of interest.
9023 */
9024 (void) XROIImage(display,resource_info,windows,image,exception);
9025 break;
9026 }
9027 case InfoCommand:
9028 break;
9029 case ZoomCommand:
9030 {
9031 /*
9032 Zoom image.
9033 */
9034 if (windows->magnify.mapped != MagickFalse)
9035 (void) XRaiseWindow(display,windows->magnify.id);
9036 else
9037 {
9038 /*
9039 Make magnify image.
9040 */
9041 XSetCursorState(display,windows,MagickTrue);
9042 (void) XMapRaised(display,windows->magnify.id);
9043 XSetCursorState(display,windows,MagickFalse);
9044 }
9045 break;
9046 }
9047 case ShowPreviewCommand:
9048 {
9049 char
9050 **previews;
9051
9052 Image
9053 *preview_image;
9054
9055 PreviewType
9056 preview;
9057
9058 static char
9059 preview_type[MagickPathExtent] = "Gamma";
9060
9061 /*
9062 Select preview type from menu.
9063 */
9064 previews=GetCommandOptions(MagickPreviewOptions);
9065 if (previews == (char **) NULL)
9066 break;
9067 XListBrowserWidget(display,windows,&windows->widget,
9068 (const char **) previews,"Preview",
9069 "Select an enhancement, effect, or F/X:",preview_type);
9070 previews=DestroyStringList(previews);
9071 if (*preview_type == '\0')
9072 break;
9073 /*
9074 Show image preview.
9075 */
9076 XSetCursorState(display,windows,MagickTrue);
9077 XCheckRefreshWindows(display,windows);
9078 preview=(PreviewType) ParseCommandOption(MagickPreviewOptions,
9079 MagickFalse,preview_type);
9080 (void) FormatImageProperty(*image,"group","%.20g",(double)
9081 windows->image.id);
9082 (void) DeleteImageProperty(*image,"label");
9083 (void) SetImageProperty(*image,"label","Preview",exception);
9084 preview_image=PreviewImage(*image,preview,exception);
9085 if (preview_image == (Image *) NULL)
9086 break;
9087 (void) AcquireUniqueFilename(filename);
9088 (void) FormatLocaleString(preview_image->filename,MagickPathExtent,
9089 "show:%s",filename);
9090 status=WriteImage(image_info,preview_image,exception);
9091 (void) RelinquishUniqueFileResource(filename);
9092 preview_image=DestroyImage(preview_image);
9093 if (status == MagickFalse)
9094 XNoticeWidget(display,windows,"Unable to show image preview",
9095 (*image)->filename);
9096 XDelay(display,1500);
9097 XSetCursorState(display,windows,MagickFalse);
9098 break;
9099 }
9100 case ShowHistogramCommand:
9101 {
9102 Image
9103 *histogram_image;
9104
9105 /*
9106 Show image histogram.
9107 */
9108 XSetCursorState(display,windows,MagickTrue);
9109 XCheckRefreshWindows(display,windows);
9110 (void) DeleteImageProperty(*image,"label");
9111 (void) FormatImageProperty(*image,"group","%.20g",(double)
9112 windows->image.id);
9113 (void) SetImageProperty(*image,"label","Histogram",exception);
9114 (void) AcquireUniqueFilename(filename);
9115 (void) FormatLocaleString((*image)->filename,MagickPathExtent,
9116 "histogram:%s",filename);
9117 status=WriteImage(image_info,*image,exception);
9118 (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9119 histogram_image=ReadImage(image_info,exception);
9120 (void) RelinquishUniqueFileResource(filename);
9121 if (histogram_image == (Image *) NULL)
9122 break;
9123 (void) FormatLocaleString(histogram_image->filename,MagickPathExtent,
9124 "show:%s",filename);
9125 status=WriteImage(image_info,histogram_image,exception);
9126 histogram_image=DestroyImage(histogram_image);
9127 if (status == MagickFalse)
9128 XNoticeWidget(display,windows,"Unable to show histogram",
9129 (*image)->filename);
9130 XDelay(display,1500);
9131 XSetCursorState(display,windows,MagickFalse);
9132 break;
9133 }
9134 case ShowMatteCommand:
9135 {
9136 Image
9137 *matte_image;
9138
9139 if ((*image)->alpha_trait == UndefinedPixelTrait)
9140 {
9141 XNoticeWidget(display,windows,
9142 "Image does not have any matte information",(*image)->filename);
9143 break;
9144 }
9145 /*
9146 Show image matte.
9147 */
9148 XSetCursorState(display,windows,MagickTrue);
9149 XCheckRefreshWindows(display,windows);
9150 (void) FormatImageProperty(*image,"group","%.20g",(double)
9151 windows->image.id);
9152 (void) DeleteImageProperty(*image,"label");
9153 (void) SetImageProperty(*image,"label","Matte",exception);
9154 (void) AcquireUniqueFilename(filename);
9155 (void) FormatLocaleString((*image)->filename,MagickPathExtent,"matte:%s",
9156 filename);
9157 status=WriteImage(image_info,*image,exception);
9158 (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9159 matte_image=ReadImage(image_info,exception);
9160 (void) RelinquishUniqueFileResource(filename);
9161 if (matte_image == (Image *) NULL)
9162 break;
9163 (void) FormatLocaleString(matte_image->filename,MagickPathExtent,
9164 "show:%s",filename);
9165 status=WriteImage(image_info,matte_image,exception);
9166 matte_image=DestroyImage(matte_image);
9167 if (status == MagickFalse)
9168 XNoticeWidget(display,windows,"Unable to show matte",
9169 (*image)->filename);
9170 XDelay(display,1500);
9171 XSetCursorState(display,windows,MagickFalse);
9172 break;
9173 }
9174 case BackgroundCommand:
9175 {
9176 /*
9177 Background image.
9178 */
9179 status=XBackgroundImage(display,resource_info,windows,image,exception);
9180 if (status == MagickFalse)
9181 break;
9182 nexus=CloneImage(*image,0,0,MagickTrue,exception);
9183 if (nexus != (Image *) NULL)
9184 XClientMessage(display,windows->image.id,windows->im_protocols,
9185 windows->im_next_image,CurrentTime);
9186 break;
9187 }
9188 case SlideShowCommand:
9189 {
9190 static char
9191 delay[MagickPathExtent] = "5";
9192
9193 /*
9194 Display next image after pausing.
9195 */
9196 (void) XDialogWidget(display,windows,"Slide Show",
9197 "Pause how many 1/100ths of a second between images:",delay);
9198 if (*delay == '\0')
9199 break;
9200 resource_info->delay=StringToUnsignedLong(delay);
9201 XClientMessage(display,windows->image.id,windows->im_protocols,
9202 windows->im_next_image,CurrentTime);
9203 break;
9204 }
9205 case PreferencesCommand:
9206 {
9207 /*
9208 Set user preferences.
9209 */
9210 status=XPreferencesWidget(display,resource_info,windows);
9211 if (status == MagickFalse)
9212 break;
9213 nexus=CloneImage(*image,0,0,MagickTrue,exception);
9214 if (nexus != (Image *) NULL)
9215 XClientMessage(display,windows->image.id,windows->im_protocols,
9216 windows->im_next_image,CurrentTime);
9217 break;
9218 }
9219 case HelpCommand:
9220 {
9221 /*
9222 User requested help.
9223 */
9224 XTextViewHelp(display,resource_info,windows,MagickFalse,
9225 "Help Viewer - Display",DisplayHelp);
9226 break;
9227 }
9228 case BrowseDocumentationCommand:
9229 {
9230 Atom
9231 mozilla_atom;
9232
9233 Window
9234 mozilla_window,
9235 root_window;
9236
9237 /*
9238 Browse the ImageMagick documentation.
9239 */
9240 root_window=XRootWindow(display,XDefaultScreen(display));
9241 mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9242 mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9243 if (mozilla_window != (Window) NULL)
9244 {
9245 char
9246 command[MagickPathExtent];
9247
9248 /*
9249 Display documentation using Netscape remote control.
9250 */
9251 (void) FormatLocaleString(command,MagickPathExtent,
9252 "openurl(%s,new-tab)",MagickAuthoritativeURL);
9253 mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9254 (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9255 8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9256 XSetCursorState(display,windows,MagickFalse);
9257 break;
9258 }
9259 XSetCursorState(display,windows,MagickTrue);
9260 XCheckRefreshWindows(display,windows);
9261 status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9262 exception);
9263 if (status == MagickFalse)
9264 XNoticeWidget(display,windows,"Unable to browse documentation",
9265 (char *) NULL);
9266 XDelay(display,1500);
9267 XSetCursorState(display,windows,MagickFalse);
9268 break;
9269 }
9270 case VersionCommand:
9271 {
9272 XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9273 GetMagickCopyright());
9274 break;
9275 }
9276 case SaveToUndoBufferCommand:
9277 break;
9278 default:
9279 {
9280 (void) XBell(display,0);
9281 break;
9282 }
9283 }
9284 image_info=DestroyImageInfo(image_info);
9285 return(nexus);
9286}
9287
9288/*
9289%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9290% %
9291% %
9292% %
9293+ X M a g n i f y I m a g e %
9294% %
9295% %
9296% %
9297%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9298%
9299% XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9300% The magnified portion is displayed in a separate window.
9301%
9302% The format of the XMagnifyImage method is:
9303%
9304% void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9305% ExceptionInfo *exception)
9306%
9307% A description of each parameter follows:
9308%
9309% o display: Specifies a connection to an X server; returned from
9310% XOpenDisplay.
9311%
9312% o windows: Specifies a pointer to a XWindows structure.
9313%
9314% o event: Specifies a pointer to a XEvent structure. If it is NULL,
9315% the entire image is refreshed.
9316%
9317% o exception: return any errors or warnings in this structure.
9318%
9319*/
9320static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9321 ExceptionInfo *exception)
9322{
9323 char
9324 text[MagickPathExtent];
9325
9326 int
9327 x,
9328 y;
9329
9330 size_t
9331 state;
9332
9333 /*
9334 Update magnified image until the mouse button is released.
9335 */
9336 (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9337 state=DefaultState;
9338 x=event->xbutton.x;
9339 y=event->xbutton.y;
9340 windows->magnify.x=(int) windows->image.x+x;
9341 windows->magnify.y=(int) windows->image.y+y;
9342 do
9343 {
9344 /*
9345 Map and unmap Info widget as text cursor crosses its boundaries.
9346 */
9347 if (windows->info.mapped != MagickFalse)
9348 {
9349 if ((x < (windows->info.x+(int) windows->info.width)) &&
9350 (y < (windows->info.y+(int) windows->info.height)))
9351 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9352 }
9353 else
9354 if ((x > (windows->info.x+(int) windows->info.width)) ||
9355 (y > (windows->info.y+(int) windows->info.height)))
9356 (void) XMapWindow(display,windows->info.id);
9357 if (windows->info.mapped != MagickFalse)
9358 {
9359 /*
9360 Display pointer position.
9361 */
9362 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
9363 windows->magnify.x,windows->magnify.y);
9364 XInfoWidget(display,windows,text);
9365 }
9366 /*
9367 Wait for next event.
9368 */
9369 XScreenEvent(display,windows,event,exception);
9370 switch (event->type)
9371 {
9372 case ButtonPress:
9373 break;
9374 case ButtonRelease:
9375 {
9376 /*
9377 User has finished magnifying image.
9378 */
9379 x=event->xbutton.x;
9380 y=event->xbutton.y;
9381 state|=ExitState;
9382 break;
9383 }
9384 case Expose:
9385 break;
9386 case MotionNotify:
9387 {
9388 x=event->xmotion.x;
9389 y=event->xmotion.y;
9390 break;
9391 }
9392 default:
9393 break;
9394 }
9395 /*
9396 Check boundary conditions.
9397 */
9398 if (x < 0)
9399 x=0;
9400 else
9401 if (x >= (int) windows->image.width)
9402 x=(int) windows->image.width-1;
9403 if (y < 0)
9404 y=0;
9405 else
9406 if (y >= (int) windows->image.height)
9407 y=(int) windows->image.height-1;
9408 } while ((state & ExitState) == 0);
9409 /*
9410 Display magnified image.
9411 */
9412 XSetCursorState(display,windows,MagickFalse);
9413}
9414
9415/*
9416%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9417% %
9418% %
9419% %
9420+ X M a g n i f y W i n d o w C o m m a n d %
9421% %
9422% %
9423% %
9424%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9425%
9426% XMagnifyWindowCommand() moves the image within an Magnify window by one
9427% pixel as specified by the key symbol.
9428%
9429% The format of the XMagnifyWindowCommand method is:
9430%
9431% void XMagnifyWindowCommand(Display *display,XWindows *windows,
9432% const MagickStatusType state,const KeySym key_symbol,
9433% ExceptionInfo *exception)
9434%
9435% A description of each parameter follows:
9436%
9437% o display: Specifies a connection to an X server; returned from
9438% XOpenDisplay.
9439%
9440% o windows: Specifies a pointer to a XWindows structure.
9441%
9442% o state: key mask.
9443%
9444% o key_symbol: Specifies a KeySym which indicates which side of the image
9445% to trim.
9446%
9447% o exception: return any errors or warnings in this structure.
9448%
9449*/
9450static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9451 const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9452{
9453 unsigned int
9454 quantum;
9455
9456 /*
9457 User specified a magnify factor or position.
9458 */
9459 quantum=1;
9460 if ((state & Mod1Mask) != 0)
9461 quantum=10;
9462 switch ((int) key_symbol)
9463 {
9464 case QuitCommand:
9465 {
9466 (void) XWithdrawWindow(display,windows->magnify.id,
9467 windows->magnify.screen);
9468 break;
9469 }
9470 case XK_Home:
9471 case XK_KP_Home:
9472 {
9473 windows->magnify.x=(int) windows->image.width/2;
9474 windows->magnify.y=(int) windows->image.height/2;
9475 break;
9476 }
9477 case XK_Left:
9478 case XK_KP_Left:
9479 {
9480 if (windows->magnify.x > 0)
9481 windows->magnify.x-=(int) quantum;
9482 break;
9483 }
9484 case XK_Up:
9485 case XK_KP_Up:
9486 {
9487 if (windows->magnify.y > 0)
9488 windows->magnify.y-=(int) quantum;
9489 break;
9490 }
9491 case XK_Right:
9492 case XK_KP_Right:
9493 {
9494 if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9495 windows->magnify.x+=(int) quantum;
9496 break;
9497 }
9498 case XK_Down:
9499 case XK_KP_Down:
9500 {
9501 if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9502 windows->magnify.y+=(int) quantum;
9503 break;
9504 }
9505 case XK_0:
9506 case XK_1:
9507 case XK_2:
9508 case XK_3:
9509 case XK_4:
9510 case XK_5:
9511 case XK_6:
9512 case XK_7:
9513 case XK_8:
9514 case XK_9:
9515 {
9516 windows->magnify.data=(key_symbol-XK_0);
9517 break;
9518 }
9519 case XK_KP_0:
9520 case XK_KP_1:
9521 case XK_KP_2:
9522 case XK_KP_3:
9523 case XK_KP_4:
9524 case XK_KP_5:
9525 case XK_KP_6:
9526 case XK_KP_7:
9527 case XK_KP_8:
9528 case XK_KP_9:
9529 {
9530 windows->magnify.data=(key_symbol-XK_KP_0);
9531 break;
9532 }
9533 default:
9534 break;
9535 }
9536 XMakeMagnifyImage(display,windows,exception);
9537}
9538
9539/*
9540%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9541% %
9542% %
9543% %
9544+ X M a k e P a n I m a g e %
9545% %
9546% %
9547% %
9548%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9549%
9550% XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9551% icon window.
9552%
9553% The format of the XMakePanImage method is:
9554%
9555% void XMakePanImage(Display *display,XResourceInfo *resource_info,
9556% XWindows *windows,Image *image,ExceptionInfo *exception)
9557%
9558% A description of each parameter follows:
9559%
9560% o display: Specifies a connection to an X server; returned from
9561% XOpenDisplay.
9562%
9563% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9564%
9565% o windows: Specifies a pointer to a XWindows structure.
9566%
9567% o image: the image.
9568%
9569% o exception: return any errors or warnings in this structure.
9570%
9571*/
9572static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9573 XWindows *windows,Image *image,ExceptionInfo *exception)
9574{
9575 MagickStatusType
9576 status;
9577
9578 /*
9579 Create and display image for panning icon.
9580 */
9581 XSetCursorState(display,windows,MagickTrue);
9582 XCheckRefreshWindows(display,windows);
9583 windows->pan.x=(int) windows->image.x;
9584 windows->pan.y=(int) windows->image.y;
9585 status=XMakeImage(display,resource_info,&windows->pan,image,
9586 windows->pan.width,windows->pan.height,exception);
9587 if (status == MagickFalse)
9588 ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
9589 image->filename);
9590 (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9591 windows->pan.pixmap);
9592 (void) XClearWindow(display,windows->pan.id);
9593 XDrawPanRectangle(display,windows);
9594 XSetCursorState(display,windows,MagickFalse);
9595}
9596
9597/*
9598%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9599% %
9600% %
9601% %
9602+ X M a t t a E d i t I m a g e %
9603% %
9604% %
9605% %
9606%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9607%
9608% XMatteEditImage() allows the user to interactively change the Matte channel
9609% of an image. If the image is PseudoClass it is promoted to DirectClass
9610% before the matte information is stored.
9611%
9612% The format of the XMatteEditImage method is:
9613%
9614% MagickBooleanType XMatteEditImage(Display *display,
9615% XResourceInfo *resource_info,XWindows *windows,Image **image,
9616% ExceptionInfo *exception)
9617%
9618% A description of each parameter follows:
9619%
9620% o display: Specifies a connection to an X server; returned from
9621% XOpenDisplay.
9622%
9623% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9624%
9625% o windows: Specifies a pointer to a XWindows structure.
9626%
9627% o image: the image; returned from ReadImage.
9628%
9629% o exception: return any errors or warnings in this structure.
9630%
9631*/
9632static MagickBooleanType XMatteEditImage(Display *display,
9633 XResourceInfo *resource_info,XWindows *windows,Image **image,
9634 ExceptionInfo *exception)
9635{
9636 const char
9637 *const MatteEditMenu[] =
9638 {
9639 "Method",
9640 "Border Color",
9641 "Fuzz",
9642 "Matte Value",
9643 "Undo",
9644 "Help",
9645 "Dismiss",
9646 (char *) NULL
9647 };
9648
9649 static char
9650 matte[MagickPathExtent] = "0";
9651
9652 static const ModeType
9653 MatteEditCommands[] =
9654 {
9655 MatteEditMethod,
9656 MatteEditBorderCommand,
9657 MatteEditFuzzCommand,
9658 MatteEditValueCommand,
9659 MatteEditUndoCommand,
9660 MatteEditHelpCommand,
9661 MatteEditDismissCommand
9662 };
9663
9664 static PaintMethod
9665 method = PointMethod;
9666
9667 static XColor
9668 border_color = { 0, 0, 0, 0, 0, 0 };
9669
9670 char
9671 command[MagickPathExtent],
9672 text[MagickPathExtent];
9673
9674 Cursor
9675 cursor;
9676
9677 int
9678 entry,
9679 id,
9680 x,
9681 x_offset,
9682 y,
9683 y_offset;
9684
9685 int
9686 i;
9687
9688 Quantum
9689 *q;
9690
9691 unsigned int
9692 height,
9693 width;
9694
9695 size_t
9696 state;
9697
9698 XEvent
9699 event;
9700
9701 /*
9702 Map Command widget.
9703 */
9704 (void) CloneString(&windows->command.name,"Matte Edit");
9705 windows->command.data=4;
9706 (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9707 (void) XMapRaised(display,windows->command.id);
9708 XClientMessage(display,windows->image.id,windows->im_protocols,
9709 windows->im_update_widget,CurrentTime);
9710 /*
9711 Make cursor.
9712 */
9713 cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9714 resource_info->background_color,resource_info->foreground_color);
9715 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9716 /*
9717 Track pointer until button 1 is pressed.
9718 */
9719 XQueryPosition(display,windows->image.id,&x,&y);
9720 (void) XSelectInput(display,windows->image.id,
9721 windows->image.attributes.event_mask | PointerMotionMask);
9722 state=DefaultState;
9723 do
9724 {
9725 if (windows->info.mapped != MagickFalse)
9726 {
9727 /*
9728 Display pointer position.
9729 */
9730 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
9731 x+windows->image.x,y+windows->image.y);
9732 XInfoWidget(display,windows,text);
9733 }
9734 /*
9735 Wait for next event.
9736 */
9737 XScreenEvent(display,windows,&event,exception);
9738 if (event.xany.window == windows->command.id)
9739 {
9740 /*
9741 Select a command from the Command widget.
9742 */
9743 id=XCommandWidget(display,windows,MatteEditMenu,&event);
9744 if (id < 0)
9745 {
9746 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9747 continue;
9748 }
9749 switch (MatteEditCommands[id])
9750 {
9751 case MatteEditMethod:
9752 {
9753 char
9754 **methods;
9755
9756 /*
9757 Select a method from the pop-up menu.
9758 */
9759 methods=GetCommandOptions(MagickMethodOptions);
9760 if (methods == (char **) NULL)
9761 break;
9762 entry=XMenuWidget(display,windows,MatteEditMenu[id],
9763 (const char **) methods,command);
9764 if (entry >= 0)
9765 method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9766 MagickFalse,methods[entry]);
9767 methods=DestroyStringList(methods);
9768 break;
9769 }
9770 case MatteEditBorderCommand:
9771 {
9772 const char
9773 *ColorMenu[MaxNumberPens];
9774
9775 int
9776 pen_number;
9777
9778 /*
9779 Initialize menu selections.
9780 */
9781 for (i=0; i < (int) (MaxNumberPens-2); i++)
9782 ColorMenu[i]=resource_info->pen_colors[i];
9783 ColorMenu[MaxNumberPens-2]="Browser...";
9784 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9785 /*
9786 Select a pen color from the pop-up menu.
9787 */
9788 pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9789 (const char **) ColorMenu,command);
9790 if (pen_number < 0)
9791 break;
9792 if (pen_number == (MaxNumberPens-2))
9793 {
9794 static char
9795 color_name[MagickPathExtent] = "gray";
9796
9797 /*
9798 Select a pen color from a dialog.
9799 */
9800 resource_info->pen_colors[pen_number]=color_name;
9801 XColorBrowserWidget(display,windows,"Select",color_name);
9802 if (*color_name == '\0')
9803 break;
9804 }
9805 /*
9806 Set border color.
9807 */
9808 (void) XParseColor(display,windows->map_info->colormap,
9809 resource_info->pen_colors[pen_number],&border_color);
9810 break;
9811 }
9812 case MatteEditFuzzCommand:
9813 {
9814 const char
9815 *const FuzzMenu[] =
9816 {
9817 "0%",
9818 "2%",
9819 "5%",
9820 "10%",
9821 "15%",
9822 "Dialog...",
9823 (char *) NULL,
9824 };
9825
9826 static char
9827 fuzz[MagickPathExtent];
9828
9829 /*
9830 Select a command from the pop-up menu.
9831 */
9832 entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9833 command);
9834 if (entry < 0)
9835 break;
9836 if (entry != 5)
9837 {
9838 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9839 QuantumRange+1.0);
9840 break;
9841 }
9842 (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
9843 (void) XDialogWidget(display,windows,"Ok",
9844 "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9845 if (*fuzz == '\0')
9846 break;
9847 (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
9848 (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9849 1.0);
9850 break;
9851 }
9852 case MatteEditValueCommand:
9853 {
9854 const char
9855 *const MatteMenu[] =
9856 {
9857 "Opaque",
9858 "Transparent",
9859 "Dialog...",
9860 (char *) NULL,
9861 };
9862
9863 static char
9864 message[MagickPathExtent];
9865
9866 /*
9867 Select a command from the pop-up menu.
9868 */
9869 entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9870 command);
9871 if (entry < 0)
9872 break;
9873 if (entry != 2)
9874 {
9875 (void) FormatLocaleString(matte,MagickPathExtent,"%g",
9876 (double) OpaqueAlpha);
9877 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9878 (void) FormatLocaleString(matte,MagickPathExtent,"%g",
9879 (double) TransparentAlpha);
9880 break;
9881 }
9882 (void) FormatLocaleString(message,MagickPathExtent,
9883 "Enter matte value (0 - " "%g" "):",(double) QuantumRange);
9884 (void) XDialogWidget(display,windows,"Matte",message,matte);
9885 if (*matte == '\0')
9886 break;
9887 break;
9888 }
9889 case MatteEditUndoCommand:
9890 {
9891 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9892 image,exception);
9893 break;
9894 }
9895 case MatteEditHelpCommand:
9896 {
9897 XTextViewHelp(display,resource_info,windows,MagickFalse,
9898 "Help Viewer - Matte Edit",ImageMatteEditHelp);
9899 break;
9900 }
9901 case MatteEditDismissCommand:
9902 {
9903 /*
9904 Prematurely exit.
9905 */
9906 state|=EscapeState;
9907 state|=ExitState;
9908 break;
9909 }
9910 default:
9911 break;
9912 }
9913 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9914 continue;
9915 }
9916 switch (event.type)
9917 {
9918 case ButtonPress:
9919 {
9920 if (event.xbutton.button != Button1)
9921 break;
9922 if ((event.xbutton.window != windows->image.id) &&
9923 (event.xbutton.window != windows->magnify.id))
9924 break;
9925 /*
9926 Update matte data.
9927 */
9928 x=event.xbutton.x;
9929 y=event.xbutton.y;
9930 (void) XMagickCommand(display,resource_info,windows,
9931 SaveToUndoBufferCommand,image,exception);
9932 state|=UpdateConfigurationState;
9933 break;
9934 }
9935 case ButtonRelease:
9936 {
9937 if (event.xbutton.button != Button1)
9938 break;
9939 if ((event.xbutton.window != windows->image.id) &&
9940 (event.xbutton.window != windows->magnify.id))
9941 break;
9942 /*
9943 Update colormap information.
9944 */
9945 x=event.xbutton.x;
9946 y=event.xbutton.y;
9947 XConfigureImageColormap(display,resource_info,windows,*image,exception);
9948 (void) XConfigureImage(display,resource_info,windows,*image,exception);
9949 XInfoWidget(display,windows,text);
9950 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9951 state&=(unsigned int) (~UpdateConfigurationState);
9952 break;
9953 }
9954 case Expose:
9955 break;
9956 case KeyPress:
9957 {
9958 char
9959 command[MagickPathExtent];
9960
9961 KeySym
9962 key_symbol;
9963
9964 if (event.xkey.window == windows->magnify.id)
9965 {
9966 Window
9967 window;
9968
9969 window=windows->magnify.id;
9970 while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9971 }
9972 if (event.xkey.window != windows->image.id)
9973 break;
9974 /*
9975 Respond to a user key press.
9976 */
9977 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9978 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9979 switch ((int) key_symbol)
9980 {
9981 case XK_Escape:
9982 case XK_F20:
9983 {
9984 /*
9985 Prematurely exit.
9986 */
9987 state|=ExitState;
9988 break;
9989 }
9990 case XK_F1:
9991 case XK_Help:
9992 {
9993 XTextViewHelp(display,resource_info,windows,MagickFalse,
9994 "Help Viewer - Matte Edit",ImageMatteEditHelp);
9995 break;
9996 }
9997 default:
9998 {
9999 (void) XBell(display,0);
10000 break;
10001 }
10002 }
10003 break;
10004 }
10005 case MotionNotify:
10006 {
10007 /*
10008 Map and unmap Info widget as cursor crosses its boundaries.
10009 */
10010 x=event.xmotion.x;
10011 y=event.xmotion.y;
10012 if (windows->info.mapped != MagickFalse)
10013 {
10014 if ((x < (windows->info.x+(int) windows->info.width)) &&
10015 (y < (windows->info.y+(int) windows->info.height)))
10016 (void) XWithdrawWindow(display,windows->info.id,
10017 windows->info.screen);
10018 }
10019 else
10020 if ((x > (windows->info.x+(int) windows->info.width)) ||
10021 (y > (windows->info.y+(int) windows->info.height)))
10022 (void) XMapWindow(display,windows->info.id);
10023 break;
10024 }
10025 default:
10026 break;
10027 }
10028 if (event.xany.window == windows->magnify.id)
10029 {
10030 x=windows->magnify.x-windows->image.x;
10031 y=windows->magnify.y-windows->image.y;
10032 }
10033 x_offset=x;
10034 y_offset=y;
10035 if ((state & UpdateConfigurationState) != 0)
10036 {
10037 CacheView
10038 *image_view;
10039
10040 int
10041 x,
10042 y;
10043
10044 /*
10045 Matte edit is relative to image configuration.
10046 */
10047 (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10048 MagickTrue);
10049 XPutPixel(windows->image.ximage,x_offset,y_offset,
10050 windows->pixel_info->background_color.pixel);
10051 width=(unsigned int) (*image)->columns;
10052 height=(unsigned int) (*image)->rows;
10053 x=0;
10054 y=0;
10055 if (windows->image.crop_geometry != (char *) NULL)
10056 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10057 &height);
10058 x_offset=((int) width*(windows->image.x+x_offset)/
10059 windows->image.ximage->width+x);
10060 y_offset=((int) height*(windows->image.y+y_offset)/
10061 windows->image.ximage->height+y);
10062 if ((x_offset < 0) || (y_offset < 0))
10063 continue;
10064 if ((x_offset >= (int) (*image)->columns) ||
10065 (y_offset >= (int) (*image)->rows))
10066 continue;
10067 if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10068 return(MagickFalse);
10069 if ((*image)->alpha_trait == UndefinedPixelTrait)
10070 (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
10071 image_view=AcquireAuthenticCacheView(*image,exception);
10072 switch (method)
10073 {
10074 case PointMethod:
10075 default:
10076 {
10077 /*
10078 Update matte information using point algorithm.
10079 */
10080 q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10081 (ssize_t) y_offset,1,1,exception);
10082 if (q == (Quantum *) NULL)
10083 break;
10084 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10085 (void) SyncCacheViewAuthenticPixels(image_view,exception);
10086 break;
10087 }
10088 case ReplaceMethod:
10089 {
10090 PixelInfo
10091 pixel,
10092 target;
10093
10094 /*
10095 Update matte information using replace algorithm.
10096 */
10097 (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
10098 x_offset,(ssize_t) y_offset,&target,exception);
10099 for (y=0; y < (int) (*image)->rows; y++)
10100 {
10101 q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10102 (*image)->columns,1,exception);
10103 if (q == (Quantum *) NULL)
10104 break;
10105 for (x=0; x < (int) (*image)->columns; x++)
10106 {
10107 GetPixelInfoPixel(*image,q,&pixel);
10108 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10109 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10110 q+=GetPixelChannels(*image);
10111 }
10112 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10113 break;
10114 }
10115 break;
10116 }
10117 case FloodfillMethod:
10118 case FillToBorderMethod:
10119 {
10120 ChannelType
10121 channel_mask;
10122
10123 DrawInfo
10124 *draw_info;
10125
10126 PixelInfo
10127 target;
10128
10129 /*
10130 Update matte information using floodfill algorithm.
10131 */
10132 (void) GetOneVirtualPixelInfo(*image,
10133 GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10134 y_offset,&target,exception);
10135 if (method == FillToBorderMethod)
10136 {
10137 target.red=(double) ScaleShortToQuantum(
10138 border_color.red);
10139 target.green=(double) ScaleShortToQuantum(
10140 border_color.green);
10141 target.blue=(double) ScaleShortToQuantum(
10142 border_color.blue);
10143 }
10144 draw_info=CloneDrawInfo(resource_info->image_info,
10145 (DrawInfo *) NULL);
10146 draw_info->fill.alpha=(double) ClampToQuantum(
10147 StringToDouble(matte,(char **) NULL));
10148 channel_mask=SetImageChannelMask(*image,AlphaChannel);
10149 (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10150 x_offset,(ssize_t) y_offset,
10151 method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
10152 (void) SetPixelChannelMask(*image,channel_mask);
10153 draw_info=DestroyDrawInfo(draw_info);
10154 break;
10155 }
10156 case ResetMethod:
10157 {
10158 /*
10159 Update matte information using reset algorithm.
10160 */
10161 if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10162 return(MagickFalse);
10163 for (y=0; y < (int) (*image)->rows; y++)
10164 {
10165 q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10166 (*image)->columns,1,exception);
10167 if (q == (Quantum *) NULL)
10168 break;
10169 for (x=0; x < (int) (*image)->columns; x++)
10170 {
10171 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10172 q+=GetPixelChannels(*image);
10173 }
10174 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10175 break;
10176 }
10177 if (StringToLong(matte) == (long) OpaqueAlpha)
10178 (*image)->alpha_trait=UndefinedPixelTrait;
10179 break;
10180 }
10181 }
10182 image_view=DestroyCacheView(image_view);
10183 state&=(unsigned int) (~UpdateConfigurationState);
10184 }
10185 } while ((state & ExitState) == 0);
10186 (void) XSelectInput(display,windows->image.id,
10187 windows->image.attributes.event_mask);
10188 XSetCursorState(display,windows,MagickFalse);
10189 (void) XFreeCursor(display,cursor);
10190 return(MagickTrue);
10191}
10192
10193/*
10194%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10195% %
10196% %
10197% %
10198+ X O p e n I m a g e %
10199% %
10200% %
10201% %
10202%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10203%
10204% XOpenImage() loads an image from a file.
10205%
10206% The format of the XOpenImage method is:
10207%
10208% Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10209% XWindows *windows,const unsigned int command)
10210%
10211% A description of each parameter follows:
10212%
10213% o display: Specifies a connection to an X server; returned from
10214% XOpenDisplay.
10215%
10216% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10217%
10218% o windows: Specifies a pointer to a XWindows structure.
10219%
10220% o command: A value other than zero indicates that the file is selected
10221% from the command line argument list.
10222%
10223*/
10224static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10225 XWindows *windows,const MagickBooleanType command)
10226{
10227 const MagickInfo
10228 *magick_info;
10229
10231 *exception;
10232
10233 Image
10234 *nexus;
10235
10236 ImageInfo
10237 *image_info;
10238
10239 static char
10240 filename[MagickPathExtent] = "\0";
10241
10242 /*
10243 Request file name from user.
10244 */
10245 if (command == MagickFalse)
10246 XFileBrowserWidget(display,windows,"Open",filename);
10247 else
10248 {
10249 char
10250 **filelist,
10251 **files;
10252
10253 int
10254 count,
10255 status;
10256
10257 int
10258 i,
10259 j;
10260
10261 /*
10262 Select next image from the command line.
10263 */
10264 status=XGetCommand(display,windows->image.id,&files,&count);
10265 if (status == 0)
10266 {
10267 ThrowXWindowException(XServerError,"UnableToGetProperty","...");
10268 return((Image *) NULL);
10269 }
10270 filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10271 if (filelist == (char **) NULL)
10272 {
10273 ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
10274 "...");
10275 (void) XFreeStringList(files);
10276 return((Image *) NULL);
10277 }
10278 j=0;
10279 for (i=1; i < count; i++)
10280 if (*files[i] != '-')
10281 filelist[j++]=files[i];
10282 filelist[j]=(char *) NULL;
10283 XListBrowserWidget(display,windows,&windows->widget,
10284 (const char **) filelist,"Load","Select Image to Load:",filename);
10285 filelist=(char **) RelinquishMagickMemory(filelist);
10286 (void) XFreeStringList(files);
10287 }
10288 if (*filename == '\0')
10289 return((Image *) NULL);
10290 image_info=CloneImageInfo(resource_info->image_info);
10291 (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10292 (void *) NULL);
10293 (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
10294 exception=AcquireExceptionInfo();
10295 (void) SetImageInfo(image_info,0,exception);
10296 if (LocaleCompare(image_info->magick,"X") == 0)
10297 {
10298 char
10299 seconds[MagickPathExtent];
10300
10301 /*
10302 User may want to delay the X server screen grab.
10303 */
10304 (void) CopyMagickString(seconds,"0",MagickPathExtent);
10305 (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10306 seconds);
10307 if (*seconds == '\0')
10308 return((Image *) NULL);
10309 XDelay(display,(size_t) (1000*StringToLong(seconds)));
10310 }
10311 magick_info=GetMagickInfo(image_info->magick,exception);
10312 if ((magick_info != (const MagickInfo *) NULL) &&
10313 GetMagickRawSupport(magick_info) == MagickTrue)
10314 {
10315 char
10316 geometry[MagickPathExtent];
10317
10318 /*
10319 Request image size from the user.
10320 */
10321 (void) CopyMagickString(geometry,"512x512",MagickPathExtent);
10322 if (image_info->size != (char *) NULL)
10323 (void) CopyMagickString(geometry,image_info->size,MagickPathExtent);
10324 (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10325 geometry);
10326 (void) CloneString(&image_info->size,geometry);
10327 }
10328 /*
10329 Load the image.
10330 */
10331 XSetCursorState(display,windows,MagickTrue);
10332 XCheckRefreshWindows(display,windows);
10333 (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
10334 nexus=ReadImage(image_info,exception);
10335 CatchException(exception);
10336 XSetCursorState(display,windows,MagickFalse);
10337 if (nexus != (Image *) NULL)
10338 XClientMessage(display,windows->image.id,windows->im_protocols,
10339 windows->im_next_image,CurrentTime);
10340 else
10341 {
10342 char
10343 *text,
10344 **textlist;
10345
10346 /*
10347 Unknown image format.
10348 */
10349 text=FileToString(filename,~0UL,exception);
10350 if (text == (char *) NULL)
10351 return((Image *) NULL);
10352 textlist=StringToList(text);
10353 if (textlist != (char **) NULL)
10354 {
10355 char
10356 title[MagickPathExtent];
10357
10358 int
10359 i;
10360
10361 (void) FormatLocaleString(title,MagickPathExtent,
10362 "Unknown format: %s",filename);
10363 XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10364 (const char **) textlist);
10365 for (i=0; textlist[i] != (char *) NULL; i++)
10366 textlist[i]=DestroyString(textlist[i]);
10367 textlist=(char **) RelinquishMagickMemory(textlist);
10368 }
10369 text=DestroyString(text);
10370 }
10371 exception=DestroyExceptionInfo(exception);
10372 image_info=DestroyImageInfo(image_info);
10373 return(nexus);
10374}
10375
10376/*
10377%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10378% %
10379% %
10380% %
10381+ X P a n I m a g e %
10382% %
10383% %
10384% %
10385%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10386%
10387% XPanImage() pans the image until the mouse button is released.
10388%
10389% The format of the XPanImage method is:
10390%
10391% void XPanImage(Display *display,XWindows *windows,XEvent *event,
10392% ExceptionInfo *exception)
10393%
10394% A description of each parameter follows:
10395%
10396% o display: Specifies a connection to an X server; returned from
10397% XOpenDisplay.
10398%
10399% o windows: Specifies a pointer to a XWindows structure.
10400%
10401% o event: Specifies a pointer to a XEvent structure. If it is NULL,
10402% the entire image is refreshed.
10403%
10404% o exception: return any errors or warnings in this structure.
10405%
10406*/
10407static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10408 ExceptionInfo *exception)
10409{
10410 char
10411 text[MagickPathExtent];
10412
10413 Cursor
10414 cursor;
10415
10416 double
10417 x_factor,
10418 y_factor;
10419
10421 pan_info;
10422
10423 size_t
10424 state;
10425
10426 /*
10427 Define cursor.
10428 */
10429 if ((windows->image.ximage->width > (int) windows->image.width) &&
10430 (windows->image.ximage->height > (int) windows->image.height))
10431 cursor=XCreateFontCursor(display,XC_fleur);
10432 else
10433 if (windows->image.ximage->width > (int) windows->image.width)
10434 cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10435 else
10436 if (windows->image.ximage->height > (int) windows->image.height)
10437 cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10438 else
10439 cursor=XCreateFontCursor(display,XC_arrow);
10440 (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10441 /*
10442 Pan image as pointer moves until the mouse button is released.
10443 */
10444 x_factor=(double) windows->image.ximage->width/windows->pan.width;
10445 y_factor=(double) windows->image.ximage->height/windows->pan.height;
10446 pan_info.width=windows->pan.width*windows->image.width/
10447 (unsigned int) windows->image.ximage->width;
10448 pan_info.height=windows->pan.height*windows->image.height/
10449 (unsigned int) windows->image.ximage->height;
10450 pan_info.x=0;
10451 pan_info.y=0;
10452 state=UpdateConfigurationState;
10453 do
10454 {
10455 switch (event->type)
10456 {
10457 case ButtonPress:
10458 {
10459 /*
10460 User choose an initial pan location.
10461 */
10462 pan_info.x=(ssize_t) event->xbutton.x;
10463 pan_info.y=(ssize_t) event->xbutton.y;
10464 state|=UpdateConfigurationState;
10465 break;
10466 }
10467 case ButtonRelease:
10468 {
10469 /*
10470 User has finished panning the image.
10471 */
10472 pan_info.x=(ssize_t) event->xbutton.x;
10473 pan_info.y=(ssize_t) event->xbutton.y;
10474 state|=UpdateConfigurationState | ExitState;
10475 break;
10476 }
10477 case MotionNotify:
10478 {
10479 pan_info.x=(ssize_t) event->xmotion.x;
10480 pan_info.y=(ssize_t) event->xmotion.y;
10481 state|=UpdateConfigurationState;
10482 }
10483 default:
10484 break;
10485 }
10486 if ((state & UpdateConfigurationState) != 0)
10487 {
10488 /*
10489 Check boundary conditions.
10490 */
10491 if (pan_info.x < (ssize_t) (pan_info.width/2))
10492 pan_info.x=0;
10493 else
10494 pan_info.x=(x_factor*(pan_info.x-((int) pan_info.width/2)));
10495 if (pan_info.x < 0)
10496 pan_info.x=0;
10497 else
10498 if ((int) (pan_info.x+windows->image.width) >
10499 windows->image.ximage->width)
10500 pan_info.x=windows->image.ximage->width-(int) windows->image.width;
10501 if (pan_info.y < (ssize_t) (pan_info.height/2))
10502 pan_info.y=0;
10503 else
10504 pan_info.y=(y_factor*(pan_info.y-((int) pan_info.height/2)));
10505 if (pan_info.y < 0)
10506 pan_info.y=0;
10507 else
10508 if ((int) (pan_info.y+windows->image.height) >
10509 windows->image.ximage->height)
10510 pan_info.y=windows->image.ximage->height-(int)
10511 windows->image.height;
10512 if ((windows->image.x != (int) pan_info.x) ||
10513 (windows->image.y != (int) pan_info.y))
10514 {
10515 /*
10516 Display image pan offset.
10517 */
10518 windows->image.x=(int) pan_info.x;
10519 windows->image.y=(int) pan_info.y;
10520 (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
10521 windows->image.width,windows->image.height,windows->image.x,
10522 windows->image.y);
10523 XInfoWidget(display,windows,text);
10524 /*
10525 Refresh Image window.
10526 */
10527 XDrawPanRectangle(display,windows);
10528 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10529 }
10530 state&=(unsigned int) (~UpdateConfigurationState);
10531 }
10532 /*
10533 Wait for next event.
10534 */
10535 if ((state & ExitState) == 0)
10536 XScreenEvent(display,windows,event,exception);
10537 } while ((state & ExitState) == 0);
10538 /*
10539 Restore cursor.
10540 */
10541 (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10542 (void) XFreeCursor(display,cursor);
10543 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10544}
10545
10546/*
10547%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10548% %
10549% %
10550% %
10551+ X P a s t e I m a g e %
10552% %
10553% %
10554% %
10555%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10556%
10557% XPasteImage() pastes an image previously saved with XCropImage in the X
10558% window image at a location the user chooses with the pointer.
10559%
10560% The format of the XPasteImage method is:
10561%
10562% MagickBooleanType XPasteImage(Display *display,
10563% XResourceInfo *resource_info,XWindows *windows,Image *image,
10564% ExceptionInfo *exception)
10565%
10566% A description of each parameter follows:
10567%
10568% o display: Specifies a connection to an X server; returned from
10569% XOpenDisplay.
10570%
10571% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10572%
10573% o windows: Specifies a pointer to a XWindows structure.
10574%
10575% o image: the image; returned from ReadImage.
10576%
10577% o exception: return any errors or warnings in this structure.
10578%
10579*/
10580static MagickBooleanType XPasteImage(Display *display,
10581 XResourceInfo *resource_info,XWindows *windows,Image *image,
10582 ExceptionInfo *exception)
10583{
10584 const char
10585 *const PasteMenu[] =
10586 {
10587 "Operator",
10588 "Help",
10589 "Dismiss",
10590 (char *) NULL
10591 };
10592
10593 static const ModeType
10594 PasteCommands[] =
10595 {
10596 PasteOperatorsCommand,
10597 PasteHelpCommand,
10598 PasteDismissCommand
10599 };
10600
10601 static CompositeOperator
10602 compose = CopyCompositeOp;
10603
10604 char
10605 text[MagickPathExtent];
10606
10607 Cursor
10608 cursor;
10609
10610 Image
10611 *paste_image;
10612
10613 int
10614 entry,
10615 id,
10616 x,
10617 y;
10618
10619 double
10620 scale_factor;
10621
10623 highlight_info,
10624 paste_info;
10625
10626 unsigned int
10627 height,
10628 width;
10629
10630 size_t
10631 state;
10632
10633 XEvent
10634 event;
10635
10636 /*
10637 Copy image.
10638 */
10639 if (resource_info->copy_image == (Image *) NULL)
10640 return(MagickFalse);
10641 paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10642 if (paste_image == (Image *) NULL)
10643 return(MagickFalse);
10644 /*
10645 Map Command widget.
10646 */
10647 (void) CloneString(&windows->command.name,"Paste");
10648 windows->command.data=1;
10649 (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10650 (void) XMapRaised(display,windows->command.id);
10651 XClientMessage(display,windows->image.id,windows->im_protocols,
10652 windows->im_update_widget,CurrentTime);
10653 /*
10654 Track pointer until button 1 is pressed.
10655 */
10656 XSetCursorState(display,windows,MagickFalse);
10657 XQueryPosition(display,windows->image.id,&x,&y);
10658 (void) XSelectInput(display,windows->image.id,
10659 windows->image.attributes.event_mask | PointerMotionMask);
10660 paste_info.x=(ssize_t) windows->image.x+x;
10661 paste_info.y=(ssize_t) windows->image.y+y;
10662 paste_info.width=0;
10663 paste_info.height=0;
10664 cursor=XCreateFontCursor(display,XC_ul_angle);
10665 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10666 state=DefaultState;
10667 do
10668 {
10669 if (windows->info.mapped != MagickFalse)
10670 {
10671 /*
10672 Display pointer position.
10673 */
10674 (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
10675 (long) paste_info.x,(long) paste_info.y);
10676 XInfoWidget(display,windows,text);
10677 }
10678 highlight_info=paste_info;
10679 highlight_info.x=paste_info.x-windows->image.x;
10680 highlight_info.y=paste_info.y-windows->image.y;
10681 XHighlightRectangle(display,windows->image.id,
10682 windows->image.highlight_context,&highlight_info);
10683 /*
10684 Wait for next event.
10685 */
10686 XScreenEvent(display,windows,&event,exception);
10687 XHighlightRectangle(display,windows->image.id,
10688 windows->image.highlight_context,&highlight_info);
10689 if (event.xany.window == windows->command.id)
10690 {
10691 /*
10692 Select a command from the Command widget.
10693 */
10694 id=XCommandWidget(display,windows,PasteMenu,&event);
10695 if (id < 0)
10696 continue;
10697 switch (PasteCommands[id])
10698 {
10699 case PasteOperatorsCommand:
10700 {
10701 char
10702 command[MagickPathExtent],
10703 **operators;
10704
10705 /*
10706 Select a command from the pop-up menu.
10707 */
10708 operators=GetCommandOptions(MagickComposeOptions);
10709 if (operators == (char **) NULL)
10710 break;
10711 entry=XMenuWidget(display,windows,PasteMenu[id],
10712 (const char **) operators,command);
10713 if (entry >= 0)
10714 compose=(CompositeOperator) ParseCommandOption(
10715 MagickComposeOptions,MagickFalse,operators[entry]);
10716 operators=DestroyStringList(operators);
10717 break;
10718 }
10719 case PasteHelpCommand:
10720 {
10721 XTextViewHelp(display,resource_info,windows,MagickFalse,
10722 "Help Viewer - Image Composite",ImagePasteHelp);
10723 break;
10724 }
10725 case PasteDismissCommand:
10726 {
10727 /*
10728 Prematurely exit.
10729 */
10730 state|=EscapeState;
10731 state|=ExitState;
10732 break;
10733 }
10734 default:
10735 break;
10736 }
10737 continue;
10738 }
10739 switch (event.type)
10740 {
10741 case ButtonPress:
10742 {
10743 if (resource_info->debug != MagickFalse)
10744 (void) LogMagickEvent(X11Event,GetMagickModule(),
10745 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10746 event.xbutton.button,event.xbutton.x,event.xbutton.y);
10747 if (event.xbutton.button != Button1)
10748 break;
10749 if (event.xbutton.window != windows->image.id)
10750 break;
10751 /*
10752 Paste rectangle is relative to image configuration.
10753 */
10754 width=(unsigned int) image->columns;
10755 height=(unsigned int) image->rows;
10756 x=0;
10757 y=0;
10758 if (windows->image.crop_geometry != (char *) NULL)
10759 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10760 &width,&height);
10761 scale_factor=(double) windows->image.ximage->width/width;
10762 paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10763 scale_factor=(double) windows->image.ximage->height/height;
10764 paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10765 (void) XCheckDefineCursor(display,windows->image.id,cursor);
10766 paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10767 paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10768 break;
10769 }
10770 case ButtonRelease:
10771 {
10772 if (resource_info->debug != MagickFalse)
10773 (void) LogMagickEvent(X11Event,GetMagickModule(),
10774 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10775 event.xbutton.button,event.xbutton.x,event.xbutton.y);
10776 if (event.xbutton.button != Button1)
10777 break;
10778 if (event.xbutton.window != windows->image.id)
10779 break;
10780 if ((paste_info.width != 0) && (paste_info.height != 0))
10781 {
10782 /*
10783 User has selected the location of the paste image.
10784 */
10785 paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10786 paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10787 state|=ExitState;
10788 }
10789 break;
10790 }
10791 case Expose:
10792 break;
10793 case KeyPress:
10794 {
10795 char
10796 command[MagickPathExtent];
10797
10798 KeySym
10799 key_symbol;
10800
10801 int
10802 length;
10803
10804 if (event.xkey.window != windows->image.id)
10805 break;
10806 /*
10807 Respond to a user key press.
10808 */
10809 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10810 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10811 *(command+length)='\0';
10812 if (resource_info->debug != MagickFalse)
10813 (void) LogMagickEvent(X11Event,GetMagickModule(),
10814 "Key press: 0x%lx (%s)",(long) key_symbol,command);
10815 switch ((int) key_symbol)
10816 {
10817 case XK_Escape:
10818 case XK_F20:
10819 {
10820 /*
10821 Prematurely exit.
10822 */
10823 paste_image=DestroyImage(paste_image);
10824 state|=EscapeState;
10825 state|=ExitState;
10826 break;
10827 }
10828 case XK_F1:
10829 case XK_Help:
10830 {
10831 (void) XSetFunction(display,windows->image.highlight_context,
10832 GXcopy);
10833 XTextViewHelp(display,resource_info,windows,MagickFalse,
10834 "Help Viewer - Image Composite",ImagePasteHelp);
10835 (void) XSetFunction(display,windows->image.highlight_context,
10836 GXinvert);
10837 break;
10838 }
10839 default:
10840 {
10841 (void) XBell(display,0);
10842 break;
10843 }
10844 }
10845 break;
10846 }
10847 case MotionNotify:
10848 {
10849 /*
10850 Map and unmap Info widget as text cursor crosses its boundaries.
10851 */
10852 x=event.xmotion.x;
10853 y=event.xmotion.y;
10854 if (windows->info.mapped != MagickFalse)
10855 {
10856 if ((x < (windows->info.x+(int) windows->info.width)) &&
10857 (y < (windows->info.y+(int) windows->info.height)))
10858 (void) XWithdrawWindow(display,windows->info.id,
10859 windows->info.screen);
10860 }
10861 else
10862 if ((x > (windows->info.x+(int) windows->info.width)) ||
10863 (y > (windows->info.y+(int) windows->info.height)))
10864 (void) XMapWindow(display,windows->info.id);
10865 paste_info.x=(ssize_t) windows->image.x+x;
10866 paste_info.y=(ssize_t) windows->image.y+y;
10867 break;
10868 }
10869 default:
10870 {
10871 if (resource_info->debug != MagickFalse)
10872 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10873 event.type);
10874 break;
10875 }
10876 }
10877 } while ((state & ExitState) == 0);
10878 (void) XSelectInput(display,windows->image.id,
10879 windows->image.attributes.event_mask);
10880 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10881 XSetCursorState(display,windows,MagickFalse);
10882 (void) XFreeCursor(display,cursor);
10883 if ((state & EscapeState) != 0)
10884 return(MagickTrue);
10885 /*
10886 Image pasting is relative to image configuration.
10887 */
10888 XSetCursorState(display,windows,MagickTrue);
10889 XCheckRefreshWindows(display,windows);
10890 width=(unsigned int) image->columns;
10891 height=(unsigned int) image->rows;
10892 x=0;
10893 y=0;
10894 if (windows->image.crop_geometry != (char *) NULL)
10895 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10896 scale_factor=(double) width/windows->image.ximage->width;
10897 paste_info.x+=x;
10898 paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10899 paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10900 scale_factor=(double) height/windows->image.ximage->height;
10901 paste_info.y+=y;
10902 paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10903 paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10904 /*
10905 Paste image with X Image window.
10906 */
10907 (void) CompositeImage(image,paste_image,compose,MagickTrue,paste_info.x,
10908 paste_info.y,exception);
10909 paste_image=DestroyImage(paste_image);
10910 XSetCursorState(display,windows,MagickFalse);
10911 /*
10912 Update image colormap.
10913 */
10914 XConfigureImageColormap(display,resource_info,windows,image,exception);
10915 (void) XConfigureImage(display,resource_info,windows,image,exception);
10916 return(MagickTrue);
10917}
10918
10919/*
10920%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10921% %
10922% %
10923% %
10924+ X P r i n t I m a g e %
10925% %
10926% %
10927% %
10928%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10929%
10930% XPrintImage() prints an image to a Postscript printer.
10931%
10932% The format of the XPrintImage method is:
10933%
10934% MagickBooleanType XPrintImage(Display *display,
10935% XResourceInfo *resource_info,XWindows *windows,Image *image,
10936% ExceptionInfo *exception)
10937%
10938% A description of each parameter follows:
10939%
10940% o display: Specifies a connection to an X server; returned from
10941% XOpenDisplay.
10942%
10943% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10944%
10945% o windows: Specifies a pointer to a XWindows structure.
10946%
10947% o image: the image.
10948%
10949% o exception: return any errors or warnings in this structure.
10950%
10951*/
10952static MagickBooleanType XPrintImage(Display *display,
10953 XResourceInfo *resource_info,XWindows *windows,Image *image,
10954 ExceptionInfo *exception)
10955{
10956 char
10957 filename[MagickPathExtent],
10958 geometry[MagickPathExtent];
10959
10960 const char
10961 *const PageSizes[] =
10962 {
10963 "Letter",
10964 "Tabloid",
10965 "Ledger",
10966 "Legal",
10967 "Statement",
10968 "Executive",
10969 "A3",
10970 "A4",
10971 "A5",
10972 "B4",
10973 "B5",
10974 "Folio",
10975 "Quarto",
10976 "10x14",
10977 (char *) NULL
10978 };
10979
10980 Image
10981 *print_image;
10982
10983 ImageInfo
10984 *image_info;
10985
10986 MagickStatusType
10987 status;
10988
10989 /*
10990 Request Postscript page geometry from user.
10991 */
10992 image_info=CloneImageInfo(resource_info->image_info);
10993 (void) FormatLocaleString(geometry,MagickPathExtent,"Letter");
10994 if (image_info->page != (char *) NULL)
10995 (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
10996 XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10997 "Select Postscript Page Geometry:",geometry);
10998 if (*geometry == '\0')
10999 return(MagickTrue);
11000 image_info->page=GetPageGeometry(geometry);
11001 /*
11002 Apply image transforms.
11003 */
11004 XSetCursorState(display,windows,MagickTrue);
11005 XCheckRefreshWindows(display,windows);
11006 print_image=CloneImage(image,0,0,MagickTrue,exception);
11007 if (print_image == (Image *) NULL)
11008 return(MagickFalse);
11009 (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
11010 windows->image.ximage->width,windows->image.ximage->height);
11011 (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11012 exception);
11013 /*
11014 Print image.
11015 */
11016 (void) AcquireUniqueFilename(filename);
11017 (void) FormatLocaleString(print_image->filename,MagickPathExtent,"print:%s",
11018 filename);
11019 status=WriteImage(image_info,print_image,exception);
11020 (void) RelinquishUniqueFileResource(filename);
11021 print_image=DestroyImage(print_image);
11022 image_info=DestroyImageInfo(image_info);
11023 XSetCursorState(display,windows,MagickFalse);
11024 return(status != 0 ? MagickTrue : MagickFalse);
11025}
11026
11027/*
11028%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11029% %
11030% %
11031% %
11032+ X R O I I m a g e %
11033% %
11034% %
11035% %
11036%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11037%
11038% XROIImage() applies an image processing technique to a region of interest.
11039%
11040% The format of the XROIImage method is:
11041%
11042% MagickBooleanType XROIImage(Display *display,
11043% XResourceInfo *resource_info,XWindows *windows,Image **image,
11044% ExceptionInfo *exception)
11045%
11046% A description of each parameter follows:
11047%
11048% o display: Specifies a connection to an X server; returned from
11049% XOpenDisplay.
11050%
11051% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11052%
11053% o windows: Specifies a pointer to a XWindows structure.
11054%
11055% o image: the image; returned from ReadImage.
11056%
11057% o exception: return any errors or warnings in this structure.
11058%
11059*/
11060static MagickBooleanType XROIImage(Display *display,
11061 XResourceInfo *resource_info,XWindows *windows,Image **image,
11062 ExceptionInfo *exception)
11063{
11064#define ApplyMenus 7
11065
11066 const char
11067 *const ROIMenu[] =
11068 {
11069 "Help",
11070 "Dismiss",
11071 (char *) NULL
11072 },
11073 *const ApplyMenu[] =
11074 {
11075 "File",
11076 "Edit",
11077 "Transform",
11078 "Enhance",
11079 "Effects",
11080 "F/X",
11081 "Miscellany",
11082 "Help",
11083 "Dismiss",
11084 (char *) NULL
11085 },
11086 *const FileMenu[] =
11087 {
11088 "Save...",
11089 "Print...",
11090 (char *) NULL
11091 },
11092 *const EditMenu[] =
11093 {
11094 "Undo",
11095 "Redo",
11096 (char *) NULL
11097 },
11098 *const TransformMenu[] =
11099 {
11100 "Flop",
11101 "Flip",
11102 "Rotate Right",
11103 "Rotate Left",
11104 (char *) NULL
11105 },
11106 *const EnhanceMenu[] =
11107 {
11108 "Hue...",
11109 "Saturation...",
11110 "Brightness...",
11111 "Gamma...",
11112 "Spiff",
11113 "Dull",
11114 "Contrast Stretch...",
11115 "Sigmoidal Contrast...",
11116 "Normalize",
11117 "Equalize",
11118 "Negate",
11119 "Grayscale",
11120 "Map...",
11121 "Quantize...",
11122 (char *) NULL
11123 },
11124 *const EffectsMenu[] =
11125 {
11126 "Despeckle",
11127 "Emboss",
11128 "Reduce Noise",
11129 "Add Noise",
11130 "Sharpen...",
11131 "Blur...",
11132 "Threshold...",
11133 "Edge Detect...",
11134 "Spread...",
11135 "Shade...",
11136 "Raise...",
11137 "Segment...",
11138 (char *) NULL
11139 },
11140 *const FXMenu[] =
11141 {
11142 "Solarize...",
11143 "Sepia Tone...",
11144 "Swirl...",
11145 "Implode...",
11146 "Vignette...",
11147 "Wave...",
11148 "Oil Paint...",
11149 "Charcoal Draw...",
11150 (char *) NULL
11151 },
11152 *const MiscellanyMenu[] =
11153 {
11154 "Image Info",
11155 "Zoom Image",
11156 "Show Preview...",
11157 "Show Histogram",
11158 "Show Matte",
11159 (char *) NULL
11160 };
11161
11162 const char
11163 *const *Menus[ApplyMenus] =
11164 {
11165 FileMenu,
11166 EditMenu,
11167 TransformMenu,
11168 EnhanceMenu,
11169 EffectsMenu,
11170 FXMenu,
11171 MiscellanyMenu
11172 };
11173
11174 static const DisplayCommand
11175 ApplyCommands[] =
11176 {
11177 NullCommand,
11178 NullCommand,
11179 NullCommand,
11180 NullCommand,
11181 NullCommand,
11182 NullCommand,
11183 NullCommand,
11184 HelpCommand,
11185 QuitCommand
11186 },
11187 FileCommands[] =
11188 {
11189 SaveCommand,
11190 PrintCommand
11191 },
11192 EditCommands[] =
11193 {
11194 UndoCommand,
11195 RedoCommand
11196 },
11197 TransformCommands[] =
11198 {
11199 FlopCommand,
11200 FlipCommand,
11201 RotateRightCommand,
11202 RotateLeftCommand
11203 },
11204 EnhanceCommands[] =
11205 {
11206 HueCommand,
11207 SaturationCommand,
11208 BrightnessCommand,
11209 GammaCommand,
11210 SpiffCommand,
11211 DullCommand,
11212 ContrastStretchCommand,
11213 SigmoidalContrastCommand,
11214 NormalizeCommand,
11215 EqualizeCommand,
11216 NegateCommand,
11217 GrayscaleCommand,
11218 MapCommand,
11219 QuantizeCommand
11220 },
11221 EffectsCommands[] =
11222 {
11223 DespeckleCommand,
11224 EmbossCommand,
11225 ReduceNoiseCommand,
11226 AddNoiseCommand,
11227 SharpenCommand,
11228 BlurCommand,
11229 ThresholdCommand,
11230 EdgeDetectCommand,
11231 SpreadCommand,
11232 ShadeCommand,
11233 RaiseCommand,
11234 SegmentCommand
11235 },
11236 FXCommands[] =
11237 {
11238 SolarizeCommand,
11239 SepiaToneCommand,
11240 SwirlCommand,
11241 ImplodeCommand,
11242 VignetteCommand,
11243 WaveCommand,
11244 OilPaintCommand,
11245 CharcoalDrawCommand
11246 },
11247 MiscellanyCommands[] =
11248 {
11249 InfoCommand,
11250 ZoomCommand,
11251 ShowPreviewCommand,
11252 ShowHistogramCommand,
11253 ShowMatteCommand
11254 },
11255 ROICommands[] =
11256 {
11257 ROIHelpCommand,
11258 ROIDismissCommand
11259 };
11260
11261 static const DisplayCommand
11262 *Commands[ApplyMenus] =
11263 {
11264 FileCommands,
11265 EditCommands,
11266 TransformCommands,
11267 EnhanceCommands,
11268 EffectsCommands,
11269 FXCommands,
11270 MiscellanyCommands
11271 };
11272
11273 char
11274 command[MagickPathExtent],
11275 text[MagickPathExtent];
11276
11277 DisplayCommand
11278 display_command;
11279
11280 Cursor
11281 cursor;
11282
11283 Image
11284 *roi_image;
11285
11286 int
11287 entry,
11288 id,
11289 x,
11290 y;
11291
11292 double
11293 scale_factor;
11294
11295 MagickProgressMonitor
11296 progress_monitor;
11297
11299 crop_info,
11300 highlight_info,
11301 roi_info;
11302
11303 unsigned int
11304 height,
11305 width;
11306
11307 size_t
11308 state;
11309
11310 XEvent
11311 event;
11312
11313 /*
11314 Map Command widget.
11315 */
11316 (void) CloneString(&windows->command.name,"ROI");
11317 windows->command.data=0;
11318 (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11319 (void) XMapRaised(display,windows->command.id);
11320 XClientMessage(display,windows->image.id,windows->im_protocols,
11321 windows->im_update_widget,CurrentTime);
11322 /*
11323 Track pointer until button 1 is pressed.
11324 */
11325 XQueryPosition(display,windows->image.id,&x,&y);
11326 (void) XSelectInput(display,windows->image.id,
11327 windows->image.attributes.event_mask | PointerMotionMask);
11328 roi_info.x=(ssize_t) windows->image.x+x;
11329 roi_info.y=(ssize_t) windows->image.y+y;
11330 roi_info.width=0;
11331 roi_info.height=0;
11332 cursor=XCreateFontCursor(display,XC_fleur);
11333 state=DefaultState;
11334 do
11335 {
11336 if (windows->info.mapped != MagickFalse)
11337 {
11338 /*
11339 Display pointer position.
11340 */
11341 (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
11342 (long) roi_info.x,(long) roi_info.y);
11343 XInfoWidget(display,windows,text);
11344 }
11345 /*
11346 Wait for next event.
11347 */
11348 XScreenEvent(display,windows,&event,exception);
11349 if (event.xany.window == windows->command.id)
11350 {
11351 /*
11352 Select a command from the Command widget.
11353 */
11354 id=XCommandWidget(display,windows,ROIMenu,&event);
11355 if (id < 0)
11356 continue;
11357 switch (ROICommands[id])
11358 {
11359 case ROIHelpCommand:
11360 {
11361 XTextViewHelp(display,resource_info,windows,MagickFalse,
11362 "Help Viewer - Region of Interest",ImageROIHelp);
11363 break;
11364 }
11365 case ROIDismissCommand:
11366 {
11367 /*
11368 Prematurely exit.
11369 */
11370 state|=EscapeState;
11371 state|=ExitState;
11372 break;
11373 }
11374 default:
11375 break;
11376 }
11377 continue;
11378 }
11379 switch (event.type)
11380 {
11381 case ButtonPress:
11382 {
11383 if (event.xbutton.button != Button1)
11384 break;
11385 if (event.xbutton.window != windows->image.id)
11386 break;
11387 /*
11388 Note first corner of region of interest rectangle-- exit loop.
11389 */
11390 (void) XCheckDefineCursor(display,windows->image.id,cursor);
11391 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11392 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11393 state|=ExitState;
11394 break;
11395 }
11396 case ButtonRelease:
11397 break;
11398 case Expose:
11399 break;
11400 case KeyPress:
11401 {
11402 KeySym
11403 key_symbol;
11404
11405 if (event.xkey.window != windows->image.id)
11406 break;
11407 /*
11408 Respond to a user key press.
11409 */
11410 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11411 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11412 switch ((int) key_symbol)
11413 {
11414 case XK_Escape:
11415 case XK_F20:
11416 {
11417 /*
11418 Prematurely exit.
11419 */
11420 state|=EscapeState;
11421 state|=ExitState;
11422 break;
11423 }
11424 case XK_F1:
11425 case XK_Help:
11426 {
11427 XTextViewHelp(display,resource_info,windows,MagickFalse,
11428 "Help Viewer - Region of Interest",ImageROIHelp);
11429 break;
11430 }
11431 default:
11432 {
11433 (void) XBell(display,0);
11434 break;
11435 }
11436 }
11437 break;
11438 }
11439 case MotionNotify:
11440 {
11441 /*
11442 Map and unmap Info widget as text cursor crosses its boundaries.
11443 */
11444 x=event.xmotion.x;
11445 y=event.xmotion.y;
11446 if (windows->info.mapped != MagickFalse)
11447 {
11448 if ((x < (windows->info.x+(int) windows->info.width)) &&
11449 (y < (windows->info.y+(int) windows->info.height)))
11450 (void) XWithdrawWindow(display,windows->info.id,
11451 windows->info.screen);
11452 }
11453 else
11454 if ((x > (windows->info.x+(int) windows->info.width)) ||
11455 (y > (windows->info.y+(int) windows->info.height)))
11456 (void) XMapWindow(display,windows->info.id);
11457 roi_info.x=(ssize_t) windows->image.x+x;
11458 roi_info.y=(ssize_t) windows->image.y+y;
11459 break;
11460 }
11461 default:
11462 break;
11463 }
11464 } while ((state & ExitState) == 0);
11465 (void) XSelectInput(display,windows->image.id,
11466 windows->image.attributes.event_mask);
11467 if ((state & EscapeState) != 0)
11468 {
11469 /*
11470 User want to exit without region of interest.
11471 */
11472 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11473 (void) XFreeCursor(display,cursor);
11474 return(MagickTrue);
11475 }
11476 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11477 do
11478 {
11479 /*
11480 Size rectangle as pointer moves until the mouse button is released.
11481 */
11482 x=(int) roi_info.x;
11483 y=(int) roi_info.y;
11484 roi_info.width=0;
11485 roi_info.height=0;
11486 state=DefaultState;
11487 do
11488 {
11489 highlight_info=roi_info;
11490 highlight_info.x=roi_info.x-windows->image.x;
11491 highlight_info.y=roi_info.y-windows->image.y;
11492 if ((highlight_info.width > 3) && (highlight_info.height > 3))
11493 {
11494 /*
11495 Display info and draw region of interest rectangle.
11496 */
11497 if (windows->info.mapped == MagickFalse)
11498 (void) XMapWindow(display,windows->info.id);
11499 (void) FormatLocaleString(text,MagickPathExtent,
11500 " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11501 roi_info.height,(double) roi_info.x,(double) roi_info.y);
11502 XInfoWidget(display,windows,text);
11503 XHighlightRectangle(display,windows->image.id,
11504 windows->image.highlight_context,&highlight_info);
11505 }
11506 else
11507 if (windows->info.mapped != MagickFalse)
11508 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11509 /*
11510 Wait for next event.
11511 */
11512 XScreenEvent(display,windows,&event,exception);
11513 if ((highlight_info.width > 3) && (highlight_info.height > 3))
11514 XHighlightRectangle(display,windows->image.id,
11515 windows->image.highlight_context,&highlight_info);
11516 switch (event.type)
11517 {
11518 case ButtonPress:
11519 {
11520 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11521 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11522 break;
11523 }
11524 case ButtonRelease:
11525 {
11526 /*
11527 User has committed to region of interest rectangle.
11528 */
11529 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11530 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11531 XSetCursorState(display,windows,MagickFalse);
11532 state|=ExitState;
11533 if (LocaleCompare(windows->command.name,"Apply") == 0)
11534 break;
11535 (void) CloneString(&windows->command.name,"Apply");
11536 windows->command.data=ApplyMenus;
11537 (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11538 break;
11539 }
11540 case Expose:
11541 break;
11542 case MotionNotify:
11543 {
11544 roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11545 roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11546 }
11547 default:
11548 break;
11549 }
11550 if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11551 ((state & ExitState) != 0))
11552 {
11553 /*
11554 Check boundary conditions.
11555 */
11556 if (roi_info.x < 0)
11557 roi_info.x=0;
11558 else
11559 if (roi_info.x > (ssize_t) windows->image.ximage->width)
11560 roi_info.x=(ssize_t) windows->image.ximage->width;
11561 if ((int) roi_info.x < x)
11562 roi_info.width=(unsigned int) (x-roi_info.x);
11563 else
11564 {
11565 roi_info.width=(unsigned int) (roi_info.x-x);
11566 roi_info.x=(ssize_t) x;
11567 }
11568 if (roi_info.y < 0)
11569 roi_info.y=0;
11570 else
11571 if (roi_info.y > (ssize_t) windows->image.ximage->height)
11572 roi_info.y=(ssize_t) windows->image.ximage->height;
11573 if ((int) roi_info.y < y)
11574 roi_info.height=(unsigned int) (y-roi_info.y);
11575 else
11576 {
11577 roi_info.height=(unsigned int) (roi_info.y-y);
11578 roi_info.y=(ssize_t) y;
11579 }
11580 }
11581 } while ((state & ExitState) == 0);
11582 /*
11583 Wait for user to grab a corner of the rectangle or press return.
11584 */
11585 state=DefaultState;
11586 display_command=NullCommand;
11587 crop_info.x=0;
11588 crop_info.y=0;
11589 (void) XMapWindow(display,windows->info.id);
11590 do
11591 {
11592 if (windows->info.mapped != MagickFalse)
11593 {
11594 /*
11595 Display pointer position.
11596 */
11597 (void) FormatLocaleString(text,MagickPathExtent,
11598 " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11599 roi_info.height,(double) roi_info.x,(double) roi_info.y);
11600 XInfoWidget(display,windows,text);
11601 }
11602 highlight_info=roi_info;
11603 highlight_info.x=roi_info.x-windows->image.x;
11604 highlight_info.y=roi_info.y-windows->image.y;
11605 if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11606 {
11607 state|=EscapeState;
11608 state|=ExitState;
11609 break;
11610 }
11611 if ((state & UpdateRegionState) != 0)
11612 {
11613 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11614 switch (display_command)
11615 {
11616 case UndoCommand:
11617 case RedoCommand:
11618 {
11619 (void) XMagickCommand(display,resource_info,windows,
11620 display_command,image,exception);
11621 break;
11622 }
11623 default:
11624 {
11625 /*
11626 Region of interest is relative to image configuration.
11627 */
11628 progress_monitor=SetImageProgressMonitor(*image,
11629 (MagickProgressMonitor) NULL,(*image)->client_data);
11630 crop_info=roi_info;
11631 width=(unsigned int) (*image)->columns;
11632 height=(unsigned int) (*image)->rows;
11633 x=0;
11634 y=0;
11635 if (windows->image.crop_geometry != (char *) NULL)
11636 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11637 &width,&height);
11638 scale_factor=(double) width/windows->image.ximage->width;
11639 crop_info.x+=x;
11640 crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11641 crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11642 scale_factor=(double)
11643 height/windows->image.ximage->height;
11644 crop_info.y+=y;
11645 crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11646 crop_info.height=(unsigned int)
11647 (scale_factor*crop_info.height+0.5);
11648 roi_image=CropImage(*image,&crop_info,exception);
11649 (void) SetImageProgressMonitor(*image,progress_monitor,
11650 (*image)->client_data);
11651 if (roi_image == (Image *) NULL)
11652 continue;
11653 /*
11654 Apply image processing technique to the region of interest.
11655 */
11656 windows->image.orphan=MagickTrue;
11657 (void) XMagickCommand(display,resource_info,windows,
11658 display_command,&roi_image,exception);
11659 progress_monitor=SetImageProgressMonitor(*image,
11660 (MagickProgressMonitor) NULL,(*image)->client_data);
11661 (void) XMagickCommand(display,resource_info,windows,
11662 SaveToUndoBufferCommand,image,exception);
11663 windows->image.orphan=MagickFalse;
11664 (void) CompositeImage(*image,roi_image,CopyCompositeOp,
11665 MagickTrue,crop_info.x,crop_info.y,exception);
11666 roi_image=DestroyImage(roi_image);
11667 (void) SetImageProgressMonitor(*image,progress_monitor,
11668 (*image)->client_data);
11669 break;
11670 }
11671 }
11672 if (display_command != InfoCommand)
11673 {
11674 XConfigureImageColormap(display,resource_info,windows,*image,
11675 exception);
11676 (void) XConfigureImage(display,resource_info,windows,*image,
11677 exception);
11678 }
11679 XCheckRefreshWindows(display,windows);
11680 XInfoWidget(display,windows,text);
11681 (void) XSetFunction(display,windows->image.highlight_context,
11682 GXinvert);
11683 state&=(unsigned int) (~UpdateRegionState);
11684 }
11685 XHighlightRectangle(display,windows->image.id,
11686 windows->image.highlight_context,&highlight_info);
11687 XScreenEvent(display,windows,&event,exception);
11688 if (event.xany.window == windows->command.id)
11689 {
11690 /*
11691 Select a command from the Command widget.
11692 */
11693 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11694 display_command=NullCommand;
11695 id=XCommandWidget(display,windows,ApplyMenu,&event);
11696 if (id >= 0)
11697 {
11698 (void) CopyMagickString(command,ApplyMenu[id],MagickPathExtent);
11699 display_command=ApplyCommands[id];
11700 if (id < ApplyMenus)
11701 {
11702 /*
11703 Select a command from a pop-up menu.
11704 */
11705 entry=XMenuWidget(display,windows,ApplyMenu[id],
11706 (const char **) Menus[id],command);
11707 if (entry >= 0)
11708 {
11709 (void) CopyMagickString(command,Menus[id][entry],
11710 MagickPathExtent);
11711 display_command=Commands[id][entry];
11712 }
11713 }
11714 }
11715 (void) XSetFunction(display,windows->image.highlight_context,
11716 GXinvert);
11717 XHighlightRectangle(display,windows->image.id,
11718 windows->image.highlight_context,&highlight_info);
11719 if (display_command == HelpCommand)
11720 {
11721 (void) XSetFunction(display,windows->image.highlight_context,
11722 GXcopy);
11723 XTextViewHelp(display,resource_info,windows,MagickFalse,
11724 "Help Viewer - Region of Interest",ImageROIHelp);
11725 (void) XSetFunction(display,windows->image.highlight_context,
11726 GXinvert);
11727 continue;
11728 }
11729 if (display_command == QuitCommand)
11730 {
11731 /*
11732 exit.
11733 */
11734 state|=EscapeState;
11735 state|=ExitState;
11736 continue;
11737 }
11738 if (display_command != NullCommand)
11739 state|=UpdateRegionState;
11740 continue;
11741 }
11742 XHighlightRectangle(display,windows->image.id,
11743 windows->image.highlight_context,&highlight_info);
11744 switch (event.type)
11745 {
11746 case ButtonPress:
11747 {
11748 x=windows->image.x;
11749 y=windows->image.y;
11750 if (event.xbutton.button != Button1)
11751 break;
11752 if (event.xbutton.window != windows->image.id)
11753 break;
11754 x=windows->image.x+event.xbutton.x;
11755 y=windows->image.y+event.xbutton.y;
11756 if ((x < (int) (roi_info.x+RoiDelta)) &&
11757 (x > (int) (roi_info.x-RoiDelta)) &&
11758 (y < (int) (roi_info.y+RoiDelta)) &&
11759 (y > (int) (roi_info.y-RoiDelta)))
11760 {
11761 roi_info.x=roi_info.x+(int) roi_info.width;
11762 roi_info.y=roi_info.y+(int) roi_info.height;
11763 state|=UpdateConfigurationState;
11764 break;
11765 }
11766 if ((x < (int) (roi_info.x+RoiDelta)) &&
11767 (x > (int) (roi_info.x-RoiDelta)) &&
11768 (y < (roi_info.y+(int) roi_info.height+RoiDelta)) &&
11769 (y > (roi_info.y+(int) roi_info.height-RoiDelta)))
11770 {
11771 roi_info.x=roi_info.x+(int) roi_info.width;
11772 state|=UpdateConfigurationState;
11773 break;
11774 }
11775 if ((x < (roi_info.x+(int) roi_info.width+RoiDelta)) &&
11776 (x > (roi_info.x+(int) roi_info.width-RoiDelta)) &&
11777 (y < (int) (roi_info.y+RoiDelta)) &&
11778 (y > (int) (roi_info.y-RoiDelta)))
11779 {
11780 roi_info.y=roi_info.y+(int) roi_info.height;
11781 state|=UpdateConfigurationState;
11782 break;
11783 }
11784 if ((x < (roi_info.x+(int) roi_info.width+RoiDelta)) &&
11785 (x > (roi_info.x+(int) roi_info.width-RoiDelta)) &&
11786 (y < (roi_info.y+(int) roi_info.height+RoiDelta)) &&
11787 (y > (roi_info.y+(int) roi_info.height-RoiDelta)))
11788 {
11789 state|=UpdateConfigurationState;
11790 break;
11791 }
11792 magick_fallthrough;
11793 }
11794 case ButtonRelease:
11795 {
11796 if (event.xbutton.window == windows->pan.id)
11797 if ((highlight_info.x != crop_info.x-windows->image.x) ||
11798 (highlight_info.y != crop_info.y-windows->image.y))
11799 XHighlightRectangle(display,windows->image.id,
11800 windows->image.highlight_context,&highlight_info);
11801 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11802 event.xbutton.time);
11803 break;
11804 }
11805 case Expose:
11806 {
11807 if (event.xexpose.window == windows->image.id)
11808 if (event.xexpose.count == 0)
11809 {
11810 event.xexpose.x=(int) highlight_info.x;
11811 event.xexpose.y=(int) highlight_info.y;
11812 event.xexpose.width=(int) highlight_info.width;
11813 event.xexpose.height=(int) highlight_info.height;
11814 XRefreshWindow(display,&windows->image,&event);
11815 }
11816 if (event.xexpose.window == windows->info.id)
11817 if (event.xexpose.count == 0)
11818 XInfoWidget(display,windows,text);
11819 break;
11820 }
11821 case KeyPress:
11822 {
11823 KeySym
11824 key_symbol;
11825
11826 if (event.xkey.window != windows->image.id)
11827 break;
11828 /*
11829 Respond to a user key press.
11830 */
11831 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11832 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11833 switch ((int) key_symbol)
11834 {
11835 case XK_Shift_L:
11836 case XK_Shift_R:
11837 break;
11838 case XK_Escape:
11839 case XK_F20:
11840 {
11841 state|=EscapeState;
11842 magick_fallthrough;
11843 }
11844 case XK_Return:
11845 {
11846 state|=ExitState;
11847 break;
11848 }
11849 case XK_Home:
11850 case XK_KP_Home:
11851 {
11852 roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11853 roi_info.y=(ssize_t) (windows->image.height/2L-
11854 roi_info.height/2L);
11855 break;
11856 }
11857 case XK_Left:
11858 case XK_KP_Left:
11859 {
11860 roi_info.x--;
11861 break;
11862 }
11863 case XK_Up:
11864 case XK_KP_Up:
11865 case XK_Next:
11866 {
11867 roi_info.y--;
11868 break;
11869 }
11870 case XK_Right:
11871 case XK_KP_Right:
11872 {
11873 roi_info.x++;
11874 break;
11875 }
11876 case XK_Prior:
11877 case XK_Down:
11878 case XK_KP_Down:
11879 {
11880 roi_info.y++;
11881 break;
11882 }
11883 case XK_F1:
11884 case XK_Help:
11885 {
11886 (void) XSetFunction(display,windows->image.highlight_context,
11887 GXcopy);
11888 XTextViewHelp(display,resource_info,windows,MagickFalse,
11889 "Help Viewer - Region of Interest",ImageROIHelp);
11890 (void) XSetFunction(display,windows->image.highlight_context,
11891 GXinvert);
11892 break;
11893 }
11894 default:
11895 {
11896 display_command=XImageWindowCommand(display,resource_info,windows,
11897 event.xkey.state,key_symbol,image,exception);
11898 if (display_command != NullCommand)
11899 state|=UpdateRegionState;
11900 break;
11901 }
11902 }
11903 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11904 event.xkey.time);
11905 break;
11906 }
11907 case KeyRelease:
11908 break;
11909 case MotionNotify:
11910 {
11911 if (event.xbutton.window != windows->image.id)
11912 break;
11913 /*
11914 Map and unmap Info widget as text cursor crosses its boundaries.
11915 */
11916 x=event.xmotion.x;
11917 y=event.xmotion.y;
11918 if (windows->info.mapped != MagickFalse)
11919 {
11920 if ((x < (windows->info.x+(int) windows->info.width)) &&
11921 (y < (windows->info.y+(int) windows->info.height)))
11922 (void) XWithdrawWindow(display,windows->info.id,
11923 windows->info.screen);
11924 }
11925 else
11926 if ((x > (windows->info.x+(int) windows->info.width)) ||
11927 (y > (windows->info.y+(int) windows->info.height)))
11928 (void) XMapWindow(display,windows->info.id);
11929 roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11930 roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11931 break;
11932 }
11933 case SelectionRequest:
11934 {
11935 XSelectionEvent
11936 notify;
11937
11938 XSelectionRequestEvent
11939 *request;
11940
11941 /*
11942 Set primary selection.
11943 */
11944 (void) FormatLocaleString(text,MagickPathExtent,
11945 "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11946 roi_info.height,(double) roi_info.x,(double) roi_info.y);
11947 request=(&(event.xselectionrequest));
11948 (void) XChangeProperty(request->display,request->requestor,
11949 request->property,request->target,8,PropModeReplace,
11950 (unsigned char *) text,(int) strlen(text));
11951 notify.type=SelectionNotify;
11952 notify.display=request->display;
11953 notify.requestor=request->requestor;
11954 notify.selection=request->selection;
11955 notify.target=request->target;
11956 notify.time=request->time;
11957 if (request->property == None)
11958 notify.property=request->target;
11959 else
11960 notify.property=request->property;
11961 (void) XSendEvent(request->display,request->requestor,False,0,
11962 (XEvent *) &notify);
11963 }
11964 default:
11965 break;
11966 }
11967 if ((state & UpdateConfigurationState) != 0)
11968 {
11969 (void) XPutBackEvent(display,&event);
11970 (void) XCheckDefineCursor(display,windows->image.id,cursor);
11971 break;
11972 }
11973 } while ((state & ExitState) == 0);
11974 } while ((state & ExitState) == 0);
11975 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11976 XSetCursorState(display,windows,MagickFalse);
11977 if ((state & EscapeState) != 0)
11978 return(MagickTrue);
11979 return(MagickTrue);
11980}
11981
11982/*
11983%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11984% %
11985% %
11986% %
11987+ X R o t a t e I m a g e %
11988% %
11989% %
11990% %
11991%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11992%
11993% XRotateImage() rotates the X image. If the degrees parameter if zero, the
11994% rotation angle is computed from the slope of a line drawn by the user.
11995%
11996% The format of the XRotateImage method is:
11997%
11998% MagickBooleanType XRotateImage(Display *display,
11999% XResourceInfo *resource_info,XWindows *windows,double degrees,
12000% Image **image,ExceptionInfo *exception)
12001%
12002% A description of each parameter follows:
12003%
12004% o display: Specifies a connection to an X server; returned from
12005% XOpenDisplay.
12006%
12007% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12008%
12009% o windows: Specifies a pointer to a XWindows structure.
12010%
12011% o degrees: Specifies the number of degrees to rotate the image.
12012%
12013% o image: the image.
12014%
12015% o exception: return any errors or warnings in this structure.
12016%
12017*/
12018static MagickBooleanType XRotateImage(Display *display,
12019 XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12020 ExceptionInfo *exception)
12021{
12022 const char
12023 *const RotateMenu[] =
12024 {
12025 "Pixel Color",
12026 "Direction",
12027 "Help",
12028 "Dismiss",
12029 (char *) NULL
12030 };
12031
12032 static ModeType
12033 direction = HorizontalRotateCommand;
12034
12035 static const ModeType
12036 DirectionCommands[] =
12037 {
12038 HorizontalRotateCommand,
12039 VerticalRotateCommand
12040 },
12041 RotateCommands[] =
12042 {
12043 RotateColorCommand,
12044 RotateDirectionCommand,
12045 RotateHelpCommand,
12046 RotateDismissCommand
12047 };
12048
12049 static unsigned int
12050 pen_id = 0;
12051
12052 char
12053 command[MagickPathExtent],
12054 text[MagickPathExtent];
12055
12056 Image
12057 *rotate_image;
12058
12059 int
12060 id,
12061 x,
12062 y;
12063
12064 double
12065 normalized_degrees;
12066
12067 int
12068 i;
12069
12070 unsigned int
12071 height,
12072 rotations,
12073 width;
12074
12075 if (degrees == 0.0)
12076 {
12077 unsigned int
12078 distance;
12079
12080 size_t
12081 state;
12082
12083 XEvent
12084 event;
12085
12086 XSegment
12087 rotate_info;
12088
12089 /*
12090 Map Command widget.
12091 */
12092 (void) CloneString(&windows->command.name,"Rotate");
12093 windows->command.data=2;
12094 (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12095 (void) XMapRaised(display,windows->command.id);
12096 XClientMessage(display,windows->image.id,windows->im_protocols,
12097 windows->im_update_widget,CurrentTime);
12098 /*
12099 Wait for first button press.
12100 */
12101 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12102 XQueryPosition(display,windows->image.id,&x,&y);
12103 rotate_info.x1=x;
12104 rotate_info.y1=y;
12105 rotate_info.x2=x;
12106 rotate_info.y2=y;
12107 state=DefaultState;
12108 do
12109 {
12110 XHighlightLine(display,windows->image.id,
12111 windows->image.highlight_context,&rotate_info);
12112 /*
12113 Wait for next event.
12114 */
12115 XScreenEvent(display,windows,&event,exception);
12116 XHighlightLine(display,windows->image.id,
12117 windows->image.highlight_context,&rotate_info);
12118 if (event.xany.window == windows->command.id)
12119 {
12120 /*
12121 Select a command from the Command widget.
12122 */
12123 id=XCommandWidget(display,windows,RotateMenu,&event);
12124 if (id < 0)
12125 continue;
12126 (void) XSetFunction(display,windows->image.highlight_context,
12127 GXcopy);
12128 switch (RotateCommands[id])
12129 {
12130 case RotateColorCommand:
12131 {
12132 const char
12133 *ColorMenu[MaxNumberPens];
12134
12135 int
12136 pen_number;
12137
12138 XColor
12139 color;
12140
12141 /*
12142 Initialize menu selections.
12143 */
12144 for (i=0; i < (int) (MaxNumberPens-2); i++)
12145 ColorMenu[i]=resource_info->pen_colors[i];
12146 ColorMenu[MaxNumberPens-2]="Browser...";
12147 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12148 /*
12149 Select a pen color from the pop-up menu.
12150 */
12151 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12152 (const char **) ColorMenu,command);
12153 if (pen_number < 0)
12154 break;
12155 if (pen_number == (MaxNumberPens-2))
12156 {
12157 static char
12158 color_name[MagickPathExtent] = "gray";
12159
12160 /*
12161 Select a pen color from a dialog.
12162 */
12163 resource_info->pen_colors[pen_number]=color_name;
12164 XColorBrowserWidget(display,windows,"Select",color_name);
12165 if (*color_name == '\0')
12166 break;
12167 }
12168 /*
12169 Set pen color.
12170 */
12171 (void) XParseColor(display,windows->map_info->colormap,
12172 resource_info->pen_colors[pen_number],&color);
12173 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12174 (unsigned int) MaxColors,&color);
12175 windows->pixel_info->pen_colors[pen_number]=color;
12176 pen_id=(unsigned int) pen_number;
12177 break;
12178 }
12179 case RotateDirectionCommand:
12180 {
12181 const char
12182 *Directions[] =
12183 {
12184 "horizontal",
12185 "vertical",
12186 (char *) NULL,
12187 };
12188
12189 /*
12190 Select a command from the pop-up menu.
12191 */
12192 id=XMenuWidget(display,windows,RotateMenu[id],
12193 Directions,command);
12194 if (id >= 0)
12195 direction=DirectionCommands[id];
12196 break;
12197 }
12198 case RotateHelpCommand:
12199 {
12200 XTextViewHelp(display,resource_info,windows,MagickFalse,
12201 "Help Viewer - Image Rotation",ImageRotateHelp);
12202 break;
12203 }
12204 case RotateDismissCommand:
12205 {
12206 /*
12207 Prematurely exit.
12208 */
12209 state|=EscapeState;
12210 state|=ExitState;
12211 break;
12212 }
12213 default:
12214 break;
12215 }
12216 (void) XSetFunction(display,windows->image.highlight_context,
12217 GXinvert);
12218 continue;
12219 }
12220 switch (event.type)
12221 {
12222 case ButtonPress:
12223 {
12224 if (event.xbutton.button != Button1)
12225 break;
12226 if (event.xbutton.window != windows->image.id)
12227 break;
12228 /*
12229 exit loop.
12230 */
12231 (void) XSetFunction(display,windows->image.highlight_context,
12232 GXcopy);
12233 rotate_info.x1=event.xbutton.x;
12234 rotate_info.y1=event.xbutton.y;
12235 state|=ExitState;
12236 break;
12237 }
12238 case ButtonRelease:
12239 break;
12240 case Expose:
12241 break;
12242 case KeyPress:
12243 {
12244 char
12245 command[MagickPathExtent];
12246
12247 KeySym
12248 key_symbol;
12249
12250 if (event.xkey.window != windows->image.id)
12251 break;
12252 /*
12253 Respond to a user key press.
12254 */
12255 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12256 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12257 switch ((int) key_symbol)
12258 {
12259 case XK_Escape:
12260 case XK_F20:
12261 {
12262 /*
12263 Prematurely exit.
12264 */
12265 state|=EscapeState;
12266 state|=ExitState;
12267 break;
12268 }
12269 case XK_F1:
12270 case XK_Help:
12271 {
12272 (void) XSetFunction(display,windows->image.highlight_context,
12273 GXcopy);
12274 XTextViewHelp(display,resource_info,windows,MagickFalse,
12275 "Help Viewer - Image Rotation",ImageRotateHelp);
12276 (void) XSetFunction(display,windows->image.highlight_context,
12277 GXinvert);
12278 break;
12279 }
12280 default:
12281 {
12282 (void) XBell(display,0);
12283 break;
12284 }
12285 }
12286 break;
12287 }
12288 case MotionNotify:
12289 {
12290 rotate_info.x1=event.xmotion.x;
12291 rotate_info.y1=event.xmotion.y;
12292 }
12293 }
12294 rotate_info.x2=rotate_info.x1;
12295 rotate_info.y2=rotate_info.y1;
12296 if (direction == HorizontalRotateCommand)
12297 rotate_info.x2+=32;
12298 else
12299 rotate_info.y2-=32;
12300 } while ((state & ExitState) == 0);
12301 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12302 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12303 if ((state & EscapeState) != 0)
12304 return(MagickTrue);
12305 /*
12306 Draw line as pointer moves until the mouse button is released.
12307 */
12308 distance=0;
12309 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12310 state=DefaultState;
12311 do
12312 {
12313 if (distance > 9)
12314 {
12315 /*
12316 Display info and draw rotation line.
12317 */
12318 if (windows->info.mapped == MagickFalse)
12319 (void) XMapWindow(display,windows->info.id);
12320 (void) FormatLocaleString(text,MagickPathExtent," %g",
12321 direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12322 XInfoWidget(display,windows,text);
12323 XHighlightLine(display,windows->image.id,
12324 windows->image.highlight_context,&rotate_info);
12325 }
12326 else
12327 if (windows->info.mapped != MagickFalse)
12328 (void) XWithdrawWindow(display,windows->info.id,
12329 windows->info.screen);
12330 /*
12331 Wait for next event.
12332 */
12333 XScreenEvent(display,windows,&event,exception);
12334 if (distance > 9)
12335 XHighlightLine(display,windows->image.id,
12336 windows->image.highlight_context,&rotate_info);
12337 switch (event.type)
12338 {
12339 case ButtonPress:
12340 break;
12341 case ButtonRelease:
12342 {
12343 /*
12344 User has committed to rotation line.
12345 */
12346 rotate_info.x2=event.xbutton.x;
12347 rotate_info.y2=event.xbutton.y;
12348 state|=ExitState;
12349 break;
12350 }
12351 case Expose:
12352 break;
12353 case MotionNotify:
12354 {
12355 rotate_info.x2=event.xmotion.x;
12356 rotate_info.y2=event.xmotion.y;
12357 }
12358 default:
12359 break;
12360 }
12361 /*
12362 Check boundary conditions.
12363 */
12364 if (rotate_info.x2 < 0)
12365 rotate_info.x2=0;
12366 else
12367 if (rotate_info.x2 > (int) windows->image.width)
12368 rotate_info.x2=(short) windows->image.width;
12369 if (rotate_info.y2 < 0)
12370 rotate_info.y2=0;
12371 else
12372 if (rotate_info.y2 > (int) windows->image.height)
12373 rotate_info.y2=(short) windows->image.height;
12374 /*
12375 Compute rotation angle from the slope of the line.
12376 */
12377 degrees=0.0;
12378 distance=(unsigned int)
12379 (((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12380 ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1)));
12381 if (distance > 9)
12382 degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12383 rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12384 } while ((state & ExitState) == 0);
12385 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12386 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12387 if (distance <= 9)
12388 return(MagickTrue);
12389 }
12390 if (direction == VerticalRotateCommand)
12391 degrees-=90.0;
12392 if (degrees == 0.0)
12393 return(MagickTrue);
12394 /*
12395 Rotate image.
12396 */
12397 normalized_degrees=degrees;
12398 while (normalized_degrees < -45.0)
12399 normalized_degrees+=360.0;
12400 for (rotations=0; normalized_degrees > 45.0; rotations++)
12401 normalized_degrees-=90.0;
12402 if (normalized_degrees != 0.0)
12403 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12404 exception);
12405 XSetCursorState(display,windows,MagickTrue);
12406 XCheckRefreshWindows(display,windows);
12407 (*image)->background_color.red=(double) ScaleShortToQuantum(
12408 windows->pixel_info->pen_colors[pen_id].red);
12409 (*image)->background_color.green=(double) ScaleShortToQuantum(
12410 windows->pixel_info->pen_colors[pen_id].green);
12411 (*image)->background_color.blue=(double) ScaleShortToQuantum(
12412 windows->pixel_info->pen_colors[pen_id].blue);
12413 rotate_image=RotateImage(*image,degrees,exception);
12414 XSetCursorState(display,windows,MagickFalse);
12415 if (rotate_image == (Image *) NULL)
12416 return(MagickFalse);
12417 *image=DestroyImage(*image);
12418 *image=rotate_image;
12419 if (windows->image.crop_geometry != (char *) NULL)
12420 {
12421 /*
12422 Rotate crop geometry.
12423 */
12424 width=(unsigned int) (*image)->columns;
12425 height=(unsigned int) (*image)->rows;
12426 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12427 switch (rotations % 4)
12428 {
12429 default:
12430 case 0:
12431 break;
12432 case 1:
12433 {
12434 /*
12435 Rotate 90 degrees.
12436 */
12437 (void) FormatLocaleString(windows->image.crop_geometry,
12438 MagickPathExtent,"%ux%u%+d%+d",height,width,(int) (*image)->columns-
12439 (int) height-y,x);
12440 break;
12441 }
12442 case 2:
12443 {
12444 /*
12445 Rotate 180 degrees.
12446 */
12447 (void) FormatLocaleString(windows->image.crop_geometry,
12448 MagickPathExtent,"%ux%u%+d%+d",width,height,(int) width-x,(int)
12449 height-y);
12450 break;
12451 }
12452 case 3:
12453 {
12454 /*
12455 Rotate 270 degrees.
12456 */
12457 (void) FormatLocaleString(windows->image.crop_geometry,
12458 MagickPathExtent,"%ux%u%+d%+d",height,width,y,(int) (*image)->rows-
12459 (int) width-x);
12460 break;
12461 }
12462 }
12463 }
12464 if (windows->image.orphan != MagickFalse)
12465 return(MagickTrue);
12466 if (normalized_degrees != 0.0)
12467 {
12468 /*
12469 Update image colormap.
12470 */
12471 windows->image.window_changes.width=(int) (*image)->columns;
12472 windows->image.window_changes.height=(int) (*image)->rows;
12473 if (windows->image.crop_geometry != (char *) NULL)
12474 {
12475 /*
12476 Obtain dimensions of image from crop geometry.
12477 */
12478 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12479 &width,&height);
12480 windows->image.window_changes.width=(int) width;
12481 windows->image.window_changes.height=(int) height;
12482 }
12483 XConfigureImageColormap(display,resource_info,windows,*image,exception);
12484 }
12485 else
12486 if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12487 {
12488 windows->image.window_changes.width=windows->image.ximage->height;
12489 windows->image.window_changes.height=windows->image.ximage->width;
12490 }
12491 /*
12492 Update image configuration.
12493 */
12494 (void) XConfigureImage(display,resource_info,windows,*image,exception);
12495 return(MagickTrue);
12496}
12497
12498/*
12499%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12500% %
12501% %
12502% %
12503+ X S a v e I m a g e %
12504% %
12505% %
12506% %
12507%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12508%
12509% XSaveImage() saves an image to a file.
12510%
12511% The format of the XSaveImage method is:
12512%
12513% MagickBooleanType XSaveImage(Display *display,
12514% XResourceInfo *resource_info,XWindows *windows,Image *image,
12515% ExceptionInfo *exception)
12516%
12517% A description of each parameter follows:
12518%
12519% o display: Specifies a connection to an X server; returned from
12520% XOpenDisplay.
12521%
12522% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12523%
12524% o windows: Specifies a pointer to a XWindows structure.
12525%
12526% o image: the image.
12527%
12528% o exception: return any errors or warnings in this structure.
12529%
12530*/
12531static MagickBooleanType XSaveImage(Display *display,
12532 XResourceInfo *resource_info,XWindows *windows,Image *image,
12533 ExceptionInfo *exception)
12534{
12535 char
12536 filename[MagickPathExtent],
12537 geometry[MagickPathExtent];
12538
12539 Image
12540 *save_image;
12541
12542 ImageInfo
12543 *image_info;
12544
12545 MagickStatusType
12546 status;
12547
12548 /*
12549 Request file name from user.
12550 */
12551 if (resource_info->write_filename != (char *) NULL)
12552 (void) CopyMagickString(filename,resource_info->write_filename,
12553 MagickPathExtent);
12554 else
12555 {
12556 char
12557 path[MagickPathExtent];
12558
12559 int
12560 status;
12561
12562 GetPathComponent(image->filename,HeadPath,path);
12563 GetPathComponent(image->filename,TailPath,filename);
12564 if (*path != '\0')
12565 {
12566 status=chdir(path);
12567 if (status == -1)
12568 (void) ThrowMagickException(exception,GetMagickModule(),
12569 FileOpenError,"UnableToOpenFile","%s",path);
12570 }
12571 }
12572 XFileBrowserWidget(display,windows,"Save",filename);
12573 if (*filename == '\0')
12574 return(MagickTrue);
12575 if (IsPathAccessible(filename) != MagickFalse)
12576 {
12577 int
12578 status;
12579
12580 /*
12581 File exists-- seek user's permission before overwriting.
12582 */
12583 status=XConfirmWidget(display,windows,"Overwrite",filename);
12584 if (status <= 0)
12585 return(MagickTrue);
12586 }
12587 image_info=CloneImageInfo(resource_info->image_info);
12588 (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
12589 (void) SetImageInfo(image_info,1,exception);
12590 if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12591 (LocaleCompare(image_info->magick,"JPG") == 0))
12592 {
12593 char
12594 quality[MagickPathExtent];
12595
12596 int
12597 status;
12598
12599 /*
12600 Request JPEG quality from user.
12601 */
12602 (void) FormatLocaleString(quality,MagickPathExtent,"%.20g",(double)
12603 image->quality);
12604 status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12605 quality);
12606 if (*quality == '\0')
12607 return(MagickTrue);
12608 image->quality=StringToUnsignedLong(quality);
12609 image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12610 }
12611 if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12612 (LocaleCompare(image_info->magick,"PDF") == 0) ||
12613 (LocaleCompare(image_info->magick,"PS") == 0) ||
12614 (LocaleCompare(image_info->magick,"PS2") == 0))
12615 {
12616 char
12617 geometry[MagickPathExtent];
12618
12619 const char
12620 *const PageSizes[] =
12621 {
12622 "Letter",
12623 "Tabloid",
12624 "Ledger",
12625 "Legal",
12626 "Statement",
12627 "Executive",
12628 "A3",
12629 "A4",
12630 "A5",
12631 "B4",
12632 "B5",
12633 "Folio",
12634 "Quarto",
12635 "10x14",
12636 (char *) NULL
12637 };
12638
12639 /*
12640 Request page geometry from user.
12641 */
12642 (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12643 if (LocaleCompare(image_info->magick,"PDF") == 0)
12644 (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12645 if (image_info->page != (char *) NULL)
12646 (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
12647 XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12648 "Select page geometry:",geometry);
12649 if (*geometry != '\0')
12650 image_info->page=GetPageGeometry(geometry);
12651 }
12652 /*
12653 Apply image transforms.
12654 */
12655 XSetCursorState(display,windows,MagickTrue);
12656 XCheckRefreshWindows(display,windows);
12657 save_image=CloneImage(image,0,0,MagickTrue,exception);
12658 if (save_image == (Image *) NULL)
12659 return(MagickFalse);
12660 (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
12661 windows->image.ximage->width,windows->image.ximage->height);
12662 (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12663 exception);
12664 /*
12665 Write image.
12666 */
12667 (void) CopyMagickString(save_image->filename,filename,MagickPathExtent);
12668 status=WriteImage(image_info,save_image,exception);
12669 if (status != MagickFalse)
12670 image->taint=MagickFalse;
12671 save_image=DestroyImage(save_image);
12672 image_info=DestroyImageInfo(image_info);
12673 XSetCursorState(display,windows,MagickFalse);
12674 return(status != 0 ? MagickTrue : MagickFalse);
12675}
12676
12677/*
12678%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12679% %
12680% %
12681% %
12682+ X S c r e e n E v e n t %
12683% %
12684% %
12685% %
12686%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12687%
12688% XScreenEvent() handles global events associated with the Pan and Magnify
12689% windows.
12690%
12691% The format of the XScreenEvent function is:
12692%
12693% void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12694% ExceptionInfo *exception)
12695%
12696% A description of each parameter follows:
12697%
12698% o display: Specifies a pointer to the Display structure; returned from
12699% XOpenDisplay.
12700%
12701% o windows: Specifies a pointer to a XWindows structure.
12702%
12703% o event: Specifies a pointer to a X11 XEvent structure.
12704%
12705% o exception: return any errors or warnings in this structure.
12706%
12707*/
12708
12709#if defined(__cplusplus) || defined(c_plusplus)
12710extern "C" {
12711#endif
12712
12713static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12714{
12715 XWindows
12716 *windows;
12717
12718 windows=(XWindows *) data;
12719 if ((event->type == ClientMessage) &&
12720 (event->xclient.window == windows->image.id))
12721 return(MagickFalse);
12722 return(MagickTrue);
12723}
12724
12725#if defined(__cplusplus) || defined(c_plusplus)
12726}
12727#endif
12728
12729static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12730 ExceptionInfo *exception)
12731{
12732 int
12733 x,
12734 y;
12735
12736 (void) XIfEvent(display,event,XPredicate,(char *) windows);
12737 if (event->xany.window == windows->command.id)
12738 return;
12739 switch (event->type)
12740 {
12741 case ButtonPress:
12742 case ButtonRelease:
12743 {
12744 if ((event->xbutton.button == Button3) &&
12745 (event->xbutton.state & Mod1Mask))
12746 {
12747 /*
12748 Convert Alt-Button3 to Button2.
12749 */
12750 event->xbutton.button=Button2;
12751 event->xbutton.state&=(unsigned int) (~Mod1Mask);
12752 }
12753 if (event->xbutton.window == windows->backdrop.id)
12754 {
12755 (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12756 event->xbutton.time);
12757 break;
12758 }
12759 if (event->xbutton.window == windows->pan.id)
12760 {
12761 XPanImage(display,windows,event,exception);
12762 break;
12763 }
12764 if (event->xbutton.window == windows->image.id)
12765 if (event->xbutton.button == Button2)
12766 {
12767 /*
12768 Update magnified image.
12769 */
12770 x=event->xbutton.x;
12771 y=event->xbutton.y;
12772 if (x < 0)
12773 x=0;
12774 else
12775 if (x >= (int) windows->image.width)
12776 x=(int) (windows->image.width-1);
12777 windows->magnify.x=(int) windows->image.x+x;
12778 if (y < 0)
12779 y=0;
12780 else
12781 if (y >= (int) windows->image.height)
12782 y=(int) (windows->image.height-1);
12783 windows->magnify.y=windows->image.y+y;
12784 if (windows->magnify.mapped == MagickFalse)
12785 (void) XMapRaised(display,windows->magnify.id);
12786 XMakeMagnifyImage(display,windows,exception);
12787 if (event->type == ButtonRelease)
12788 (void) XWithdrawWindow(display,windows->info.id,
12789 windows->info.screen);
12790 break;
12791 }
12792 break;
12793 }
12794 case ClientMessage:
12795 {
12796 /*
12797 If client window delete message, exit.
12798 */
12799 if (event->xclient.message_type != windows->wm_protocols)
12800 break;
12801 if (*event->xclient.data.l != (long) windows->wm_delete_window)
12802 break;
12803 if (event->xclient.window == windows->magnify.id)
12804 {
12805 (void) XWithdrawWindow(display,windows->magnify.id,
12806 windows->magnify.screen);
12807 break;
12808 }
12809 break;
12810 }
12811 case ConfigureNotify:
12812 {
12813 if (event->xconfigure.window == windows->magnify.id)
12814 {
12815 unsigned int
12816 magnify;
12817
12818 /*
12819 Magnify window has a new configuration.
12820 */
12821 windows->magnify.width=(unsigned int) event->xconfigure.width;
12822 windows->magnify.height=(unsigned int) event->xconfigure.height;
12823 if (windows->magnify.mapped == MagickFalse)
12824 break;
12825 magnify=1;
12826 while ((int) magnify <= event->xconfigure.width)
12827 magnify<<=1;
12828 while ((int) magnify <= event->xconfigure.height)
12829 magnify<<=1;
12830 magnify>>=1;
12831 if (((int) magnify != event->xconfigure.width) ||
12832 ((int) magnify != event->xconfigure.height))
12833 {
12834 XWindowChanges
12835 window_changes;
12836
12837 window_changes.width=(int) magnify;
12838 window_changes.height=(int) magnify;
12839 (void) XReconfigureWMWindow(display,windows->magnify.id,
12840 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12841 &window_changes);
12842 break;
12843 }
12844 XMakeMagnifyImage(display,windows,exception);
12845 break;
12846 }
12847 break;
12848 }
12849 case Expose:
12850 {
12851 if (event->xexpose.window == windows->image.id)
12852 {
12853 XRefreshWindow(display,&windows->image,event);
12854 break;
12855 }
12856 if (event->xexpose.window == windows->pan.id)
12857 if (event->xexpose.count == 0)
12858 {
12859 XDrawPanRectangle(display,windows);
12860 break;
12861 }
12862 if (event->xexpose.window == windows->magnify.id)
12863 if (event->xexpose.count == 0)
12864 {
12865 XMakeMagnifyImage(display,windows,exception);
12866 break;
12867 }
12868 break;
12869 }
12870 case KeyPress:
12871 {
12872 char
12873 command[MagickPathExtent];
12874
12875 KeySym
12876 key_symbol;
12877
12878 if (event->xkey.window != windows->magnify.id)
12879 break;
12880 /*
12881 Respond to a user key press.
12882 */
12883 (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12884 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12885 XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12886 exception);
12887 break;
12888 }
12889 case MapNotify:
12890 {
12891 if (event->xmap.window == windows->magnify.id)
12892 {
12893 windows->magnify.mapped=MagickTrue;
12894 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12895 break;
12896 }
12897 if (event->xmap.window == windows->info.id)
12898 {
12899 windows->info.mapped=MagickTrue;
12900 break;
12901 }
12902 break;
12903 }
12904 case MotionNotify:
12905 {
12906 while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12907 if (event->xmotion.window == windows->image.id)
12908 if (windows->magnify.mapped != MagickFalse)
12909 {
12910 /*
12911 Update magnified image.
12912 */
12913 x=event->xmotion.x;
12914 y=event->xmotion.y;
12915 if (x < 0)
12916 x=0;
12917 else
12918 if (x >= (int) windows->image.width)
12919 x=(int) (windows->image.width-1);
12920 windows->magnify.x=(int) windows->image.x+x;
12921 if (y < 0)
12922 y=0;
12923 else
12924 if (y >= (int) windows->image.height)
12925 y=(int) (windows->image.height-1);
12926 windows->magnify.y=windows->image.y+y;
12927 XMakeMagnifyImage(display,windows,exception);
12928 }
12929 break;
12930 }
12931 case UnmapNotify:
12932 {
12933 if (event->xunmap.window == windows->magnify.id)
12934 {
12935 windows->magnify.mapped=MagickFalse;
12936 break;
12937 }
12938 if (event->xunmap.window == windows->info.id)
12939 {
12940 windows->info.mapped=MagickFalse;
12941 break;
12942 }
12943 break;
12944 }
12945 default:
12946 break;
12947 }
12948}
12949
12950/*
12951%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12952% %
12953% %
12954% %
12955+ X S e t C r o p G e o m e t r y %
12956% %
12957% %
12958% %
12959%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12960%
12961% XSetCropGeometry() accepts a cropping geometry relative to the Image window
12962% and translates it to a cropping geometry relative to the image.
12963%
12964% The format of the XSetCropGeometry method is:
12965%
12966% void XSetCropGeometry(Display *display,XWindows *windows,
12967% RectangleInfo *crop_info,Image *image)
12968%
12969% A description of each parameter follows:
12970%
12971% o display: Specifies a connection to an X server; returned from
12972% XOpenDisplay.
12973%
12974% o windows: Specifies a pointer to a XWindows structure.
12975%
12976% o crop_info: A pointer to a RectangleInfo that defines a region of the
12977% Image window to crop.
12978%
12979% o image: the image.
12980%
12981*/
12982static void XSetCropGeometry(Display *display,XWindows *windows,
12983 RectangleInfo *crop_info,Image *image)
12984{
12985 char
12986 text[MagickPathExtent];
12987
12988 int
12989 x,
12990 y;
12991
12992 double
12993 scale_factor;
12994
12995 unsigned int
12996 height,
12997 width;
12998
12999 if (windows->info.mapped != MagickFalse)
13000 {
13001 /*
13002 Display info on cropping rectangle.
13003 */
13004 (void) FormatLocaleString(text,MagickPathExtent," %.20gx%.20g%+.20g%+.20g",
13005 (double) crop_info->width,(double) crop_info->height,(double)
13006 crop_info->x,(double) crop_info->y);
13007 XInfoWidget(display,windows,text);
13008 }
13009 /*
13010 Cropping geometry is relative to any previous crop geometry.
13011 */
13012 x=0;
13013 y=0;
13014 width=(unsigned int) image->columns;
13015 height=(unsigned int) image->rows;
13016 if (windows->image.crop_geometry != (char *) NULL)
13017 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13018 else
13019 windows->image.crop_geometry=AcquireString((char *) NULL);
13020 /*
13021 Define the crop geometry string from the cropping rectangle.
13022 */
13023 scale_factor=(double) width/windows->image.ximage->width;
13024 if (crop_info->x > 0)
13025 x+=(int) (scale_factor*crop_info->x+0.5);
13026 width=(unsigned int) (scale_factor*crop_info->width+0.5);
13027 if (width == 0)
13028 width=1;
13029 scale_factor=(double) height/windows->image.ximage->height;
13030 if (crop_info->y > 0)
13031 y+=(int) (scale_factor*crop_info->y+0.5);
13032 height=(unsigned int) (scale_factor*crop_info->height+0.5);
13033 if (height == 0)
13034 height=1;
13035 (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
13036 "%ux%u%+d%+d",width,height,x,y);
13037}
13038
13039/*
13040%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13041% %
13042% %
13043% %
13044+ X T i l e I m a g e %
13045% %
13046% %
13047% %
13048%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13049%
13050% XTileImage() loads or deletes a selected tile from a visual image directory.
13051% The load or delete command is chosen from a menu.
13052%
13053% The format of the XTileImage method is:
13054%
13055% Image *XTileImage(Display *display,XResourceInfo *resource_info,
13056% XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13057%
13058% A description of each parameter follows:
13059%
13060% o tile_image: XTileImage reads or deletes the tile image
13061% and returns it. A null image is returned if an error occurs.
13062%
13063% o display: Specifies a connection to an X server; returned from
13064% XOpenDisplay.
13065%
13066% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13067%
13068% o windows: Specifies a pointer to a XWindows structure.
13069%
13070% o image: the image; returned from ReadImage.
13071%
13072% o event: Specifies a pointer to a XEvent structure. If it is NULL,
13073% the entire image is refreshed.
13074%
13075% o exception: return any errors or warnings in this structure.
13076%
13077*/
13078static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13079 XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13080{
13081 const char
13082 *const VerbMenu[] =
13083 {
13084 "Load",
13085 "Next",
13086 "Former",
13087 "Delete",
13088 "Update",
13089 (char *) NULL,
13090 };
13091
13092 static const ModeType
13093 TileCommands[] =
13094 {
13095 TileLoadCommand,
13096 TileNextCommand,
13097 TileFormerCommand,
13098 TileDeleteCommand,
13099 TileUpdateCommand
13100 };
13101
13102 char
13103 command[MagickPathExtent],
13104 filename[MagickPathExtent];
13105
13106 Image
13107 *tile_image;
13108
13109 int
13110 id,
13111 status,
13112 tile,
13113 x,
13114 y;
13115
13116 double
13117 scale_factor;
13118
13119 char
13120 *p,
13121 *q;
13122
13123 int
13124 i;
13125
13126 unsigned int
13127 height,
13128 width;
13129
13130 /*
13131 Tile image is relative to montage image configuration.
13132 */
13133 x=0;
13134 y=0;
13135 width=(unsigned int) image->columns;
13136 height=(unsigned int) image->rows;
13137 if (windows->image.crop_geometry != (char *) NULL)
13138 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13139 scale_factor=(double) width/windows->image.ximage->width;
13140 event->xbutton.x+=windows->image.x;
13141 event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13142 scale_factor=(double) height/windows->image.ximage->height;
13143 event->xbutton.y+=windows->image.y;
13144 event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13145 /*
13146 Determine size and location of each tile in the visual image directory.
13147 */
13148 width=(unsigned int) image->columns;
13149 height=(unsigned int) image->rows;
13150 x=0;
13151 y=0;
13152 (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13153 tile=((event->xbutton.y-y)/(int) height)*(((int) image->columns-x)/(int)
13154 width)+(event->xbutton.x-x)/(int) width;
13155 if (tile < 0)
13156 {
13157 /*
13158 Button press is outside any tile.
13159 */
13160 (void) XBell(display,0);
13161 return((Image *) NULL);
13162 }
13163 /*
13164 Determine file name from the tile directory.
13165 */
13166 p=image->directory;
13167 for (i=tile; (i != 0) && (*p != '\0'); )
13168 {
13169 if (*p == '\xff')
13170 i--;
13171 p++;
13172 }
13173 if (*p == '\0')
13174 {
13175 /*
13176 Button press is outside any tile.
13177 */
13178 (void) XBell(display,0);
13179 return((Image *) NULL);
13180 }
13181 /*
13182 Select a command from the pop-up menu.
13183 */
13184 id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13185 if (id < 0)
13186 return((Image *) NULL);
13187 q=p;
13188 while ((*q != '\xff') && (*q != '\0'))
13189 q++;
13190 (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13191 /*
13192 Perform command for the selected tile.
13193 */
13194 XSetCursorState(display,windows,MagickTrue);
13195 XCheckRefreshWindows(display,windows);
13196 tile_image=NewImageList();
13197 switch (TileCommands[id])
13198 {
13199 case TileLoadCommand:
13200 {
13201 /*
13202 Load tile image.
13203 */
13204 XCheckRefreshWindows(display,windows);
13205 (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13206 MagickPathExtent);
13207 (void) CopyMagickString(resource_info->image_info->filename,filename,
13208 MagickPathExtent);
13209 tile_image=ReadImage(resource_info->image_info,exception);
13210 CatchException(exception);
13211 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13212 break;
13213 }
13214 case TileNextCommand:
13215 {
13216 /*
13217 Display next image.
13218 */
13219 XClientMessage(display,windows->image.id,windows->im_protocols,
13220 windows->im_next_image,CurrentTime);
13221 break;
13222 }
13223 case TileFormerCommand:
13224 {
13225 /*
13226 Display former image.
13227 */
13228 XClientMessage(display,windows->image.id,windows->im_protocols,
13229 windows->im_former_image,CurrentTime);
13230 break;
13231 }
13232 case TileDeleteCommand:
13233 {
13234 /*
13235 Delete tile image.
13236 */
13237 if (IsPathAccessible(filename) == MagickFalse)
13238 {
13239 XNoticeWidget(display,windows,"Image file does not exist:",filename);
13240 break;
13241 }
13242 status=XConfirmWidget(display,windows,"Really delete tile",filename);
13243 if (status <= 0)
13244 break;
13245 status=ShredFile(filename) == MagickFalse ? 0 : 1;
13246 status|=remove_utf8(filename);
13247 if (status != MagickFalse)
13248 {
13249 XNoticeWidget(display,windows,"Unable to delete image file:",
13250 filename);
13251 break;
13252 }
13253 magick_fallthrough;
13254 }
13255 case TileUpdateCommand:
13256 {
13257 int
13258 x_offset,
13259 y_offset;
13260
13261 PixelInfo
13262 pixel;
13263
13264 int
13265 j;
13266
13267 Quantum
13268 *s;
13269
13270 /*
13271 Ensure all the images exist.
13272 */
13273 tile=0;
13274 GetPixelInfo(image,&pixel);
13275 for (p=image->directory; *p != '\0'; p++)
13276 {
13277 CacheView
13278 *image_view;
13279
13280 q=p;
13281 while ((*q != '\xff') && (*q != '\0'))
13282 q++;
13283 (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13284 p=q;
13285 if (IsPathAccessible(filename) != MagickFalse)
13286 {
13287 tile++;
13288 continue;
13289 }
13290 /*
13291 Overwrite tile with background color.
13292 */
13293 x_offset=((int) width*(tile % (((int) image->columns-x)/(int) width))+
13294 x);
13295 y_offset=((int) height*(tile/(((int) image->columns-x)/(int) width))+
13296 y);
13297 image_view=AcquireAuthenticCacheView(image,exception);
13298 (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
13299 for (i=0; i < (int) height; i++)
13300 {
13301 s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13302 y_offset+i,width,1,exception);
13303 if (s == (Quantum *) NULL)
13304 break;
13305 for (j=0; j < (int) width; j++)
13306 {
13307 SetPixelViaPixelInfo(image,&pixel,s);
13308 s+=GetPixelChannels(image);
13309 }
13310 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13311 break;
13312 }
13313 image_view=DestroyCacheView(image_view);
13314 tile++;
13315 }
13316 windows->image.window_changes.width=(int) image->columns;
13317 windows->image.window_changes.height=(int) image->rows;
13318 XConfigureImageColormap(display,resource_info,windows,image,exception);
13319 (void) XConfigureImage(display,resource_info,windows,image,exception);
13320 break;
13321 }
13322 default:
13323 break;
13324 }
13325 XSetCursorState(display,windows,MagickFalse);
13326 return(tile_image);
13327}
13328
13329/*
13330%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13331% %
13332% %
13333% %
13334+ X T r a n s l a t e I m a g e %
13335% %
13336% %
13337% %
13338%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13339%
13340% XTranslateImage() translates the image within an Image window by one pixel
13341% as specified by the key symbol. If the image has a montage string the
13342% translation is respect to the width and height contained within the string.
13343%
13344% The format of the XTranslateImage method is:
13345%
13346% void XTranslateImage(Display *display,XWindows *windows,
13347% Image *image,const KeySym key_symbol)
13348%
13349% A description of each parameter follows:
13350%
13351% o display: Specifies a connection to an X server; returned from
13352% XOpenDisplay.
13353%
13354% o windows: Specifies a pointer to a XWindows structure.
13355%
13356% o image: the image.
13357%
13358% o key_symbol: Specifies a KeySym which indicates which side of the image
13359% to trim.
13360%
13361*/
13362static void XTranslateImage(Display *display,XWindows *windows,
13363 Image *image,const KeySym key_symbol)
13364{
13365 char
13366 text[MagickPathExtent];
13367
13368 int
13369 x,
13370 y;
13371
13372 unsigned int
13373 x_offset,
13374 y_offset;
13375
13376 /*
13377 User specified a pan position offset.
13378 */
13379 x_offset=windows->image.width;
13380 y_offset=windows->image.height;
13381 if (image->montage != (char *) NULL)
13382 (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13383 switch ((int) key_symbol)
13384 {
13385 case XK_Home:
13386 case XK_KP_Home:
13387 {
13388 windows->image.x=(int) windows->image.width/2;
13389 windows->image.y=(int) windows->image.height/2;
13390 break;
13391 }
13392 case XK_Left:
13393 case XK_KP_Left:
13394 {
13395 windows->image.x-=(int) x_offset;
13396 break;
13397 }
13398 case XK_Next:
13399 case XK_Up:
13400 case XK_KP_Up:
13401 {
13402 windows->image.y-=(int) y_offset;
13403 break;
13404 }
13405 case XK_Right:
13406 case XK_KP_Right:
13407 {
13408 windows->image.x+=(int) x_offset;
13409 break;
13410 }
13411 case XK_Prior:
13412 case XK_Down:
13413 case XK_KP_Down:
13414 {
13415 windows->image.y+=(int) y_offset;
13416 break;
13417 }
13418 default:
13419 return;
13420 }
13421 /*
13422 Check boundary conditions.
13423 */
13424 if (windows->image.x < 0)
13425 windows->image.x=0;
13426 else
13427 if ((windows->image.x+(int) windows->image.width) > windows->image.ximage->width)
13428 windows->image.x=windows->image.ximage->width-(int) windows->image.width;
13429 if (windows->image.y < 0)
13430 windows->image.y=0;
13431 else
13432 if ((windows->image.y+(int) windows->image.height) > windows->image.ximage->height)
13433 windows->image.y=windows->image.ximage->height-(int)
13434 windows->image.height;
13435 /*
13436 Refresh Image window.
13437 */
13438 (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
13439 windows->image.width,windows->image.height,windows->image.x,
13440 windows->image.y);
13441 XInfoWidget(display,windows,text);
13442 XCheckRefreshWindows(display,windows);
13443 XDrawPanRectangle(display,windows);
13444 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13445 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13446}
13447
13448/*
13449%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13450% %
13451% %
13452% %
13453+ X T r i m I m a g e %
13454% %
13455% %
13456% %
13457%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13458%
13459% XTrimImage() trims the edges from the Image window.
13460%
13461% The format of the XTrimImage method is:
13462%
13463% MagickBooleanType XTrimImage(Display *display,
13464% XResourceInfo *resource_info,XWindows *windows,Image *image,
13465% ExceptionInfo *exception)
13466%
13467% A description of each parameter follows:
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% o image: the image.
13477%
13478% o exception: return any errors or warnings in this structure.
13479%
13480*/
13481static MagickBooleanType XTrimImage(Display *display,
13482 XResourceInfo *resource_info,XWindows *windows,Image *image,
13483 ExceptionInfo *exception)
13484{
13486 trim_info;
13487
13488 int
13489 x,
13490 y;
13491
13492 size_t
13493 background,
13494 pixel;
13495
13496 /*
13497 Trim edges from image.
13498 */
13499 XSetCursorState(display,windows,MagickTrue);
13500 XCheckRefreshWindows(display,windows);
13501 /*
13502 Crop the left edge.
13503 */
13504 background=XGetPixel(windows->image.ximage,0,0);
13505 trim_info.width=(size_t) windows->image.ximage->width;
13506 for (x=0; x < windows->image.ximage->width; x++)
13507 {
13508 for (y=0; y < windows->image.ximage->height; y++)
13509 {
13510 pixel=XGetPixel(windows->image.ximage,x,y);
13511 if (pixel != background)
13512 break;
13513 }
13514 if (y < windows->image.ximage->height)
13515 break;
13516 }
13517 trim_info.x=(ssize_t) x;
13518 if (trim_info.x == (ssize_t) windows->image.ximage->width)
13519 {
13520 XSetCursorState(display,windows,MagickFalse);
13521 return(MagickFalse);
13522 }
13523 /*
13524 Crop the right edge.
13525 */
13526 background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13527 for (x=windows->image.ximage->width-1; x != 0; x--)
13528 {
13529 for (y=0; y < windows->image.ximage->height; y++)
13530 {
13531 pixel=XGetPixel(windows->image.ximage,x,y);
13532 if (pixel != background)
13533 break;
13534 }
13535 if (y < windows->image.ximage->height)
13536 break;
13537 }
13538 trim_info.width=(size_t) (x-trim_info.x+1);
13539 /*
13540 Crop the top edge.
13541 */
13542 background=XGetPixel(windows->image.ximage,0,0);
13543 trim_info.height=(size_t) windows->image.ximage->height;
13544 for (y=0; y < windows->image.ximage->height; y++)
13545 {
13546 for (x=0; x < windows->image.ximage->width; x++)
13547 {
13548 pixel=XGetPixel(windows->image.ximage,x,y);
13549 if (pixel != background)
13550 break;
13551 }
13552 if (x < windows->image.ximage->width)
13553 break;
13554 }
13555 trim_info.y=(ssize_t) y;
13556 /*
13557 Crop the bottom edge.
13558 */
13559 background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13560 for (y=windows->image.ximage->height-1; y != 0; y--)
13561 {
13562 for (x=0; x < windows->image.ximage->width; x++)
13563 {
13564 pixel=XGetPixel(windows->image.ximage,x,y);
13565 if (pixel != background)
13566 break;
13567 }
13568 if (x < windows->image.ximage->width)
13569 break;
13570 }
13571 trim_info.height=(size_t) (y-trim_info.y+1);
13572 if (((unsigned int) trim_info.width != windows->image.width) ||
13573 ((unsigned int) trim_info.height != windows->image.height))
13574 {
13575 /*
13576 Reconfigure Image window as defined by the trimming rectangle.
13577 */
13578 XSetCropGeometry(display,windows,&trim_info,image);
13579 windows->image.window_changes.width=(int) trim_info.width;
13580 windows->image.window_changes.height=(int) trim_info.height;
13581 (void) XConfigureImage(display,resource_info,windows,image,exception);
13582 }
13583 XSetCursorState(display,windows,MagickFalse);
13584 return(MagickTrue);
13585}
13586
13587/*
13588%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13589% %
13590% %
13591% %
13592+ X V i s u a l D i r e c t o r y I m a g e %
13593% %
13594% %
13595% %
13596%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13597%
13598% XVisualDirectoryImage() creates a Visual Image Directory.
13599%
13600% The format of the XVisualDirectoryImage method is:
13601%
13602% Image *XVisualDirectoryImage(Display *display,
13603% XResourceInfo *resource_info,XWindows *windows,
13604% ExceptionInfo *exception)
13605%
13606% A description of each parameter follows:
13607%
13608% o display: Specifies a connection to an X server; returned from
13609% XOpenDisplay.
13610%
13611% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13612%
13613% o windows: Specifies a pointer to a XWindows structure.
13614%
13615% o exception: return any errors or warnings in this structure.
13616%
13617*/
13618static Image *XVisualDirectoryImage(Display *display,
13619 XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13620{
13621#define TileImageTag "Scale/Image"
13622#define XClientName "montage"
13623
13624 char
13625 **filelist;
13626
13627 Image
13628 *images,
13629 *montage_image,
13630 *next_image,
13631 *thumbnail_image;
13632
13633 ImageInfo
13634 *read_info;
13635
13636 int
13637 number_files;
13638
13639 MagickBooleanType
13640 backdrop;
13641
13642 MagickStatusType
13643 status;
13644
13646 *montage_info;
13647
13649 geometry;
13650
13651 int
13652 i;
13653
13654 static char
13655 filename[MagickPathExtent] = "\0",
13656 filenames[MagickPathExtent] = "*";
13657
13658 XResourceInfo
13659 background_resources;
13660
13661 /*
13662 Request file name from user.
13663 */
13664 XFileBrowserWidget(display,windows,"Directory",filenames);
13665 if (*filenames == '\0')
13666 return((Image *) NULL);
13667 /*
13668 Expand the filenames.
13669 */
13670 filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13671 if (filelist == (char **) NULL)
13672 {
13673 ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13674 filenames);
13675 return((Image *) NULL);
13676 }
13677 number_files=1;
13678 filelist[0]=filenames;
13679 status=ExpandFilenames(&number_files,&filelist);
13680 if ((status == MagickFalse) || (number_files == 0))
13681 {
13682 if (number_files == 0)
13683 ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13684 else
13685 ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13686 filenames);
13687 return((Image *) NULL);
13688 }
13689 /*
13690 Set image background resources.
13691 */
13692 background_resources=(*resource_info);
13693 background_resources.window_id=AcquireString("");
13694 (void) FormatLocaleString(background_resources.window_id,MagickPathExtent,
13695 "0x%lx",windows->image.id);
13696 background_resources.backdrop=MagickTrue;
13697 /*
13698 Read each image and convert them to a tile.
13699 */
13700 backdrop=((windows->visual_info->klass == TrueColor) ||
13701 (windows->visual_info->klass == DirectColor)) ? MagickTrue : MagickFalse;
13702 read_info=CloneImageInfo(resource_info->image_info);
13703 (void) SetImageOption(read_info,"jpeg:size","120x120");
13704 (void) CloneString(&read_info->size,DefaultTileGeometry);
13705 (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13706 (void *) NULL);
13707 images=NewImageList();
13708 XSetCursorState(display,windows,MagickTrue);
13709 XCheckRefreshWindows(display,windows);
13710 for (i=0; i < (int) number_files; i++)
13711 {
13712 (void) CopyMagickString(read_info->filename,filelist[i],MagickPathExtent);
13713 filelist[i]=DestroyString(filelist[i]);
13714 *read_info->magick='\0';
13715 next_image=ReadImage(read_info,exception);
13716 CatchException(exception);
13717 if (next_image != (Image *) NULL)
13718 {
13719 (void) DeleteImageProperty(next_image,"label");
13720 (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13721 read_info,next_image,DefaultTileLabel,exception),exception);
13722 (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13723 exception);
13724 thumbnail_image=ThumbnailImage(next_image,geometry.width,
13725 geometry.height,exception);
13726 if (thumbnail_image != (Image *) NULL)
13727 {
13728 next_image=DestroyImage(next_image);
13729 next_image=thumbnail_image;
13730 }
13731 if (backdrop)
13732 {
13733 (void) XDisplayBackgroundImage(display,&background_resources,
13734 next_image,exception);
13735 XSetCursorState(display,windows,MagickTrue);
13736 }
13737 AppendImageToList(&images,next_image);
13738 if (images->progress_monitor != (MagickProgressMonitor) NULL)
13739 {
13740 MagickBooleanType
13741 proceed;
13742
13743 proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13744 (MagickSizeType) number_files);
13745 if (proceed == MagickFalse)
13746 break;
13747 }
13748 }
13749 }
13750 filelist=(char **) RelinquishMagickMemory(filelist);
13751 if (images == (Image *) NULL)
13752 {
13753 read_info=DestroyImageInfo(read_info);
13754 XSetCursorState(display,windows,MagickFalse);
13755 ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13756 return((Image *) NULL);
13757 }
13758 /*
13759 Create the Visual Image Directory.
13760 */
13761 montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13762 montage_info->pointsize=10;
13763 if (resource_info->font != (char *) NULL)
13764 (void) CloneString(&montage_info->font,resource_info->font);
13765 (void) CopyMagickString(montage_info->filename,filename,MagickPathExtent);
13766 montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13767 images),exception);
13768 images=DestroyImageList(images);
13769 montage_info=DestroyMontageInfo(montage_info);
13770 read_info=DestroyImageInfo(read_info);
13771 XSetCursorState(display,windows,MagickFalse);
13772 if (montage_image == (Image *) NULL)
13773 return(montage_image);
13774 XClientMessage(display,windows->image.id,windows->im_protocols,
13775 windows->im_next_image,CurrentTime);
13776 return(montage_image);
13777}
13778
13779/*
13780%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13781% %
13782% %
13783% %
13784% X D i s p l a y B a c k g r o u n d I m a g e %
13785% %
13786% %
13787% %
13788%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13789%
13790% XDisplayBackgroundImage() displays an image in the background of a window.
13791%
13792% The format of the XDisplayBackgroundImage method is:
13793%
13794% MagickBooleanType XDisplayBackgroundImage(Display *display,
13795% XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13796%
13797% A description of each parameter follows:
13798%
13799% o display: Specifies a connection to an X server; returned from
13800% XOpenDisplay.
13801%
13802% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13803%
13804% o image: the image.
13805%
13806% o exception: return any errors or warnings in this structure.
13807%
13808*/
13809MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13810 XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13811{
13812 char
13813 geometry[MagickPathExtent],
13814 visual_type[MagickPathExtent];
13815
13816 int
13817 height,
13818 status,
13819 width;
13820
13822 geometry_info;
13823
13824 static XPixelInfo
13825 pixel;
13826
13827 static XStandardColormap
13828 *map_info;
13829
13830 static XVisualInfo
13831 *visual_info = (XVisualInfo *) NULL;
13832
13833 static XWindowInfo
13834 window_info;
13835
13836 size_t
13837 delay;
13838
13839 Window
13840 root_window;
13841
13842 XGCValues
13843 context_values;
13844
13845 XResourceInfo
13846 resources;
13847
13848 XWindowAttributes
13849 window_attributes;
13850
13851 /*
13852 Determine target window.
13853 */
13854 assert(image != (Image *) NULL);
13855 assert(image->signature == MagickCoreSignature);
13856 if (IsEventLogging() != MagickFalse)
13857 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13858 resources=(*resource_info);
13859 window_info.id=(Window) NULL;
13860 root_window=XRootWindow(display,XDefaultScreen(display));
13861 if (LocaleCompare(resources.window_id,"root") == 0)
13862 window_info.id=root_window;
13863 else
13864 {
13865 if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13866 window_info.id=XWindowByID(display,root_window,
13867 (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13868 if (window_info.id == (Window) NULL)
13869 window_info.id=XWindowByName(display,root_window,resources.window_id);
13870 }
13871 if (window_info.id == (Window) NULL)
13872 {
13873 ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13874 resources.window_id);
13875 return(MagickFalse);
13876 }
13877 /*
13878 Determine window visual id.
13879 */
13880 window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13881 window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13882 (void) CopyMagickString(visual_type,"default",MagickPathExtent);
13883 status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13884 if (status != 0)
13885 (void) FormatLocaleString(visual_type,MagickPathExtent,"0x%lx",
13886 XVisualIDFromVisual(window_attributes.visual));
13887 if (visual_info == (XVisualInfo *) NULL)
13888 {
13889 /*
13890 Allocate standard colormap.
13891 */
13892 map_info=XAllocStandardColormap();
13893 if (map_info == (XStandardColormap *) NULL)
13894 ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13895 image->filename);
13896 map_info->colormap=(Colormap) NULL;
13897 pixel.pixels=(unsigned long *) NULL;
13898 /*
13899 Initialize visual info.
13900 */
13901 resources.map_type=(char *) NULL;
13902 resources.visual_type=visual_type;
13903 visual_info=XBestVisualInfo(display,map_info,&resources);
13904 if (visual_info == (XVisualInfo *) NULL)
13905 ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13906 resources.visual_type);
13907 /*
13908 Initialize window info.
13909 */
13910 window_info.ximage=(XImage *) NULL;
13911 window_info.matte_image=(XImage *) NULL;
13912 window_info.pixmap=(Pixmap) NULL;
13913 window_info.matte_pixmap=(Pixmap) NULL;
13914 }
13915 /*
13916 Free previous root colors.
13917 */
13918 if (window_info.id == root_window)
13919 (void) XDestroyWindowColors(display,root_window);
13920 /*
13921 Initialize Standard Colormap.
13922 */
13923 resources.colormap=SharedColormap;
13924 XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13925 exception);
13926 /*
13927 Graphic context superclass.
13928 */
13929 context_values.background=pixel.foreground_color.pixel;
13930 context_values.foreground=pixel.background_color.pixel;
13931 pixel.annotate_context=XCreateGC(display,window_info.id,
13932 (size_t) (GCBackground | GCForeground),&context_values);
13933 if (pixel.annotate_context == (GC) NULL)
13934 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13935 image->filename);
13936 /*
13937 Initialize Image window attributes.
13938 */
13939 window_info.name=AcquireString("\0");
13940 window_info.icon_name=AcquireString("\0");
13941 XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13942 &resources,&window_info);
13943 /*
13944 Create the X image.
13945 */
13946 window_info.width=(unsigned int) image->columns;
13947 window_info.height=(unsigned int) image->rows;
13948 if ((image->columns != window_info.width) ||
13949 (image->rows != window_info.height))
13950 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13951 image->filename);
13952 (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>",
13953 window_attributes.width,window_attributes.height);
13954 geometry_info.width=window_info.width;
13955 geometry_info.height=window_info.height;
13956 geometry_info.x=(ssize_t) window_info.x;
13957 geometry_info.y=(ssize_t) window_info.y;
13958 (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13959 &geometry_info.width,&geometry_info.height);
13960 window_info.width=(unsigned int) geometry_info.width;
13961 window_info.height=(unsigned int) geometry_info.height;
13962 window_info.x=(int) geometry_info.x;
13963 window_info.y=(int) geometry_info.y;
13964 status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13965 window_info.height,exception) == MagickFalse ? 0 : 1;
13966 if (status == MagickFalse)
13967 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13968 image->filename);
13969 window_info.x=0;
13970 window_info.y=0;
13971 if (resource_info->debug != MagickFalse)
13972 {
13973 (void) LogMagickEvent(X11Event,GetMagickModule(),
13974 "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13975 (double) image->columns,(double) image->rows);
13976 if (image->colors != 0)
13977 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13978 image->colors);
13979 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13980 }
13981 /*
13982 Adjust image dimensions as specified by backdrop or geometry options.
13983 */
13984 width=(int) window_info.width;
13985 height=(int) window_info.height;
13986 if (resources.backdrop != MagickFalse)
13987 {
13988 /*
13989 Center image on window.
13990 */
13991 window_info.x=(window_attributes.width/2)-(window_info.ximage->width/2);
13992 window_info.y=(window_attributes.height/2)-(window_info.ximage->height/2);
13993 width=window_attributes.width;
13994 height=window_attributes.height;
13995 }
13996 if ((resources.image_geometry != (char *) NULL) &&
13997 (*resources.image_geometry != '\0'))
13998 {
13999 char
14000 default_geometry[MagickPathExtent];
14001
14002 int
14003 flags,
14004 gravity;
14005
14006 XSizeHints
14007 *size_hints;
14008
14009 /*
14010 User specified geometry.
14011 */
14012 size_hints=XAllocSizeHints();
14013 if (size_hints == (XSizeHints *) NULL)
14014 ThrowXWindowFatalException(ResourceLimitFatalError,
14015 "MemoryAllocationFailed",image->filename);
14016 size_hints->flags=0L;
14017 (void) FormatLocaleString(default_geometry,MagickPathExtent,"%dx%d",
14018 width,height);
14019 flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
14020 default_geometry,window_info.border_width,size_hints,&window_info.x,
14021 &window_info.y,&width,&height,&gravity);
14022 if (flags & (XValue | YValue))
14023 {
14024 width=window_attributes.width;
14025 height=window_attributes.height;
14026 }
14027 (void) XFree((void *) size_hints);
14028 }
14029 /*
14030 Create the X pixmap.
14031 */
14032 window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14033 (unsigned int) height,window_info.depth);
14034 if (window_info.pixmap == (Pixmap) NULL)
14035 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14036 image->filename);
14037 /*
14038 Display pixmap on the window.
14039 */
14040 if (((unsigned int) width > window_info.width) ||
14041 ((unsigned int) height > window_info.height))
14042 (void) XFillRectangle(display,window_info.pixmap,
14043 window_info.annotate_context,0,0,(unsigned int) width,
14044 (unsigned int) height);
14045 (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14046 window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14047 window_info.width,(unsigned int) window_info.height);
14048 (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14049 (void) XClearWindow(display,window_info.id);
14050 delay=1000*image->delay/(size_t) MagickMax(image->ticks_per_second,1L);
14051 XDelay(display,delay == 0UL ? 10UL : delay);
14052 (void) XSync(display,MagickFalse);
14053 return(window_info.id == root_window ? MagickTrue : MagickFalse);
14054}
14055
14056/*
14057%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14058% %
14059% %
14060% %
14061+ X D i s p l a y I m a g e %
14062% %
14063% %
14064% %
14065%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14066%
14067% XDisplayImage() displays an image via X11. A new image is created and
14068% returned if the user interactively transforms the displayed image.
14069%
14070% The format of the XDisplayImage method is:
14071%
14072% Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14073% char **argv,int argc,Image **image,size_t *state,
14074% ExceptionInfo *exception)
14075%
14076% A description of each parameter follows:
14077%
14078% o nexus: Method XDisplayImage returns an image when the
14079% user chooses 'Open Image' from the command menu or picks a tile
14080% from the image directory. Otherwise a null image is returned.
14081%
14082% o display: Specifies a connection to an X server; returned from
14083% XOpenDisplay.
14084%
14085% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14086%
14087% o argv: Specifies the application's argument list.
14088%
14089% o argc: Specifies the number of arguments.
14090%
14091% o image: Specifies an address to an address of an Image structure;
14092%
14093% o exception: return any errors or warnings in this structure.
14094%
14095*/
14096MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14097 char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14098{
14099#define MagnifySize 256 /* must be a power of 2 */
14100#define MagickMenus 10
14101#define MagickTitle "Commands"
14102
14103 const char
14104 *const CommandMenu[] =
14105 {
14106 "File",
14107 "Edit",
14108 "View",
14109 "Transform",
14110 "Enhance",
14111 "Effects",
14112 "F/X",
14113 "Image Edit",
14114 "Miscellany",
14115 "Help",
14116 (char *) NULL
14117 },
14118 *const FileMenu[] =
14119 {
14120 "Open...",
14121 "Next",
14122 "Former",
14123 "Select...",
14124 "Save...",
14125 "Print...",
14126 "Delete...",
14127 "New...",
14128 "Visual Directory...",
14129 "Quit",
14130 (char *) NULL
14131 },
14132 *const EditMenu[] =
14133 {
14134 "Undo",
14135 "Redo",
14136 "Cut",
14137 "Copy",
14138 "Paste",
14139 (char *) NULL
14140 },
14141 *const ViewMenu[] =
14142 {
14143 "Half Size",
14144 "Original Size",
14145 "Double Size",
14146 "Resize...",
14147 "Apply",
14148 "Refresh",
14149 "Restore",
14150 (char *) NULL
14151 },
14152 *const TransformMenu[] =
14153 {
14154 "Crop",
14155 "Chop",
14156 "Flop",
14157 "Flip",
14158 "Rotate Right",
14159 "Rotate Left",
14160 "Rotate...",
14161 "Shear...",
14162 "Roll...",
14163 "Trim Edges",
14164 (char *) NULL
14165 },
14166 *const EnhanceMenu[] =
14167 {
14168 "Hue...",
14169 "Saturation...",
14170 "Brightness...",
14171 "Gamma...",
14172 "Spiff",
14173 "Dull",
14174 "Contrast Stretch...",
14175 "Sigmoidal Contrast...",
14176 "Normalize",
14177 "Equalize",
14178 "Negate",
14179 "Grayscale",
14180 "Map...",
14181 "Quantize...",
14182 (char *) NULL
14183 },
14184 *const EffectsMenu[] =
14185 {
14186 "Despeckle",
14187 "Emboss",
14188 "Reduce Noise",
14189 "Add Noise...",
14190 "Sharpen...",
14191 "Blur...",
14192 "Threshold...",
14193 "Edge Detect...",
14194 "Spread...",
14195 "Shade...",
14196 "Raise...",
14197 "Segment...",
14198 (char *) NULL
14199 },
14200 *const FXMenu[] =
14201 {
14202 "Solarize...",
14203 "Sepia Tone...",
14204 "Swirl...",
14205 "Implode...",
14206 "Vignette...",
14207 "Wave...",
14208 "Oil Paint...",
14209 "Charcoal Draw...",
14210 (char *) NULL
14211 },
14212 *const ImageEditMenu[] =
14213 {
14214 "Annotate...",
14215 "Draw...",
14216 "Color...",
14217 "Matte...",
14218 "Composite...",
14219 "Add Border...",
14220 "Add Frame...",
14221 "Comment...",
14222 "Launch...",
14223 "Region of Interest...",
14224 (char *) NULL
14225 },
14226 *const MiscellanyMenu[] =
14227 {
14228 "Image Info",
14229 "Zoom Image",
14230 "Show Preview...",
14231 "Show Histogram",
14232 "Show Matte",
14233 "Background...",
14234 "Slide Show...",
14235 "Preferences...",
14236 (char *) NULL
14237 },
14238 *const HelpMenu[] =
14239 {
14240 "Overview",
14241 "Browse Documentation",
14242 "About Display",
14243 (char *) NULL
14244 },
14245 *const ShortCutsMenu[] =
14246 {
14247 "Next",
14248 "Former",
14249 "Open...",
14250 "Save...",
14251 "Print...",
14252 "Undo",
14253 "Restore",
14254 "Image Info",
14255 "Quit",
14256 (char *) NULL
14257 },
14258 *const VirtualMenu[] =
14259 {
14260 "Image Info",
14261 "Print",
14262 "Next",
14263 "Quit",
14264 (char *) NULL
14265 };
14266
14267 const char
14268 *const *Menus[MagickMenus] =
14269 {
14270 FileMenu,
14271 EditMenu,
14272 ViewMenu,
14273 TransformMenu,
14274 EnhanceMenu,
14275 EffectsMenu,
14276 FXMenu,
14277 ImageEditMenu,
14278 MiscellanyMenu,
14279 HelpMenu
14280 };
14281
14282 static DisplayCommand
14283 CommandMenus[] =
14284 {
14285 NullCommand,
14286 NullCommand,
14287 NullCommand,
14288 NullCommand,
14289 NullCommand,
14290 NullCommand,
14291 NullCommand,
14292 NullCommand,
14293 NullCommand,
14294 NullCommand,
14295 },
14296 FileCommands[] =
14297 {
14298 OpenCommand,
14299 NextCommand,
14300 FormerCommand,
14301 SelectCommand,
14302 SaveCommand,
14303 PrintCommand,
14304 DeleteCommand,
14305 NewCommand,
14306 VisualDirectoryCommand,
14307 QuitCommand
14308 },
14309 EditCommands[] =
14310 {
14311 UndoCommand,
14312 RedoCommand,
14313 CutCommand,
14314 CopyCommand,
14315 PasteCommand
14316 },
14317 ViewCommands[] =
14318 {
14319 HalfSizeCommand,
14320 OriginalSizeCommand,
14321 DoubleSizeCommand,
14322 ResizeCommand,
14323 ApplyCommand,
14324 RefreshCommand,
14325 RestoreCommand
14326 },
14327 TransformCommands[] =
14328 {
14329 CropCommand,
14330 ChopCommand,
14331 FlopCommand,
14332 FlipCommand,
14333 RotateRightCommand,
14334 RotateLeftCommand,
14335 RotateCommand,
14336 ShearCommand,
14337 RollCommand,
14338 TrimCommand
14339 },
14340 EnhanceCommands[] =
14341 {
14342 HueCommand,
14343 SaturationCommand,
14344 BrightnessCommand,
14345 GammaCommand,
14346 SpiffCommand,
14347 DullCommand,
14348 ContrastStretchCommand,
14349 SigmoidalContrastCommand,
14350 NormalizeCommand,
14351 EqualizeCommand,
14352 NegateCommand,
14353 GrayscaleCommand,
14354 MapCommand,
14355 QuantizeCommand
14356 },
14357 EffectsCommands[] =
14358 {
14359 DespeckleCommand,
14360 EmbossCommand,
14361 ReduceNoiseCommand,
14362 AddNoiseCommand,
14363 SharpenCommand,
14364 BlurCommand,
14365 ThresholdCommand,
14366 EdgeDetectCommand,
14367 SpreadCommand,
14368 ShadeCommand,
14369 RaiseCommand,
14370 SegmentCommand
14371 },
14372 FXCommands[] =
14373 {
14374 SolarizeCommand,
14375 SepiaToneCommand,
14376 SwirlCommand,
14377 ImplodeCommand,
14378 VignetteCommand,
14379 WaveCommand,
14380 OilPaintCommand,
14381 CharcoalDrawCommand
14382 },
14383 ImageEditCommands[] =
14384 {
14385 AnnotateCommand,
14386 DrawCommand,
14387 ColorCommand,
14388 MatteCommand,
14389 CompositeCommand,
14390 AddBorderCommand,
14391 AddFrameCommand,
14392 CommentCommand,
14393 LaunchCommand,
14394 RegionOfInterestCommand
14395 },
14396 MiscellanyCommands[] =
14397 {
14398 InfoCommand,
14399 ZoomCommand,
14400 ShowPreviewCommand,
14401 ShowHistogramCommand,
14402 ShowMatteCommand,
14403 BackgroundCommand,
14404 SlideShowCommand,
14405 PreferencesCommand
14406 },
14407 HelpCommands[] =
14408 {
14409 HelpCommand,
14410 BrowseDocumentationCommand,
14411 VersionCommand
14412 },
14413 ShortCutsCommands[] =
14414 {
14415 NextCommand,
14416 FormerCommand,
14417 OpenCommand,
14418 SaveCommand,
14419 PrintCommand,
14420 UndoCommand,
14421 RestoreCommand,
14422 InfoCommand,
14423 QuitCommand
14424 },
14425 VirtualCommands[] =
14426 {
14427 InfoCommand,
14428 PrintCommand,
14429 NextCommand,
14430 QuitCommand
14431 };
14432
14433 static DisplayCommand
14434 *Commands[MagickMenus] =
14435 {
14436 FileCommands,
14437 EditCommands,
14438 ViewCommands,
14439 TransformCommands,
14440 EnhanceCommands,
14441 EffectsCommands,
14442 FXCommands,
14443 ImageEditCommands,
14444 MiscellanyCommands,
14445 HelpCommands
14446 };
14447
14448 char
14449 command[MagickPathExtent],
14450 *directory,
14451 geometry[MagickPathExtent],
14452 resource_name[MagickPathExtent];
14453
14454 DisplayCommand
14455 display_command;
14456
14457 Image
14458 *display_image,
14459 *nexus;
14460
14461 int
14462 entry,
14463 id;
14464
14465 KeySym
14466 key_symbol;
14467
14468 MagickStatusType
14469 context_mask,
14470 status;
14471
14473 geometry_info;
14474
14475 int
14476 i;
14477
14478 static char
14479 working_directory[MagickPathExtent];
14480
14481 static XPoint
14482 vid_info;
14483
14484 static XWindowInfo
14485 *magick_windows[MaxXWindows];
14486
14487 static unsigned int
14488 number_windows;
14489
14490 struct stat
14491 attributes;
14492
14493 time_t
14494 timer,
14495 timestamp,
14496 update_time;
14497
14498 unsigned int
14499 height,
14500 width;
14501
14502 size_t
14503 delay;
14504
14505 WarningHandler
14506 warning_handler;
14507
14508 Window
14509 root_window;
14510
14511 XClassHint
14512 *class_hints;
14513
14514 XEvent
14515 event;
14516
14517 XFontStruct
14518 *font_info;
14519
14520 XGCValues
14521 context_values;
14522
14523 XPixelInfo
14524 *icon_pixel,
14525 *pixel;
14526
14527 XResourceInfo
14528 *icon_resources;
14529
14530 XStandardColormap
14531 *icon_map,
14532 *map_info;
14533
14534 XVisualInfo
14535 *icon_visual,
14536 *visual_info;
14537
14538 XWindowChanges
14539 window_changes;
14540
14541 XWindows
14542 *windows;
14543
14544 XWMHints
14545 *manager_hints;
14546
14547 assert(image != (Image **) NULL);
14548 assert((*image)->signature == MagickCoreSignature);
14549 if (IsEventLogging() != MagickFalse)
14550 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14551 display_image=(*image);
14552 warning_handler=(WarningHandler) NULL;
14553 windows=XSetWindows((XWindows *) ~0);
14554 if (windows != (XWindows *) NULL)
14555 {
14556 int
14557 status;
14558
14559 if (*working_directory == '\0')
14560 (void) CopyMagickString(working_directory,".",MagickPathExtent);
14561 status=chdir(working_directory);
14562 if (status == -1)
14563 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14564 "UnableToOpenFile","%s",working_directory);
14565 warning_handler=resource_info->display_warnings ?
14566 SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14567 warning_handler=resource_info->display_warnings ?
14568 SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14569 }
14570 else
14571 {
14572 /*
14573 Allocate windows structure.
14574 */
14575 resource_info->colors=display_image->colors;
14576 windows=XSetWindows(XInitializeWindows(display,resource_info));
14577 if (windows == (XWindows *) NULL)
14578 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14579 (*image)->filename);
14580 /*
14581 Initialize window id's.
14582 */
14583 number_windows=0;
14584 magick_windows[number_windows++]=(&windows->icon);
14585 magick_windows[number_windows++]=(&windows->backdrop);
14586 magick_windows[number_windows++]=(&windows->image);
14587 magick_windows[number_windows++]=(&windows->info);
14588 magick_windows[number_windows++]=(&windows->command);
14589 magick_windows[number_windows++]=(&windows->widget);
14590 magick_windows[number_windows++]=(&windows->popup);
14591 magick_windows[number_windows++]=(&windows->magnify);
14592 magick_windows[number_windows++]=(&windows->pan);
14593 for (i=0; i < (int) number_windows; i++)
14594 magick_windows[i]->id=(Window) NULL;
14595 vid_info.x=0;
14596 vid_info.y=0;
14597 }
14598 /*
14599 Initialize font info.
14600 */
14601 if (windows->font_info != (XFontStruct *) NULL)
14602 (void) XFreeFont(display,windows->font_info);
14603 windows->font_info=XBestFont(display,resource_info,MagickFalse);
14604 if (windows->font_info == (XFontStruct *) NULL)
14605 ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14606 resource_info->font);
14607 /*
14608 Initialize Standard Colormap.
14609 */
14610 map_info=windows->map_info;
14611 icon_map=windows->icon_map;
14612 visual_info=windows->visual_info;
14613 icon_visual=windows->icon_visual;
14614 pixel=windows->pixel_info;
14615 icon_pixel=windows->icon_pixel;
14616 font_info=windows->font_info;
14617 icon_resources=windows->icon_resources;
14618 class_hints=windows->class_hints;
14619 manager_hints=windows->manager_hints;
14620 root_window=XRootWindow(display,visual_info->screen);
14621 nexus=NewImageList();
14622 if (resource_info->debug != MagickFalse)
14623 {
14624 (void) LogMagickEvent(X11Event,GetMagickModule(),
14625 "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14626 (double) display_image->scene,(double) display_image->columns,
14627 (double) display_image->rows);
14628 if (display_image->colors != 0)
14629 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14630 display_image->colors);
14631 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14632 display_image->magick);
14633 }
14634 XMakeStandardColormap(display,visual_info,resource_info,display_image,
14635 map_info,pixel,exception);
14636 display_image->taint=MagickFalse;
14637 /*
14638 Initialize graphic context.
14639 */
14640 windows->context.id=(Window) NULL;
14641 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14642 resource_info,&windows->context);
14643 (void) CloneString(&class_hints->res_name,resource_info->client_name);
14644 (void) CloneString(&class_hints->res_class,resource_info->client_name);
14645 class_hints->res_class[0]=(char) LocaleToUppercase((int)
14646 class_hints->res_class[0]);
14647 manager_hints->flags=InputHint | StateHint;
14648 manager_hints->input=MagickFalse;
14649 manager_hints->initial_state=WithdrawnState;
14650 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14651 &windows->context);
14652 if (resource_info->debug != MagickFalse)
14653 (void) LogMagickEvent(X11Event,GetMagickModule(),
14654 "Window id: 0x%lx (context)",windows->context.id);
14655 context_values.background=pixel->background_color.pixel;
14656 context_values.font=font_info->fid;
14657 context_values.foreground=pixel->foreground_color.pixel;
14658 context_values.graphics_exposures=MagickFalse;
14659 context_mask=(MagickStatusType)
14660 (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14661 if (pixel->annotate_context != (GC) NULL)
14662 (void) XFreeGC(display,pixel->annotate_context);
14663 pixel->annotate_context=XCreateGC(display,windows->context.id,
14664 context_mask,&context_values);
14665 if (pixel->annotate_context == (GC) NULL)
14666 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14667 display_image->filename);
14668 context_values.background=pixel->depth_color.pixel;
14669 if (pixel->widget_context != (GC) NULL)
14670 (void) XFreeGC(display,pixel->widget_context);
14671 pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14672 &context_values);
14673 if (pixel->widget_context == (GC) NULL)
14674 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14675 display_image->filename);
14676 context_values.background=pixel->foreground_color.pixel;
14677 context_values.foreground=pixel->background_color.pixel;
14678 context_values.plane_mask=context_values.background ^
14679 context_values.foreground;
14680 if (pixel->highlight_context != (GC) NULL)
14681 (void) XFreeGC(display,pixel->highlight_context);
14682 pixel->highlight_context=XCreateGC(display,windows->context.id,
14683 (size_t) (context_mask | GCPlaneMask),&context_values);
14684 if (pixel->highlight_context == (GC) NULL)
14685 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14686 display_image->filename);
14687 (void) XDestroyWindow(display,windows->context.id);
14688 /*
14689 Initialize icon window.
14690 */
14691 XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14692 icon_resources,&windows->icon);
14693 windows->icon.geometry=resource_info->icon_geometry;
14694 XBestIconSize(display,&windows->icon,display_image);
14695 windows->icon.attributes.colormap=XDefaultColormap(display,
14696 icon_visual->screen);
14697 windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14698 manager_hints->flags=InputHint | StateHint;
14699 manager_hints->input=MagickFalse;
14700 manager_hints->initial_state=IconicState;
14701 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14702 &windows->icon);
14703 if (resource_info->debug != MagickFalse)
14704 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14705 windows->icon.id);
14706 /*
14707 Initialize graphic context for icon window.
14708 */
14709 if (icon_pixel->annotate_context != (GC) NULL)
14710 (void) XFreeGC(display,icon_pixel->annotate_context);
14711 context_values.background=icon_pixel->background_color.pixel;
14712 context_values.foreground=icon_pixel->foreground_color.pixel;
14713 icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14714 (size_t) (GCBackground | GCForeground),&context_values);
14715 if (icon_pixel->annotate_context == (GC) NULL)
14716 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14717 display_image->filename);
14718 windows->icon.annotate_context=icon_pixel->annotate_context;
14719 /*
14720 Initialize Image window.
14721 */
14722 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14723 &windows->image);
14724 windows->image.shape=MagickTrue; /* non-rectangular shape hint */
14725 if (resource_info->use_shared_memory == MagickFalse)
14726 windows->image.shared_memory=MagickFalse;
14727 if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14728 {
14729 char
14730 *title;
14731
14732 title=InterpretImageProperties(resource_info->image_info,display_image,
14733 resource_info->title,exception);
14734 (void) CloneString(&windows->image.name,title);
14735 (void) CloneString(&windows->image.icon_name,title);
14736 title=DestroyString(title);
14737 }
14738 else
14739 {
14740 char
14741 filename[MagickPathExtent],
14742 window_name[MagickPathExtent];
14743
14744 /*
14745 Window name is the base of the filename.
14746 */
14747 GetPathComponent(display_image->magick_filename,TailPath,filename);
14748 if (display_image->scene == 0)
14749 (void) FormatLocaleString(window_name,MagickPathExtent,"%s: %s",
14750 MagickPackageName,filename);
14751 else
14752 (void) FormatLocaleString(window_name,MagickPathExtent,
14753 "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14754 (double) display_image->scene,(double) GetImageListLength(
14755 display_image));
14756 (void) CloneString(&windows->image.name,window_name);
14757 (void) CloneString(&windows->image.icon_name,filename);
14758 }
14759 if (resource_info->immutable)
14760 windows->image.immutable=MagickTrue;
14761 windows->image.use_pixmap=resource_info->use_pixmap;
14762 windows->image.geometry=resource_info->image_geometry;
14763 (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
14764 XDisplayWidth(display,visual_info->screen),
14765 XDisplayHeight(display,visual_info->screen));
14766 geometry_info.width=display_image->columns;
14767 geometry_info.height=display_image->rows;
14768 geometry_info.x=0;
14769 geometry_info.y=0;
14770 (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14771 &geometry_info.width,&geometry_info.height);
14772 windows->image.width=(unsigned int) geometry_info.width;
14773 windows->image.height=(unsigned int) geometry_info.height;
14774 windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14775 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14776 KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14777 PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14778 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14779 resource_info,&windows->backdrop);
14780 if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14781 {
14782 /*
14783 Initialize backdrop window.
14784 */
14785 windows->backdrop.x=0;
14786 windows->backdrop.y=0;
14787 (void) CloneString(&windows->backdrop.name,"Backdrop");
14788 windows->backdrop.flags=(size_t) (USSize | USPosition);
14789 windows->backdrop.width=(unsigned int)
14790 XDisplayWidth(display,visual_info->screen);
14791 windows->backdrop.height=(unsigned int)
14792 XDisplayHeight(display,visual_info->screen);
14793 windows->backdrop.border_width=0;
14794 windows->backdrop.immutable=MagickTrue;
14795 windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14796 ButtonReleaseMask;
14797 windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14798 StructureNotifyMask;
14799 manager_hints->flags=IconWindowHint | InputHint | StateHint;
14800 manager_hints->icon_window=windows->icon.id;
14801 manager_hints->input=MagickTrue;
14802 manager_hints->initial_state=resource_info->iconic ? IconicState :
14803 NormalState;
14804 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14805 &windows->backdrop);
14806 if (resource_info->debug != MagickFalse)
14807 (void) LogMagickEvent(X11Event,GetMagickModule(),
14808 "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14809 (void) XMapWindow(display,windows->backdrop.id);
14810 (void) XClearWindow(display,windows->backdrop.id);
14811 if (windows->image.id != (Window) NULL)
14812 {
14813 (void) XDestroyWindow(display,windows->image.id);
14814 windows->image.id=(Window) NULL;
14815 }
14816 /*
14817 Position image in the center the backdrop.
14818 */
14819 windows->image.flags|=USPosition;
14820 windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14821 ((int) windows->image.width/2);
14822 windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14823 ((int) windows->image.height/2);
14824 }
14825 manager_hints->flags=IconWindowHint | InputHint | StateHint;
14826 manager_hints->icon_window=windows->icon.id;
14827 manager_hints->input=MagickTrue;
14828 manager_hints->initial_state=resource_info->iconic ? IconicState :
14829 NormalState;
14830 if (windows->group_leader.id != (Window) NULL)
14831 {
14832 /*
14833 Follow the leader.
14834 */
14835 manager_hints->flags|=WindowGroupHint;
14836 manager_hints->window_group=windows->group_leader.id;
14837 (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14838 if (resource_info->debug != MagickFalse)
14839 (void) LogMagickEvent(X11Event,GetMagickModule(),
14840 "Window id: 0x%lx (group leader)",windows->group_leader.id);
14841 }
14842 XMakeWindow(display,
14843 (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14844 argv,argc,class_hints,manager_hints,&windows->image);
14845 (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14846 XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14847 if (windows->group_leader.id != (Window) NULL)
14848 (void) XSetTransientForHint(display,windows->image.id,
14849 windows->group_leader.id);
14850 if (resource_info->debug != MagickFalse)
14851 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14852 windows->image.id);
14853 /*
14854 Initialize Info widget.
14855 */
14856 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14857 &windows->info);
14858 (void) CloneString(&windows->info.name,"Info");
14859 (void) CloneString(&windows->info.icon_name,"Info");
14860 windows->info.border_width=1;
14861 windows->info.x=2;
14862 windows->info.y=2;
14863 windows->info.flags|=PPosition;
14864 windows->info.attributes.win_gravity=UnmapGravity;
14865 windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14866 StructureNotifyMask;
14867 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14868 manager_hints->input=MagickFalse;
14869 manager_hints->initial_state=NormalState;
14870 manager_hints->window_group=windows->image.id;
14871 XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14872 &windows->info);
14873 windows->info.highlight_stipple=XCreateBitmapFromData(display,
14874 windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14875 windows->info.shadow_stipple=XCreateBitmapFromData(display,
14876 windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14877 (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14878 if (windows->image.mapped != MagickFalse)
14879 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14880 if (resource_info->debug != MagickFalse)
14881 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14882 windows->info.id);
14883 /*
14884 Initialize Command widget.
14885 */
14886 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14887 resource_info,&windows->command);
14888 windows->command.data=MagickMenus;
14889 (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14890 (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.command",
14891 resource_info->client_name);
14892 windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14893 resource_name,"geometry",(char *) NULL);
14894 (void) CloneString(&windows->command.name,MagickTitle);
14895 windows->command.border_width=0;
14896 windows->command.flags|=PPosition;
14897 windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14898 ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14899 OwnerGrabButtonMask | StructureNotifyMask;
14900 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14901 manager_hints->input=MagickTrue;
14902 manager_hints->initial_state=NormalState;
14903 manager_hints->window_group=windows->image.id;
14904 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14905 &windows->command);
14906 windows->command.highlight_stipple=XCreateBitmapFromData(display,
14907 windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14908 HighlightHeight);
14909 windows->command.shadow_stipple=XCreateBitmapFromData(display,
14910 windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14911 (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14912 if (windows->command.mapped != MagickFalse)
14913 (void) XMapRaised(display,windows->command.id);
14914 if (resource_info->debug != MagickFalse)
14915 (void) LogMagickEvent(X11Event,GetMagickModule(),
14916 "Window id: 0x%lx (command)",windows->command.id);
14917 /*
14918 Initialize Widget window.
14919 */
14920 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14921 resource_info,&windows->widget);
14922 (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.widget",
14923 resource_info->client_name);
14924 windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14925 resource_name,"geometry",(char *) NULL);
14926 windows->widget.border_width=0;
14927 windows->widget.flags|=PPosition;
14928 windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14929 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14930 KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14931 StructureNotifyMask;
14932 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14933 manager_hints->input=MagickTrue;
14934 manager_hints->initial_state=NormalState;
14935 manager_hints->window_group=windows->image.id;
14936 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14937 &windows->widget);
14938 windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14939 windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14940 windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14941 windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14942 (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14943 if (resource_info->debug != MagickFalse)
14944 (void) LogMagickEvent(X11Event,GetMagickModule(),
14945 "Window id: 0x%lx (widget)",windows->widget.id);
14946 /*
14947 Initialize popup window.
14948 */
14949 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14950 resource_info,&windows->popup);
14951 windows->popup.border_width=0;
14952 windows->popup.flags|=PPosition;
14953 windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14954 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14955 KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14956 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14957 manager_hints->input=MagickTrue;
14958 manager_hints->initial_state=NormalState;
14959 manager_hints->window_group=windows->image.id;
14960 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14961 &windows->popup);
14962 windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14963 windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14964 windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14965 windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14966 (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14967 if (resource_info->debug != MagickFalse)
14968 (void) LogMagickEvent(X11Event,GetMagickModule(),
14969 "Window id: 0x%lx (pop up)",windows->popup.id);
14970 /*
14971 Initialize Magnify window and cursor.
14972 */
14973 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14974 resource_info,&windows->magnify);
14975 if (resource_info->use_shared_memory == MagickFalse)
14976 windows->magnify.shared_memory=MagickFalse;
14977 (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.magnify",
14978 resource_info->client_name);
14979 windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14980 resource_name,"geometry",(char *) NULL);
14981 (void) FormatLocaleString(windows->magnify.name,MagickPathExtent,
14982 "Magnify %uX",resource_info->magnify);
14983 if (windows->magnify.cursor != (Cursor) NULL)
14984 (void) XFreeCursor(display,windows->magnify.cursor);
14985 windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14986 map_info->colormap,resource_info->background_color,
14987 resource_info->foreground_color);
14988 if (windows->magnify.cursor == (Cursor) NULL)
14989 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14990 display_image->filename);
14991 windows->magnify.width=MagnifySize;
14992 windows->magnify.height=MagnifySize;
14993 windows->magnify.flags|=PPosition;
14994 windows->magnify.min_width=MagnifySize;
14995 windows->magnify.min_height=MagnifySize;
14996 windows->magnify.width_inc=MagnifySize;
14997 windows->magnify.height_inc=MagnifySize;
14998 windows->magnify.data=resource_info->magnify;
14999 windows->magnify.attributes.cursor=windows->magnify.cursor;
15000 windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
15001 ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
15002 StructureNotifyMask;
15003 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15004 manager_hints->input=MagickTrue;
15005 manager_hints->initial_state=NormalState;
15006 manager_hints->window_group=windows->image.id;
15007 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15008 &windows->magnify);
15009 if (resource_info->debug != MagickFalse)
15010 (void) LogMagickEvent(X11Event,GetMagickModule(),
15011 "Window id: 0x%lx (magnify)",windows->magnify.id);
15012 (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
15013 /*
15014 Initialize panning window.
15015 */
15016 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
15017 resource_info,&windows->pan);
15018 (void) CloneString(&windows->pan.name,"Pan Icon");
15019 windows->pan.width=windows->icon.width;
15020 windows->pan.height=windows->icon.height;
15021 (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.pan",
15022 resource_info->client_name);
15023 windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
15024 resource_name,"geometry",(char *) NULL);
15025 (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15026 &windows->pan.width,&windows->pan.height);
15027 windows->pan.flags|=PPosition;
15028 windows->pan.immutable=MagickTrue;
15029 windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15030 ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15031 StructureNotifyMask;
15032 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15033 manager_hints->input=MagickFalse;
15034 manager_hints->initial_state=NormalState;
15035 manager_hints->window_group=windows->image.id;
15036 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15037 &windows->pan);
15038 if (resource_info->debug != MagickFalse)
15039 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15040 windows->pan.id);
15041 (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15042 if (windows->info.mapped != MagickFalse)
15043 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15044 if ((windows->image.mapped == MagickFalse) ||
15045 (windows->backdrop.id != (Window) NULL))
15046 (void) XMapWindow(display,windows->image.id);
15047 /*
15048 Set our progress monitor and warning handlers.
15049 */
15050 if (warning_handler == (WarningHandler) NULL)
15051 {
15052 warning_handler=resource_info->display_warnings ?
15053 SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15054 warning_handler=resource_info->display_warnings ?
15055 SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15056 }
15057 /*
15058 Initialize Image and Magnify X images.
15059 */
15060 windows->image.x=0;
15061 windows->image.y=0;
15062 windows->magnify.shape=MagickFalse;
15063 width=(unsigned int) display_image->columns;
15064 height=(unsigned int) display_image->rows;
15065 if ((display_image->columns != width) || (display_image->rows != height))
15066 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15067 display_image->filename);
15068 status=XMakeImage(display,resource_info,&windows->image,display_image,
15069 width,height,exception);
15070 if (status == MagickFalse)
15071 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15072 display_image->filename);
15073 status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15074 windows->magnify.width,windows->magnify.height,exception);
15075 if (status == MagickFalse)
15076 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15077 display_image->filename);
15078 if (windows->magnify.mapped != MagickFalse)
15079 (void) XMapRaised(display,windows->magnify.id);
15080 if (windows->pan.mapped != MagickFalse)
15081 (void) XMapRaised(display,windows->pan.id);
15082 windows->image.window_changes.width=(int) display_image->columns;
15083 windows->image.window_changes.height=(int) display_image->rows;
15084 (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15085 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15086 (void) XSync(display,MagickFalse);
15087 /*
15088 Respond to events.
15089 */
15090 delay=display_image->delay/(size_t)
15091 MagickMax(display_image->ticks_per_second,1L);
15092 timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15093 update_time=0;
15094 if (resource_info->update != MagickFalse)
15095 {
15096 MagickBooleanType
15097 status;
15098
15099 /*
15100 Determine when file data was last modified.
15101 */
15102 status=GetPathAttributes(display_image->filename,&attributes);
15103 if (status != MagickFalse)
15104 update_time=attributes.st_mtime;
15105 }
15106 *state&=(unsigned int) (~FormerImageState);
15107 *state&=(unsigned int) (~MontageImageState);
15108 *state&=(unsigned int) (~NextImageState);
15109 do
15110 {
15111 /*
15112 Handle a window event.
15113 */
15114 if (windows->image.mapped != MagickFalse)
15115 if ((display_image->delay != 0) || (resource_info->update != 0))
15116 {
15117 if (timer < GetMagickTime())
15118 {
15119 if (resource_info->update == MagickFalse)
15120 *state|=NextImageState | ExitState;
15121 else
15122 {
15123 MagickBooleanType
15124 status;
15125
15126 /*
15127 Determine if image file was modified.
15128 */
15129 status=GetPathAttributes(display_image->filename,&attributes);
15130 if (status != MagickFalse)
15131 if (update_time != attributes.st_mtime)
15132 {
15133 /*
15134 Redisplay image.
15135 */
15136 (void) FormatLocaleString(
15137 resource_info->image_info->filename,MagickPathExtent,
15138 "%s:%s",display_image->magick,
15139 display_image->filename);
15140 nexus=ReadImage(resource_info->image_info,exception);
15141 if (nexus != (Image *) NULL)
15142 *state|=NextImageState | ExitState;
15143 }
15144 delay=display_image->delay/(size_t) MagickMax(
15145 display_image->ticks_per_second,1L);
15146 timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15147 }
15148 }
15149 if (XEventsQueued(display,QueuedAfterFlush) == 0)
15150 {
15151 /*
15152 Do not block if delay > 0.
15153 */
15154 XDelay(display,SuspendTime << 2);
15155 continue;
15156 }
15157 }
15158 timestamp=GetMagickTime();
15159 (void) XNextEvent(display,&event);
15160 if ((windows->image.stasis == MagickFalse) ||
15161 (windows->magnify.stasis == MagickFalse))
15162 {
15163 if ((GetMagickTime()-timestamp) > 0)
15164 {
15165 windows->image.stasis=MagickTrue;
15166 windows->magnify.stasis=MagickTrue;
15167 }
15168 }
15169 if (event.xany.window == windows->command.id)
15170 {
15171 /*
15172 Select a command from the Command widget.
15173 */
15174 id=XCommandWidget(display,windows,CommandMenu,&event);
15175 if (id < 0)
15176 continue;
15177 (void) CopyMagickString(command,CommandMenu[id],MagickPathExtent);
15178 display_command=CommandMenus[id];
15179 if (id < MagickMenus)
15180 {
15181 /*
15182 Select a command from a pop-up menu.
15183 */
15184 entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15185 command);
15186 if (entry < 0)
15187 continue;
15188 (void) CopyMagickString(command,Menus[id][entry],MagickPathExtent);
15189 display_command=Commands[id][entry];
15190 }
15191 if (display_command != NullCommand)
15192 nexus=XMagickCommand(display,resource_info,windows,display_command,
15193 &display_image,exception);
15194 continue;
15195 }
15196 switch (event.type)
15197 {
15198 case ButtonPress:
15199 {
15200 if (resource_info->debug != MagickFalse)
15201 (void) LogMagickEvent(X11Event,GetMagickModule(),
15202 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15203 event.xbutton.button,event.xbutton.x,event.xbutton.y);
15204 if ((event.xbutton.button == Button3) &&
15205 (event.xbutton.state & Mod1Mask))
15206 {
15207 /*
15208 Convert Alt-Button3 to Button2.
15209 */
15210 event.xbutton.button=Button2;
15211 event.xbutton.state&=(unsigned int) (~Mod1Mask);
15212 }
15213 if (event.xbutton.window == windows->backdrop.id)
15214 {
15215 (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15216 event.xbutton.time);
15217 break;
15218 }
15219 if (event.xbutton.window == windows->image.id)
15220 {
15221 switch (event.xbutton.button)
15222 {
15223 case Button1:
15224 {
15225 if (resource_info->immutable)
15226 {
15227 /*
15228 Select a command from the Virtual menu.
15229 */
15230 entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15231 command);
15232 if (entry >= 0)
15233 nexus=XMagickCommand(display,resource_info,windows,
15234 VirtualCommands[entry],&display_image,exception);
15235 break;
15236 }
15237 /*
15238 Map/unmap Command widget.
15239 */
15240 if (windows->command.mapped != MagickFalse)
15241 (void) XWithdrawWindow(display,windows->command.id,
15242 windows->command.screen);
15243 else
15244 {
15245 (void) XCommandWidget(display,windows,CommandMenu,
15246 (XEvent *) NULL);
15247 (void) XMapRaised(display,windows->command.id);
15248 }
15249 break;
15250 }
15251 case Button2:
15252 {
15253 /*
15254 User pressed the image magnify button.
15255 */
15256 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15257 &display_image,exception);
15258 XMagnifyImage(display,windows,&event,exception);
15259 break;
15260 }
15261 case Button3:
15262 {
15263 if (resource_info->immutable)
15264 {
15265 /*
15266 Select a command from the Virtual menu.
15267 */
15268 entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15269 command);
15270 if (entry >= 0)
15271 nexus=XMagickCommand(display,resource_info,windows,
15272 VirtualCommands[entry],&display_image,exception);
15273 break;
15274 }
15275 if (display_image->montage != (char *) NULL)
15276 {
15277 /*
15278 Open or delete a tile from a visual image directory.
15279 */
15280 nexus=XTileImage(display,resource_info,windows,
15281 display_image,&event,exception);
15282 if (nexus != (Image *) NULL)
15283 *state|=MontageImageState | NextImageState | ExitState;
15284 vid_info.x=(short int) windows->image.x;
15285 vid_info.y=(short int) windows->image.y;
15286 break;
15287 }
15288 /*
15289 Select a command from the Short Cuts menu.
15290 */
15291 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15292 command);
15293 if (entry >= 0)
15294 nexus=XMagickCommand(display,resource_info,windows,
15295 ShortCutsCommands[entry],&display_image,exception);
15296 break;
15297 }
15298 case Button4:
15299 {
15300 /*
15301 Wheel up.
15302 */
15303 XTranslateImage(display,windows,*image,XK_Up);
15304 break;
15305 }
15306 case Button5:
15307 {
15308 /*
15309 Wheel down.
15310 */
15311 XTranslateImage(display,windows,*image,XK_Down);
15312 break;
15313 }
15314 default:
15315 break;
15316 }
15317 break;
15318 }
15319 if (event.xbutton.window == windows->magnify.id)
15320 {
15321 const char
15322 *const MagnifyMenu[] =
15323 {
15324 "2",
15325 "4",
15326 "5",
15327 "6",
15328 "7",
15329 "8",
15330 "9",
15331 "3",
15332 (char *) NULL,
15333 };
15334
15335 int
15336 factor;
15337
15338 static KeySym
15339 MagnifyCommands[] =
15340 {
15341 XK_2,
15342 XK_4,
15343 XK_5,
15344 XK_6,
15345 XK_7,
15346 XK_8,
15347 XK_9,
15348 XK_3
15349 };
15350
15351 /*
15352 Select a magnify factor from the pop-up menu.
15353 */
15354 factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15355 if (factor >= 0)
15356 XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15357 exception);
15358 break;
15359 }
15360 if (event.xbutton.window == windows->pan.id)
15361 {
15362 switch (event.xbutton.button)
15363 {
15364 case Button4:
15365 {
15366 /*
15367 Wheel up.
15368 */
15369 XTranslateImage(display,windows,*image,XK_Up);
15370 break;
15371 }
15372 case Button5:
15373 {
15374 /*
15375 Wheel down.
15376 */
15377 XTranslateImage(display,windows,*image,XK_Down);
15378 break;
15379 }
15380 default:
15381 {
15382 XPanImage(display,windows,&event,exception);
15383 break;
15384 }
15385 }
15386 break;
15387 }
15388 delay=display_image->delay/(size_t)
15389 MagickMax(display_image->ticks_per_second,1L);
15390 timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15391 break;
15392 }
15393 case ButtonRelease:
15394 {
15395 if (resource_info->debug != MagickFalse)
15396 (void) LogMagickEvent(X11Event,GetMagickModule(),
15397 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15398 event.xbutton.button,event.xbutton.x,event.xbutton.y);
15399 break;
15400 }
15401 case ClientMessage:
15402 {
15403 if (resource_info->debug != MagickFalse)
15404 (void) LogMagickEvent(X11Event,GetMagickModule(),
15405 "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15406 event.xclient.message_type,event.xclient.format,(unsigned long)
15407 event.xclient.data.l[0]);
15408 if (event.xclient.message_type == windows->im_protocols)
15409 {
15410 if (*event.xclient.data.l == (long) windows->im_update_widget)
15411 {
15412 (void) CloneString(&windows->command.name,MagickTitle);
15413 windows->command.data=MagickMenus;
15414 (void) XCommandWidget(display,windows,CommandMenu,
15415 (XEvent *) NULL);
15416 break;
15417 }
15418 if (*event.xclient.data.l == (long) windows->im_update_colormap)
15419 {
15420 /*
15421 Update graphic context and window colormap.
15422 */
15423 for (i=0; i < (int) number_windows; i++)
15424 {
15425 if (magick_windows[i]->id == windows->icon.id)
15426 continue;
15427 context_values.background=pixel->background_color.pixel;
15428 context_values.foreground=pixel->foreground_color.pixel;
15429 (void) XChangeGC(display,magick_windows[i]->annotate_context,
15430 context_mask,&context_values);
15431 (void) XChangeGC(display,magick_windows[i]->widget_context,
15432 context_mask,&context_values);
15433 context_values.background=pixel->foreground_color.pixel;
15434 context_values.foreground=pixel->background_color.pixel;
15435 context_values.plane_mask=context_values.background ^
15436 context_values.foreground;
15437 (void) XChangeGC(display,magick_windows[i]->highlight_context,
15438 (size_t) (context_mask | GCPlaneMask),
15439 &context_values);
15440 magick_windows[i]->attributes.background_pixel=
15441 pixel->background_color.pixel;
15442 magick_windows[i]->attributes.border_pixel=
15443 pixel->border_color.pixel;
15444 magick_windows[i]->attributes.colormap=map_info->colormap;
15445 (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15446 (unsigned long) magick_windows[i]->mask,
15447 &magick_windows[i]->attributes);
15448 }
15449 if (windows->pan.mapped != MagickFalse)
15450 {
15451 (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15452 windows->pan.pixmap);
15453 (void) XClearWindow(display,windows->pan.id);
15454 XDrawPanRectangle(display,windows);
15455 }
15456 if (windows->backdrop.id != (Window) NULL)
15457 (void) XInstallColormap(display,map_info->colormap);
15458 break;
15459 }
15460 if (*event.xclient.data.l == (long) windows->im_former_image)
15461 {
15462 *state|=FormerImageState | ExitState;
15463 break;
15464 }
15465 if (*event.xclient.data.l == (long) windows->im_next_image)
15466 {
15467 *state|=NextImageState | ExitState;
15468 break;
15469 }
15470 if (*event.xclient.data.l == (long) windows->im_retain_colors)
15471 {
15472 *state|=RetainColorsState;
15473 break;
15474 }
15475 if (*event.xclient.data.l == (long) windows->im_exit)
15476 {
15477 *state|=ExitState;
15478 break;
15479 }
15480 break;
15481 }
15482 if (event.xclient.message_type == windows->dnd_protocols)
15483 {
15484 Atom
15485 selection,
15486 type;
15487
15488 int
15489 format,
15490 status;
15491
15492 unsigned char
15493 *data;
15494
15495 unsigned long
15496 after,
15497 length;
15498
15499 /*
15500 Display image named by the Drag-and-Drop selection.
15501 */
15502 if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15503 break;
15504 selection=XInternAtom(display,"DndSelection",MagickFalse);
15505 status=XGetWindowProperty(display,root_window,selection,0L,(long)
15506 MagickPathExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15507 &length,&after,&data);
15508 if ((status != Success) || (length == 0))
15509 break;
15510 if (*event.xclient.data.l == 2)
15511 {
15512 /*
15513 Offix DND.
15514 */
15515 (void) CopyMagickString(resource_info->image_info->filename,
15516 (char *) data,MagickPathExtent);
15517 }
15518 else
15519 {
15520 /*
15521 XDND.
15522 */
15523 if (strncmp((char *) data, "file:", 5) != 0)
15524 {
15525 (void) XFree((void *) data);
15526 break;
15527 }
15528 (void) CopyMagickString(resource_info->image_info->filename,
15529 ((char *) data)+5,MagickPathExtent);
15530 }
15531 nexus=ReadImage(resource_info->image_info,exception);
15532 CatchException(exception);
15533 if (nexus != (Image *) NULL)
15534 *state|=NextImageState | ExitState;
15535 (void) XFree((void *) data);
15536 break;
15537 }
15538 /*
15539 If client window delete message, exit.
15540 */
15541 if (event.xclient.message_type != windows->wm_protocols)
15542 break;
15543 if (*event.xclient.data.l != (long) windows->wm_delete_window)
15544 break;
15545 (void) XWithdrawWindow(display,event.xclient.window,
15546 visual_info->screen);
15547 if (event.xclient.window == windows->image.id)
15548 {
15549 *state|=ExitState;
15550 break;
15551 }
15552 if (event.xclient.window == windows->pan.id)
15553 {
15554 /*
15555 Restore original image size when pan window is deleted.
15556 */
15557 windows->image.window_changes.width=windows->image.ximage->width;
15558 windows->image.window_changes.height=windows->image.ximage->height;
15559 (void) XConfigureImage(display,resource_info,windows,
15560 display_image,exception);
15561 }
15562 break;
15563 }
15564 case ConfigureNotify:
15565 {
15566 if (resource_info->debug != MagickFalse)
15567 (void) LogMagickEvent(X11Event,GetMagickModule(),
15568 "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15569 event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15570 event.xconfigure.y,event.xconfigure.send_event);
15571 if (event.xconfigure.window == windows->image.id)
15572 {
15573 /*
15574 Image window has a new configuration.
15575 */
15576 if (event.xconfigure.send_event != 0)
15577 {
15578 XWindowChanges
15579 window_changes;
15580
15581 /*
15582 Position the transient windows relative of the Image window.
15583 */
15584 if (windows->command.geometry == (char *) NULL)
15585 if (windows->command.mapped == MagickFalse)
15586 {
15587 windows->command.x=event.xconfigure.x-(int)
15588 windows->command.width-25;
15589 windows->command.y=event.xconfigure.y;
15590 XConstrainWindowPosition(display,&windows->command);
15591 window_changes.x=windows->command.x;
15592 window_changes.y=windows->command.y;
15593 (void) XReconfigureWMWindow(display,windows->command.id,
15594 windows->command.screen,(unsigned int) (CWX | CWY),
15595 &window_changes);
15596 }
15597 if (windows->widget.geometry == (char *) NULL)
15598 if (windows->widget.mapped == MagickFalse)
15599 {
15600 windows->widget.x=event.xconfigure.x+
15601 event.xconfigure.width/10;
15602 windows->widget.y=event.xconfigure.y+
15603 event.xconfigure.height/10;
15604 XConstrainWindowPosition(display,&windows->widget);
15605 window_changes.x=windows->widget.x;
15606 window_changes.y=windows->widget.y;
15607 (void) XReconfigureWMWindow(display,windows->widget.id,
15608 windows->widget.screen,(unsigned int) (CWX | CWY),
15609 &window_changes);
15610 }
15611 if (windows->magnify.geometry == (char *) NULL)
15612 if (windows->magnify.mapped == MagickFalse)
15613 {
15614 windows->magnify.x=event.xconfigure.x+
15615 event.xconfigure.width+25;
15616 windows->magnify.y=event.xconfigure.y;
15617 XConstrainWindowPosition(display,&windows->magnify);
15618 window_changes.x=windows->magnify.x;
15619 window_changes.y=windows->magnify.y;
15620 (void) XReconfigureWMWindow(display,windows->magnify.id,
15621 windows->magnify.screen,(unsigned int) (CWX | CWY),
15622 &window_changes);
15623 }
15624 if (windows->pan.geometry == (char *) NULL)
15625 if (windows->pan.mapped == MagickFalse)
15626 {
15627 windows->pan.x=event.xconfigure.x+(int)
15628 event.xconfigure.width+25;
15629 windows->pan.y=event.xconfigure.y+(int)
15630 windows->magnify.height+50;
15631 XConstrainWindowPosition(display,&windows->pan);
15632 window_changes.x=windows->pan.x;
15633 window_changes.y=windows->pan.y;
15634 (void) XReconfigureWMWindow(display,windows->pan.id,
15635 windows->pan.screen,(unsigned int) (CWX | CWY),
15636 &window_changes);
15637 }
15638 }
15639 if ((event.xconfigure.width == (int) windows->image.width) &&
15640 (event.xconfigure.height == (int) windows->image.height))
15641 break;
15642 windows->image.width=(unsigned int) event.xconfigure.width;
15643 windows->image.height=(unsigned int) event.xconfigure.height;
15644 windows->image.x=0;
15645 windows->image.y=0;
15646 if (display_image->montage != (char *) NULL)
15647 {
15648 windows->image.x=vid_info.x;
15649 windows->image.y=vid_info.y;
15650 }
15651 if (windows->image.mapped != MagickFalse &&
15652 windows->image.stasis != MagickFalse)
15653 {
15654 /*
15655 Update image window configuration.
15656 */
15657 windows->image.window_changes.width=event.xconfigure.width;
15658 windows->image.window_changes.height=event.xconfigure.height;
15659 (void) XConfigureImage(display,resource_info,windows,
15660 display_image,exception);
15661 }
15662 /*
15663 Update pan window configuration.
15664 */
15665 if ((event.xconfigure.width < windows->image.ximage->width) ||
15666 (event.xconfigure.height < windows->image.ximage->height))
15667 {
15668 (void) XMapRaised(display,windows->pan.id);
15669 XDrawPanRectangle(display,windows);
15670 }
15671 else
15672 if (windows->pan.mapped != MagickFalse)
15673 (void) XWithdrawWindow(display,windows->pan.id,
15674 windows->pan.screen);
15675 break;
15676 }
15677 if (event.xconfigure.window == windows->magnify.id)
15678 {
15679 unsigned int
15680 magnify;
15681
15682 /*
15683 Magnify window has a new configuration.
15684 */
15685 windows->magnify.width=(unsigned int) event.xconfigure.width;
15686 windows->magnify.height=(unsigned int) event.xconfigure.height;
15687 if (windows->magnify.mapped == MagickFalse)
15688 break;
15689 magnify=1;
15690 while ((int) magnify <= event.xconfigure.width)
15691 magnify<<=1;
15692 while ((int) magnify <= event.xconfigure.height)
15693 magnify<<=1;
15694 magnify>>=1;
15695 if (((int) magnify != event.xconfigure.width) ||
15696 ((int) magnify != event.xconfigure.height))
15697 {
15698 window_changes.width=(int) magnify;
15699 window_changes.height=(int) magnify;
15700 (void) XReconfigureWMWindow(display,windows->magnify.id,
15701 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15702 &window_changes);
15703 break;
15704 }
15705 if (windows->magnify.mapped != MagickFalse &&
15706 windows->magnify.stasis != MagickFalse)
15707 {
15708 status=XMakeImage(display,resource_info,&windows->magnify,
15709 display_image,windows->magnify.width,windows->magnify.height,
15710 exception);
15711 XMakeMagnifyImage(display,windows,exception);
15712 }
15713 break;
15714 }
15715 if (windows->magnify.mapped != MagickFalse &&
15716 (event.xconfigure.window == windows->pan.id))
15717 {
15718 /*
15719 Pan icon window has a new configuration.
15720 */
15721 if (event.xconfigure.send_event != 0)
15722 {
15723 windows->pan.x=event.xconfigure.x;
15724 windows->pan.y=event.xconfigure.y;
15725 }
15726 windows->pan.width=(unsigned int) event.xconfigure.width;
15727 windows->pan.height=(unsigned int) event.xconfigure.height;
15728 break;
15729 }
15730 if (event.xconfigure.window == windows->icon.id)
15731 {
15732 /*
15733 Icon window has a new configuration.
15734 */
15735 windows->icon.width=(unsigned int) event.xconfigure.width;
15736 windows->icon.height=(unsigned int) event.xconfigure.height;
15737 break;
15738 }
15739 break;
15740 }
15741 case DestroyNotify:
15742 {
15743 /*
15744 Group leader has exited.
15745 */
15746 if (resource_info->debug != MagickFalse)
15747 (void) LogMagickEvent(X11Event,GetMagickModule(),
15748 "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15749 if (event.xdestroywindow.window == windows->group_leader.id)
15750 {
15751 *state|=ExitState;
15752 break;
15753 }
15754 break;
15755 }
15756 case EnterNotify:
15757 {
15758 /*
15759 Selectively install colormap.
15760 */
15761 if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15762 if (event.xcrossing.mode != NotifyUngrab)
15763 XInstallColormap(display,map_info->colormap);
15764 break;
15765 }
15766 case Expose:
15767 {
15768 if (resource_info->debug != MagickFalse)
15769 (void) LogMagickEvent(X11Event,GetMagickModule(),
15770 "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15771 event.xexpose.width,event.xexpose.height,event.xexpose.x,
15772 event.xexpose.y);
15773 /*
15774 Refresh windows that are now exposed.
15775 */
15776 if ((event.xexpose.window == windows->image.id) &&
15777 windows->image.mapped != MagickFalse)
15778 {
15779 XRefreshWindow(display,&windows->image,&event);
15780 delay=display_image->delay/(size_t) MagickMax(
15781 display_image->ticks_per_second,1L);
15782 timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15783 break;
15784 }
15785 if ((event.xexpose.window == windows->magnify.id) &&
15786 windows->magnify.mapped != MagickFalse)
15787 {
15788 XMakeMagnifyImage(display,windows,exception);
15789 break;
15790 }
15791 if (event.xexpose.window == windows->pan.id)
15792 {
15793 XDrawPanRectangle(display,windows);
15794 break;
15795 }
15796 if (event.xexpose.window == windows->icon.id)
15797 {
15798 XRefreshWindow(display,&windows->icon,&event);
15799 break;
15800 }
15801 break;
15802 }
15803 case KeyPress:
15804 {
15805 int
15806 length;
15807
15808 /*
15809 Respond to a user key press.
15810 */
15811 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15812 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15813 *(command+length)='\0';
15814 if (resource_info->debug != MagickFalse)
15815 (void) LogMagickEvent(X11Event,GetMagickModule(),
15816 "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15817 key_symbol,command);
15818 if (event.xkey.window == windows->image.id)
15819 {
15820 display_command=XImageWindowCommand(display,resource_info,windows,
15821 event.xkey.state,key_symbol,&display_image,exception);
15822 if (display_command != NullCommand)
15823 nexus=XMagickCommand(display,resource_info,windows,
15824 display_command,&display_image,exception);
15825 }
15826 if (event.xkey.window == windows->magnify.id)
15827 XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15828 exception);
15829 if (event.xkey.window == windows->pan.id)
15830 {
15831 if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15832 (void) XWithdrawWindow(display,windows->pan.id,
15833 windows->pan.screen);
15834 else
15835 if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15836 XTextViewHelp(display,resource_info,windows,MagickFalse,
15837 "Help Viewer - Image Pan",ImagePanHelp);
15838 else
15839 XTranslateImage(display,windows,*image,key_symbol);
15840 }
15841 delay=display_image->delay/(size_t) MagickMax(
15842 display_image->ticks_per_second,1L);
15843 timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15844 break;
15845 }
15846 case KeyRelease:
15847 {
15848 /*
15849 Respond to a user key release.
15850 */
15851 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15852 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15853 if (resource_info->debug != MagickFalse)
15854 (void) LogMagickEvent(X11Event,GetMagickModule(),
15855 "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15856 break;
15857 }
15858 case LeaveNotify:
15859 {
15860 /*
15861 Selectively uninstall colormap.
15862 */
15863 if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15864 if (event.xcrossing.mode != NotifyUngrab)
15865 XUninstallColormap(display,map_info->colormap);
15866 break;
15867 }
15868 case MapNotify:
15869 {
15870 if (resource_info->debug != MagickFalse)
15871 (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15872 event.xmap.window);
15873 if (event.xmap.window == windows->backdrop.id)
15874 {
15875 (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15876 CurrentTime);
15877 windows->backdrop.mapped=MagickTrue;
15878 break;
15879 }
15880 if (event.xmap.window == windows->image.id)
15881 {
15882 if (windows->backdrop.id != (Window) NULL)
15883 (void) XInstallColormap(display,map_info->colormap);
15884 if (LocaleCompare(display_image->magick,"LOGO") == 0)
15885 {
15886 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15887 nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15888 }
15889 if (((int) windows->image.width < windows->image.ximage->width) ||
15890 ((int) windows->image.height < windows->image.ximage->height))
15891 (void) XMapRaised(display,windows->pan.id);
15892 windows->image.mapped=MagickTrue;
15893 break;
15894 }
15895 if (event.xmap.window == windows->magnify.id)
15896 {
15897 XMakeMagnifyImage(display,windows,exception);
15898 windows->magnify.mapped=MagickTrue;
15899 (void) XWithdrawWindow(display,windows->info.id,
15900 windows->info.screen);
15901 break;
15902 }
15903 if (event.xmap.window == windows->pan.id)
15904 {
15905 XMakePanImage(display,resource_info,windows,display_image,
15906 exception);
15907 windows->pan.mapped=MagickTrue;
15908 break;
15909 }
15910 if (event.xmap.window == windows->info.id)
15911 {
15912 windows->info.mapped=MagickTrue;
15913 break;
15914 }
15915 if (event.xmap.window == windows->icon.id)
15916 {
15917 MagickBooleanType
15918 taint;
15919
15920 /*
15921 Create an icon image.
15922 */
15923 taint=display_image->taint;
15924 XMakeStandardColormap(display,icon_visual,icon_resources,
15925 display_image,icon_map,icon_pixel,exception);
15926 (void) XMakeImage(display,icon_resources,&windows->icon,
15927 display_image,windows->icon.width,windows->icon.height,
15928 exception);
15929 display_image->taint=taint;
15930 (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15931 windows->icon.pixmap);
15932 (void) XClearWindow(display,windows->icon.id);
15933 (void) XWithdrawWindow(display,windows->info.id,
15934 windows->info.screen);
15935 windows->icon.mapped=MagickTrue;
15936 break;
15937 }
15938 if (event.xmap.window == windows->command.id)
15939 {
15940 windows->command.mapped=MagickTrue;
15941 break;
15942 }
15943 if (event.xmap.window == windows->popup.id)
15944 {
15945 windows->popup.mapped=MagickTrue;
15946 break;
15947 }
15948 if (event.xmap.window == windows->widget.id)
15949 {
15950 windows->widget.mapped=MagickTrue;
15951 break;
15952 }
15953 break;
15954 }
15955 case MappingNotify:
15956 {
15957 (void) XRefreshKeyboardMapping(&event.xmapping);
15958 break;
15959 }
15960 case NoExpose:
15961 break;
15962 case PropertyNotify:
15963 {
15964 Atom
15965 type;
15966
15967 int
15968 format,
15969 status;
15970
15971 unsigned char
15972 *data;
15973
15974 unsigned long
15975 after,
15976 length;
15977
15978 if (resource_info->debug != MagickFalse)
15979 (void) LogMagickEvent(X11Event,GetMagickModule(),
15980 "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15981 event.xproperty.atom,event.xproperty.state);
15982 if (event.xproperty.atom != windows->im_remote_command)
15983 break;
15984 /*
15985 Display image named by the remote command protocol.
15986 */
15987 status=XGetWindowProperty(display,event.xproperty.window,
15988 event.xproperty.atom,0L,(long) MagickPathExtent,MagickFalse,(Atom)
15989 AnyPropertyType,&type,&format,&length,&after,&data);
15990 if ((status != Success) || (length == 0))
15991 break;
15992 if (LocaleCompare((char *) data,"-quit") == 0)
15993 {
15994 XClientMessage(display,windows->image.id,windows->im_protocols,
15995 windows->im_exit,CurrentTime);
15996 (void) XFree((void *) data);
15997 break;
15998 }
15999 (void) CopyMagickString(resource_info->image_info->filename,
16000 (char *) data,MagickPathExtent);
16001 (void) XFree((void *) data);
16002 nexus=ReadImage(resource_info->image_info,exception);
16003 CatchException(exception);
16004 if (nexus != (Image *) NULL)
16005 *state|=NextImageState | ExitState;
16006 break;
16007 }
16008 case ReparentNotify:
16009 {
16010 if (resource_info->debug != MagickFalse)
16011 (void) LogMagickEvent(X11Event,GetMagickModule(),
16012 "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
16013 event.xreparent.window);
16014 break;
16015 }
16016 case UnmapNotify:
16017 {
16018 if (resource_info->debug != MagickFalse)
16019 (void) LogMagickEvent(X11Event,GetMagickModule(),
16020 "Unmap Notify: 0x%lx",event.xunmap.window);
16021 if (event.xunmap.window == windows->backdrop.id)
16022 {
16023 windows->backdrop.mapped=MagickFalse;
16024 break;
16025 }
16026 if (event.xunmap.window == windows->image.id)
16027 {
16028 windows->image.mapped=MagickFalse;
16029 break;
16030 }
16031 if (event.xunmap.window == windows->magnify.id)
16032 {
16033 windows->magnify.mapped=MagickFalse;
16034 break;
16035 }
16036 if (event.xunmap.window == windows->pan.id)
16037 {
16038 windows->pan.mapped=MagickFalse;
16039 break;
16040 }
16041 if (event.xunmap.window == windows->info.id)
16042 {
16043 windows->info.mapped=MagickFalse;
16044 break;
16045 }
16046 if (event.xunmap.window == windows->icon.id)
16047 {
16048 if (map_info->colormap == icon_map->colormap)
16049 XConfigureImageColormap(display,resource_info,windows,
16050 display_image,exception);
16051 (void) XFreeStandardColormap(display,icon_visual,icon_map,
16052 icon_pixel);
16053 windows->icon.mapped=MagickFalse;
16054 break;
16055 }
16056 if (event.xunmap.window == windows->command.id)
16057 {
16058 windows->command.mapped=MagickFalse;
16059 break;
16060 }
16061 if (event.xunmap.window == windows->popup.id)
16062 {
16063 if (windows->backdrop.id != (Window) NULL)
16064 (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16065 CurrentTime);
16066 windows->popup.mapped=MagickFalse;
16067 break;
16068 }
16069 if (event.xunmap.window == windows->widget.id)
16070 {
16071 if (windows->backdrop.id != (Window) NULL)
16072 (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16073 CurrentTime);
16074 windows->widget.mapped=MagickFalse;
16075 break;
16076 }
16077 break;
16078 }
16079 default:
16080 {
16081 if (resource_info->debug != MagickFalse)
16082 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16083 event.type);
16084 break;
16085 }
16086 }
16087 } while (!(*state & ExitState));
16088 if ((*state & ExitState) == 0)
16089 (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16090 &display_image,exception);
16091 else
16092 if (resource_info->confirm_edit != MagickFalse)
16093 {
16094 /*
16095 Query user if image has changed.
16096 */
16097 if ((resource_info->immutable == MagickFalse) &&
16098 display_image->taint != MagickFalse)
16099 {
16100 int
16101 status;
16102
16103 status=XConfirmWidget(display,windows,"Your image changed.",
16104 "Do you want to save it");
16105 if (status == 0)
16106 *state&=(unsigned int) (~ExitState);
16107 else
16108 if (status > 0)
16109 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16110 &display_image,exception);
16111 }
16112 }
16113 if ((windows->visual_info->klass == GrayScale) ||
16114 (windows->visual_info->klass == PseudoColor) ||
16115 (windows->visual_info->klass == DirectColor))
16116 {
16117 /*
16118 Withdraw pan and Magnify window.
16119 */
16120 if (windows->info.mapped != MagickFalse)
16121 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16122 if (windows->magnify.mapped != MagickFalse)
16123 (void) XWithdrawWindow(display,windows->magnify.id,
16124 windows->magnify.screen);
16125 if (windows->command.mapped != MagickFalse)
16126 (void) XWithdrawWindow(display,windows->command.id,
16127 windows->command.screen);
16128 }
16129 if (windows->pan.mapped != MagickFalse)
16130 (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16131 if (resource_info->backdrop == MagickFalse)
16132 if (windows->backdrop.mapped)
16133 {
16134 (void) XWithdrawWindow(display,windows->backdrop.id,
16135 windows->backdrop.screen);
16136 (void) XDestroyWindow(display,windows->backdrop.id);
16137 windows->backdrop.id=(Window) NULL;
16138 (void) XWithdrawWindow(display,windows->image.id,
16139 windows->image.screen);
16140 (void) XDestroyWindow(display,windows->image.id);
16141 windows->image.id=(Window) NULL;
16142 }
16143 XSetCursorState(display,windows,MagickTrue);
16144 XCheckRefreshWindows(display,windows);
16145 if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16146 *state&=(unsigned int) (~ExitState);
16147 if (*state & ExitState)
16148 {
16149 /*
16150 Free Standard Colormap.
16151 */
16152 (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16153 if (resource_info->map_type == (char *) NULL)
16154 (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16155 /*
16156 Free X resources.
16157 */
16158 if (resource_info->copy_image != (Image *) NULL)
16159 {
16160 resource_info->copy_image=DestroyImage(resource_info->copy_image);
16161 resource_info->copy_image=NewImageList();
16162 }
16163 DestroyXResources();
16164 }
16165 (void) XSync(display,MagickFalse);
16166 /*
16167 Restore our progress monitor and warning handlers.
16168 */
16169 (void) SetErrorHandler(warning_handler);
16170 (void) SetWarningHandler(warning_handler);
16171 /*
16172 Change to home directory.
16173 */
16174 directory=getcwd(working_directory,MagickPathExtent);
16175 (void) directory;
16176 {
16177 int
16178 status;
16179
16180 if (*resource_info->home_directory == '\0')
16181 (void) CopyMagickString(resource_info->home_directory,".",MagickPathExtent);
16182 status=chdir(resource_info->home_directory);
16183 if (status == -1)
16184 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16185 "UnableToOpenFile","%s",resource_info->home_directory);
16186 }
16187 *image=display_image;
16188 return(nexus);
16189}
16190#else
16191
16192/*
16193%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16194% %
16195% %
16196% %
16197+ D i s p l a y I m a g e s %
16198% %
16199% %
16200% %
16201%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16202%
16203% DisplayImages() displays an image sequence to any X window screen. It
16204% returns a value other than 0 if successful. Check the exception member
16205% of image to determine the reason for any failure.
16206%
16207% The format of the DisplayImages method is:
16208%
16209% MagickBooleanType DisplayImages(const ImageInfo *image_info,
16210% Image *images,ExceptionInfo *exception)
16211%
16212% A description of each parameter follows:
16213%
16214% o image_info: the image info.
16215%
16216% o image: the image.
16217%
16218% o exception: return any errors or warnings in this structure.
16219%
16220*/
16221MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16222 Image *image,ExceptionInfo *exception)
16223{
16224 assert(image_info != (const ImageInfo *) NULL);
16225 assert(image_info->signature == MagickCoreSignature);
16226 assert(image != (Image *) NULL);
16227 assert(image->signature == MagickCoreSignature);
16228 (void) image_info;
16229 if (IsEventLogging() != MagickFalse)
16230 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16231 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16232 "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
16233 return(MagickFalse);
16234}
16235
16236/*
16237%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16238% %
16239% %
16240% %
16241+ R e m o t e D i s p l a y C o m m a n d %
16242% %
16243% %
16244% %
16245%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16246%
16247% RemoteDisplayCommand() encourages a remote display program to display the
16248% specified image filename.
16249%
16250% The format of the RemoteDisplayCommand method is:
16251%
16252% MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16253% const char *window,const char *filename,ExceptionInfo *exception)
16254%
16255% A description of each parameter follows:
16256%
16257% o image_info: the image info.
16258%
16259% o window: Specifies the name or id of an X window.
16260%
16261% o filename: the name of the image filename to display.
16262%
16263% o exception: return any errors or warnings in this structure.
16264%
16265*/
16266MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16267 const char *window,const char *filename,ExceptionInfo *exception)
16268{
16269 assert(image_info != (const ImageInfo *) NULL);
16270 assert(image_info->signature == MagickCoreSignature);
16271 assert(filename != (char *) NULL);
16272 if (IsEventLogging() != MagickFalse)
16273 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16274 (void) window;
16275 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16276 "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image_info->filename);
16277 return(MagickFalse);
16278}
16279#endif