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