MagickCore 7.1.1-39
Convert, Edit, Or Compose Bitmap Images
Loading...
Searching...
No Matches
display.c
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% DDDD IIIII SSSSS PPPP L AAA Y Y %
7% D D I SS P P L A A Y Y %
8% D D I SSS PPPP L AAAAA Y %
9% D D I SS P L A A Y %
10% DDDD IIIII SSSSS P LLLLL A A Y %
11% %
12% %
13% MagickCore Methods to Interactively Display and Edit an Image %
14% %
15% Software Design %
16% Cristy %
17% July 1992 %
18% %
19% %
20% Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization %
21% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% https://imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
42#include "MagickCore/studio.h"
43#include "MagickCore/artifact.h"
44#include "MagickCore/attribute.h"
45#include "MagickCore/blob.h"
46#include "MagickCore/cache.h"
47#include "MagickCore/cache-private.h"
48#include "MagickCore/channel.h"
49#include "MagickCore/client.h"
50#include "MagickCore/color.h"
51#include "MagickCore/colorspace.h"
52#include "MagickCore/composite.h"
53#include "MagickCore/constitute.h"
54#include "MagickCore/decorate.h"
55#include "MagickCore/delegate.h"
56#include "MagickCore/display.h"
57#include "MagickCore/display-private.h"
58#include "MagickCore/distort.h"
59#include "MagickCore/draw.h"
60#include "MagickCore/effect.h"
61#include "MagickCore/enhance.h"
62#include "MagickCore/exception.h"
63#include "MagickCore/exception-private.h"
64#include "MagickCore/fx.h"
65#include "MagickCore/geometry.h"
66#include "MagickCore/image.h"
67#include "MagickCore/image-private.h"
68#include "MagickCore/list.h"
69#include "MagickCore/locale-private.h"
70#include "MagickCore/log.h"
71#include "MagickCore/magick.h"
72#include "MagickCore/memory_.h"
73#include "MagickCore/monitor.h"
74#include "MagickCore/monitor-private.h"
75#include "MagickCore/montage.h"
76#include "MagickCore/nt-base-private.h"
77#include "MagickCore/option.h"
78#include "MagickCore/paint.h"
79#include "MagickCore/pixel.h"
80#include "MagickCore/pixel-accessor.h"
81#include "MagickCore/property.h"
82#include "MagickCore/quantum.h"
83#include "MagickCore/quantum-private.h"
84#include "MagickCore/resize.h"
85#include "MagickCore/resource_.h"
86#include "MagickCore/shear.h"
87#include "MagickCore/segment.h"
88#include "MagickCore/statistic.h"
89#include "MagickCore/string_.h"
90#include "MagickCore/string-private.h"
91#include "MagickCore/timer-private.h"
92#include "MagickCore/transform.h"
93#include "MagickCore/transform-private.h"
94#include "MagickCore/threshold.h"
95#include "MagickCore/utility.h"
96#include "MagickCore/utility-private.h"
97#include "MagickCore/version.h"
98#include "MagickCore/visual-effects.h"
99#include "MagickCore/widget.h"
100#include "MagickCore/widget-private.h"
101#include "MagickCore/xwindow.h"
102#include "MagickCore/xwindow-private.h"
103
104#if defined(MAGICKCORE_X11_DELEGATE)
105/*
106 Define declarations.
107*/
108#define MaxColors MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
109
110/*
111 Constant declarations.
112*/
113static const unsigned char
114 HighlightBitmap[8] =
115 {
116 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
117 },
118 OpaqueBitmap[8] =
119 {
120 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
121 },
122 ShadowBitmap[8] =
123 {
124 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
125 };
126
127/*
128 Help widget declarations.
129*/
130static const char
131 ImageAnnotateHelp[] =
132 {
133 "In annotate mode, the Command widget has these options:\n"
134 "\n"
135 " Font Name\n"
136 " fixed\n"
137 " variable\n"
138 " 5x8\n"
139 " 6x10\n"
140 " 7x13bold\n"
141 " 8x13bold\n"
142 " 9x15bold\n"
143 " 10x20\n"
144 " 12x24\n"
145 " Browser...\n"
146 " Font Color\n"
147 " black\n"
148 " blue\n"
149 " cyan\n"
150 " green\n"
151 " gray\n"
152 " red\n"
153 " magenta\n"
154 " yellow\n"
155 " white\n"
156 " transparent\n"
157 " Browser...\n"
158 " Font Color\n"
159 " black\n"
160 " blue\n"
161 " cyan\n"
162 " green\n"
163 " gray\n"
164 " red\n"
165 " magenta\n"
166 " yellow\n"
167 " white\n"
168 " transparent\n"
169 " Browser...\n"
170 " Rotate Text\n"
171 " -90\n"
172 " -45\n"
173 " -30\n"
174 " 0\n"
175 " 30\n"
176 " 45\n"
177 " 90\n"
178 " 180\n"
179 " Dialog...\n"
180 " Help\n"
181 " Dismiss\n"
182 "\n"
183 "Choose a font name from the Font Name sub-menu. Additional\n"
184 "font names can be specified with the font browser. You can\n"
185 "change the menu names by setting the X resources font1\n"
186 "through font9.\n"
187 "\n"
188 "Choose a font color from the Font Color sub-menu.\n"
189 "Additional font colors can be specified with the color\n"
190 "browser. You can change the menu colors by setting the X\n"
191 "resources pen1 through pen9.\n"
192 "\n"
193 "If you select the color browser and press Grab, you can\n"
194 "choose the font color by moving the pointer to the desired\n"
195 "color on the screen and press any button.\n"
196 "\n"
197 "If you choose to rotate the text, choose Rotate Text from the\n"
198 "menu and select an angle. Typically you will only want to\n"
199 "rotate one line of text at a time. Depending on the angle you\n"
200 "choose, subsequent lines may end up overwriting each other.\n"
201 "\n"
202 "Choosing a font and its color is optional. The default font\n"
203 "is fixed and the default color is black. However, you must\n"
204 "choose a location to begin entering text and press button 1.\n"
205 "An underscore character will appear at the location of the\n"
206 "pointer. The cursor changes to a pencil to indicate you are\n"
207 "in text mode. To exit immediately, press Dismiss.\n"
208 "\n"
209 "In text mode, any key presses will display the character at\n"
210 "the location of the underscore and advance the underscore\n"
211 "cursor. Enter your text and once completed press Apply to\n"
212 "finish your image annotation. To correct errors press BACK\n"
213 "SPACE. To delete an entire line of text, press DELETE. Any\n"
214 "text that exceeds the boundaries of the image window is\n"
215 "automagically continued onto the next line.\n"
216 "\n"
217 "The actual color you request for the font is saved in the\n"
218 "image. However, the color that appears in your image window\n"
219 "may be different. For example, on a monochrome screen the\n"
220 "text will appear black or white even if you choose the color\n"
221 "red as the font color. However, the image saved to a file\n"
222 "with -write is written with red lettering. To assure the\n"
223 "correct color text in the final image, any PseudoClass image\n"
224 "is promoted to DirectClass (see miff(5)). To force a\n"
225 "PseudoClass image to remain PseudoClass, use -colors.\n"
226 },
227 ImageChopHelp[] =
228 {
229 "In chop mode, the Command widget has these options:\n"
230 "\n"
231 " Direction\n"
232 " horizontal\n"
233 " vertical\n"
234 " Help\n"
235 " Dismiss\n"
236 "\n"
237 "If the you choose the horizontal direction (this the\n"
238 "default), the area of the image between the two horizontal\n"
239 "endpoints of the chop line is removed. Otherwise, the area\n"
240 "of the image between the two vertical endpoints of the chop\n"
241 "line is removed.\n"
242 "\n"
243 "Select a location within the image window to begin your chop,\n"
244 "press and hold any button. Next, move the pointer to\n"
245 "another location in the image. As you move a line will\n"
246 "connect the initial location and the pointer. When you\n"
247 "release the button, the area within the image to chop is\n"
248 "determined by which direction you choose from the Command\n"
249 "widget.\n"
250 "\n"
251 "To cancel the image chopping, move the pointer back to the\n"
252 "starting point of the line and release the button.\n"
253 },
254 ImageColorEditHelp[] =
255 {
256 "In color edit mode, the Command widget has these options:\n"
257 "\n"
258 " Method\n"
259 " point\n"
260 " replace\n"
261 " floodfill\n"
262 " filltoborder\n"
263 " reset\n"
264 " Pixel Color\n"
265 " black\n"
266 " blue\n"
267 " cyan\n"
268 " green\n"
269 " gray\n"
270 " red\n"
271 " magenta\n"
272 " yellow\n"
273 " white\n"
274 " Browser...\n"
275 " Border Color\n"
276 " black\n"
277 " blue\n"
278 " cyan\n"
279 " green\n"
280 " gray\n"
281 " red\n"
282 " magenta\n"
283 " yellow\n"
284 " white\n"
285 " Browser...\n"
286 " Fuzz\n"
287 " 0%\n"
288 " 2%\n"
289 " 5%\n"
290 " 10%\n"
291 " 15%\n"
292 " Dialog...\n"
293 " Undo\n"
294 " Help\n"
295 " Dismiss\n"
296 "\n"
297 "Choose a color editing method from the Method sub-menu\n"
298 "of the Command widget. The point method recolors any pixel\n"
299 "selected with the pointer until the button is released. The\n"
300 "replace method recolors any pixel that matches the color of\n"
301 "the pixel you select with a button press. Floodfill recolors\n"
302 "any pixel that matches the color of the pixel you select with\n"
303 "a button press and is a neighbor. Whereas filltoborder recolors\n"
304 "any neighbor pixel that is not the border color. Finally reset\n"
305 "changes the entire image to the designated color.\n"
306 "\n"
307 "Next, choose a pixel color from the Pixel Color sub-menu.\n"
308 "Additional pixel colors can be specified with the color\n"
309 "browser. You can change the menu colors by setting the X\n"
310 "resources pen1 through pen9.\n"
311 "\n"
312 "Now press button 1 to select a pixel within the image window\n"
313 "to change its color. Additional pixels may be recolored as\n"
314 "prescribed by the method you choose.\n"
315 "\n"
316 "If the Magnify widget is mapped, it can be helpful in positioning\n"
317 "your pointer within the image (refer to button 2).\n"
318 "\n"
319 "The actual color you request for the pixels is saved in the\n"
320 "image. However, the color that appears in your image window\n"
321 "may be different. For example, on a monochrome screen the\n"
322 "pixel will appear black or white even if you choose the\n"
323 "color red as the pixel color. However, the image saved to a\n"
324 "file with -write is written with red pixels. To assure the\n"
325 "correct color text in the final image, any PseudoClass image\n"
326 "is promoted to DirectClass (see miff(5)). To force a\n"
327 "PseudoClass image to remain PseudoClass, use -colors.\n"
328 },
329 ImageCompositeHelp[] =
330 {
331 "First a widget window is displayed requesting you to enter an\n"
332 "image name. Press Composite, Grab or type a file name.\n"
333 "Press Cancel if you choose not to create a composite image.\n"
334 "When you choose Grab, move the pointer to the desired window\n"
335 "and press any button.\n"
336 "\n"
337 "If the Composite image does not have any matte information,\n"
338 "you are informed and the file browser is displayed again.\n"
339 "Enter the name of a mask image. The image is typically\n"
340 "grayscale and the same size as the composite image. If the\n"
341 "image is not grayscale, it is converted to grayscale and the\n"
342 "resulting intensities are used as matte information.\n"
343 "\n"
344 "A small window appears showing the location of the cursor in\n"
345 "the image window. You are now in composite mode. To exit\n"
346 "immediately, press Dismiss. In composite mode, the Command\n"
347 "widget has these options:\n"
348 "\n"
349 " Operators\n"
350 " Over\n"
351 " In\n"
352 " Out\n"
353 " Atop\n"
354 " Xor\n"
355 " Plus\n"
356 " Minus\n"
357 " Add\n"
358 " Subtract\n"
359 " Difference\n"
360 " Multiply\n"
361 " Bumpmap\n"
362 " Copy\n"
363 " CopyRed\n"
364 " CopyGreen\n"
365 " CopyBlue\n"
366 " CopyOpacity\n"
367 " Clear\n"
368 " Dissolve\n"
369 " Displace\n"
370 " Help\n"
371 " Dismiss\n"
372 "\n"
373 "Choose a composite operation from the Operators sub-menu of\n"
374 "the Command widget. How each operator behaves is described\n"
375 "below. Image window is the image currently displayed on\n"
376 "your X server and image is the image obtained with the File\n"
377 "Browser widget.\n"
378 "\n"
379 "Over The result is the union of the two image shapes,\n"
380 " with image obscuring image window in the region of\n"
381 " overlap.\n"
382 "\n"
383 "In The result is simply image cut by the shape of\n"
384 " image window. None of the image data of image\n"
385 " window is in the result.\n"
386 "\n"
387 "Out The resulting image is image with the shape of\n"
388 " image window cut out.\n"
389 "\n"
390 "Atop The result is the same shape as the image window,\n"
391 " with image obscuring image window where the image\n"
392 " shapes overlap. Note this differs from over\n"
393 " because the portion of image outside image window's\n"
394 " shape does not appear in the result.\n"
395 "\n"
396 "Xor The result is the image data from both image and\n"
397 " image window that is outside the overlap region.\n"
398 " The overlap region is blank.\n"
399 "\n"
400 "Plus The result is just the sum of the image data.\n"
401 " Output values are cropped to QuantumRange (no overflow).\n"
402 "\n"
403 "Minus The result of image - image window, with underflow\n"
404 " cropped to zero.\n"
405 "\n"
406 "Add The result of image + image window, with overflow\n"
407 " wrapping around (mod 256).\n"
408 "\n"
409 "Subtract The result of image - image window, with underflow\n"
410 " wrapping around (mod 256). The add and subtract\n"
411 " operators can be used to perform reversible\n"
412 " transformations.\n"
413 "\n"
414 "Difference\n"
415 " The result of abs(image - image window). This\n"
416 " useful for comparing two very similar images.\n"
417 "\n"
418 "Multiply\n"
419 " The result of image * image window. This\n"
420 " useful for the creation of drop-shadows.\n"
421 "\n"
422 "Bumpmap The result of surface normals from image * image\n"
423 " window.\n"
424 "\n"
425 "Copy The resulting image is image window replaced with\n"
426 " image. Here the matte information is ignored.\n"
427 "\n"
428 "CopyRed The red layer of the image window is replace with\n"
429 " the red layer of the image. The other layers are\n"
430 " untouched.\n"
431 "\n"
432 "CopyGreen\n"
433 " The green layer of the image window is replace with\n"
434 " the green layer of the image. The other layers are\n"
435 " untouched.\n"
436 "\n"
437 "CopyBlue The blue layer of the image window is replace with\n"
438 " the blue layer of the image. The other layers are\n"
439 " untouched.\n"
440 "\n"
441 "CopyOpacity\n"
442 " The matte layer of the image window is replace with\n"
443 " the matte layer of the image. The other layers are\n"
444 " untouched.\n"
445 "\n"
446 "The image compositor requires a matte, or alpha channel in\n"
447 "the image for some operations. This extra channel usually\n"
448 "defines a mask which represents a sort of a cookie-cutter\n"
449 "for the image. This the case when matte is opaque (full\n"
450 "coverage) for pixels inside the shape, zero outside, and\n"
451 "between 0 and QuantumRange on the boundary. If image does not\n"
452 "have a matte channel, it is initialized with 0 for any pixel\n"
453 "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
454 "\n"
455 "If you choose Dissolve, the composite operator becomes Over. The\n"
456 "image matte channel percent transparency is initialized to factor.\n"
457 "The image window is initialized to (100-factor). Where factor is the\n"
458 "value you specify in the Dialog widget.\n"
459 "\n"
460 "Displace shifts the image pixels as defined by a displacement\n"
461 "map. With this option, image is used as a displacement map.\n"
462 "Black, within the displacement map, is a maximum positive\n"
463 "displacement. White is a maximum negative displacement and\n"
464 "middle gray is neutral. The displacement is scaled to determine\n"
465 "the pixel shift. By default, the displacement applies in both the\n"
466 "horizontal and vertical directions. However, if you specify a mask,\n"
467 "image is the horizontal X displacement and mask the vertical Y\n"
468 "displacement.\n"
469 "\n"
470 "Note that matte information for image window is not retained\n"
471 "for colormapped X server visuals (e.g. StaticColor,\n"
472 "StaticColor, GrayScale, PseudoColor). Correct compositing\n"
473 "behavior may require a TrueColor or DirectColor visual or a\n"
474 "Standard Colormap.\n"
475 "\n"
476 "Choosing a composite operator is optional. The default\n"
477 "operator is replace. However, you must choose a location to\n"
478 "composite your image and press button 1. Press and hold the\n"
479 "button before releasing and an outline of the image will\n"
480 "appear to help you identify your location.\n"
481 "\n"
482 "The actual colors of the composite image is saved. However,\n"
483 "the color that appears in image window may be different.\n"
484 "For example, on a monochrome screen image window will appear\n"
485 "black or white even though your composited image may have\n"
486 "many colors. If the image is saved to a file it is written\n"
487 "with the correct colors. To assure the correct colors are\n"
488 "saved in the final image, any PseudoClass image is promoted\n"
489 "to DirectClass (see miff(5)). To force a PseudoClass image\n"
490 "to remain PseudoClass, use -colors.\n"
491 },
492 ImageCutHelp[] =
493 {
494 "In cut mode, the Command widget has these options:\n"
495 "\n"
496 " Help\n"
497 " Dismiss\n"
498 "\n"
499 "To define a cut region, press button 1 and drag. The\n"
500 "cut region is defined by a highlighted rectangle that\n"
501 "expands or contracts as it follows the pointer. Once you\n"
502 "are satisfied with the cut region, release the button.\n"
503 "You are now in rectify mode. In rectify mode, the Command\n"
504 "widget has these options:\n"
505 "\n"
506 " Cut\n"
507 " Help\n"
508 " Dismiss\n"
509 "\n"
510 "You can make adjustments by moving the pointer to one of the\n"
511 "cut rectangle corners, pressing a button, and dragging.\n"
512 "Finally, press Cut to commit your copy region. To\n"
513 "exit without cutting the image, press Dismiss.\n"
514 },
515 ImageCopyHelp[] =
516 {
517 "In copy mode, the Command widget has these options:\n"
518 "\n"
519 " Help\n"
520 " Dismiss\n"
521 "\n"
522 "To define a copy region, press button 1 and drag. The\n"
523 "copy region is defined by a highlighted rectangle that\n"
524 "expands or contracts as it follows the pointer. Once you\n"
525 "are satisfied with the copy region, release the button.\n"
526 "You are now in rectify mode. In rectify mode, the Command\n"
527 "widget has these options:\n"
528 "\n"
529 " Copy\n"
530 " Help\n"
531 " Dismiss\n"
532 "\n"
533 "You can make adjustments by moving the pointer to one of the\n"
534 "copy rectangle corners, pressing a button, and dragging.\n"
535 "Finally, press Copy to commit your copy region. To\n"
536 "exit without copying the image, press Dismiss.\n"
537 },
538 ImageCropHelp[] =
539 {
540 "In crop mode, the Command widget has these options:\n"
541 "\n"
542 " Help\n"
543 " Dismiss\n"
544 "\n"
545 "To define a cropping region, press button 1 and drag. The\n"
546 "cropping region is defined by a highlighted rectangle that\n"
547 "expands or contracts as it follows the pointer. Once you\n"
548 "are satisfied with the cropping region, release the button.\n"
549 "You are now in rectify mode. In rectify mode, the Command\n"
550 "widget has these options:\n"
551 "\n"
552 " Crop\n"
553 " Help\n"
554 " Dismiss\n"
555 "\n"
556 "You can make adjustments by moving the pointer to one of the\n"
557 "cropping rectangle corners, pressing a button, and dragging.\n"
558 "Finally, press Crop to commit your cropping region. To\n"
559 "exit without cropping the image, press Dismiss.\n"
560 },
561 ImageDrawHelp[] =
562 {
563 "The cursor changes to a crosshair to indicate you are in\n"
564 "draw mode. To exit immediately, press Dismiss. In draw mode,\n"
565 "the Command widget has these options:\n"
566 "\n"
567 " Element\n"
568 " point\n"
569 " line\n"
570 " rectangle\n"
571 " fill rectangle\n"
572 " circle\n"
573 " fill circle\n"
574 " ellipse\n"
575 " fill ellipse\n"
576 " polygon\n"
577 " fill polygon\n"
578 " Color\n"
579 " black\n"
580 " blue\n"
581 " cyan\n"
582 " green\n"
583 " gray\n"
584 " red\n"
585 " magenta\n"
586 " yellow\n"
587 " white\n"
588 " transparent\n"
589 " Browser...\n"
590 " Stipple\n"
591 " Brick\n"
592 " Diagonal\n"
593 " Scales\n"
594 " Vertical\n"
595 " Wavy\n"
596 " Translucent\n"
597 " Opaque\n"
598 " Open...\n"
599 " Width\n"
600 " 1\n"
601 " 2\n"
602 " 4\n"
603 " 8\n"
604 " 16\n"
605 " Dialog...\n"
606 " Undo\n"
607 " Help\n"
608 " Dismiss\n"
609 "\n"
610 "Choose a drawing primitive from the Element sub-menu.\n"
611 "\n"
612 "Choose a color from the Color sub-menu. Additional\n"
613 "colors can be specified with the color browser.\n"
614 "\n"
615 "If you choose the color browser and press Grab, you can\n"
616 "select the color by moving the pointer to the desired\n"
617 "color on the screen and press any button. The transparent\n"
618 "color updates the image matte channel and is useful for\n"
619 "image compositing.\n"
620 "\n"
621 "Choose a stipple, if appropriate, from the Stipple sub-menu.\n"
622 "Additional stipples can be specified with the file browser.\n"
623 "Stipples obtained from the file browser must be on disk in the\n"
624 "X11 bitmap format.\n"
625 "\n"
626 "Choose a width, if appropriate, from the Width sub-menu. To\n"
627 "choose a specific width select the Dialog widget.\n"
628 "\n"
629 "Choose a point in the Image window and press button 1 and\n"
630 "hold. Next, move the pointer to another location in the\n"
631 "image. As you move, a line connects the initial location and\n"
632 "the pointer. When you release the button, the image is\n"
633 "updated with the primitive you just drew. For polygons, the\n"
634 "image is updated when you press and release the button without\n"
635 "moving the pointer.\n"
636 "\n"
637 "To cancel image drawing, move the pointer back to the\n"
638 "starting point of the line and release the button.\n"
639 },
640 DisplayHelp[] =
641 {
642 "BUTTONS\n"
643 " The effects of each button press is described below. Three\n"
644 " buttons are required. If you have a two button mouse,\n"
645 " button 1 and 3 are returned. Press ALT and button 3 to\n"
646 " simulate button 2.\n"
647 "\n"
648 " 1 Press this button to map or unmap the Command widget.\n"
649 "\n"
650 " 2 Press and drag to define a region of the image to\n"
651 " magnify.\n"
652 "\n"
653 " 3 Press and drag to choose from a select set of commands.\n"
654 " This button behaves differently if the image being\n"
655 " displayed is a visual image directory. Here, choose a\n"
656 " particular tile of the directory and press this button and\n"
657 " drag to select a command from a pop-up menu. Choose from\n"
658 " these menu items:\n"
659 "\n"
660 " Open\n"
661 " Next\n"
662 " Former\n"
663 " Delete\n"
664 " Update\n"
665 "\n"
666 " If you choose Open, the image represented by the tile is\n"
667 " displayed. To return to the visual image directory, choose\n"
668 " Next from the Command widget. Next and Former moves to the\n"
669 " next or former image respectively. Choose Delete to delete\n"
670 " a particular image tile. Finally, choose Update to\n"
671 " synchronize all the image tiles with their respective\n"
672 " images.\n"
673 "\n"
674 "COMMAND WIDGET\n"
675 " The Command widget lists a number of sub-menus and commands.\n"
676 " They are\n"
677 "\n"
678 " File\n"
679 " Open...\n"
680 " Next\n"
681 " Former\n"
682 " Select...\n"
683 " Save...\n"
684 " Print...\n"
685 " Delete...\n"
686 " New...\n"
687 " Visual Directory...\n"
688 " Quit\n"
689 " Edit\n"
690 " Undo\n"
691 " Redo\n"
692 " Cut\n"
693 " Copy\n"
694 " Paste\n"
695 " View\n"
696 " Half Size\n"
697 " Original Size\n"
698 " Double Size\n"
699 " Resize...\n"
700 " Apply\n"
701 " Refresh\n"
702 " Restore\n"
703 " Transform\n"
704 " Crop\n"
705 " Chop\n"
706 " Flop\n"
707 " Flip\n"
708 " Rotate Right\n"
709 " Rotate Left\n"
710 " Rotate...\n"
711 " Shear...\n"
712 " Roll...\n"
713 " Trim Edges\n"
714 " Enhance\n"
715 " Brightness...\n"
716 " Saturation...\n"
717 " Hue...\n"
718 " Gamma...\n"
719 " Sharpen...\n"
720 " Dull\n"
721 " Contrast Stretch...\n"
722 " Sigmoidal Contrast...\n"
723 " Normalize\n"
724 " Equalize\n"
725 " Negate\n"
726 " Grayscale\n"
727 " Map...\n"
728 " Quantize...\n"
729 " Effects\n"
730 " Despeckle\n"
731 " Emboss\n"
732 " Reduce Noise\n"
733 " Add Noise\n"
734 " Sharpen...\n"
735 " Blur...\n"
736 " Threshold...\n"
737 " Edge Detect...\n"
738 " Spread...\n"
739 " Shade...\n"
740 " Painting...\n"
741 " Segment...\n"
742 " F/X\n"
743 " Solarize...\n"
744 " Sepia Tone...\n"
745 " Swirl...\n"
746 " Implode...\n"
747 " Vignette...\n"
748 " Wave...\n"
749 " Oil Painting...\n"
750 " Charcoal Drawing...\n"
751 " Image Edit\n"
752 " Annotate...\n"
753 " Draw...\n"
754 " Color...\n"
755 " Matte...\n"
756 " Composite...\n"
757 " Add Border...\n"
758 " Add Frame...\n"
759 " Comment...\n"
760 " Launch...\n"
761 " Region of Interest...\n"
762 " Miscellany\n"
763 " Image Info\n"
764 " Zoom Image\n"
765 " Show Preview...\n"
766 " Show Histogram\n"
767 " Show Matte\n"
768 " Background...\n"
769 " Slide Show\n"
770 " Preferences...\n"
771 " Help\n"
772 " Overview\n"
773 " Browse Documentation\n"
774 " About Display\n"
775 "\n"
776 " Menu items with a indented triangle have a sub-menu. They\n"
777 " are represented above as the indented items. To access a\n"
778 " sub-menu item, move the pointer to the appropriate menu and\n"
779 " press a button and drag. When you find the desired sub-menu\n"
780 " item, release the button and the command is executed. Move\n"
781 " the pointer away from the sub-menu if you decide not to\n"
782 " execute a particular command.\n"
783 "\n"
784 "KEYBOARD ACCELERATORS\n"
785 " Accelerators are one or two key presses that effect a\n"
786 " particular command. The keyboard accelerators that\n"
787 " display(1) understands is:\n"
788 "\n"
789 " Ctl+O Press to open an image from a file.\n"
790 "\n"
791 " space Press to display the next image.\n"
792 "\n"
793 " If the image is a multi-paged document such as a Postscript\n"
794 " document, you can skip ahead several pages by preceding\n"
795 " this command with a number. For example to display the\n"
796 " third page beyond the current page, press 3<space>.\n"
797 "\n"
798 " backspace Press to display the former image.\n"
799 "\n"
800 " If the image is a multi-paged document such as a Postscript\n"
801 " document, you can skip behind several pages by preceding\n"
802 " this command with a number. For example to display the\n"
803 " third page preceding the current page, press 3<backspace>.\n"
804 "\n"
805 " Ctl+S Press to write the image to a file.\n"
806 "\n"
807 " Ctl+P Press to print the image to a Postscript printer.\n"
808 "\n"
809 " Ctl+D Press to delete an image file.\n"
810 "\n"
811 " Ctl+N Press to create a blank canvas.\n"
812 "\n"
813 " Ctl+Q Press to discard all images and exit program.\n"
814 "\n"
815 " Ctl+Z Press to undo last image transformation.\n"
816 "\n"
817 " Ctl+R Press to redo last image transformation.\n"
818 "\n"
819 " Ctl+X Press to cut a region of the image.\n"
820 "\n"
821 " Ctl+C Press to copy a region of the image.\n"
822 "\n"
823 " Ctl+V Press to paste a region to the image.\n"
824 "\n"
825 " < Press to half the image size.\n"
826 "\n"
827 " - Press to return to the original image size.\n"
828 "\n"
829 " > Press to double the image size.\n"
830 "\n"
831 " % Press to resize the image to a width and height you\n"
832 " specify.\n"
833 "\n"
834 "Cmd-A Press to make any image transformations permanent."
835 "\n"
836 " By default, any image size transformations are applied\n"
837 " to the original image to create the image displayed on\n"
838 " the X server. However, the transformations are not\n"
839 " permanent (i.e. the original image does not change\n"
840 " size only the X image does). For example, if you\n"
841 " press > the X image will appear to double in size,\n"
842 " but the original image will in fact remain the same size.\n"
843 " To force the original image to double in size, press >\n"
844 " followed by Cmd-A.\n"
845 "\n"
846 " @ Press to refresh the image window.\n"
847 "\n"
848 " C Press to cut out a rectangular region of the image.\n"
849 "\n"
850 " [ Press to chop the image.\n"
851 "\n"
852 " H Press to flop image in the horizontal direction.\n"
853 "\n"
854 " V Press to flip image in the vertical direction.\n"
855 "\n"
856 " / Press to rotate the image 90 degrees clockwise.\n"
857 "\n"
858 " \\ Press to rotate the image 90 degrees counter-clockwise.\n"
859 "\n"
860 " * Press to rotate the image the number of degrees you\n"
861 " specify.\n"
862 "\n"
863 " S Press to shear the image the number of degrees you\n"
864 " specify.\n"
865 "\n"
866 " R Press to roll the image.\n"
867 "\n"
868 " T Press to trim the image edges.\n"
869 "\n"
870 " Shft-H Press to vary the image hue.\n"
871 "\n"
872 " Shft-S Press to vary the color saturation.\n"
873 "\n"
874 " Shft-L Press to vary the color brightness.\n"
875 "\n"
876 " Shft-G Press to gamma correct the image.\n"
877 "\n"
878 " Shft-C Press to sharpen the image contrast.\n"
879 "\n"
880 " Shft-Z Press to dull the image contrast.\n"
881 "\n"
882 " = Press to perform histogram equalization on the image.\n"
883 "\n"
884 " Shft-N Press to perform histogram normalization on the image.\n"
885 "\n"
886 " Shft-~ Press to negate the colors of the image.\n"
887 "\n"
888 " . Press to convert the image colors to gray.\n"
889 "\n"
890 " Shft-# Press to set the maximum number of unique colors in the\n"
891 " image.\n"
892 "\n"
893 " F2 Press to reduce the speckles in an image.\n"
894 "\n"
895 " F3 Press to eliminate peak noise from an image.\n"
896 "\n"
897 " F4 Press to add noise to an image.\n"
898 "\n"
899 " F5 Press to sharpen an image.\n"
900 "\n"
901 " F6 Press to delete an image file.\n"
902 "\n"
903 " F7 Press to threshold the image.\n"
904 "\n"
905 " F8 Press to detect edges within an image.\n"
906 "\n"
907 " F9 Press to emboss an image.\n"
908 "\n"
909 " F10 Press to displace pixels by a random amount.\n"
910 "\n"
911 " F11 Press to negate all pixels above the threshold level.\n"
912 "\n"
913 " F12 Press to shade the image using a distant light source.\n"
914 "\n"
915 " F13 Press to lighten or darken image edges to create a 3-D effect.\n"
916 "\n"
917 " F14 Press to segment the image by color.\n"
918 "\n"
919 " Meta-S Press to swirl image pixels about the center.\n"
920 "\n"
921 " Meta-I Press to implode image pixels about the center.\n"
922 "\n"
923 " Meta-W Press to alter an image along a sine wave.\n"
924 "\n"
925 " Meta-P Press to simulate an oil painting.\n"
926 "\n"
927 " Meta-C Press to simulate a charcoal drawing.\n"
928 "\n"
929 " Alt-A Press to annotate the image with text.\n"
930 "\n"
931 " Alt-D Press to draw on an image.\n"
932 "\n"
933 " Alt-P Press to edit an image pixel color.\n"
934 "\n"
935 " Alt-M Press to edit the image matte information.\n"
936 "\n"
937 " Alt-V Press to composite the image with another.\n"
938 "\n"
939 " Alt-B Press to add a border to the image.\n"
940 "\n"
941 " Alt-F Press to add an ornamental border to the image.\n"
942 "\n"
943 " Alt-Shft-!\n"
944 " Press to add an image comment.\n"
945 "\n"
946 " Ctl-A Press to apply image processing techniques to a region\n"
947 " of interest.\n"
948 "\n"
949 " Shft-? Press to display information about the image.\n"
950 "\n"
951 " Shft-+ Press to map the zoom image window.\n"
952 "\n"
953 " Shft-P Press to preview an image enhancement, effect, or f/x.\n"
954 "\n"
955 " F1 Press to display helpful information about display(1).\n"
956 "\n"
957 " Find Press to browse documentation about ImageMagick.\n"
958 "\n"
959 " 1-9 Press to change the level of magnification.\n"
960 "\n"
961 " Use the arrow keys to move the image one pixel up, down,\n"
962 " left, or right within the magnify window. Be sure to first\n"
963 " map the magnify window by pressing button 2.\n"
964 "\n"
965 " Press ALT and one of the arrow keys to trim off one pixel\n"
966 " from any side of the image.\n"
967 },
968 ImageMatteEditHelp[] =
969 {
970 "Matte information within an image is useful for some\n"
971 "operations such as image compositing (See IMAGE\n"
972 "COMPOSITING). This extra channel usually defines a mask\n"
973 "which represents a sort of a cookie-cutter for the image.\n"
974 "This the case when matte is opaque (full coverage) for\n"
975 "pixels inside the shape, zero outside, and between 0 and\n"
976 "QuantumRange on the boundary.\n"
977 "\n"
978 "A small window appears showing the location of the cursor in\n"
979 "the image window. You are now in matte edit mode. To exit\n"
980 "immediately, press Dismiss. In matte edit mode, the Command\n"
981 "widget has these options:\n"
982 "\n"
983 " Method\n"
984 " point\n"
985 " replace\n"
986 " floodfill\n"
987 " filltoborder\n"
988 " reset\n"
989 " Border Color\n"
990 " black\n"
991 " blue\n"
992 " cyan\n"
993 " green\n"
994 " gray\n"
995 " red\n"
996 " magenta\n"
997 " yellow\n"
998 " white\n"
999 " Browser...\n"
1000 " Fuzz\n"
1001 " 0%\n"
1002 " 2%\n"
1003 " 5%\n"
1004 " 10%\n"
1005 " 15%\n"
1006 " Dialog...\n"
1007 " Matte\n"
1008 " Opaque\n"
1009 " Transparent\n"
1010 " Dialog...\n"
1011 " Undo\n"
1012 " Help\n"
1013 " Dismiss\n"
1014 "\n"
1015 "Choose a matte editing method from the Method sub-menu of\n"
1016 "the Command widget. The point method changes the matte value\n"
1017 "of any pixel selected with the pointer until the button is\n"
1018 "is released. The replace method changes the matte value of\n"
1019 "any pixel that matches the color of the pixel you select with\n"
1020 "a button press. Floodfill changes the matte value of any pixel\n"
1021 "that matches the color of the pixel you select with a button\n"
1022 "press and is a neighbor. Whereas filltoborder changes the matte\n"
1023 "value any neighbor pixel that is not the border color. Finally\n"
1024 "reset changes the entire image to the designated matte value.\n"
1025 "\n"
1026 "Choose Matte Value and pick Opaque or Transparent. For other values\n"
1027 "select the Dialog entry. Here a dialog appears requesting a matte\n"
1028 "value. The value you select is assigned as the opacity value of the\n"
1029 "selected pixel or pixels.\n"
1030 "\n"
1031 "Now, press any button to select a pixel within the image\n"
1032 "window to change its matte value.\n"
1033 "\n"
1034 "If the Magnify widget is mapped, it can be helpful in positioning\n"
1035 "your pointer within the image (refer to button 2).\n"
1036 "\n"
1037 "Matte information is only valid in a DirectClass image.\n"
1038 "Therefore, any PseudoClass image is promoted to DirectClass\n"
1039 "(see miff(5)). Note that matte information for PseudoClass\n"
1040 "is not retained for colormapped X server visuals (e.g.\n"
1041 "StaticColor, StaticColor, GrayScale, PseudoColor) unless you\n"
1042 "immediately save your image to a file (refer to Write).\n"
1043 "Correct matte editing behavior may require a TrueColor or\n"
1044 "DirectColor visual or a Standard Colormap.\n"
1045 },
1046 ImagePanHelp[] =
1047 {
1048 "When an image exceeds the width or height of the X server\n"
1049 "screen, display maps a small panning icon. The rectangle\n"
1050 "within the panning icon shows the area that is currently\n"
1051 "displayed in the image window. To pan about the image,\n"
1052 "press any button and drag the pointer within the panning\n"
1053 "icon. The pan rectangle moves with the pointer and the\n"
1054 "image window is updated to reflect the location of the\n"
1055 "rectangle within the panning icon. When you have selected\n"
1056 "the area of the image you wish to view, release the button.\n"
1057 "\n"
1058 "Use the arrow keys to pan the image one pixel up, down,\n"
1059 "left, or right within the image window.\n"
1060 "\n"
1061 "The panning icon is withdrawn if the image becomes smaller\n"
1062 "than the dimensions of the X server screen.\n"
1063 },
1064 ImagePasteHelp[] =
1065 {
1066 "A small window appears showing the location of the cursor in\n"
1067 "the image window. You are now in paste mode. To exit\n"
1068 "immediately, press Dismiss. In paste mode, the Command\n"
1069 "widget has these options:\n"
1070 "\n"
1071 " Operators\n"
1072 " over\n"
1073 " in\n"
1074 " out\n"
1075 " atop\n"
1076 " xor\n"
1077 " plus\n"
1078 " minus\n"
1079 " add\n"
1080 " subtract\n"
1081 " difference\n"
1082 " replace\n"
1083 " Help\n"
1084 " Dismiss\n"
1085 "\n"
1086 "Choose a composite operation from the Operators sub-menu of\n"
1087 "the Command widget. How each operator behaves is described\n"
1088 "below. Image window is the image currently displayed on\n"
1089 "your X server and image is the image obtained with the File\n"
1090 "Browser widget.\n"
1091 "\n"
1092 "Over The result is the union of the two image shapes,\n"
1093 " with image obscuring image window in the region of\n"
1094 " overlap.\n"
1095 "\n"
1096 "In The result is simply image cut by the shape of\n"
1097 " image window. None of the image data of image\n"
1098 " window is in the result.\n"
1099 "\n"
1100 "Out The resulting image is image with the shape of\n"
1101 " image window cut out.\n"
1102 "\n"
1103 "Atop The result is the same shape as the image window,\n"
1104 " with image obscuring image window where the image\n"
1105 " shapes overlap. Note this differs from over\n"
1106 " because the portion of image outside image window's\n"
1107 " shape does not appear in the result.\n"
1108 "\n"
1109 "Xor The result is the image data from both image and\n"
1110 " image window that is outside the overlap region.\n"
1111 " The overlap region is blank.\n"
1112 "\n"
1113 "Plus The result is just the sum of the image data.\n"
1114 " Output values are cropped to QuantumRange (no overflow).\n"
1115 " This operation is independent of the matte\n"
1116 " channels.\n"
1117 "\n"
1118 "Minus The result of image - image window, with underflow\n"
1119 " cropped to zero.\n"
1120 "\n"
1121 "Add The result of image + image window, with overflow\n"
1122 " wrapping around (mod 256).\n"
1123 "\n"
1124 "Subtract The result of image - image window, with underflow\n"
1125 " wrapping around (mod 256). The add and subtract\n"
1126 " operators can be used to perform reversible\n"
1127 " transformations.\n"
1128 "\n"
1129 "Difference\n"
1130 " The result of abs(image - image window). This\n"
1131 " useful for comparing two very similar images.\n"
1132 "\n"
1133 "Copy The resulting image is image window replaced with\n"
1134 " image. Here the matte information is ignored.\n"
1135 "\n"
1136 "CopyRed The red layer of the image window is replace with\n"
1137 " the red layer of the image. The other layers are\n"
1138 " untouched.\n"
1139 "\n"
1140 "CopyGreen\n"
1141 " The green layer of the image window is replace with\n"
1142 " the green layer of the image. The other layers are\n"
1143 " untouched.\n"
1144 "\n"
1145 "CopyBlue The blue layer of the image window is replace with\n"
1146 " the blue layer of the image. The other layers are\n"
1147 " untouched.\n"
1148 "\n"
1149 "CopyOpacity\n"
1150 " The matte layer of the image window is replace with\n"
1151 " the matte layer of the image. The other layers are\n"
1152 " untouched.\n"
1153 "\n"
1154 "The image compositor requires a matte, or alpha channel in\n"
1155 "the image for some operations. This extra channel usually\n"
1156 "defines a mask which represents a sort of a cookie-cutter\n"
1157 "for the image. This the case when matte is opaque (full\n"
1158 "coverage) for pixels inside the shape, zero outside, and\n"
1159 "between 0 and QuantumRange on the boundary. If image does not\n"
1160 "have a matte channel, it is initialized with 0 for any pixel\n"
1161 "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
1162 "\n"
1163 "Note that matte information for image window is not retained\n"
1164 "for colormapped X server visuals (e.g. StaticColor,\n"
1165 "StaticColor, GrayScale, PseudoColor). Correct compositing\n"
1166 "behavior may require a TrueColor or DirectColor visual or a\n"
1167 "Standard Colormap.\n"
1168 "\n"
1169 "Choosing a composite operator is optional. The default\n"
1170 "operator is replace. However, you must choose a location to\n"
1171 "paste your image and press button 1. Press and hold the\n"
1172 "button before releasing and an outline of the image will\n"
1173 "appear to help you identify your location.\n"
1174 "\n"
1175 "The actual colors of the pasted image is saved. However,\n"
1176 "the color that appears in image window may be different.\n"
1177 "For example, on a monochrome screen image window will appear\n"
1178 "black or white even though your pasted image may have\n"
1179 "many colors. If the image is saved to a file it is written\n"
1180 "with the correct colors. To assure the correct colors are\n"
1181 "saved in the final image, any PseudoClass image is promoted\n"
1182 "to DirectClass (see miff(5)). To force a PseudoClass image\n"
1183 "to remain PseudoClass, use -colors.\n"
1184 },
1185 ImageROIHelp[] =
1186 {
1187 "In region of interest mode, the Command widget has these\n"
1188 "options:\n"
1189 "\n"
1190 " Help\n"
1191 " Dismiss\n"
1192 "\n"
1193 "To define a region of interest, press button 1 and drag.\n"
1194 "The region of interest is defined by a highlighted rectangle\n"
1195 "that expands or contracts as it follows the pointer. Once\n"
1196 "you are satisfied with the region of interest, release the\n"
1197 "button. You are now in apply mode. In apply mode the\n"
1198 "Command widget has these options:\n"
1199 "\n"
1200 " File\n"
1201 " Save...\n"
1202 " Print...\n"
1203 " Edit\n"
1204 " Undo\n"
1205 " Redo\n"
1206 " Transform\n"
1207 " Flop\n"
1208 " Flip\n"
1209 " Rotate Right\n"
1210 " Rotate Left\n"
1211 " Enhance\n"
1212 " Hue...\n"
1213 " Saturation...\n"
1214 " Brightness...\n"
1215 " Gamma...\n"
1216 " Spiff\n"
1217 " Dull\n"
1218 " Contrast Stretch\n"
1219 " Sigmoidal Contrast...\n"
1220 " Normalize\n"
1221 " Equalize\n"
1222 " Negate\n"
1223 " Grayscale\n"
1224 " Map...\n"
1225 " Quantize...\n"
1226 " Effects\n"
1227 " Despeckle\n"
1228 " Emboss\n"
1229 " Reduce Noise\n"
1230 " Sharpen...\n"
1231 " Blur...\n"
1232 " Threshold...\n"
1233 " Edge Detect...\n"
1234 " Spread...\n"
1235 " Shade...\n"
1236 " Raise...\n"
1237 " Segment...\n"
1238 " F/X\n"
1239 " Solarize...\n"
1240 " Sepia Tone...\n"
1241 " Swirl...\n"
1242 " Implode...\n"
1243 " Vignette...\n"
1244 " Wave...\n"
1245 " Oil Painting...\n"
1246 " Charcoal Drawing...\n"
1247 " Miscellany\n"
1248 " Image Info\n"
1249 " Zoom Image\n"
1250 " Show Preview...\n"
1251 " Show Histogram\n"
1252 " Show Matte\n"
1253 " Help\n"
1254 " Dismiss\n"
1255 "\n"
1256 "You can make adjustments to the region of interest by moving\n"
1257 "the pointer to one of the rectangle corners, pressing a\n"
1258 "button, and dragging. Finally, choose an image processing\n"
1259 "technique from the Command widget. You can choose more than\n"
1260 "one image processing technique to apply to an area.\n"
1261 "Alternatively, you can move the region of interest before\n"
1262 "applying another image processing technique. To exit, press\n"
1263 "Dismiss.\n"
1264 },
1265 ImageRotateHelp[] =
1266 {
1267 "In rotate mode, the Command widget has these options:\n"
1268 "\n"
1269 " Pixel Color\n"
1270 " black\n"
1271 " blue\n"
1272 " cyan\n"
1273 " green\n"
1274 " gray\n"
1275 " red\n"
1276 " magenta\n"
1277 " yellow\n"
1278 " white\n"
1279 " Browser...\n"
1280 " Direction\n"
1281 " horizontal\n"
1282 " vertical\n"
1283 " Help\n"
1284 " Dismiss\n"
1285 "\n"
1286 "Choose a background color from the Pixel Color sub-menu.\n"
1287 "Additional background colors can be specified with the color\n"
1288 "browser. You can change the menu colors by setting the X\n"
1289 "resources pen1 through pen9.\n"
1290 "\n"
1291 "If you choose the color browser and press Grab, you can\n"
1292 "select the background color by moving the pointer to the\n"
1293 "desired color on the screen and press any button.\n"
1294 "\n"
1295 "Choose a point in the image window and press this button and\n"
1296 "hold. Next, move the pointer to another location in the\n"
1297 "image. As you move a line connects the initial location and\n"
1298 "the pointer. When you release the button, the degree of\n"
1299 "image rotation is determined by the slope of the line you\n"
1300 "just drew. The slope is relative to the direction you\n"
1301 "choose from the Direction sub-menu of the Command widget.\n"
1302 "\n"
1303 "To cancel the image rotation, move the pointer back to the\n"
1304 "starting point of the line and release the button.\n"
1305 };
1306
1307/*
1308 Enumeration declarations.
1309*/
1310typedef enum
1311{
1312 CopyMode,
1313 CropMode,
1314 CutMode
1315} ClipboardMode;
1316
1317typedef enum
1318{
1319 OpenCommand,
1320 NextCommand,
1321 FormerCommand,
1322 SelectCommand,
1323 SaveCommand,
1324 PrintCommand,
1325 DeleteCommand,
1326 NewCommand,
1327 VisualDirectoryCommand,
1328 QuitCommand,
1329 UndoCommand,
1330 RedoCommand,
1331 CutCommand,
1332 CopyCommand,
1333 PasteCommand,
1334 HalfSizeCommand,
1335 OriginalSizeCommand,
1336 DoubleSizeCommand,
1337 ResizeCommand,
1338 ApplyCommand,
1339 RefreshCommand,
1340 RestoreCommand,
1341 CropCommand,
1342 ChopCommand,
1343 FlopCommand,
1344 FlipCommand,
1345 RotateRightCommand,
1346 RotateLeftCommand,
1347 RotateCommand,
1348 ShearCommand,
1349 RollCommand,
1350 TrimCommand,
1351 HueCommand,
1352 SaturationCommand,
1353 BrightnessCommand,
1354 GammaCommand,
1355 SpiffCommand,
1356 DullCommand,
1357 ContrastStretchCommand,
1358 SigmoidalContrastCommand,
1359 NormalizeCommand,
1360 EqualizeCommand,
1361 NegateCommand,
1362 GrayscaleCommand,
1363 MapCommand,
1364 QuantizeCommand,
1365 DespeckleCommand,
1366 EmbossCommand,
1367 ReduceNoiseCommand,
1368 AddNoiseCommand,
1369 SharpenCommand,
1370 BlurCommand,
1371 ThresholdCommand,
1372 EdgeDetectCommand,
1373 SpreadCommand,
1374 ShadeCommand,
1375 RaiseCommand,
1376 SegmentCommand,
1377 SolarizeCommand,
1378 SepiaToneCommand,
1379 SwirlCommand,
1380 ImplodeCommand,
1381 VignetteCommand,
1382 WaveCommand,
1383 OilPaintCommand,
1384 CharcoalDrawCommand,
1385 AnnotateCommand,
1386 DrawCommand,
1387 ColorCommand,
1388 MatteCommand,
1389 CompositeCommand,
1390 AddBorderCommand,
1391 AddFrameCommand,
1392 CommentCommand,
1393 LaunchCommand,
1394 RegionOfInterestCommand,
1395 ROIHelpCommand,
1396 ROIDismissCommand,
1397 InfoCommand,
1398 ZoomCommand,
1399 ShowPreviewCommand,
1400 ShowHistogramCommand,
1401 ShowMatteCommand,
1402 BackgroundCommand,
1403 SlideShowCommand,
1404 PreferencesCommand,
1405 HelpCommand,
1406 BrowseDocumentationCommand,
1407 VersionCommand,
1408 SaveToUndoBufferCommand,
1409 FreeBuffersCommand,
1410 NullCommand
1411} DisplayCommand;
1412
1413typedef enum
1414{
1415 AnnotateNameCommand,
1416 AnnotateFontColorCommand,
1417 AnnotateBackgroundColorCommand,
1418 AnnotateRotateCommand,
1419 AnnotateHelpCommand,
1420 AnnotateDismissCommand,
1421 TextHelpCommand,
1422 TextApplyCommand,
1423 ChopDirectionCommand,
1424 ChopHelpCommand,
1425 ChopDismissCommand,
1426 HorizontalChopCommand,
1427 VerticalChopCommand,
1428 ColorEditMethodCommand,
1429 ColorEditColorCommand,
1430 ColorEditBorderCommand,
1431 ColorEditFuzzCommand,
1432 ColorEditUndoCommand,
1433 ColorEditHelpCommand,
1434 ColorEditDismissCommand,
1435 CompositeOperatorsCommand,
1436 CompositeDissolveCommand,
1437 CompositeDisplaceCommand,
1438 CompositeHelpCommand,
1439 CompositeDismissCommand,
1440 CropHelpCommand,
1441 CropDismissCommand,
1442 RectifyCopyCommand,
1443 RectifyHelpCommand,
1444 RectifyDismissCommand,
1445 DrawElementCommand,
1446 DrawColorCommand,
1447 DrawStippleCommand,
1448 DrawWidthCommand,
1449 DrawUndoCommand,
1450 DrawHelpCommand,
1451 DrawDismissCommand,
1452 MatteEditMethod,
1453 MatteEditBorderCommand,
1454 MatteEditFuzzCommand,
1455 MatteEditValueCommand,
1456 MatteEditUndoCommand,
1457 MatteEditHelpCommand,
1458 MatteEditDismissCommand,
1459 PasteOperatorsCommand,
1460 PasteHelpCommand,
1461 PasteDismissCommand,
1462 RotateColorCommand,
1463 RotateDirectionCommand,
1464 RotateCropCommand,
1465 RotateSharpenCommand,
1466 RotateHelpCommand,
1467 RotateDismissCommand,
1468 HorizontalRotateCommand,
1469 VerticalRotateCommand,
1470 TileLoadCommand,
1471 TileNextCommand,
1472 TileFormerCommand,
1473 TileDeleteCommand,
1474 TileUpdateCommand
1475} ModeType;
1476
1477/*
1478 Stipples.
1479*/
1480#define BricksWidth 20
1481#define BricksHeight 20
1482#define DiagonalWidth 16
1483#define DiagonalHeight 16
1484#define HighlightWidth 8
1485#define HighlightHeight 8
1486#define OpaqueWidth 8
1487#define OpaqueHeight 8
1488#define ScalesWidth 16
1489#define ScalesHeight 16
1490#define ShadowWidth 8
1491#define ShadowHeight 8
1492#define VerticalWidth 16
1493#define VerticalHeight 16
1494#define WavyWidth 16
1495#define WavyHeight 16
1496
1497/*
1498 Constant declaration.
1499*/
1500static const int
1501 RoiDelta = 8;
1502
1503static const unsigned char
1504 BricksBitmap[] =
1505 {
1506 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1507 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1508 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1509 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1510 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1511 },
1512 DiagonalBitmap[] =
1513 {
1514 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1515 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1516 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1517 },
1518 ScalesBitmap[] =
1519 {
1520 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1521 0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1522 0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1523 },
1524 VerticalBitmap[] =
1525 {
1526 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1527 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1528 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1529 },
1530 WavyBitmap[] =
1531 {
1532 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1533 0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1534 0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1535 };
1536
1537/*
1538 Function prototypes.
1539*/
1540static DisplayCommand
1541 XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1542 const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1543
1544static Image
1545 *XMagickCommand(Display *,XResourceInfo *,XWindows *,const DisplayCommand,
1546 Image **,ExceptionInfo *),
1547 *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1548 *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1549 ExceptionInfo *),
1550 *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1551 ExceptionInfo *);
1552
1553static MagickBooleanType
1554 XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1555 ExceptionInfo *),
1556 XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1557 ExceptionInfo *),
1558 XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1559 ExceptionInfo *),
1560 XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1561 ExceptionInfo *),
1562 XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1563 ExceptionInfo *),
1564 XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1565 ExceptionInfo *),
1566 XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1567 XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1568 ExceptionInfo *),
1569 XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1570 ExceptionInfo *),
1571 XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1572 XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1573 XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1574 ExceptionInfo *),
1575 XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1576 XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1577 XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1578
1579static void
1580 XDrawPanRectangle(Display *,XWindows *),
1581 XImageCache(Display *,XResourceInfo *,XWindows *,const DisplayCommand,Image **,
1582 ExceptionInfo *),
1583 XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1584 XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1585 XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1586 XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1587 const KeySym,ExceptionInfo *),
1588 XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1589 XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1590 XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1591
1592/*
1593%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1594% %
1595% %
1596% %
1597% D i s p l a y I m a g e s %
1598% %
1599% %
1600% %
1601%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1602%
1603% DisplayImages() displays an image sequence to any X window screen. It
1604% returns a value other than 0 if successful. Check the exception member
1605% of image to determine the reason for any failure.
1606%
1607% The format of the DisplayImages method is:
1608%
1609% MagickBooleanType DisplayImages(const ImageInfo *image_info,
1610% Image *images,ExceptionInfo *exception)
1611%
1612% A description of each parameter follows:
1613%
1614% o image_info: the image info.
1615%
1616% o image: the image.
1617%
1618% o exception: return any errors or warnings in this structure.
1619%
1620*/
1621MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1622 Image *images,ExceptionInfo *exception)
1623{
1624 char
1625 *argv[1];
1626
1627 Display
1628 *display;
1629
1630 Image
1631 *image;
1632
1633 size_t
1634 state;
1635
1636 ssize_t
1637 i;
1638
1639 XrmDatabase
1640 resource_database;
1641
1642 XResourceInfo
1643 resource_info;
1644
1645 assert(image_info != (const ImageInfo *) NULL);
1646 assert(image_info->signature == MagickCoreSignature);
1647 assert(images != (Image *) NULL);
1648 assert(images->signature == MagickCoreSignature);
1649 if (IsEventLogging() != MagickFalse)
1650 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1651 display=XOpenDisplay(image_info->server_name);
1652 if (display == (Display *) NULL)
1653 {
1654 (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1655 "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1656 return(MagickFalse);
1657 }
1658 if (exception->severity != UndefinedException)
1659 CatchException(exception);
1660 (void) XSetErrorHandler(XError);
1661 resource_database=XGetResourceDatabase(display,GetClientName());
1662 (void) memset(&resource_info,0,sizeof(resource_info));
1663 XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1664 if (image_info->page != (char *) NULL)
1665 resource_info.image_geometry=AcquireString(image_info->page);
1666 resource_info.immutable=MagickTrue;
1667 argv[0]=AcquireString(GetClientName());
1668 state=DefaultState;
1669 for (i=0; (state & ExitState) == 0; i++)
1670 {
1671 if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1672 break;
1673 image=GetImageFromList(images,i % (ssize_t) GetImageListLength(images));
1674 (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1675 }
1676 (void) SetErrorHandler((ErrorHandler) NULL);
1677 (void) SetWarningHandler((WarningHandler) NULL);
1678 argv[0]=DestroyString(argv[0]);
1679 (void) XCloseDisplay(display);
1680 XDestroyResourceInfo(&resource_info);
1681 if (exception->severity != UndefinedException)
1682 return(MagickFalse);
1683 return(MagickTrue);
1684}
1685
1686/*
1687%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1688% %
1689% %
1690% %
1691% R e m o t e D i s p l a y C o m m a n d %
1692% %
1693% %
1694% %
1695%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1696%
1697% RemoteDisplayCommand() encourages a remote display program to display the
1698% specified image filename.
1699%
1700% The format of the RemoteDisplayCommand method is:
1701%
1702% MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1703% const char *window,const char *filename,ExceptionInfo *exception)
1704%
1705% A description of each parameter follows:
1706%
1707% o image_info: the image info.
1708%
1709% o window: Specifies the name or id of an X window.
1710%
1711% o filename: the name of the image filename to display.
1712%
1713% o exception: return any errors or warnings in this structure.
1714%
1715*/
1716MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1717 const char *window,const char *filename,ExceptionInfo *exception)
1718{
1719 Display
1720 *display;
1721
1722 MagickStatusType
1723 status;
1724
1725 assert(image_info != (const ImageInfo *) NULL);
1726 assert(image_info->signature == MagickCoreSignature);
1727 assert(filename != (char *) NULL);
1728 if (IsEventLogging() != MagickFalse)
1729 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1730 display=XOpenDisplay(image_info->server_name);
1731 if (display == (Display *) NULL)
1732 {
1733 (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1734 "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1735 return(MagickFalse);
1736 }
1737 (void) XSetErrorHandler(XError);
1738 status=XRemoteCommand(display,window,filename);
1739 (void) XCloseDisplay(display);
1740 return(status != 0 ? MagickTrue : MagickFalse);
1741}
1742
1743/*
1744%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1745% %
1746% %
1747% %
1748+ X A n n o t a t e E d i t I m a g e %
1749% %
1750% %
1751% %
1752%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1753%
1754% XAnnotateEditImage() annotates the image with text.
1755%
1756% The format of the XAnnotateEditImage method is:
1757%
1758% MagickBooleanType XAnnotateEditImage(Display *display,
1759% XResourceInfo *resource_info,XWindows *windows,Image *image,
1760% ExceptionInfo *exception)
1761%
1762% A description of each parameter follows:
1763%
1764% o display: Specifies a connection to an X server; returned from
1765% XOpenDisplay.
1766%
1767% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1768%
1769% o windows: Specifies a pointer to a XWindows structure.
1770%
1771% o image: the image; returned from ReadImage.
1772%
1773*/
1774
1775static MagickBooleanType XAnnotateEditImage(Display *display,
1776 XResourceInfo *resource_info,XWindows *windows,Image *image,
1777 ExceptionInfo *exception)
1778{
1779 const char
1780 *const AnnotateMenu[] =
1781 {
1782 "Font Name",
1783 "Font Color",
1784 "Box Color",
1785 "Rotate Text",
1786 "Help",
1787 "Dismiss",
1788 (char *) NULL
1789 },
1790 *const TextMenu[] =
1791 {
1792 "Help",
1793 "Apply",
1794 (char *) NULL
1795 };
1796
1797 static const ModeType
1798 AnnotateCommands[] =
1799 {
1800 AnnotateNameCommand,
1801 AnnotateFontColorCommand,
1802 AnnotateBackgroundColorCommand,
1803 AnnotateRotateCommand,
1804 AnnotateHelpCommand,
1805 AnnotateDismissCommand
1806 },
1807 TextCommands[] =
1808 {
1809 TextHelpCommand,
1810 TextApplyCommand
1811 };
1812
1813 static MagickBooleanType
1814 transparent_box = MagickTrue,
1815 transparent_pen = MagickFalse;
1816
1817 static double
1818 degrees = 0.0;
1819
1820 static unsigned int
1821 box_id = MaxNumberPens-2,
1822 font_id = 0,
1823 pen_id = 0;
1824
1825 char
1826 command[MagickPathExtent],
1827 *p,
1828 text[MagickPathExtent];
1829
1830 const char
1831 *ColorMenu[MaxNumberPens+1];
1832
1833 Cursor
1834 cursor;
1835
1836 GC
1837 annotate_context;
1838
1839 int
1840 id,
1841 pen_number,
1842 status,
1843 x,
1844 y;
1845
1846 KeySym
1847 key_symbol;
1848
1849 size_t
1850 state;
1851
1852 ssize_t
1853 i;
1854
1855 unsigned int
1856 height,
1857 width;
1858
1859 XAnnotateInfo
1860 *annotate_info,
1861 *previous_info;
1862
1863 XColor
1864 color;
1865
1866 XFontStruct
1867 *font_info;
1868
1869 XEvent
1870 event,
1871 text_event;
1872
1873 /*
1874 Map Command widget.
1875 */
1876 (void) CloneString(&windows->command.name,"Annotate");
1877 windows->command.data=4;
1878 (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1879 (void) XMapRaised(display,windows->command.id);
1880 XClientMessage(display,windows->image.id,windows->im_protocols,
1881 windows->im_update_widget,CurrentTime);
1882 /*
1883 Track pointer until button 1 is pressed.
1884 */
1885 XQueryPosition(display,windows->image.id,&x,&y);
1886 (void) XSelectInput(display,windows->image.id,
1887 windows->image.attributes.event_mask | PointerMotionMask);
1888 cursor=XCreateFontCursor(display,XC_left_side);
1889 (void) XCheckDefineCursor(display,windows->image.id,cursor);
1890 state=DefaultState;
1891 do
1892 {
1893 if (windows->info.mapped != MagickFalse)
1894 {
1895 /*
1896 Display pointer position.
1897 */
1898 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
1899 x+windows->image.x,y+windows->image.y);
1900 XInfoWidget(display,windows,text);
1901 }
1902 /*
1903 Wait for next event.
1904 */
1905 XScreenEvent(display,windows,&event,exception);
1906 if (event.xany.window == windows->command.id)
1907 {
1908 /*
1909 Select a command from the Command widget.
1910 */
1911 id=XCommandWidget(display,windows,AnnotateMenu,&event);
1912 (void) XCheckDefineCursor(display,windows->image.id,cursor);
1913 if (id < 0)
1914 continue;
1915 switch (AnnotateCommands[id])
1916 {
1917 case AnnotateNameCommand:
1918 {
1919 const char
1920 *FontMenu[MaxNumberFonts];
1921
1922 int
1923 font_number;
1924
1925 /*
1926 Initialize menu selections.
1927 */
1928 for (i=0; i < MaxNumberFonts; i++)
1929 FontMenu[i]=resource_info->font_name[i];
1930 FontMenu[MaxNumberFonts-2]="Browser...";
1931 FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1932 /*
1933 Select a font name from the pop-up menu.
1934 */
1935 font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1936 (const char **) FontMenu,command);
1937 if (font_number < 0)
1938 break;
1939 if (font_number == (MaxNumberFonts-2))
1940 {
1941 static char
1942 font_name[MagickPathExtent] = "fixed";
1943
1944 /*
1945 Select a font name from a browser.
1946 */
1947 resource_info->font_name[font_number]=font_name;
1948 XFontBrowserWidget(display,windows,"Select",font_name);
1949 if (*font_name == '\0')
1950 break;
1951 }
1952 /*
1953 Initialize font info.
1954 */
1955 font_info=XLoadQueryFont(display,resource_info->font_name[
1956 font_number]);
1957 if (font_info == (XFontStruct *) NULL)
1958 {
1959 XNoticeWidget(display,windows,"Unable to load font:",
1960 resource_info->font_name[font_number]);
1961 break;
1962 }
1963 font_id=(unsigned int) font_number;
1964 (void) XFreeFont(display,font_info);
1965 break;
1966 }
1967 case AnnotateFontColorCommand:
1968 {
1969 /*
1970 Initialize menu selections.
1971 */
1972 for (i=0; i < (int) (MaxNumberPens-2); i++)
1973 ColorMenu[i]=resource_info->pen_colors[i];
1974 ColorMenu[MaxNumberPens-2]="transparent";
1975 ColorMenu[MaxNumberPens-1]="Browser...";
1976 ColorMenu[MaxNumberPens]=(const char *) NULL;
1977 /*
1978 Select a pen color from the pop-up menu.
1979 */
1980 pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
1981 (const char **) ColorMenu,command);
1982 if (pen_number < 0)
1983 break;
1984 transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
1985 MagickFalse;
1986 if (transparent_pen != MagickFalse)
1987 break;
1988 if (pen_number == (MaxNumberPens-1))
1989 {
1990 static char
1991 color_name[MagickPathExtent] = "gray";
1992
1993 /*
1994 Select a pen color from a dialog.
1995 */
1996 resource_info->pen_colors[pen_number]=color_name;
1997 XColorBrowserWidget(display,windows,"Select",color_name);
1998 if (*color_name == '\0')
1999 break;
2000 }
2001 /*
2002 Set pen color.
2003 */
2004 (void) XParseColor(display,windows->map_info->colormap,
2005 resource_info->pen_colors[pen_number],&color);
2006 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2007 (unsigned int) MaxColors,&color);
2008 windows->pixel_info->pen_colors[pen_number]=color;
2009 pen_id=(unsigned int) pen_number;
2010 break;
2011 }
2012 case AnnotateBackgroundColorCommand:
2013 {
2014 /*
2015 Initialize menu selections.
2016 */
2017 for (i=0; i < (int) (MaxNumberPens-2); i++)
2018 ColorMenu[i]=resource_info->pen_colors[i];
2019 ColorMenu[MaxNumberPens-2]="transparent";
2020 ColorMenu[MaxNumberPens-1]="Browser...";
2021 ColorMenu[MaxNumberPens]=(const char *) NULL;
2022 /*
2023 Select a pen color from the pop-up menu.
2024 */
2025 pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2026 (const char **) ColorMenu,command);
2027 if (pen_number < 0)
2028 break;
2029 transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2030 MagickFalse;
2031 if (transparent_box != MagickFalse)
2032 break;
2033 if (pen_number == (MaxNumberPens-1))
2034 {
2035 static char
2036 color_name[MagickPathExtent] = "gray";
2037
2038 /*
2039 Select a pen color from a dialog.
2040 */
2041 resource_info->pen_colors[pen_number]=color_name;
2042 XColorBrowserWidget(display,windows,"Select",color_name);
2043 if (*color_name == '\0')
2044 break;
2045 }
2046 /*
2047 Set pen color.
2048 */
2049 (void) XParseColor(display,windows->map_info->colormap,
2050 resource_info->pen_colors[pen_number],&color);
2051 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2052 (unsigned int) MaxColors,&color);
2053 windows->pixel_info->pen_colors[pen_number]=color;
2054 box_id=(unsigned int) pen_number;
2055 break;
2056 }
2057 case AnnotateRotateCommand:
2058 {
2059 int
2060 entry;
2061
2062 const char
2063 *const RotateMenu[] =
2064 {
2065 "-90",
2066 "-45",
2067 "-30",
2068 "0",
2069 "30",
2070 "45",
2071 "90",
2072 "180",
2073 "Dialog...",
2074 (char *) NULL,
2075 };
2076
2077 static char
2078 angle[MagickPathExtent] = "30.0";
2079
2080 /*
2081 Select a command from the pop-up menu.
2082 */
2083 entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2084 command);
2085 if (entry < 0)
2086 break;
2087 if (entry != 8)
2088 {
2089 degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2090 break;
2091 }
2092 (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2093 angle);
2094 if (*angle == '\0')
2095 break;
2096 degrees=StringToDouble(angle,(char **) NULL);
2097 break;
2098 }
2099 case AnnotateHelpCommand:
2100 {
2101 XTextViewHelp(display,resource_info,windows,MagickFalse,
2102 "Help Viewer - Image Annotation",ImageAnnotateHelp);
2103 break;
2104 }
2105 case AnnotateDismissCommand:
2106 {
2107 /*
2108 Prematurely exit.
2109 */
2110 state|=EscapeState;
2111 state|=ExitState;
2112 break;
2113 }
2114 default:
2115 break;
2116 }
2117 continue;
2118 }
2119 switch (event.type)
2120 {
2121 case ButtonPress:
2122 {
2123 if (event.xbutton.button != Button1)
2124 break;
2125 if (event.xbutton.window != windows->image.id)
2126 break;
2127 /*
2128 Change to text entering mode.
2129 */
2130 x=event.xbutton.x;
2131 y=event.xbutton.y;
2132 state|=ExitState;
2133 break;
2134 }
2135 case ButtonRelease:
2136 break;
2137 case Expose:
2138 break;
2139 case KeyPress:
2140 {
2141 if (event.xkey.window != windows->image.id)
2142 break;
2143 /*
2144 Respond to a user key press.
2145 */
2146 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2147 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2148 switch ((int) key_symbol)
2149 {
2150 case XK_Escape:
2151 case XK_F20:
2152 {
2153 /*
2154 Prematurely exit.
2155 */
2156 state|=EscapeState;
2157 state|=ExitState;
2158 break;
2159 }
2160 case XK_F1:
2161 case XK_Help:
2162 {
2163 XTextViewHelp(display,resource_info,windows,MagickFalse,
2164 "Help Viewer - Image Annotation",ImageAnnotateHelp);
2165 break;
2166 }
2167 default:
2168 {
2169 (void) XBell(display,0);
2170 break;
2171 }
2172 }
2173 break;
2174 }
2175 case MotionNotify:
2176 {
2177 /*
2178 Map and unmap Info widget as cursor crosses its boundaries.
2179 */
2180 x=event.xmotion.x;
2181 y=event.xmotion.y;
2182 if (windows->info.mapped != MagickFalse)
2183 {
2184 if ((x < (windows->info.x+(int) windows->info.width)) &&
2185 (y < (windows->info.y+(int) windows->info.height)))
2186 (void) XWithdrawWindow(display,windows->info.id,
2187 windows->info.screen);
2188 }
2189 else
2190 if ((x > (windows->info.x+(int) windows->info.width)) ||
2191 (y > (windows->info.y+(int) windows->info.height)))
2192 (void) XMapWindow(display,windows->info.id);
2193 break;
2194 }
2195 default:
2196 break;
2197 }
2198 } while ((state & ExitState) == 0);
2199 (void) XSelectInput(display,windows->image.id,
2200 windows->image.attributes.event_mask);
2201 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2202 if ((state & EscapeState) != 0)
2203 return(MagickTrue);
2204 /*
2205 Set font info and check boundary conditions.
2206 */
2207 font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2208 if (font_info == (XFontStruct *) NULL)
2209 {
2210 XNoticeWidget(display,windows,"Unable to load font:",
2211 resource_info->font_name[font_id]);
2212 font_info=windows->font_info;
2213 }
2214 if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2215 x=(int) windows->image.width-font_info->max_bounds.width;
2216 if (y < (int) (font_info->ascent+font_info->descent))
2217 y=(int) font_info->ascent+font_info->descent;
2218 if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2219 ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2220 return(MagickFalse);
2221 /*
2222 Initialize annotate structure.
2223 */
2224 annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2225 if (annotate_info == (XAnnotateInfo *) NULL)
2226 return(MagickFalse);
2227 XGetAnnotateInfo(annotate_info);
2228 annotate_info->x=x;
2229 annotate_info->y=y;
2230 if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2231 annotate_info->stencil=OpaqueStencil;
2232 else
2233 if (transparent_box == MagickFalse)
2234 annotate_info->stencil=BackgroundStencil;
2235 else
2236 annotate_info->stencil=ForegroundStencil;
2237 annotate_info->height=(unsigned int) (font_info->ascent+font_info->descent);
2238 annotate_info->degrees=degrees;
2239 annotate_info->font_info=font_info;
2240 annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2241 windows->image.width/(size_t) MagickMax(font_info->min_bounds.width,1)+2UL,
2242 sizeof(*annotate_info->text));
2243 if (annotate_info->text == (char *) NULL)
2244 return(MagickFalse);
2245 /*
2246 Create cursor and set graphic context.
2247 */
2248 cursor=XCreateFontCursor(display,XC_pencil);
2249 (void) XCheckDefineCursor(display,windows->image.id,cursor);
2250 annotate_context=windows->image.annotate_context;
2251 (void) XSetFont(display,annotate_context,font_info->fid);
2252 (void) XSetBackground(display,annotate_context,
2253 windows->pixel_info->pen_colors[box_id].pixel);
2254 (void) XSetForeground(display,annotate_context,
2255 windows->pixel_info->pen_colors[pen_id].pixel);
2256 /*
2257 Begin annotating the image with text.
2258 */
2259 (void) CloneString(&windows->command.name,"Text");
2260 windows->command.data=0;
2261 (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2262 state=DefaultState;
2263 (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2264 text_event.xexpose.width=(int) font_info->max_bounds.width;
2265 text_event.xexpose.height=font_info->max_bounds.ascent+
2266 font_info->max_bounds.descent;
2267 p=annotate_info->text;
2268 do
2269 {
2270 /*
2271 Display text cursor.
2272 */
2273 *p='\0';
2274 (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2275 /*
2276 Wait for next event.
2277 */
2278 XScreenEvent(display,windows,&event,exception);
2279 if (event.xany.window == windows->command.id)
2280 {
2281 /*
2282 Select a command from the Command widget.
2283 */
2284 (void) XSetBackground(display,annotate_context,
2285 windows->pixel_info->background_color.pixel);
2286 (void) XSetForeground(display,annotate_context,
2287 windows->pixel_info->foreground_color.pixel);
2288 id=XCommandWidget(display,windows,AnnotateMenu,&event);
2289 (void) XSetBackground(display,annotate_context,
2290 windows->pixel_info->pen_colors[box_id].pixel);
2291 (void) XSetForeground(display,annotate_context,
2292 windows->pixel_info->pen_colors[pen_id].pixel);
2293 if (id < 0)
2294 continue;
2295 switch (TextCommands[id])
2296 {
2297 case TextHelpCommand:
2298 {
2299 XTextViewHelp(display,resource_info,windows,MagickFalse,
2300 "Help Viewer - Image Annotation",ImageAnnotateHelp);
2301 (void) XCheckDefineCursor(display,windows->image.id,cursor);
2302 break;
2303 }
2304 case TextApplyCommand:
2305 {
2306 /*
2307 Finished annotating.
2308 */
2309 annotate_info->width=(unsigned int) XTextWidth(font_info,
2310 annotate_info->text,(int) strlen(annotate_info->text));
2311 XRefreshWindow(display,&windows->image,&text_event);
2312 state|=ExitState;
2313 break;
2314 }
2315 default:
2316 break;
2317 }
2318 continue;
2319 }
2320 /*
2321 Erase text cursor.
2322 */
2323 text_event.xexpose.x=x;
2324 text_event.xexpose.y=y-font_info->max_bounds.ascent;
2325 (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2326 (unsigned int) text_event.xexpose.width,(unsigned int)
2327 text_event.xexpose.height,MagickFalse);
2328 XRefreshWindow(display,&windows->image,&text_event);
2329 switch (event.type)
2330 {
2331 case ButtonPress:
2332 {
2333 if (event.xbutton.window != windows->image.id)
2334 break;
2335 if (event.xbutton.button == Button2)
2336 {
2337 /*
2338 Request primary selection.
2339 */
2340 (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2341 windows->image.id,CurrentTime);
2342 break;
2343 }
2344 break;
2345 }
2346 case Expose:
2347 {
2348 if (event.xexpose.count == 0)
2349 {
2350 XAnnotateInfo
2351 *text_info;
2352
2353 /*
2354 Refresh Image window.
2355 */
2356 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2357 text_info=annotate_info;
2358 while (text_info != (XAnnotateInfo *) NULL)
2359 {
2360 if (annotate_info->stencil == ForegroundStencil)
2361 (void) XDrawString(display,windows->image.id,annotate_context,
2362 text_info->x,text_info->y,text_info->text,
2363 (int) strlen(text_info->text));
2364 else
2365 (void) XDrawImageString(display,windows->image.id,
2366 annotate_context,text_info->x,text_info->y,text_info->text,
2367 (int) strlen(text_info->text));
2368 text_info=text_info->previous;
2369 }
2370 (void) XDrawString(display,windows->image.id,annotate_context,
2371 x,y,"_",1);
2372 }
2373 break;
2374 }
2375 case KeyPress:
2376 {
2377 int
2378 length;
2379
2380 if (event.xkey.window != windows->image.id)
2381 break;
2382 /*
2383 Respond to a user key press.
2384 */
2385 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2386 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2387 *(command+length)='\0';
2388 if (((event.xkey.state & ControlMask) != 0) ||
2389 ((event.xkey.state & Mod1Mask) != 0))
2390 state|=ModifierState;
2391 if ((state & ModifierState) != 0)
2392 switch ((int) key_symbol)
2393 {
2394 case XK_u:
2395 case XK_U:
2396 {
2397 key_symbol=DeleteCommand;
2398 break;
2399 }
2400 default:
2401 break;
2402 }
2403 switch ((int) key_symbol)
2404 {
2405 case XK_BackSpace:
2406 {
2407 /*
2408 Erase one character.
2409 */
2410 if (p == annotate_info->text)
2411 {
2412 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2413 break;
2414 else
2415 {
2416 /*
2417 Go to end of the previous line of text.
2418 */
2419 annotate_info=annotate_info->previous;
2420 p=annotate_info->text;
2421 x=annotate_info->x+(int) annotate_info->width;
2422 y=annotate_info->y;
2423 if (annotate_info->width != 0)
2424 p+=strlen(annotate_info->text);
2425 break;
2426 }
2427 }
2428 p--;
2429 x-=XTextWidth(font_info,p,1);
2430 text_event.xexpose.x=x;
2431 text_event.xexpose.y=y-font_info->max_bounds.ascent;
2432 XRefreshWindow(display,&windows->image,&text_event);
2433 break;
2434 }
2435 case XK_bracketleft:
2436 {
2437 key_symbol=XK_Escape;
2438 break;
2439 }
2440 case DeleteCommand:
2441 {
2442 /*
2443 Erase the entire line of text.
2444 */
2445 while (p != annotate_info->text)
2446 {
2447 p--;
2448 x-=XTextWidth(font_info,p,1);
2449 text_event.xexpose.x=x;
2450 XRefreshWindow(display,&windows->image,&text_event);
2451 }
2452 break;
2453 }
2454 case XK_Escape:
2455 case XK_F20:
2456 {
2457 /*
2458 Finished annotating.
2459 */
2460 annotate_info->width=(unsigned int) XTextWidth(font_info,
2461 annotate_info->text,(int) strlen(annotate_info->text));
2462 XRefreshWindow(display,&windows->image,&text_event);
2463 state|=ExitState;
2464 break;
2465 }
2466 default:
2467 {
2468 /*
2469 Draw a single character on the Image window.
2470 */
2471 if ((state & ModifierState) != 0)
2472 break;
2473 if (*command == '\0')
2474 break;
2475 *p=(*command);
2476 if (annotate_info->stencil == ForegroundStencil)
2477 (void) XDrawString(display,windows->image.id,annotate_context,
2478 x,y,p,1);
2479 else
2480 (void) XDrawImageString(display,windows->image.id,
2481 annotate_context,x,y,p,1);
2482 x+=XTextWidth(font_info,p,1);
2483 p++;
2484 if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2485 break;
2486 magick_fallthrough;
2487 }
2488 case XK_Return:
2489 case XK_KP_Enter:
2490 {
2491 /*
2492 Advance to the next line of text.
2493 */
2494 *p='\0';
2495 annotate_info->width=(unsigned int) XTextWidth(font_info,
2496 annotate_info->text,(int) strlen(annotate_info->text));
2497 if (annotate_info->next != (XAnnotateInfo *) NULL)
2498 {
2499 /*
2500 Line of text already exists.
2501 */
2502 annotate_info=annotate_info->next;
2503 x=annotate_info->x;
2504 y=annotate_info->y;
2505 p=annotate_info->text;
2506 break;
2507 }
2508 annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2509 sizeof(*annotate_info->next));
2510 if (annotate_info->next == (XAnnotateInfo *) NULL)
2511 return(MagickFalse);
2512 *annotate_info->next=(*annotate_info);
2513 annotate_info->next->previous=annotate_info;
2514 annotate_info=annotate_info->next;
2515 annotate_info->text=(char *) AcquireQuantumMemory((size_t) (
2516 (ssize_t) windows->image.width/MagickMax((ssize_t)
2517 font_info->min_bounds.width,1)+2L),sizeof(*annotate_info->text));
2518 if (annotate_info->text == (char *) NULL)
2519 return(MagickFalse);
2520 annotate_info->y+=(ssize_t) annotate_info->height;
2521 if (annotate_info->y > (int) windows->image.height)
2522 annotate_info->y=(int) annotate_info->height;
2523 annotate_info->next=(XAnnotateInfo *) NULL;
2524 x=annotate_info->x;
2525 y=annotate_info->y;
2526 p=annotate_info->text;
2527 break;
2528 }
2529 }
2530 break;
2531 }
2532 case KeyRelease:
2533 {
2534 /*
2535 Respond to a user key release.
2536 */
2537 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2538 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2539 state&=(size_t) (~ModifierState);
2540 break;
2541 }
2542 case SelectionNotify:
2543 {
2544 Atom
2545 type;
2546
2547 int
2548 format;
2549
2550 unsigned char
2551 *data;
2552
2553 unsigned long
2554 after,
2555 length;
2556
2557 /*
2558 Obtain response from primary selection.
2559 */
2560 if (event.xselection.property == (Atom) None)
2561 break;
2562 status=XGetWindowProperty(display,event.xselection.requestor,
2563 event.xselection.property,0L,(long) MagickPathExtent,True,XA_STRING,
2564 &type,&format,&length,&after,&data);
2565 if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2566 (length == 0))
2567 break;
2568 /*
2569 Annotate Image window with primary selection.
2570 */
2571 for (i=0; i < (ssize_t) length; i++)
2572 {
2573 if ((char) data[i] != '\n')
2574 {
2575 /*
2576 Draw a single character on the Image window.
2577 */
2578 *p=(char) data[i];
2579 (void) XDrawString(display,windows->image.id,annotate_context,
2580 x,y,p,1);
2581 x+=XTextWidth(font_info,p,1);
2582 p++;
2583 if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2584 continue;
2585 }
2586 /*
2587 Advance to the next line of text.
2588 */
2589 *p='\0';
2590 annotate_info->width=(unsigned int) XTextWidth(font_info,
2591 annotate_info->text,(int) strlen(annotate_info->text));
2592 if (annotate_info->next != (XAnnotateInfo *) NULL)
2593 {
2594 /*
2595 Line of text already exists.
2596 */
2597 annotate_info=annotate_info->next;
2598 x=annotate_info->x;
2599 y=annotate_info->y;
2600 p=annotate_info->text;
2601 continue;
2602 }
2603 annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2604 sizeof(*annotate_info->next));
2605 if (annotate_info->next == (XAnnotateInfo *) NULL)
2606 return(MagickFalse);
2607 *annotate_info->next=(*annotate_info);
2608 annotate_info->next->previous=annotate_info;
2609 annotate_info=annotate_info->next;
2610 annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2611 (windows->image.width/MagickMax((ssize_t)
2612 font_info->min_bounds.width,1)+2L),sizeof(*annotate_info->text));
2613 if (annotate_info->text == (char *) NULL)
2614 return(MagickFalse);
2615 annotate_info->y+=(ssize_t) annotate_info->height;
2616 if (annotate_info->y > (int) windows->image.height)
2617 annotate_info->y=(int) annotate_info->height;
2618 annotate_info->next=(XAnnotateInfo *) NULL;
2619 x=annotate_info->x;
2620 y=annotate_info->y;
2621 p=annotate_info->text;
2622 }
2623 (void) XFree((void *) data);
2624 break;
2625 }
2626 default:
2627 break;
2628 }
2629 } while ((state & ExitState) == 0);
2630 (void) XFreeCursor(display,cursor);
2631 /*
2632 Annotation is relative to image configuration.
2633 */
2634 width=(unsigned int) image->columns;
2635 height=(unsigned int) image->rows;
2636 x=0;
2637 y=0;
2638 if (windows->image.crop_geometry != (char *) NULL)
2639 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2640 /*
2641 Initialize annotated image.
2642 */
2643 XSetCursorState(display,windows,MagickTrue);
2644 XCheckRefreshWindows(display,windows);
2645 while (annotate_info != (XAnnotateInfo *) NULL)
2646 {
2647 if (annotate_info->width == 0)
2648 {
2649 /*
2650 No text on this line-- go to the next line of text.
2651 */
2652 previous_info=annotate_info->previous;
2653 annotate_info->text=(char *)
2654 RelinquishMagickMemory(annotate_info->text);
2655 annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2656 annotate_info=previous_info;
2657 continue;
2658 }
2659 /*
2660 Determine pixel index for box and pen color.
2661 */
2662 windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2663 if (windows->pixel_info->colors != 0)
2664 for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2665 if (windows->pixel_info->pixels[i] ==
2666 windows->pixel_info->pen_colors[box_id].pixel)
2667 {
2668 windows->pixel_info->box_index=(unsigned short) i;
2669 break;
2670 }
2671 windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2672 if (windows->pixel_info->colors != 0)
2673 for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2674 if (windows->pixel_info->pixels[i] ==
2675 windows->pixel_info->pen_colors[pen_id].pixel)
2676 {
2677 windows->pixel_info->pen_index=(unsigned short) i;
2678 break;
2679 }
2680 /*
2681 Define the annotate geometry string.
2682 */
2683 annotate_info->x=(int)
2684 width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2685 annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2686 windows->image.y)/windows->image.ximage->height;
2687 (void) FormatLocaleString(annotate_info->geometry,MagickPathExtent,
2688 "%gx%g%+g%+g",(double) width*annotate_info->width/
2689 windows->image.ximage->width,(double) height*annotate_info->height/
2690 windows->image.ximage->height,(double) annotate_info->x+x,(double)
2691 annotate_info->y+y);
2692 /*
2693 Annotate image with text.
2694 */
2695 status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2696 exception) == MagickFalse ? 0 : 1;
2697 if (status == 0)
2698 return(MagickFalse);
2699 /*
2700 Free up memory.
2701 */
2702 previous_info=annotate_info->previous;
2703 annotate_info->text=DestroyString(annotate_info->text);
2704 annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2705 annotate_info=previous_info;
2706 }
2707 (void) XSetForeground(display,annotate_context,
2708 windows->pixel_info->foreground_color.pixel);
2709 (void) XSetBackground(display,annotate_context,
2710 windows->pixel_info->background_color.pixel);
2711 (void) XSetFont(display,annotate_context,windows->font_info->fid);
2712 XSetCursorState(display,windows,MagickFalse);
2713 (void) XFreeFont(display,font_info);
2714 /*
2715 Update image configuration.
2716 */
2717 XConfigureImageColormap(display,resource_info,windows,image,exception);
2718 (void) XConfigureImage(display,resource_info,windows,image,exception);
2719 return(MagickTrue);
2720}
2721
2722/*
2723%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2724% %
2725% %
2726% %
2727+ X B a c k g r o u n d I m a g e %
2728% %
2729% %
2730% %
2731%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2732%
2733% XBackgroundImage() displays the image in the background of a window.
2734%
2735% The format of the XBackgroundImage method is:
2736%
2737% MagickBooleanType XBackgroundImage(Display *display,
2738% XResourceInfo *resource_info,XWindows *windows,Image **image,
2739% ExceptionInfo *exception)
2740%
2741% A description of each parameter follows:
2742%
2743% o display: Specifies a connection to an X server; returned from
2744% XOpenDisplay.
2745%
2746% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2747%
2748% o windows: Specifies a pointer to a XWindows structure.
2749%
2750% o image: the image.
2751%
2752% o exception: return any errors or warnings in this structure.
2753%
2754*/
2755static MagickBooleanType XBackgroundImage(Display *display,
2756 XResourceInfo *resource_info,XWindows *windows,Image **image,
2757 ExceptionInfo *exception)
2758{
2759#define BackgroundImageTag "Background/Image"
2760
2761 int
2762 status;
2763
2764 static char
2765 window_id[MagickPathExtent] = "root";
2766
2767 XResourceInfo
2768 background_resources;
2769
2770 /*
2771 Put image in background.
2772 */
2773 status=XDialogWidget(display,windows,"Background",
2774 "Enter window id (id 0x00 selects window with pointer):",window_id);
2775 if (*window_id == '\0')
2776 return(MagickFalse);
2777 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2778 exception);
2779 XInfoWidget(display,windows,BackgroundImageTag);
2780 XSetCursorState(display,windows,MagickTrue);
2781 XCheckRefreshWindows(display,windows);
2782 background_resources=(*resource_info);
2783 background_resources.window_id=window_id;
2784 background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2785 status=XDisplayBackgroundImage(display,&background_resources,*image,
2786 exception) == MagickFalse ? 0 : 1;
2787 if (status != MagickFalse)
2788 XClientMessage(display,windows->image.id,windows->im_protocols,
2789 windows->im_retain_colors,CurrentTime);
2790 XSetCursorState(display,windows,MagickFalse);
2791 (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2792 exception);
2793 return(MagickTrue);
2794}
2795
2796/*
2797%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2798% %
2799% %
2800% %
2801+ X C h o p I m a g e %
2802% %
2803% %
2804% %
2805%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2806%
2807% XChopImage() chops the X image.
2808%
2809% The format of the XChopImage method is:
2810%
2811% MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2812% XWindows *windows,Image **image,ExceptionInfo *exception)
2813%
2814% A description of each parameter follows:
2815%
2816% o display: Specifies a connection to an X server; returned from
2817% XOpenDisplay.
2818%
2819% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2820%
2821% o windows: Specifies a pointer to a XWindows structure.
2822%
2823% o image: the image.
2824%
2825% o exception: return any errors or warnings in this structure.
2826%
2827*/
2828static MagickBooleanType XChopImage(Display *display,
2829 XResourceInfo *resource_info,XWindows *windows,Image **image,
2830 ExceptionInfo *exception)
2831{
2832 const char
2833 *const ChopMenu[] =
2834 {
2835 "Direction",
2836 "Help",
2837 "Dismiss",
2838 (char *) NULL
2839 };
2840
2841 static ModeType
2842 direction = HorizontalChopCommand;
2843
2844 static const ModeType
2845 ChopCommands[] =
2846 {
2847 ChopDirectionCommand,
2848 ChopHelpCommand,
2849 ChopDismissCommand
2850 },
2851 DirectionCommands[] =
2852 {
2853 HorizontalChopCommand,
2854 VerticalChopCommand
2855 };
2856
2857 char
2858 text[MagickPathExtent];
2859
2860 double
2861 scale_factor;
2862
2863 Image
2864 *chop_image;
2865
2866 int
2867 id,
2868 x,
2869 y;
2870
2872 chop_info;
2873
2874 size_t
2875 state;
2876
2877 unsigned int
2878 distance,
2879 height,
2880 width;
2881
2882 XEvent
2883 event;
2884
2885 XSegment
2886 segment_info;
2887
2888 /*
2889 Map Command widget.
2890 */
2891 (void) CloneString(&windows->command.name,"Chop");
2892 windows->command.data=1;
2893 (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2894 (void) XMapRaised(display,windows->command.id);
2895 XClientMessage(display,windows->image.id,windows->im_protocols,
2896 windows->im_update_widget,CurrentTime);
2897 /*
2898 Track pointer until button 1 is pressed.
2899 */
2900 XQueryPosition(display,windows->image.id,&x,&y);
2901 (void) XSelectInput(display,windows->image.id,
2902 windows->image.attributes.event_mask | PointerMotionMask);
2903 state=DefaultState;
2904 (void) memset(&segment_info,0,sizeof(segment_info));
2905 do
2906 {
2907 if (windows->info.mapped != MagickFalse)
2908 {
2909 /*
2910 Display pointer position.
2911 */
2912 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
2913 x+windows->image.x,y+windows->image.y);
2914 XInfoWidget(display,windows,text);
2915 }
2916 /*
2917 Wait for next event.
2918 */
2919 XScreenEvent(display,windows,&event,exception);
2920 if (event.xany.window == windows->command.id)
2921 {
2922 /*
2923 Select a command from the Command widget.
2924 */
2925 id=XCommandWidget(display,windows,ChopMenu,&event);
2926 if (id < 0)
2927 continue;
2928 switch (ChopCommands[id])
2929 {
2930 case ChopDirectionCommand:
2931 {
2932 char
2933 command[MagickPathExtent];
2934
2935 const char
2936 *const Directions[] =
2937 {
2938 "horizontal",
2939 "vertical",
2940 (char *) NULL,
2941 };
2942
2943 /*
2944 Select a command from the pop-up menu.
2945 */
2946 id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2947 if (id >= 0)
2948 direction=DirectionCommands[id];
2949 break;
2950 }
2951 case ChopHelpCommand:
2952 {
2953 XTextViewHelp(display,resource_info,windows,MagickFalse,
2954 "Help Viewer - Image Chop",ImageChopHelp);
2955 break;
2956 }
2957 case ChopDismissCommand:
2958 {
2959 /*
2960 Prematurely exit.
2961 */
2962 state|=EscapeState;
2963 state|=ExitState;
2964 break;
2965 }
2966 default:
2967 break;
2968 }
2969 continue;
2970 }
2971 switch (event.type)
2972 {
2973 case ButtonPress:
2974 {
2975 if (event.xbutton.button != Button1)
2976 break;
2977 if (event.xbutton.window != windows->image.id)
2978 break;
2979 /*
2980 User has committed to start point of chopping line.
2981 */
2982 segment_info.x1=(short int) event.xbutton.x;
2983 segment_info.x2=(short int) event.xbutton.x;
2984 segment_info.y1=(short int) event.xbutton.y;
2985 segment_info.y2=(short int) event.xbutton.y;
2986 state|=ExitState;
2987 break;
2988 }
2989 case ButtonRelease:
2990 break;
2991 case Expose:
2992 break;
2993 case KeyPress:
2994 {
2995 char
2996 command[MagickPathExtent];
2997
2998 KeySym
2999 key_symbol;
3000
3001 if (event.xkey.window != windows->image.id)
3002 break;
3003 /*
3004 Respond to a user key press.
3005 */
3006 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3007 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3008 switch ((int) key_symbol)
3009 {
3010 case XK_Escape:
3011 case XK_F20:
3012 {
3013 /*
3014 Prematurely exit.
3015 */
3016 state|=EscapeState;
3017 state|=ExitState;
3018 break;
3019 }
3020 case XK_F1:
3021 case XK_Help:
3022 {
3023 (void) XSetFunction(display,windows->image.highlight_context,
3024 GXcopy);
3025 XTextViewHelp(display,resource_info,windows,MagickFalse,
3026 "Help Viewer - Image Chop",ImageChopHelp);
3027 (void) XSetFunction(display,windows->image.highlight_context,
3028 GXinvert);
3029 break;
3030 }
3031 default:
3032 {
3033 (void) XBell(display,0);
3034 break;
3035 }
3036 }
3037 break;
3038 }
3039 case MotionNotify:
3040 {
3041 /*
3042 Map and unmap Info widget as text cursor crosses its boundaries.
3043 */
3044 x=event.xmotion.x;
3045 y=event.xmotion.y;
3046 if (windows->info.mapped != MagickFalse)
3047 {
3048 if ((x < (windows->info.x+(int) windows->info.width)) &&
3049 (y < (windows->info.y+(int) windows->info.height)))
3050 (void) XWithdrawWindow(display,windows->info.id,
3051 windows->info.screen);
3052 }
3053 else
3054 if ((x > (windows->info.x+(int) windows->info.width)) ||
3055 (y > (windows->info.y+(int) windows->info.height)))
3056 (void) XMapWindow(display,windows->info.id);
3057 }
3058 }
3059 } while ((state & ExitState) == 0);
3060 (void) XSelectInput(display,windows->image.id,
3061 windows->image.attributes.event_mask);
3062 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3063 if ((state & EscapeState) != 0)
3064 return(MagickTrue);
3065 /*
3066 Draw line as pointer moves until the mouse button is released.
3067 */
3068 chop_info.width=0;
3069 chop_info.height=0;
3070 chop_info.x=0;
3071 chop_info.y=0;
3072 distance=0;
3073 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3074 state=DefaultState;
3075 do
3076 {
3077 if (distance > 9)
3078 {
3079 /*
3080 Display info and draw chopping line.
3081 */
3082 if (windows->info.mapped == MagickFalse)
3083 (void) XMapWindow(display,windows->info.id);
3084 (void) FormatLocaleString(text,MagickPathExtent,
3085 " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3086 chop_info.height,(double) chop_info.x,(double) chop_info.y);
3087 XInfoWidget(display,windows,text);
3088 XHighlightLine(display,windows->image.id,
3089 windows->image.highlight_context,&segment_info);
3090 }
3091 else
3092 if (windows->info.mapped != MagickFalse)
3093 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3094 /*
3095 Wait for next event.
3096 */
3097 XScreenEvent(display,windows,&event,exception);
3098 if (distance > 9)
3099 XHighlightLine(display,windows->image.id,
3100 windows->image.highlight_context,&segment_info);
3101 switch (event.type)
3102 {
3103 case ButtonPress:
3104 {
3105 segment_info.x2=(short int) event.xmotion.x;
3106 segment_info.y2=(short int) event.xmotion.y;
3107 break;
3108 }
3109 case ButtonRelease:
3110 {
3111 /*
3112 User has committed to chopping line.
3113 */
3114 segment_info.x2=(short int) event.xbutton.x;
3115 segment_info.y2=(short int) event.xbutton.y;
3116 state|=ExitState;
3117 break;
3118 }
3119 case Expose:
3120 break;
3121 case MotionNotify:
3122 {
3123 segment_info.x2=(short int) event.xmotion.x;
3124 segment_info.y2=(short int) event.xmotion.y;
3125 }
3126 default:
3127 break;
3128 }
3129 /*
3130 Check boundary conditions.
3131 */
3132 if (segment_info.x2 < 0)
3133 segment_info.x2=0;
3134 else
3135 if (segment_info.x2 > windows->image.ximage->width)
3136 segment_info.x2=windows->image.ximage->width;
3137 if (segment_info.y2 < 0)
3138 segment_info.y2=0;
3139 else
3140 if (segment_info.y2 > windows->image.ximage->height)
3141 segment_info.y2=windows->image.ximage->height;
3142 distance=(unsigned int)
3143 (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3144 ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3145 /*
3146 Compute chopping geometry.
3147 */
3148 if (direction == HorizontalChopCommand)
3149 {
3150 chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3151 chop_info.height=0;
3152 chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3153 chop_info.y=0;
3154 if (segment_info.x1 > segment_info.x2)
3155 {
3156 chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3157 chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3158 }
3159 }
3160 else
3161 {
3162 chop_info.width=0;
3163 chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3164 chop_info.x=0;
3165 chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3166 if (segment_info.y1 > segment_info.y2)
3167 {
3168 chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3169 chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3170 }
3171 }
3172 } while ((state & ExitState) == 0);
3173 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3174 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3175 if (distance <= 9)
3176 return(MagickTrue);
3177 /*
3178 Image chopping is relative to image configuration.
3179 */
3180 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3181 exception);
3182 XSetCursorState(display,windows,MagickTrue);
3183 XCheckRefreshWindows(display,windows);
3184 windows->image.window_changes.width=windows->image.ximage->width-
3185 (int) chop_info.width;
3186 windows->image.window_changes.height=windows->image.ximage->height-
3187 (int) chop_info.height;
3188 width=(unsigned int) (*image)->columns;
3189 height=(unsigned int) (*image)->rows;
3190 x=0;
3191 y=0;
3192 if (windows->image.crop_geometry != (char *) NULL)
3193 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3194 scale_factor=(double) width/windows->image.ximage->width;
3195 chop_info.x+=x;
3196 chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3197 chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3198 scale_factor=(double) height/windows->image.ximage->height;
3199 chop_info.y+=y;
3200 chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3201 chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3202 /*
3203 Chop image.
3204 */
3205 chop_image=ChopImage(*image,&chop_info,exception);
3206 XSetCursorState(display,windows,MagickFalse);
3207 if (chop_image == (Image *) NULL)
3208 return(MagickFalse);
3209 *image=DestroyImage(*image);
3210 *image=chop_image;
3211 /*
3212 Update image configuration.
3213 */
3214 XConfigureImageColormap(display,resource_info,windows,*image,exception);
3215 (void) XConfigureImage(display,resource_info,windows,*image,exception);
3216 return(MagickTrue);
3217}
3218
3219/*
3220%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3221% %
3222% %
3223% %
3224+ X C o l o r E d i t I m a g e %
3225% %
3226% %
3227% %
3228%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3229%
3230% XColorEditImage() allows the user to interactively change the color of one
3231% pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3232%
3233% The format of the XColorEditImage method is:
3234%
3235% MagickBooleanType XColorEditImage(Display *display,
3236% XResourceInfo *resource_info,XWindows *windows,Image **image,
3237% ExceptionInfo *exception)
3238%
3239% A description of each parameter follows:
3240%
3241% o display: Specifies a connection to an X server; returned from
3242% XOpenDisplay.
3243%
3244% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3245%
3246% o windows: Specifies a pointer to a XWindows structure.
3247%
3248% o image: the image; returned from ReadImage.
3249%
3250% o exception: return any errors or warnings in this structure.
3251%
3252*/
3253static MagickBooleanType XColorEditImage(Display *display,
3254 XResourceInfo *resource_info,XWindows *windows,Image **image,
3255 ExceptionInfo *exception)
3256{
3257 const char
3258 *const ColorEditMenu[] =
3259 {
3260 "Method",
3261 "Pixel Color",
3262 "Border Color",
3263 "Fuzz",
3264 "Undo",
3265 "Help",
3266 "Dismiss",
3267 (char *) NULL
3268 };
3269
3270 static const ModeType
3271 ColorEditCommands[] =
3272 {
3273 ColorEditMethodCommand,
3274 ColorEditColorCommand,
3275 ColorEditBorderCommand,
3276 ColorEditFuzzCommand,
3277 ColorEditUndoCommand,
3278 ColorEditHelpCommand,
3279 ColorEditDismissCommand
3280 };
3281
3282 static PaintMethod
3283 method = PointMethod;
3284
3285 static unsigned int
3286 pen_id = 0;
3287
3288 static XColor
3289 border_color = { 0, 0, 0, 0, 0, 0 };
3290
3291 char
3292 command[MagickPathExtent],
3293 text[MagickPathExtent];
3294
3295 Cursor
3296 cursor;
3297
3298 int
3299 entry,
3300 id,
3301 x,
3302 x_offset,
3303 y,
3304 y_offset;
3305
3306 Quantum
3307 *q;
3308
3309 size_t
3310 state;
3311
3312 ssize_t
3313 i;
3314
3315 unsigned int
3316 height,
3317 width;
3318
3319 XColor
3320 color;
3321
3322 XEvent
3323 event;
3324
3325 /*
3326 Map Command widget.
3327 */
3328 (void) CloneString(&windows->command.name,"Color Edit");
3329 windows->command.data=4;
3330 (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3331 (void) XMapRaised(display,windows->command.id);
3332 XClientMessage(display,windows->image.id,windows->im_protocols,
3333 windows->im_update_widget,CurrentTime);
3334 /*
3335 Make cursor.
3336 */
3337 cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3338 resource_info->background_color,resource_info->foreground_color);
3339 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3340 /*
3341 Track pointer until button 1 is pressed.
3342 */
3343 XQueryPosition(display,windows->image.id,&x,&y);
3344 (void) XSelectInput(display,windows->image.id,
3345 windows->image.attributes.event_mask | PointerMotionMask);
3346 state=DefaultState;
3347 do
3348 {
3349 if (windows->info.mapped != MagickFalse)
3350 {
3351 /*
3352 Display pointer position.
3353 */
3354 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
3355 x+windows->image.x,y+windows->image.y);
3356 XInfoWidget(display,windows,text);
3357 }
3358 /*
3359 Wait for next event.
3360 */
3361 XScreenEvent(display,windows,&event,exception);
3362 if (event.xany.window == windows->command.id)
3363 {
3364 /*
3365 Select a command from the Command widget.
3366 */
3367 id=XCommandWidget(display,windows,ColorEditMenu,&event);
3368 if (id < 0)
3369 {
3370 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3371 continue;
3372 }
3373 switch (ColorEditCommands[id])
3374 {
3375 case ColorEditMethodCommand:
3376 {
3377 char
3378 **methods;
3379
3380 /*
3381 Select a method from the pop-up menu.
3382 */
3383 methods=(char **) GetCommandOptions(MagickMethodOptions);
3384 if (methods == (char **) NULL)
3385 break;
3386 entry=XMenuWidget(display,windows,ColorEditMenu[id],
3387 (const char **) methods,command);
3388 if (entry >= 0)
3389 method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3390 MagickFalse,methods[entry]);
3391 methods=DestroyStringList(methods);
3392 break;
3393 }
3394 case ColorEditColorCommand:
3395 {
3396 const char
3397 *ColorMenu[MaxNumberPens];
3398
3399 int
3400 pen_number;
3401
3402 /*
3403 Initialize menu selections.
3404 */
3405 for (i=0; i < (int) (MaxNumberPens-2); i++)
3406 ColorMenu[i]=resource_info->pen_colors[i];
3407 ColorMenu[MaxNumberPens-2]="Browser...";
3408 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3409 /*
3410 Select a pen color from the pop-up menu.
3411 */
3412 pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3413 (const char **) ColorMenu,command);
3414 if (pen_number < 0)
3415 break;
3416 if (pen_number == (MaxNumberPens-2))
3417 {
3418 static char
3419 color_name[MagickPathExtent] = "gray";
3420
3421 /*
3422 Select a pen color from a dialog.
3423 */
3424 resource_info->pen_colors[pen_number]=color_name;
3425 XColorBrowserWidget(display,windows,"Select",color_name);
3426 if (*color_name == '\0')
3427 break;
3428 }
3429 /*
3430 Set pen color.
3431 */
3432 (void) XParseColor(display,windows->map_info->colormap,
3433 resource_info->pen_colors[pen_number],&color);
3434 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3435 (unsigned int) MaxColors,&color);
3436 windows->pixel_info->pen_colors[pen_number]=color;
3437 pen_id=(unsigned int) pen_number;
3438 break;
3439 }
3440 case ColorEditBorderCommand:
3441 {
3442 const char
3443 *ColorMenu[MaxNumberPens];
3444
3445 int
3446 pen_number;
3447
3448 /*
3449 Initialize menu selections.
3450 */
3451 for (i=0; i < (int) (MaxNumberPens-2); i++)
3452 ColorMenu[i]=resource_info->pen_colors[i];
3453 ColorMenu[MaxNumberPens-2]="Browser...";
3454 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3455 /*
3456 Select a pen color from the pop-up menu.
3457 */
3458 pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3459 (const char **) ColorMenu,command);
3460 if (pen_number < 0)
3461 break;
3462 if (pen_number == (MaxNumberPens-2))
3463 {
3464 static char
3465 color_name[MagickPathExtent] = "gray";
3466
3467 /*
3468 Select a pen color from a dialog.
3469 */
3470 resource_info->pen_colors[pen_number]=color_name;
3471 XColorBrowserWidget(display,windows,"Select",color_name);
3472 if (*color_name == '\0')
3473 break;
3474 }
3475 /*
3476 Set border color.
3477 */
3478 (void) XParseColor(display,windows->map_info->colormap,
3479 resource_info->pen_colors[pen_number],&border_color);
3480 break;
3481 }
3482 case ColorEditFuzzCommand:
3483 {
3484 const char
3485 *const FuzzMenu[] =
3486 {
3487 "0%",
3488 "2%",
3489 "5%",
3490 "10%",
3491 "15%",
3492 "Dialog...",
3493 (char *) NULL,
3494 };
3495
3496 static char
3497 fuzz[MagickPathExtent];
3498
3499 /*
3500 Select a command from the pop-up menu.
3501 */
3502 entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3503 command);
3504 if (entry < 0)
3505 break;
3506 if (entry != 5)
3507 {
3508 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3509 QuantumRange+1.0);
3510 break;
3511 }
3512 (void) (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
3513 (void) XDialogWidget(display,windows,"Ok",
3514 "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3515 if (*fuzz == '\0')
3516 break;
3517 (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
3518 (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3519 1.0);
3520 break;
3521 }
3522 case ColorEditUndoCommand:
3523 {
3524 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3525 image,exception);
3526 break;
3527 }
3528 case ColorEditHelpCommand:
3529 default:
3530 {
3531 XTextViewHelp(display,resource_info,windows,MagickFalse,
3532 "Help Viewer - Image Annotation",ImageColorEditHelp);
3533 break;
3534 }
3535 case ColorEditDismissCommand:
3536 {
3537 /*
3538 Prematurely exit.
3539 */
3540 state|=EscapeState;
3541 state|=ExitState;
3542 break;
3543 }
3544 }
3545 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3546 continue;
3547 }
3548 switch (event.type)
3549 {
3550 case ButtonPress:
3551 {
3552 if (event.xbutton.button != Button1)
3553 break;
3554 if ((event.xbutton.window != windows->image.id) &&
3555 (event.xbutton.window != windows->magnify.id))
3556 break;
3557 /*
3558 exit loop.
3559 */
3560 x=event.xbutton.x;
3561 y=event.xbutton.y;
3562 (void) XMagickCommand(display,resource_info,windows,
3563 SaveToUndoBufferCommand,image,exception);
3564 state|=UpdateConfigurationState;
3565 break;
3566 }
3567 case ButtonRelease:
3568 {
3569 if (event.xbutton.button != Button1)
3570 break;
3571 if ((event.xbutton.window != windows->image.id) &&
3572 (event.xbutton.window != windows->magnify.id))
3573 break;
3574 /*
3575 Update colormap information.
3576 */
3577 x=event.xbutton.x;
3578 y=event.xbutton.y;
3579 XConfigureImageColormap(display,resource_info,windows,*image,exception);
3580 (void) XConfigureImage(display,resource_info,windows,*image,exception);
3581 XInfoWidget(display,windows,text);
3582 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3583 state&=(size_t) (~UpdateConfigurationState);
3584 break;
3585 }
3586 case Expose:
3587 break;
3588 case KeyPress:
3589 {
3590 KeySym
3591 key_symbol;
3592
3593 if (event.xkey.window == windows->magnify.id)
3594 {
3595 Window
3596 window;
3597
3598 window=windows->magnify.id;
3599 while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3600 }
3601 if (event.xkey.window != windows->image.id)
3602 break;
3603 /*
3604 Respond to a user key press.
3605 */
3606 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3607 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3608 switch ((int) key_symbol)
3609 {
3610 case XK_Escape:
3611 case XK_F20:
3612 {
3613 /*
3614 Prematurely exit.
3615 */
3616 state|=ExitState;
3617 break;
3618 }
3619 case XK_F1:
3620 case XK_Help:
3621 {
3622 XTextViewHelp(display,resource_info,windows,MagickFalse,
3623 "Help Viewer - Image Annotation",ImageColorEditHelp);
3624 break;
3625 }
3626 default:
3627 {
3628 (void) XBell(display,0);
3629 break;
3630 }
3631 }
3632 break;
3633 }
3634 case MotionNotify:
3635 {
3636 /*
3637 Map and unmap Info widget as cursor crosses its boundaries.
3638 */
3639 x=event.xmotion.x;
3640 y=event.xmotion.y;
3641 if (windows->info.mapped != MagickFalse)
3642 {
3643 if ((x < (windows->info.x+(int) windows->info.width)) &&
3644 (y < (windows->info.y+(int) windows->info.height)))
3645 (void) XWithdrawWindow(display,windows->info.id,
3646 windows->info.screen);
3647 }
3648 else
3649 if ((x > (windows->info.x+(int) windows->info.width)) ||
3650 (y > (windows->info.y+(int) windows->info.height)))
3651 (void) XMapWindow(display,windows->info.id);
3652 break;
3653 }
3654 default:
3655 break;
3656 }
3657 if (event.xany.window == windows->magnify.id)
3658 {
3659 x=windows->magnify.x-windows->image.x;
3660 y=windows->magnify.y-windows->image.y;
3661 }
3662 x_offset=x;
3663 y_offset=y;
3664 if ((state & UpdateConfigurationState) != 0)
3665 {
3666 CacheView
3667 *image_view;
3668
3669 int
3670 x,
3671 y;
3672
3673 /*
3674 Pixel edit is relative to image configuration.
3675 */
3676 (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3677 MagickTrue);
3678 color=windows->pixel_info->pen_colors[pen_id];
3679 XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3680 width=(unsigned int) (*image)->columns;
3681 height=(unsigned int) (*image)->rows;
3682 x=0;
3683 y=0;
3684 if (windows->image.crop_geometry != (char *) NULL)
3685 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3686 &width,&height);
3687 x_offset=((int) width*(windows->image.x+x_offset)/(int)
3688 windows->image.ximage->width+x);
3689 y_offset=((int) height*(windows->image.y+y_offset)/(int)
3690 windows->image.ximage->height+y);
3691 if ((x_offset < 0) || (y_offset < 0))
3692 continue;
3693 if ((x_offset >= (int) (*image)->columns) ||
3694 (y_offset >= (int) (*image)->rows))
3695 continue;
3696 image_view=AcquireAuthenticCacheView(*image,exception);
3697 switch (method)
3698 {
3699 case PointMethod:
3700 default:
3701 {
3702 /*
3703 Update color information using point algorithm.
3704 */
3705 if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3706 return(MagickFalse);
3707 q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3708 (ssize_t) y_offset,1,1,exception);
3709 if (q == (Quantum *) NULL)
3710 break;
3711 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3712 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3713 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3714 (void) SyncCacheViewAuthenticPixels(image_view,exception);
3715 break;
3716 }
3717 case ReplaceMethod:
3718 {
3719 PixelInfo
3720 pixel,
3721 target;
3722
3723 /*
3724 Update color information using replace algorithm.
3725 */
3726 (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3727 x_offset,(ssize_t) y_offset,&target,exception);
3728 if ((*image)->storage_class == DirectClass)
3729 {
3730 for (y=0; y < (int) (*image)->rows; y++)
3731 {
3732 q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3733 (*image)->columns,1,exception);
3734 if (q == (Quantum *) NULL)
3735 break;
3736 for (x=0; x < (int) (*image)->columns; x++)
3737 {
3738 GetPixelInfoPixel(*image,q,&pixel);
3739 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3740 {
3741 SetPixelRed(*image,ScaleShortToQuantum(
3742 color.red),q);
3743 SetPixelGreen(*image,ScaleShortToQuantum(
3744 color.green),q);
3745 SetPixelBlue(*image,ScaleShortToQuantum(
3746 color.blue),q);
3747 }
3748 q+=GetPixelChannels(*image);
3749 }
3750 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3751 break;
3752 }
3753 }
3754 else
3755 {
3756 for (i=0; i < (ssize_t) (*image)->colors; i++)
3757 if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3758 {
3759 (*image)->colormap[i].red=(double) ScaleShortToQuantum(
3760 color.red);
3761 (*image)->colormap[i].green=(double) ScaleShortToQuantum(
3762 color.green);
3763 (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
3764 color.blue);
3765 }
3766 (void) SyncImage(*image,exception);
3767 }
3768 break;
3769 }
3770 case FloodfillMethod:
3771 case FillToBorderMethod:
3772 {
3773 DrawInfo
3774 *draw_info;
3775
3776 PixelInfo
3777 target;
3778
3779 /*
3780 Update color information using floodfill algorithm.
3781 */
3782 (void) GetOneVirtualPixelInfo(*image,
3783 GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3784 y_offset,&target,exception);
3785 if (method == FillToBorderMethod)
3786 {
3787 target.red=(double)
3788 ScaleShortToQuantum(border_color.red);
3789 target.green=(double)
3790 ScaleShortToQuantum(border_color.green);
3791 target.blue=(double)
3792 ScaleShortToQuantum(border_color.blue);
3793 }
3794 draw_info=CloneDrawInfo(resource_info->image_info,
3795 (DrawInfo *) NULL);
3796 (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3797 AllCompliance,&draw_info->fill,exception);
3798 (void) FloodfillPaintImage(*image,draw_info,&target,
3799 (ssize_t)x_offset,(ssize_t)y_offset,
3800 method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
3801 draw_info=DestroyDrawInfo(draw_info);
3802 break;
3803 }
3804 case ResetMethod:
3805 {
3806 /*
3807 Update color information using reset algorithm.
3808 */
3809 if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3810 return(MagickFalse);
3811 for (y=0; y < (int) (*image)->rows; y++)
3812 {
3813 q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3814 (*image)->columns,1,exception);
3815 if (q == (Quantum *) NULL)
3816 break;
3817 for (x=0; x < (int) (*image)->columns; x++)
3818 {
3819 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3820 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3821 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3822 q+=GetPixelChannels(*image);
3823 }
3824 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3825 break;
3826 }
3827 break;
3828 }
3829 }
3830 image_view=DestroyCacheView(image_view);
3831 state&=(~0x0080U);
3832 }
3833 } while ((state & ExitState) == 0);
3834 (void) XSelectInput(display,windows->image.id,
3835 windows->image.attributes.event_mask);
3836 XSetCursorState(display,windows,MagickFalse);
3837 (void) XFreeCursor(display,cursor);
3838 return(MagickTrue);
3839}
3840
3841/*
3842%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3843% %
3844% %
3845% %
3846+ X C o m p o s i t e I m a g e %
3847% %
3848% %
3849% %
3850%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3851%
3852% XCompositeImage() requests an image name from the user, reads the image and
3853% composites it with the X window image at a location the user chooses with
3854% the pointer.
3855%
3856% The format of the XCompositeImage method is:
3857%
3858% MagickBooleanType XCompositeImage(Display *display,
3859% XResourceInfo *resource_info,XWindows *windows,Image *image,
3860% ExceptionInfo *exception)
3861%
3862% A description of each parameter follows:
3863%
3864% o display: Specifies a connection to an X server; returned from
3865% XOpenDisplay.
3866%
3867% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3868%
3869% o windows: Specifies a pointer to a XWindows structure.
3870%
3871% o image: the image; returned from ReadImage.
3872%
3873% o exception: return any errors or warnings in this structure.
3874%
3875*/
3876static MagickBooleanType XCompositeImage(Display *display,
3877 XResourceInfo *resource_info,XWindows *windows,Image *image,
3878 ExceptionInfo *exception)
3879{
3880 const char
3881 *const CompositeMenu[] =
3882 {
3883 "Operators",
3884 "Dissolve",
3885 "Displace",
3886 "Help",
3887 "Dismiss",
3888 (char *) NULL
3889 };
3890
3891 static char
3892 displacement_geometry[MagickPathExtent] = "30x30",
3893 filename[MagickPathExtent] = "\0";
3894
3895 static CompositeOperator
3896 compose = CopyCompositeOp;
3897
3898 static const ModeType
3899 CompositeCommands[] =
3900 {
3901 CompositeOperatorsCommand,
3902 CompositeDissolveCommand,
3903 CompositeDisplaceCommand,
3904 CompositeHelpCommand,
3905 CompositeDismissCommand
3906 };
3907
3908 char
3909 text[MagickPathExtent];
3910
3911 Cursor
3912 cursor;
3913
3914 Image
3915 *composite_image;
3916
3917 int
3918 entry,
3919 id,
3920 x,
3921 y;
3922
3923 double
3924 blend,
3925 scale_factor;
3926
3928 highlight_info,
3929 composite_info;
3930
3931 unsigned int
3932 height,
3933 width;
3934
3935 size_t
3936 state;
3937
3938 XEvent
3939 event;
3940
3941 /*
3942 Request image file name from user.
3943 */
3944 XFileBrowserWidget(display,windows,"Composite",filename);
3945 if (*filename == '\0')
3946 return(MagickTrue);
3947 /*
3948 Read image.
3949 */
3950 XSetCursorState(display,windows,MagickTrue);
3951 XCheckRefreshWindows(display,windows);
3952 (void) CopyMagickString(resource_info->image_info->filename,filename,
3953 MagickPathExtent);
3954 composite_image=ReadImage(resource_info->image_info,exception);
3955 CatchException(exception);
3956 XSetCursorState(display,windows,MagickFalse);
3957 if (composite_image == (Image *) NULL)
3958 return(MagickFalse);
3959 /*
3960 Map Command widget.
3961 */
3962 (void) CloneString(&windows->command.name,"Composite");
3963 windows->command.data=1;
3964 (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3965 (void) XMapRaised(display,windows->command.id);
3966 XClientMessage(display,windows->image.id,windows->im_protocols,
3967 windows->im_update_widget,CurrentTime);
3968 /*
3969 Track pointer until button 1 is pressed.
3970 */
3971 XQueryPosition(display,windows->image.id,&x,&y);
3972 (void) XSelectInput(display,windows->image.id,
3973 windows->image.attributes.event_mask | PointerMotionMask);
3974 composite_info.x=(ssize_t) windows->image.x+x;
3975 composite_info.y=(ssize_t) windows->image.y+y;
3976 composite_info.width=0;
3977 composite_info.height=0;
3978 cursor=XCreateFontCursor(display,XC_ul_angle);
3979 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3980 blend=0.0;
3981 state=DefaultState;
3982 do
3983 {
3984 if (windows->info.mapped != MagickFalse)
3985 {
3986 /*
3987 Display pointer position.
3988 */
3989 (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
3990 (long) composite_info.x,(long) composite_info.y);
3991 XInfoWidget(display,windows,text);
3992 }
3993 highlight_info=composite_info;
3994 highlight_info.x=composite_info.x-windows->image.x;
3995 highlight_info.y=composite_info.y-windows->image.y;
3996 XHighlightRectangle(display,windows->image.id,
3997 windows->image.highlight_context,&highlight_info);
3998 /*
3999 Wait for next event.
4000 */
4001 XScreenEvent(display,windows,&event,exception);
4002 XHighlightRectangle(display,windows->image.id,
4003 windows->image.highlight_context,&highlight_info);
4004 if (event.xany.window == windows->command.id)
4005 {
4006 /*
4007 Select a command from the Command widget.
4008 */
4009 id=XCommandWidget(display,windows,CompositeMenu,&event);
4010 if (id < 0)
4011 continue;
4012 switch (CompositeCommands[id])
4013 {
4014 case CompositeOperatorsCommand:
4015 {
4016 char
4017 command[MagickPathExtent],
4018 **operators;
4019
4020 /*
4021 Select a command from the pop-up menu.
4022 */
4023 operators=GetCommandOptions(MagickComposeOptions);
4024 if (operators == (char **) NULL)
4025 break;
4026 entry=XMenuWidget(display,windows,CompositeMenu[id],
4027 (const char **) operators,command);
4028 if (entry >= 0)
4029 compose=(CompositeOperator) ParseCommandOption(
4030 MagickComposeOptions,MagickFalse,operators[entry]);
4031 operators=DestroyStringList(operators);
4032 break;
4033 }
4034 case CompositeDissolveCommand:
4035 {
4036 static char
4037 factor[MagickPathExtent] = "20.0";
4038
4039 /*
4040 Dissolve the two images a given percent.
4041 */
4042 (void) XSetFunction(display,windows->image.highlight_context,
4043 GXcopy);
4044 (void) XDialogWidget(display,windows,"Dissolve",
4045 "Enter the blend factor (0.0 - 99.9%):",factor);
4046 (void) XSetFunction(display,windows->image.highlight_context,
4047 GXinvert);
4048 if (*factor == '\0')
4049 break;
4050 blend=StringToDouble(factor,(char **) NULL);
4051 compose=DissolveCompositeOp;
4052 break;
4053 }
4054 case CompositeDisplaceCommand:
4055 {
4056 /*
4057 Get horizontal and vertical scale displacement geometry.
4058 */
4059 (void) XSetFunction(display,windows->image.highlight_context,
4060 GXcopy);
4061 (void) XDialogWidget(display,windows,"Displace",
4062 "Enter the horizontal and vertical scale:",displacement_geometry);
4063 (void) XSetFunction(display,windows->image.highlight_context,
4064 GXinvert);
4065 if (*displacement_geometry == '\0')
4066 break;
4067 compose=DisplaceCompositeOp;
4068 break;
4069 }
4070 case CompositeHelpCommand:
4071 {
4072 (void) XSetFunction(display,windows->image.highlight_context,
4073 GXcopy);
4074 XTextViewHelp(display,resource_info,windows,MagickFalse,
4075 "Help Viewer - Image Composite",ImageCompositeHelp);
4076 (void) XSetFunction(display,windows->image.highlight_context,
4077 GXinvert);
4078 break;
4079 }
4080 case CompositeDismissCommand:
4081 {
4082 /*
4083 Prematurely exit.
4084 */
4085 state|=EscapeState;
4086 state|=ExitState;
4087 break;
4088 }
4089 default:
4090 break;
4091 }
4092 continue;
4093 }
4094 switch (event.type)
4095 {
4096 case ButtonPress:
4097 {
4098 if (resource_info->debug != MagickFalse)
4099 (void) LogMagickEvent(X11Event,GetMagickModule(),
4100 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4101 event.xbutton.button,event.xbutton.x,event.xbutton.y);
4102 if (event.xbutton.button != Button1)
4103 break;
4104 if (event.xbutton.window != windows->image.id)
4105 break;
4106 /*
4107 Change cursor.
4108 */
4109 composite_info.width=composite_image->columns;
4110 composite_info.height=composite_image->rows;
4111 (void) XCheckDefineCursor(display,windows->image.id,cursor);
4112 composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4113 composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4114 break;
4115 }
4116 case ButtonRelease:
4117 {
4118 if (resource_info->debug != MagickFalse)
4119 (void) LogMagickEvent(X11Event,GetMagickModule(),
4120 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4121 event.xbutton.button,event.xbutton.x,event.xbutton.y);
4122 if (event.xbutton.button != Button1)
4123 break;
4124 if (event.xbutton.window != windows->image.id)
4125 break;
4126 if ((composite_info.width != 0) && (composite_info.height != 0))
4127 {
4128 /*
4129 User has selected the location of the composite image.
4130 */
4131 composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4132 composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4133 state|=ExitState;
4134 }
4135 break;
4136 }
4137 case Expose:
4138 break;
4139 case KeyPress:
4140 {
4141 char
4142 command[MagickPathExtent];
4143
4144 KeySym
4145 key_symbol;
4146
4147 int
4148 length;
4149
4150 if (event.xkey.window != windows->image.id)
4151 break;
4152 /*
4153 Respond to a user key press.
4154 */
4155 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4156 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4157 *(command+length)='\0';
4158 if (resource_info->debug != MagickFalse)
4159 (void) LogMagickEvent(X11Event,GetMagickModule(),
4160 "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4161 switch ((int) key_symbol)
4162 {
4163 case XK_Escape:
4164 case XK_F20:
4165 {
4166 /*
4167 Prematurely exit.
4168 */
4169 composite_image=DestroyImage(composite_image);
4170 state|=EscapeState;
4171 state|=ExitState;
4172 break;
4173 }
4174 case XK_F1:
4175 case XK_Help:
4176 {
4177 (void) XSetFunction(display,windows->image.highlight_context,
4178 GXcopy);
4179 XTextViewHelp(display,resource_info,windows,MagickFalse,
4180 "Help Viewer - Image Composite",ImageCompositeHelp);
4181 (void) XSetFunction(display,windows->image.highlight_context,
4182 GXinvert);
4183 break;
4184 }
4185 default:
4186 {
4187 (void) XBell(display,0);
4188 break;
4189 }
4190 }
4191 break;
4192 }
4193 case MotionNotify:
4194 {
4195 /*
4196 Map and unmap Info widget as text cursor crosses its boundaries.
4197 */
4198 x=event.xmotion.x;
4199 y=event.xmotion.y;
4200 if (windows->info.mapped != MagickFalse)
4201 {
4202 if ((x < (windows->info.x+(int) windows->info.width)) &&
4203 (y < (windows->info.y+(int) windows->info.height)))
4204 (void) XWithdrawWindow(display,windows->info.id,
4205 windows->info.screen);
4206 }
4207 else
4208 if ((x > (windows->info.x+(int) windows->info.width)) ||
4209 (y > (windows->info.y+(int) windows->info.height)))
4210 (void) XMapWindow(display,windows->info.id);
4211 composite_info.x=(ssize_t) windows->image.x+x;
4212 composite_info.y=(ssize_t) windows->image.y+y;
4213 break;
4214 }
4215 default:
4216 {
4217 if (resource_info->debug != MagickFalse)
4218 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4219 event.type);
4220 break;
4221 }
4222 }
4223 } while ((state & ExitState) == 0);
4224 (void) XSelectInput(display,windows->image.id,
4225 windows->image.attributes.event_mask);
4226 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4227 XSetCursorState(display,windows,MagickFalse);
4228 (void) XFreeCursor(display,cursor);
4229 if ((state & EscapeState) != 0)
4230 return(MagickTrue);
4231 /*
4232 Image compositing is relative to image configuration.
4233 */
4234 XSetCursorState(display,windows,MagickTrue);
4235 XCheckRefreshWindows(display,windows);
4236 width=(unsigned int) image->columns;
4237 height=(unsigned int) image->rows;
4238 x=0;
4239 y=0;
4240 if (windows->image.crop_geometry != (char *) NULL)
4241 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4242 scale_factor=(double) width/windows->image.ximage->width;
4243 composite_info.x+=x;
4244 composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4245 composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4246 scale_factor=(double) height/windows->image.ximage->height;
4247 composite_info.y+=y;
4248 composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4249 composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4250 if ((composite_info.width != composite_image->columns) ||
4251 (composite_info.height != composite_image->rows))
4252 {
4253 Image
4254 *resize_image;
4255
4256 /*
4257 Scale composite image.
4258 */
4259 resize_image=ResizeImage(composite_image,composite_info.width,
4260 composite_info.height,composite_image->filter,exception);
4261 composite_image=DestroyImage(composite_image);
4262 if (resize_image == (Image *) NULL)
4263 {
4264 XSetCursorState(display,windows,MagickFalse);
4265 return(MagickFalse);
4266 }
4267 composite_image=resize_image;
4268 }
4269 if (compose == DisplaceCompositeOp)
4270 (void) SetImageArtifact(composite_image,"compose:args",
4271 displacement_geometry);
4272 if (blend != 0.0)
4273 {
4274 CacheView
4275 *image_view;
4276
4277 int
4278 y;
4279
4280 Quantum
4281 opacity;
4282
4283 int
4284 x;
4285
4286 Quantum
4287 *q;
4288
4289 /*
4290 Create mattes for blending.
4291 */
4292 (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4293 opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4294 ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4295 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4296 return(MagickFalse);
4297 image->alpha_trait=BlendPixelTrait;
4298 image_view=AcquireAuthenticCacheView(image,exception);
4299 for (y=0; y < (int) image->rows; y++)
4300 {
4301 q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4302 exception);
4303 if (q == (Quantum *) NULL)
4304 break;
4305 for (x=0; x < (int) image->columns; x++)
4306 {
4307 SetPixelAlpha(image,opacity,q);
4308 q+=GetPixelChannels(image);
4309 }
4310 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4311 break;
4312 }
4313 image_view=DestroyCacheView(image_view);
4314 }
4315 /*
4316 Composite image with X Image window.
4317 */
4318 (void) CompositeImage(image,composite_image,compose,MagickTrue,
4319 composite_info.x,composite_info.y,exception);
4320 composite_image=DestroyImage(composite_image);
4321 XSetCursorState(display,windows,MagickFalse);
4322 /*
4323 Update image configuration.
4324 */
4325 XConfigureImageColormap(display,resource_info,windows,image,exception);
4326 (void) XConfigureImage(display,resource_info,windows,image,exception);
4327 return(MagickTrue);
4328}
4329
4330/*
4331%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4332% %
4333% %
4334% %
4335+ X C o n f i g u r e I m a g e %
4336% %
4337% %
4338% %
4339%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4340%
4341% XConfigureImage() creates a new X image. It also notifies the window
4342% manager of the new image size and configures the transient widows.
4343%
4344% The format of the XConfigureImage method is:
4345%
4346% MagickBooleanType XConfigureImage(Display *display,
4347% XResourceInfo *resource_info,XWindows *windows,Image *image,
4348% ExceptionInfo *exception)
4349%
4350% A description of each parameter follows:
4351%
4352% o display: Specifies a connection to an X server; returned from
4353% XOpenDisplay.
4354%
4355% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4356%
4357% o windows: Specifies a pointer to a XWindows structure.
4358%
4359% o image: the image.
4360%
4361% o exception: return any errors or warnings in this structure.
4362%
4363% o exception: return any errors or warnings in this structure.
4364%
4365*/
4366static MagickBooleanType XConfigureImage(Display *display,
4367 XResourceInfo *resource_info,XWindows *windows,Image *image,
4368 ExceptionInfo *exception)
4369{
4370 char
4371 geometry[MagickPathExtent];
4372
4373 MagickStatusType
4374 status;
4375
4376 size_t
4377 mask,
4378 height,
4379 width;
4380
4381 ssize_t
4382 x,
4383 y;
4384
4385 XSizeHints
4386 *size_hints;
4387
4388 XWindowChanges
4389 window_changes;
4390
4391 /*
4392 Dismiss if window dimensions are zero.
4393 */
4394 width=(unsigned int) windows->image.window_changes.width;
4395 height=(unsigned int) windows->image.window_changes.height;
4396 if (resource_info->debug != MagickFalse)
4397 (void) LogMagickEvent(X11Event,GetMagickModule(),
4398 "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4399 windows->image.ximage->height,(double) width,(double) height);
4400 if ((width*height) == 0)
4401 return(MagickTrue);
4402 x=0;
4403 y=0;
4404 /*
4405 Resize image to fit Image window dimensions.
4406 */
4407 XSetCursorState(display,windows,MagickTrue);
4408 (void) XFlush(display);
4409 if (((int) width != windows->image.ximage->width) ||
4410 ((int) height != windows->image.ximage->height))
4411 image->taint=MagickTrue;
4412 windows->magnify.x=(int)
4413 width*windows->magnify.x/windows->image.ximage->width;
4414 windows->magnify.y=(int)
4415 height*windows->magnify.y/windows->image.ximage->height;
4416 windows->image.x=((int) width*windows->image.x/windows->image.ximage->width);
4417 windows->image.y=((int) height*windows->image.y/
4418 windows->image.ximage->height);
4419 status=XMakeImage(display,resource_info,&windows->image,image,
4420 (unsigned int) width,(unsigned int) height,exception);
4421 if (status == MagickFalse)
4422 XNoticeWidget(display,windows,"Unable to configure X image:",
4423 windows->image.name);
4424 /*
4425 Notify window manager of the new configuration.
4426 */
4427 if (resource_info->image_geometry != (char *) NULL)
4428 (void) FormatLocaleString(geometry,MagickPathExtent,"%s>!",
4429 resource_info->image_geometry);
4430 else
4431 (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
4432 XDisplayWidth(display,windows->image.screen),
4433 XDisplayHeight(display,windows->image.screen));
4434 (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4435 window_changes.width=(int) width;
4436 if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4437 window_changes.width=XDisplayWidth(display,windows->image.screen);
4438 window_changes.height=(int) height;
4439 if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4440 window_changes.height=XDisplayHeight(display,windows->image.screen);
4441 mask=(size_t) (CWWidth | CWHeight);
4442 if (resource_info->backdrop)
4443 {
4444 mask|=CWX | CWY;
4445 window_changes.x=((XDisplayWidth(display,windows->image.screen)/2)-
4446 ((int) width/2));
4447 window_changes.y=((XDisplayHeight(display,windows->image.screen)/2)-
4448 ((int) height/2));
4449 }
4450 (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4451 (unsigned int) mask,&window_changes);
4452 (void) XClearWindow(display,windows->image.id);
4453 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4454 /*
4455 Update Magnify window configuration.
4456 */
4457 if (windows->magnify.mapped != MagickFalse)
4458 XMakeMagnifyImage(display,windows,exception);
4459 windows->pan.crop_geometry=windows->image.crop_geometry;
4460 XBestIconSize(display,&windows->pan,image);
4461 while (((windows->pan.width << 1) < MaxIconSize) &&
4462 ((windows->pan.height << 1) < MaxIconSize))
4463 {
4464 windows->pan.width<<=1;
4465 windows->pan.height<<=1;
4466 }
4467 if (windows->pan.geometry != (char *) NULL)
4468 (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4469 &windows->pan.width,&windows->pan.height);
4470 window_changes.width=(int) windows->pan.width;
4471 window_changes.height=(int) windows->pan.height;
4472 size_hints=XAllocSizeHints();
4473 if (size_hints != (XSizeHints *) NULL)
4474 {
4475 /*
4476 Set new size hints.
4477 */
4478 size_hints->flags=PSize | PMinSize | PMaxSize;
4479 size_hints->width=window_changes.width;
4480 size_hints->height=window_changes.height;
4481 size_hints->min_width=size_hints->width;
4482 size_hints->min_height=size_hints->height;
4483 size_hints->max_width=size_hints->width;
4484 size_hints->max_height=size_hints->height;
4485 (void) XSetNormalHints(display,windows->pan.id,size_hints);
4486 (void) XFree((void *) size_hints);
4487 }
4488 (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4489 (unsigned int) (CWWidth | CWHeight),&window_changes);
4490 /*
4491 Update icon window configuration.
4492 */
4493 windows->icon.crop_geometry=windows->image.crop_geometry;
4494 XBestIconSize(display,&windows->icon,image);
4495 window_changes.width=(int) windows->icon.width;
4496 window_changes.height=(int) windows->icon.height;
4497 (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4498 (unsigned int) (CWWidth | CWHeight),&window_changes);
4499 XSetCursorState(display,windows,MagickFalse);
4500 return(status != 0 ? MagickTrue : MagickFalse);
4501}
4502
4503/*
4504%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4505% %
4506% %
4507% %
4508+ X C r o p I m a g e %
4509% %
4510% %
4511% %
4512%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4513%
4514% XCropImage() allows the user to select a region of the image and crop, copy,
4515% or cut it. For copy or cut, the image can subsequently be composited onto
4516% the image with XPasteImage.
4517%
4518% The format of the XCropImage method is:
4519%
4520% MagickBooleanType XCropImage(Display *display,
4521% XResourceInfo *resource_info,XWindows *windows,Image *image,
4522% const ClipboardMode mode,ExceptionInfo *exception)
4523%
4524% A description of each parameter follows:
4525%
4526% o display: Specifies a connection to an X server; returned from
4527% XOpenDisplay.
4528%
4529% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4530%
4531% o windows: Specifies a pointer to a XWindows structure.
4532%
4533% o image: the image; returned from ReadImage.
4534%
4535% o mode: This unsigned value specified whether the image should be
4536% cropped, copied, or cut.
4537%
4538% o exception: return any errors or warnings in this structure.
4539%
4540*/
4541static MagickBooleanType XCropImage(Display *display,
4542 XResourceInfo *resource_info,XWindows *windows,Image *image,
4543 const ClipboardMode mode,ExceptionInfo *exception)
4544{
4545 const char
4546 *const CropModeMenu[] =
4547 {
4548 "Help",
4549 "Dismiss",
4550 (char *) NULL
4551 },
4552 *RectifyModeMenu[] =
4553 {
4554 "Crop",
4555 "Help",
4556 "Dismiss",
4557 (char *) NULL
4558 };
4559
4560 static const ModeType
4561 CropCommands[] =
4562 {
4563 CropHelpCommand,
4564 CropDismissCommand
4565 },
4566 RectifyCommands[] =
4567 {
4568 RectifyCopyCommand,
4569 RectifyHelpCommand,
4570 RectifyDismissCommand
4571 };
4572
4573 CacheView
4574 *image_view;
4575
4576 char
4577 command[MagickPathExtent],
4578 text[MagickPathExtent];
4579
4580 Cursor
4581 cursor;
4582
4583 int
4584 id,
4585 x,
4586 y;
4587
4588 KeySym
4589 key_symbol;
4590
4591 Image
4592 *crop_image;
4593
4594 double
4595 scale_factor;
4596
4598 crop_info,
4599 highlight_info;
4600
4601 Quantum
4602 *q;
4603
4604 unsigned int
4605 height,
4606 width;
4607
4608 size_t
4609 state;
4610
4611 XEvent
4612 event;
4613
4614 /*
4615 Map Command widget.
4616 */
4617 switch (mode)
4618 {
4619 case CopyMode:
4620 {
4621 (void) CloneString(&windows->command.name,"Copy");
4622 break;
4623 }
4624 case CropMode:
4625 {
4626 (void) CloneString(&windows->command.name,"Crop");
4627 break;
4628 }
4629 case CutMode:
4630 {
4631 (void) CloneString(&windows->command.name,"Cut");
4632 break;
4633 }
4634 }
4635 RectifyModeMenu[0]=windows->command.name;
4636 windows->command.data=0;
4637 (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4638 (void) XMapRaised(display,windows->command.id);
4639 XClientMessage(display,windows->image.id,windows->im_protocols,
4640 windows->im_update_widget,CurrentTime);
4641 /*
4642 Track pointer until button 1 is pressed.
4643 */
4644 XQueryPosition(display,windows->image.id,&x,&y);
4645 (void) XSelectInput(display,windows->image.id,
4646 windows->image.attributes.event_mask | PointerMotionMask);
4647 crop_info.x=(ssize_t) windows->image.x+x;
4648 crop_info.y=(ssize_t) windows->image.y+y;
4649 crop_info.width=0;
4650 crop_info.height=0;
4651 cursor=XCreateFontCursor(display,XC_fleur);
4652 state=DefaultState;
4653 do
4654 {
4655 if (windows->info.mapped != MagickFalse)
4656 {
4657 /*
4658 Display pointer position.
4659 */
4660 (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
4661 (long) crop_info.x,(long) crop_info.y);
4662 XInfoWidget(display,windows,text);
4663 }
4664 /*
4665 Wait for next event.
4666 */
4667 XScreenEvent(display,windows,&event,exception);
4668 if (event.xany.window == windows->command.id)
4669 {
4670 /*
4671 Select a command from the Command widget.
4672 */
4673 id=XCommandWidget(display,windows,CropModeMenu,&event);
4674 if (id < 0)
4675 continue;
4676 switch (CropCommands[id])
4677 {
4678 case CropHelpCommand:
4679 {
4680 switch (mode)
4681 {
4682 case CopyMode:
4683 {
4684 XTextViewHelp(display,resource_info,windows,MagickFalse,
4685 "Help Viewer - Image Copy",ImageCopyHelp);
4686 break;
4687 }
4688 case CropMode:
4689 {
4690 XTextViewHelp(display,resource_info,windows,MagickFalse,
4691 "Help Viewer - Image Crop",ImageCropHelp);
4692 break;
4693 }
4694 case CutMode:
4695 {
4696 XTextViewHelp(display,resource_info,windows,MagickFalse,
4697 "Help Viewer - Image Cut",ImageCutHelp);
4698 break;
4699 }
4700 }
4701 break;
4702 }
4703 case CropDismissCommand:
4704 {
4705 /*
4706 Prematurely exit.
4707 */
4708 state|=EscapeState;
4709 state|=ExitState;
4710 break;
4711 }
4712 default:
4713 break;
4714 }
4715 continue;
4716 }
4717 switch (event.type)
4718 {
4719 case ButtonPress:
4720 {
4721 if (event.xbutton.button != Button1)
4722 break;
4723 if (event.xbutton.window != windows->image.id)
4724 break;
4725 /*
4726 Note first corner of cropping rectangle-- exit loop.
4727 */
4728 (void) XCheckDefineCursor(display,windows->image.id,cursor);
4729 crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4730 crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4731 state|=ExitState;
4732 break;
4733 }
4734 case ButtonRelease:
4735 break;
4736 case Expose:
4737 break;
4738 case KeyPress:
4739 {
4740 if (event.xkey.window != windows->image.id)
4741 break;
4742 /*
4743 Respond to a user key press.
4744 */
4745 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4746 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4747 switch ((int) key_symbol)
4748 {
4749 case XK_Escape:
4750 case XK_F20:
4751 {
4752 /*
4753 Prematurely exit.
4754 */
4755 state|=EscapeState;
4756 state|=ExitState;
4757 break;
4758 }
4759 case XK_F1:
4760 case XK_Help:
4761 {
4762 switch (mode)
4763 {
4764 case CopyMode:
4765 {
4766 XTextViewHelp(display,resource_info,windows,MagickFalse,
4767 "Help Viewer - Image Copy",ImageCopyHelp);
4768 break;
4769 }
4770 case CropMode:
4771 {
4772 XTextViewHelp(display,resource_info,windows,MagickFalse,
4773 "Help Viewer - Image Crop",ImageCropHelp);
4774 break;
4775 }
4776 case CutMode:
4777 {
4778 XTextViewHelp(display,resource_info,windows,MagickFalse,
4779 "Help Viewer - Image Cut",ImageCutHelp);
4780 break;
4781 }
4782 }
4783 break;
4784 }
4785 default:
4786 {
4787 (void) XBell(display,0);
4788 break;
4789 }
4790 }
4791 break;
4792 }
4793 case MotionNotify:
4794 {
4795 if (event.xmotion.window != windows->image.id)
4796 break;
4797 /*
4798 Map and unmap Info widget as text cursor crosses its boundaries.
4799 */
4800 x=event.xmotion.x;
4801 y=event.xmotion.y;
4802 if (windows->info.mapped != MagickFalse)
4803 {
4804 if ((x < (windows->info.x+(int) windows->info.width)) &&
4805 (y < (windows->info.y+(int) windows->info.height)))
4806 (void) XWithdrawWindow(display,windows->info.id,
4807 windows->info.screen);
4808 }
4809 else
4810 if ((x > (windows->info.x+(int) windows->info.width)) ||
4811 (y > (windows->info.y+(int) windows->info.height)))
4812 (void) XMapWindow(display,windows->info.id);
4813 crop_info.x=(ssize_t) windows->image.x+x;
4814 crop_info.y=(ssize_t) windows->image.y+y;
4815 break;
4816 }
4817 default:
4818 break;
4819 }
4820 } while ((state & ExitState) == 0);
4821 (void) XSelectInput(display,windows->image.id,
4822 windows->image.attributes.event_mask);
4823 if ((state & EscapeState) != 0)
4824 {
4825 /*
4826 User want to exit without cropping.
4827 */
4828 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4829 (void) XFreeCursor(display,cursor);
4830 return(MagickTrue);
4831 }
4832 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4833 do
4834 {
4835 /*
4836 Size rectangle as pointer moves until the mouse button is released.
4837 */
4838 x=(int) crop_info.x;
4839 y=(int) crop_info.y;
4840 crop_info.width=0;
4841 crop_info.height=0;
4842 state=DefaultState;
4843 do
4844 {
4845 highlight_info=crop_info;
4846 highlight_info.x=crop_info.x-windows->image.x;
4847 highlight_info.y=crop_info.y-windows->image.y;
4848 if ((highlight_info.width > 3) && (highlight_info.height > 3))
4849 {
4850 /*
4851 Display info and draw cropping rectangle.
4852 */
4853 if (windows->info.mapped == MagickFalse)
4854 (void) XMapWindow(display,windows->info.id);
4855 (void) FormatLocaleString(text,MagickPathExtent,
4856 " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4857 crop_info.height,(double) crop_info.x,(double) crop_info.y);
4858 XInfoWidget(display,windows,text);
4859 XHighlightRectangle(display,windows->image.id,
4860 windows->image.highlight_context,&highlight_info);
4861 }
4862 else
4863 if (windows->info.mapped != MagickFalse)
4864 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4865 /*
4866 Wait for next event.
4867 */
4868 XScreenEvent(display,windows,&event,exception);
4869 if ((highlight_info.width > 3) && (highlight_info.height > 3))
4870 XHighlightRectangle(display,windows->image.id,
4871 windows->image.highlight_context,&highlight_info);
4872 switch (event.type)
4873 {
4874 case ButtonPress:
4875 {
4876 crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4877 crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4878 break;
4879 }
4880 case ButtonRelease:
4881 {
4882 /*
4883 User has committed to cropping rectangle.
4884 */
4885 crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4886 crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4887 XSetCursorState(display,windows,MagickFalse);
4888 state|=ExitState;
4889 windows->command.data=0;
4890 (void) XCommandWidget(display,windows,RectifyModeMenu,
4891 (XEvent *) NULL);
4892 break;
4893 }
4894 case Expose:
4895 break;
4896 case MotionNotify:
4897 {
4898 crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4899 crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4900 }
4901 default:
4902 break;
4903 }
4904 if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4905 ((state & ExitState) != 0))
4906 {
4907 /*
4908 Check boundary conditions.
4909 */
4910 if (crop_info.x < 0)
4911 crop_info.x=0;
4912 else
4913 if (crop_info.x > (ssize_t) windows->image.ximage->width)
4914 crop_info.x=(ssize_t) windows->image.ximage->width;
4915 if ((int) crop_info.x < x)
4916 crop_info.width=(unsigned int) (x-crop_info.x);
4917 else
4918 {
4919 crop_info.width=(unsigned int) (crop_info.x-x);
4920 crop_info.x=(ssize_t) x;
4921 }
4922 if (crop_info.y < 0)
4923 crop_info.y=0;
4924 else
4925 if (crop_info.y > (ssize_t) windows->image.ximage->height)
4926 crop_info.y=(ssize_t) windows->image.ximage->height;
4927 if ((int) crop_info.y < y)
4928 crop_info.height=(unsigned int) (y-crop_info.y);
4929 else
4930 {
4931 crop_info.height=(unsigned int) (crop_info.y-y);
4932 crop_info.y=(ssize_t) y;
4933 }
4934 }
4935 } while ((state & ExitState) == 0);
4936 /*
4937 Wait for user to grab a corner of the rectangle or press return.
4938 */
4939 state=DefaultState;
4940 (void) XMapWindow(display,windows->info.id);
4941 do
4942 {
4943 if (windows->info.mapped != MagickFalse)
4944 {
4945 /*
4946 Display pointer position.
4947 */
4948 (void) FormatLocaleString(text,MagickPathExtent,
4949 " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4950 crop_info.height,(double) crop_info.x,(double) crop_info.y);
4951 XInfoWidget(display,windows,text);
4952 }
4953 highlight_info=crop_info;
4954 highlight_info.x=crop_info.x-windows->image.x;
4955 highlight_info.y=crop_info.y-windows->image.y;
4956 if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4957 {
4958 state|=EscapeState;
4959 state|=ExitState;
4960 break;
4961 }
4962 XHighlightRectangle(display,windows->image.id,
4963 windows->image.highlight_context,&highlight_info);
4964 XScreenEvent(display,windows,&event,exception);
4965 if (event.xany.window == windows->command.id)
4966 {
4967 /*
4968 Select a command from the Command widget.
4969 */
4970 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4971 id=XCommandWidget(display,windows,RectifyModeMenu,&event);
4972 (void) XSetFunction(display,windows->image.highlight_context,
4973 GXinvert);
4974 XHighlightRectangle(display,windows->image.id,
4975 windows->image.highlight_context,&highlight_info);
4976 if (id >= 0)
4977 switch (RectifyCommands[id])
4978 {
4979 case RectifyCopyCommand:
4980 {
4981 state|=ExitState;
4982 break;
4983 }
4984 case RectifyHelpCommand:
4985 {
4986 (void) XSetFunction(display,windows->image.highlight_context,
4987 GXcopy);
4988 switch (mode)
4989 {
4990 case CopyMode:
4991 {
4992 XTextViewHelp(display,resource_info,windows,MagickFalse,
4993 "Help Viewer - Image Copy",ImageCopyHelp);
4994 break;
4995 }
4996 case CropMode:
4997 {
4998 XTextViewHelp(display,resource_info,windows,MagickFalse,
4999 "Help Viewer - Image Crop",ImageCropHelp);
5000 break;
5001 }
5002 case CutMode:
5003 {
5004 XTextViewHelp(display,resource_info,windows,MagickFalse,
5005 "Help Viewer - Image Cut",ImageCutHelp);
5006 break;
5007 }
5008 }
5009 (void) XSetFunction(display,windows->image.highlight_context,
5010 GXinvert);
5011 break;
5012 }
5013 case RectifyDismissCommand:
5014 {
5015 /*
5016 Prematurely exit.
5017 */
5018 state|=EscapeState;
5019 state|=ExitState;
5020 break;
5021 }
5022 default:
5023 break;
5024 }
5025 continue;
5026 }
5027 XHighlightRectangle(display,windows->image.id,
5028 windows->image.highlight_context,&highlight_info);
5029 switch (event.type)
5030 {
5031 case ButtonPress:
5032 {
5033 if (event.xbutton.button != Button1)
5034 break;
5035 if (event.xbutton.window != windows->image.id)
5036 break;
5037 x=windows->image.x+event.xbutton.x;
5038 y=windows->image.y+event.xbutton.y;
5039 if ((x < (int) (crop_info.x+RoiDelta)) &&
5040 (x > (int) (crop_info.x-RoiDelta)) &&
5041 (y < (int) (crop_info.y+RoiDelta)) &&
5042 (y > (int) (crop_info.y-RoiDelta)))
5043 {
5044 crop_info.x=crop_info.x+(ssize_t) crop_info.width;
5045 crop_info.y=crop_info.y+(ssize_t) crop_info.height;
5046 state|=UpdateConfigurationState;
5047 break;
5048 }
5049 if ((x < (int) (crop_info.x+RoiDelta)) &&
5050 (x > (int) (crop_info.x-RoiDelta)) &&
5051 (y < (crop_info.y+(int) crop_info.height+RoiDelta)) &&
5052 (y > (crop_info.y+(int) crop_info.height-RoiDelta)))
5053 {
5054 crop_info.x=(crop_info.x+(int) crop_info.width);
5055 state|=UpdateConfigurationState;
5056 break;
5057 }
5058 if ((x < (crop_info.x+(int) crop_info.width+RoiDelta)) &&
5059 (x > (crop_info.x+(int) crop_info.width-RoiDelta)) &&
5060 (y < (int) (crop_info.y+RoiDelta)) &&
5061 (y > (int) (crop_info.y-RoiDelta)))
5062 {
5063 crop_info.y=(crop_info.y+(ssize_t) crop_info.height);
5064 state|=UpdateConfigurationState;
5065 break;
5066 }
5067 if ((x < (crop_info.x+(int) crop_info.width+RoiDelta)) &&
5068 (x > (crop_info.x+(int) crop_info.width-RoiDelta)) &&
5069 (y < (crop_info.y+(int) crop_info.height+RoiDelta)) &&
5070 (y > (crop_info.y+(int) crop_info.height-RoiDelta)))
5071 {
5072 state|=UpdateConfigurationState;
5073 break;
5074 }
5075 magick_fallthrough;
5076 }
5077 case ButtonRelease:
5078 {
5079 if (event.xbutton.window == windows->pan.id)
5080 if ((highlight_info.x != crop_info.x-windows->image.x) ||
5081 (highlight_info.y != crop_info.y-windows->image.y))
5082 XHighlightRectangle(display,windows->image.id,
5083 windows->image.highlight_context,&highlight_info);
5084 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5085 event.xbutton.time);
5086 break;
5087 }
5088 case Expose:
5089 {
5090 if (event.xexpose.window == windows->image.id)
5091 if (event.xexpose.count == 0)
5092 {
5093 event.xexpose.x=(int) highlight_info.x;
5094 event.xexpose.y=(int) highlight_info.y;
5095 event.xexpose.width=(int) highlight_info.width;
5096 event.xexpose.height=(int) highlight_info.height;
5097 XRefreshWindow(display,&windows->image,&event);
5098 }
5099 if (event.xexpose.window == windows->info.id)
5100 if (event.xexpose.count == 0)
5101 XInfoWidget(display,windows,text);
5102 break;
5103 }
5104 case KeyPress:
5105 {
5106 if (event.xkey.window != windows->image.id)
5107 break;
5108 /*
5109 Respond to a user key press.
5110 */
5111 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5112 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5113 switch ((int) key_symbol)
5114 {
5115 case XK_Escape:
5116 case XK_F20:
5117 {
5118 state|=EscapeState;
5119 magick_fallthrough;
5120 }
5121 case XK_Return:
5122 {
5123 state|=ExitState;
5124 break;
5125 }
5126 case XK_Home:
5127 case XK_KP_Home:
5128 {
5129 crop_info.x=(ssize_t) (windows->image.width/2L-crop_info.width/
5130 2L);
5131 crop_info.y=(ssize_t) (windows->image.height/2L-crop_info.height/
5132 2L);
5133 break;
5134 }
5135 case XK_Left:
5136 case XK_KP_Left:
5137 {
5138 crop_info.x--;
5139 break;
5140 }
5141 case XK_Up:
5142 case XK_KP_Up:
5143 case XK_Next:
5144 {
5145 crop_info.y--;
5146 break;
5147 }
5148 case XK_Right:
5149 case XK_KP_Right:
5150 {
5151 crop_info.x++;
5152 break;
5153 }
5154 case XK_Prior:
5155 case XK_Down:
5156 case XK_KP_Down:
5157 {
5158 crop_info.y++;
5159 break;
5160 }
5161 case XK_F1:
5162 case XK_Help:
5163 {
5164 (void) XSetFunction(display,windows->image.highlight_context,
5165 GXcopy);
5166 switch (mode)
5167 {
5168 case CopyMode:
5169 {
5170 XTextViewHelp(display,resource_info,windows,MagickFalse,
5171 "Help Viewer - Image Copy",ImageCopyHelp);
5172 break;
5173 }
5174 case CropMode:
5175 {
5176 XTextViewHelp(display,resource_info,windows,MagickFalse,
5177 "Help Viewer - Image Cropg",ImageCropHelp);
5178 break;
5179 }
5180 case CutMode:
5181 {
5182 XTextViewHelp(display,resource_info,windows,MagickFalse,
5183 "Help Viewer - Image Cutg",ImageCutHelp);
5184 break;
5185 }
5186 }
5187 (void) XSetFunction(display,windows->image.highlight_context,
5188 GXinvert);
5189 break;
5190 }
5191 default:
5192 {
5193 (void) XBell(display,0);
5194 break;
5195 }
5196 }
5197 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5198 event.xkey.time);
5199 break;
5200 }
5201 case KeyRelease:
5202 break;
5203 case MotionNotify:
5204 {
5205 if (event.xmotion.window != windows->image.id)
5206 break;
5207 /*
5208 Map and unmap Info widget as text cursor crosses its boundaries.
5209 */
5210 x=event.xmotion.x;
5211 y=event.xmotion.y;
5212 if (windows->info.mapped != MagickFalse)
5213 {
5214 if ((x < (windows->info.x+(int) windows->info.width)) &&
5215 (y < (windows->info.y+(int) windows->info.height)))
5216 (void) XWithdrawWindow(display,windows->info.id,
5217 windows->info.screen);
5218 }
5219 else
5220 if ((x > (windows->info.x+(int) windows->info.width)) ||
5221 (y > (windows->info.y+(int) windows->info.height)))
5222 (void) XMapWindow(display,windows->info.id);
5223 crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5224 crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5225 break;
5226 }
5227 case SelectionRequest:
5228 {
5229 XSelectionEvent
5230 notify;
5231
5232 XSelectionRequestEvent
5233 *request;
5234
5235 /*
5236 Set primary selection.
5237 */
5238 (void) FormatLocaleString(text,MagickPathExtent,
5239 "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5240 crop_info.height,(double) crop_info.x,(double) crop_info.y);
5241 request=(&(event.xselectionrequest));
5242 (void) XChangeProperty(request->display,request->requestor,
5243 request->property,request->target,8,PropModeReplace,
5244 (unsigned char *) text,(int) strlen(text));
5245 notify.type=SelectionNotify;
5246 notify.display=request->display;
5247 notify.requestor=request->requestor;
5248 notify.selection=request->selection;
5249 notify.target=request->target;
5250 notify.time=request->time;
5251 if (request->property == None)
5252 notify.property=request->target;
5253 else
5254 notify.property=request->property;
5255 (void) XSendEvent(request->display,request->requestor,False,0,
5256 (XEvent *) &notify);
5257 }
5258 default:
5259 break;
5260 }
5261 if ((state & UpdateConfigurationState) != 0)
5262 {
5263 (void) XPutBackEvent(display,&event);
5264 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5265 break;
5266 }
5267 } while ((state & ExitState) == 0);
5268 } while ((state & ExitState) == 0);
5269 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5270 XSetCursorState(display,windows,MagickFalse);
5271 if ((state & EscapeState) != 0)
5272 return(MagickTrue);
5273 if (mode == CropMode)
5274 if (((int) crop_info.width != windows->image.ximage->width) ||
5275 ((int) crop_info.height != windows->image.ximage->height))
5276 {
5277 /*
5278 Reconfigure Image window as defined by cropping rectangle.
5279 */
5280 XSetCropGeometry(display,windows,&crop_info,image);
5281 windows->image.window_changes.width=(int) crop_info.width;
5282 windows->image.window_changes.height=(int) crop_info.height;
5283 (void) XConfigureImage(display,resource_info,windows,image,exception);
5284 return(MagickTrue);
5285 }
5286 /*
5287 Copy image before applying image transforms.
5288 */
5289 XSetCursorState(display,windows,MagickTrue);
5290 XCheckRefreshWindows(display,windows);
5291 width=(unsigned int) image->columns;
5292 height=(unsigned int) image->rows;
5293 x=0;
5294 y=0;
5295 if (windows->image.crop_geometry != (char *) NULL)
5296 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5297 scale_factor=(double) width/windows->image.ximage->width;
5298 crop_info.x+=x;
5299 crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5300 crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5301 scale_factor=(double) height/windows->image.ximage->height;
5302 crop_info.y+=y;
5303 crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5304 crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5305 crop_info.x+=image->page.x;
5306 crop_info.y+=image->page.y;
5307 crop_image=CropImage(image,&crop_info,exception);
5308 XSetCursorState(display,windows,MagickFalse);
5309 if (crop_image == (Image *) NULL)
5310 return(MagickFalse);
5311 if (resource_info->copy_image != (Image *) NULL)
5312 resource_info->copy_image=DestroyImage(resource_info->copy_image);
5313 resource_info->copy_image=crop_image;
5314 if (mode == CopyMode)
5315 {
5316 (void) XConfigureImage(display,resource_info,windows,image,exception);
5317 return(MagickTrue);
5318 }
5319 /*
5320 Cut image.
5321 */
5322 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5323 return(MagickFalse);
5324 image->alpha_trait=BlendPixelTrait;
5325 image_view=AcquireAuthenticCacheView(image,exception);
5326 for (y=0; y < (int) crop_info.height; y++)
5327 {
5328 q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5329 crop_info.width,1,exception);
5330 if (q == (Quantum *) NULL)
5331 break;
5332 for (x=0; x < (int) crop_info.width; x++)
5333 {
5334 SetPixelAlpha(image,TransparentAlpha,q);
5335 q+=GetPixelChannels(image);
5336 }
5337 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5338 break;
5339 }
5340 image_view=DestroyCacheView(image_view);
5341 /*
5342 Update image configuration.
5343 */
5344 XConfigureImageColormap(display,resource_info,windows,image,exception);
5345 (void) XConfigureImage(display,resource_info,windows,image,exception);
5346 return(MagickTrue);
5347}
5348
5349/*
5350%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5351% %
5352% %
5353% %
5354+ X D r a w I m a g e %
5355% %
5356% %
5357% %
5358%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5359%
5360% XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5361% the image.
5362%
5363% The format of the XDrawEditImage method is:
5364%
5365% MagickBooleanType XDrawEditImage(Display *display,
5366% XResourceInfo *resource_info,XWindows *windows,Image **image,
5367% ExceptionInfo *exception)
5368%
5369% A description of each parameter follows:
5370%
5371% o display: Specifies a connection to an X server; returned from
5372% XOpenDisplay.
5373%
5374% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5375%
5376% o windows: Specifies a pointer to a XWindows structure.
5377%
5378% o image: the image.
5379%
5380% o exception: return any errors or warnings in this structure.
5381%
5382*/
5383static MagickBooleanType XDrawEditImage(Display *display,
5384 XResourceInfo *resource_info,XWindows *windows,Image **image,
5385 ExceptionInfo *exception)
5386{
5387 const char
5388 *const DrawMenu[] =
5389 {
5390 "Element",
5391 "Color",
5392 "Stipple",
5393 "Width",
5394 "Undo",
5395 "Help",
5396 "Dismiss",
5397 (char *) NULL
5398 };
5399
5400 static ElementType
5401 element = PointElement;
5402
5403 static const ModeType
5404 DrawCommands[] =
5405 {
5406 DrawElementCommand,
5407 DrawColorCommand,
5408 DrawStippleCommand,
5409 DrawWidthCommand,
5410 DrawUndoCommand,
5411 DrawHelpCommand,
5412 DrawDismissCommand
5413 };
5414
5415 static Pixmap
5416 stipple = (Pixmap) NULL;
5417
5418 static unsigned int
5419 pen_id = 0,
5420 line_width = 1;
5421
5422 char
5423 command[MagickPathExtent],
5424 text[MagickPathExtent];
5425
5426 Cursor
5427 cursor;
5428
5429 int
5430 entry,
5431 id,
5432 number_coordinates,
5433 x,
5434 y;
5435
5436 double
5437 degrees;
5438
5439 MagickStatusType
5440 status;
5441
5443 rectangle_info;
5444
5445 int
5446 i;
5447
5448 unsigned int
5449 distance,
5450 height,
5451 max_coordinates,
5452 width;
5453
5454 size_t
5455 state;
5456
5457 Window
5458 root_window;
5459
5460 XDrawInfo
5461 draw_info;
5462
5463 XEvent
5464 event;
5465
5466 XPoint
5467 *coordinate_info;
5468
5469 XSegment
5470 line_info;
5471
5472 /*
5473 Allocate polygon info.
5474 */
5475 max_coordinates=2048;
5476 coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5477 sizeof(*coordinate_info));
5478 if (coordinate_info == (XPoint *) NULL)
5479 {
5480 (void) ThrowMagickException(exception,GetMagickModule(),
5481 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5482 return(MagickFalse);
5483 }
5484 /*
5485 Map Command widget.
5486 */
5487 (void) CloneString(&windows->command.name,"Draw");
5488 windows->command.data=4;
5489 (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5490 (void) XMapRaised(display,windows->command.id);
5491 XClientMessage(display,windows->image.id,windows->im_protocols,
5492 windows->im_update_widget,CurrentTime);
5493 /*
5494 Wait for first button press.
5495 */
5496 root_window=XRootWindow(display,XDefaultScreen(display));
5497 draw_info.stencil=OpaqueStencil;
5498 status=MagickTrue;
5499 cursor=XCreateFontCursor(display,XC_tcross);
5500 for ( ; ; )
5501 {
5502 XQueryPosition(display,windows->image.id,&x,&y);
5503 (void) XSelectInput(display,windows->image.id,
5504 windows->image.attributes.event_mask | PointerMotionMask);
5505 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5506 state=DefaultState;
5507 do
5508 {
5509 if (windows->info.mapped != MagickFalse)
5510 {
5511 /*
5512 Display pointer position.
5513 */
5514 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
5515 x+windows->image.x,y+windows->image.y);
5516 XInfoWidget(display,windows,text);
5517 }
5518 /*
5519 Wait for next event.
5520 */
5521 XScreenEvent(display,windows,&event,exception);
5522 if (event.xany.window == windows->command.id)
5523 {
5524 /*
5525 Select a command from the Command widget.
5526 */
5527 id=XCommandWidget(display,windows,DrawMenu,&event);
5528 if (id < 0)
5529 continue;
5530 switch (DrawCommands[id])
5531 {
5532 case DrawElementCommand:
5533 {
5534 const char
5535 *const Elements[] =
5536 {
5537 "point",
5538 "line",
5539 "rectangle",
5540 "fill rectangle",
5541 "circle",
5542 "fill circle",
5543 "ellipse",
5544 "fill ellipse",
5545 "polygon",
5546 "fill polygon",
5547 (char *) NULL,
5548 };
5549
5550 /*
5551 Select a command from the pop-up menu.
5552 */
5553 element=(ElementType) (XMenuWidget(display,windows,
5554 DrawMenu[id],Elements,command)+1);
5555 break;
5556 }
5557 case DrawColorCommand:
5558 {
5559 const char
5560 *ColorMenu[MaxNumberPens+1];
5561
5562 int
5563 pen_number;
5564
5565 MagickBooleanType
5566 transparent;
5567
5568 XColor
5569 color;
5570
5571 /*
5572 Initialize menu selections.
5573 */
5574 for (i=0; i < (int) (MaxNumberPens-2); i++)
5575 ColorMenu[i]=resource_info->pen_colors[i];
5576 ColorMenu[MaxNumberPens-2]="transparent";
5577 ColorMenu[MaxNumberPens-1]="Browser...";
5578 ColorMenu[MaxNumberPens]=(char *) NULL;
5579 /*
5580 Select a pen color from the pop-up menu.
5581 */
5582 pen_number=XMenuWidget(display,windows,DrawMenu[id],
5583 (const char **) ColorMenu,command);
5584 if (pen_number < 0)
5585 break;
5586 transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5587 MagickFalse;
5588 if (transparent != MagickFalse)
5589 {
5590 draw_info.stencil=TransparentStencil;
5591 break;
5592 }
5593 if (pen_number == (MaxNumberPens-1))
5594 {
5595 static char
5596 color_name[MagickPathExtent] = "gray";
5597
5598 /*
5599 Select a pen color from a dialog.
5600 */
5601 resource_info->pen_colors[pen_number]=color_name;
5602 XColorBrowserWidget(display,windows,"Select",color_name);
5603 if (*color_name == '\0')
5604 break;
5605 }
5606 /*
5607 Set pen color.
5608 */
5609 (void) XParseColor(display,windows->map_info->colormap,
5610 resource_info->pen_colors[pen_number],&color);
5611 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5612 (unsigned int) MaxColors,&color);
5613 windows->pixel_info->pen_colors[pen_number]=color;
5614 pen_id=(unsigned int) pen_number;
5615 draw_info.stencil=OpaqueStencil;
5616 break;
5617 }
5618 case DrawStippleCommand:
5619 {
5620 const char
5621 *StipplesMenu[] =
5622 {
5623 "Brick",
5624 "Diagonal",
5625 "Scales",
5626 "Vertical",
5627 "Wavy",
5628 "Translucent",
5629 "Opaque",
5630 (char *) NULL,
5631 (char *) NULL,
5632 };
5633
5634 Image
5635 *stipple_image;
5636
5637 ImageInfo
5638 *image_info;
5639
5640 int
5641 status;
5642
5643 static char
5644 filename[MagickPathExtent] = "\0";
5645
5646 /*
5647 Select a command from the pop-up menu.
5648 */
5649 StipplesMenu[7]="Open...";
5650 entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5651 command);
5652 if (entry < 0)
5653 break;
5654 if (stipple != (Pixmap) NULL)
5655 (void) XFreePixmap(display,stipple);
5656 stipple=(Pixmap) NULL;
5657 if (entry != 7)
5658 {
5659 switch (entry)
5660 {
5661 case 0:
5662 {
5663 stipple=XCreateBitmapFromData(display,root_window,
5664 (char *) BricksBitmap,BricksWidth,BricksHeight);
5665 break;
5666 }
5667 case 1:
5668 {
5669 stipple=XCreateBitmapFromData(display,root_window,
5670 (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5671 break;
5672 }
5673 case 2:
5674 {
5675 stipple=XCreateBitmapFromData(display,root_window,
5676 (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5677 break;
5678 }
5679 case 3:
5680 {
5681 stipple=XCreateBitmapFromData(display,root_window,
5682 (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5683 break;
5684 }
5685 case 4:
5686 {
5687 stipple=XCreateBitmapFromData(display,root_window,
5688 (char *) WavyBitmap,WavyWidth,WavyHeight);
5689 break;
5690 }
5691 case 5:
5692 {
5693 stipple=XCreateBitmapFromData(display,root_window,
5694 (char *) HighlightBitmap,HighlightWidth,
5695 HighlightHeight);
5696 break;
5697 }
5698 case 6:
5699 default:
5700 {
5701 stipple=XCreateBitmapFromData(display,root_window,
5702 (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5703 break;
5704 }
5705 }
5706 break;
5707 }
5708 XFileBrowserWidget(display,windows,"Stipple",filename);
5709 if (*filename == '\0')
5710 break;
5711 /*
5712 Read image.
5713 */
5714 XSetCursorState(display,windows,MagickTrue);
5715 XCheckRefreshWindows(display,windows);
5716 image_info=AcquireImageInfo();
5717 (void) CopyMagickString(image_info->filename,filename,
5718 MagickPathExtent);
5719 stipple_image=ReadImage(image_info,exception);
5720 CatchException(exception);
5721 XSetCursorState(display,windows,MagickFalse);
5722 if (stipple_image == (Image *) NULL)
5723 break;
5724 (void) AcquireUniqueFileResource(filename);
5725 (void) FormatLocaleString(stipple_image->filename,
5726 MagickPathExtent,"xbm:%s",filename);
5727 (void) WriteImage(image_info,stipple_image,exception);
5728 stipple_image=DestroyImage(stipple_image);
5729 image_info=DestroyImageInfo(image_info);
5730 status=XReadBitmapFile(display,root_window,filename,&width,
5731 &height,&stipple,&x,&y);
5732 (void) RelinquishUniqueFileResource(filename);
5733 if ((status != BitmapSuccess) != 0)
5734 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5735 filename);
5736 break;
5737 }
5738 case DrawWidthCommand:
5739 {
5740 const char
5741 *const WidthsMenu[] =
5742 {
5743 "1",
5744 "2",
5745 "4",
5746 "8",
5747 "16",
5748 "Dialog...",
5749 (char *) NULL,
5750 };
5751
5752 static char
5753 width[MagickPathExtent] = "0";
5754
5755 /*
5756 Select a command from the pop-up menu.
5757 */
5758 entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5759 command);
5760 if (entry < 0)
5761 break;
5762 if (entry != 5)
5763 {
5764 line_width=(unsigned int) StringToUnsignedLong(
5765 WidthsMenu[entry]);
5766 break;
5767 }
5768 (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5769 width);
5770 if (*width == '\0')
5771 break;
5772 line_width=(unsigned int) StringToUnsignedLong(width);
5773 break;
5774 }
5775 case DrawUndoCommand:
5776 {
5777 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5778 image,exception);
5779 break;
5780 }
5781 case DrawHelpCommand:
5782 {
5783 XTextViewHelp(display,resource_info,windows,MagickFalse,
5784 "Help Viewer - Image Rotation",ImageDrawHelp);
5785 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5786 break;
5787 }
5788 case DrawDismissCommand:
5789 {
5790 /*
5791 Prematurely exit.
5792 */
5793 state|=EscapeState;
5794 state|=ExitState;
5795 break;
5796 }
5797 default:
5798 break;
5799 }
5800 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5801 continue;
5802 }
5803 switch (event.type)
5804 {
5805 case ButtonPress:
5806 {
5807 if (event.xbutton.button != Button1)
5808 break;
5809 if (event.xbutton.window != windows->image.id)
5810 break;
5811 /*
5812 exit loop.
5813 */
5814 x=event.xbutton.x;
5815 y=event.xbutton.y;
5816 state|=ExitState;
5817 break;
5818 }
5819 case ButtonRelease:
5820 break;
5821 case Expose:
5822 break;
5823 case KeyPress:
5824 {
5825 KeySym
5826 key_symbol;
5827
5828 if (event.xkey.window != windows->image.id)
5829 break;
5830 /*
5831 Respond to a user key press.
5832 */
5833 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5834 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5835 switch ((int) key_symbol)
5836 {
5837 case XK_Escape:
5838 case XK_F20:
5839 {
5840 /*
5841 Prematurely exit.
5842 */
5843 state|=EscapeState;
5844 state|=ExitState;
5845 break;
5846 }
5847 case XK_F1:
5848 case XK_Help:
5849 {
5850 XTextViewHelp(display,resource_info,windows,MagickFalse,
5851 "Help Viewer - Image Rotation",ImageDrawHelp);
5852 break;
5853 }
5854 default:
5855 {
5856 (void) XBell(display,0);
5857 break;
5858 }
5859 }
5860 break;
5861 }
5862 case MotionNotify:
5863 {
5864 /*
5865 Map and unmap Info widget as text cursor crosses its boundaries.
5866 */
5867 x=event.xmotion.x;
5868 y=event.xmotion.y;
5869 if (windows->info.mapped != MagickFalse)
5870 {
5871 if ((x < (windows->info.x+(int) windows->info.width)) &&
5872 (y < (windows->info.y+(int) windows->info.height)))
5873 (void) XWithdrawWindow(display,windows->info.id,
5874 windows->info.screen);
5875 }
5876 else
5877 if ((x > (windows->info.x+(int) windows->info.width)) ||
5878 (y > (windows->info.y+(int) windows->info.height)))
5879 (void) XMapWindow(display,windows->info.id);
5880 break;
5881 }
5882 }
5883 } while ((state & ExitState) == 0);
5884 (void) XSelectInput(display,windows->image.id,
5885 windows->image.attributes.event_mask);
5886 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5887 if ((state & EscapeState) != 0)
5888 break;
5889 /*
5890 Draw element as pointer moves until the button is released.
5891 */
5892 distance=0;
5893 degrees=0.0;
5894 line_info.x1=x;
5895 line_info.y1=y;
5896 line_info.x2=x;
5897 line_info.y2=y;
5898 rectangle_info.x=(ssize_t) x;
5899 rectangle_info.y=(ssize_t) y;
5900 rectangle_info.width=0;
5901 rectangle_info.height=0;
5902 number_coordinates=1;
5903 coordinate_info->x=x;
5904 coordinate_info->y=y;
5905 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5906 state=DefaultState;
5907 do
5908 {
5909 switch (element)
5910 {
5911 case PointElement:
5912 default:
5913 {
5914 if (number_coordinates > 1)
5915 {
5916 (void) XDrawLines(display,windows->image.id,
5917 windows->image.highlight_context,coordinate_info,
5918 number_coordinates,CoordModeOrigin);
5919 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d",
5920 coordinate_info[number_coordinates-1].x,
5921 coordinate_info[number_coordinates-1].y);
5922 XInfoWidget(display,windows,text);
5923 }
5924 break;
5925 }
5926 case LineElement:
5927 {
5928 if (distance > 9)
5929 {
5930 /*
5931 Display angle of the line.
5932 */
5933 degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5934 line_info.y1),(double) (line_info.x2-line_info.x1)));
5935 (void) FormatLocaleString(text,MagickPathExtent," %g",
5936 (double) degrees);
5937 XInfoWidget(display,windows,text);
5938 XHighlightLine(display,windows->image.id,
5939 windows->image.highlight_context,&line_info);
5940 }
5941 else
5942 if (windows->info.mapped != MagickFalse)
5943 (void) XWithdrawWindow(display,windows->info.id,
5944 windows->info.screen);
5945 break;
5946 }
5947 case RectangleElement:
5948 case FillRectangleElement:
5949 {
5950 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5951 {
5952 /*
5953 Display info and draw drawing rectangle.
5954 */
5955 (void) FormatLocaleString(text,MagickPathExtent,
5956 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5957 (double) rectangle_info.height,(double) rectangle_info.x,
5958 (double) rectangle_info.y);
5959 XInfoWidget(display,windows,text);
5960 XHighlightRectangle(display,windows->image.id,
5961 windows->image.highlight_context,&rectangle_info);
5962 }
5963 else
5964 if (windows->info.mapped != MagickFalse)
5965 (void) XWithdrawWindow(display,windows->info.id,
5966 windows->info.screen);
5967 break;
5968 }
5969 case CircleElement:
5970 case FillCircleElement:
5971 case EllipseElement:
5972 case FillEllipseElement:
5973 {
5974 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5975 {
5976 /*
5977 Display info and draw drawing rectangle.
5978 */
5979 (void) FormatLocaleString(text,MagickPathExtent,
5980 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5981 (double) rectangle_info.height,(double) rectangle_info.x,
5982 (double) rectangle_info.y);
5983 XInfoWidget(display,windows,text);
5984 XHighlightEllipse(display,windows->image.id,
5985 windows->image.highlight_context,&rectangle_info);
5986 }
5987 else
5988 if (windows->info.mapped != MagickFalse)
5989 (void) XWithdrawWindow(display,windows->info.id,
5990 windows->info.screen);
5991 break;
5992 }
5993 case PolygonElement:
5994 case FillPolygonElement:
5995 {
5996 if (number_coordinates > 1)
5997 (void) XDrawLines(display,windows->image.id,
5998 windows->image.highlight_context,coordinate_info,
5999 number_coordinates,CoordModeOrigin);
6000 if (distance > 9)
6001 {
6002 /*
6003 Display angle of the line.
6004 */
6005 degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6006 line_info.y1),(double) (line_info.x2-line_info.x1)));
6007 (void) FormatLocaleString(text,MagickPathExtent," %g",
6008 (double) degrees);
6009 XInfoWidget(display,windows,text);
6010 XHighlightLine(display,windows->image.id,
6011 windows->image.highlight_context,&line_info);
6012 }
6013 else
6014 if (windows->info.mapped != MagickFalse)
6015 (void) XWithdrawWindow(display,windows->info.id,
6016 windows->info.screen);
6017 break;
6018 }
6019 }
6020 /*
6021 Wait for next event.
6022 */
6023 XScreenEvent(display,windows,&event,exception);
6024 switch (element)
6025 {
6026 case PointElement:
6027 default:
6028 {
6029 if (number_coordinates > 1)
6030 (void) XDrawLines(display,windows->image.id,
6031 windows->image.highlight_context,coordinate_info,
6032 number_coordinates,CoordModeOrigin);
6033 break;
6034 }
6035 case LineElement:
6036 {
6037 if (distance > 9)
6038 XHighlightLine(display,windows->image.id,
6039 windows->image.highlight_context,&line_info);
6040 break;
6041 }
6042 case RectangleElement:
6043 case FillRectangleElement:
6044 {
6045 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6046 XHighlightRectangle(display,windows->image.id,
6047 windows->image.highlight_context,&rectangle_info);
6048 break;
6049 }
6050 case CircleElement:
6051 case FillCircleElement:
6052 case EllipseElement:
6053 case FillEllipseElement:
6054 {
6055 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6056 XHighlightEllipse(display,windows->image.id,
6057 windows->image.highlight_context,&rectangle_info);
6058 break;
6059 }
6060 case PolygonElement:
6061 case FillPolygonElement:
6062 {
6063 if (number_coordinates > 1)
6064 (void) XDrawLines(display,windows->image.id,
6065 windows->image.highlight_context,coordinate_info,
6066 number_coordinates,CoordModeOrigin);
6067 if (distance > 9)
6068 XHighlightLine(display,windows->image.id,
6069 windows->image.highlight_context,&line_info);
6070 break;
6071 }
6072 }
6073 switch (event.type)
6074 {
6075 case ButtonPress:
6076 break;
6077 case ButtonRelease:
6078 {
6079 /*
6080 User has committed to element.
6081 */
6082 line_info.x2=event.xbutton.x;
6083 line_info.y2=event.xbutton.y;
6084 rectangle_info.x=(ssize_t) event.xbutton.x;
6085 rectangle_info.y=(ssize_t) event.xbutton.y;
6086 coordinate_info[number_coordinates].x=event.xbutton.x;
6087 coordinate_info[number_coordinates].y=event.xbutton.y;
6088 if (((element != PolygonElement) &&
6089 (element != FillPolygonElement)) || (distance <= 9))
6090 {
6091 state|=ExitState;
6092 break;
6093 }
6094 number_coordinates++;
6095 if (number_coordinates < (int) max_coordinates)
6096 {
6097 line_info.x1=event.xbutton.x;
6098 line_info.y1=event.xbutton.y;
6099 break;
6100 }
6101 max_coordinates<<=1;
6102 coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6103 max_coordinates,sizeof(*coordinate_info));
6104 if (coordinate_info == (XPoint *) NULL)
6105 (void) ThrowMagickException(exception,GetMagickModule(),
6106 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6107 break;
6108 }
6109 case Expose:
6110 break;
6111 case MotionNotify:
6112 {
6113 if (event.xmotion.window != windows->image.id)
6114 break;
6115 if (element != PointElement)
6116 {
6117 line_info.x2=event.xmotion.x;
6118 line_info.y2=event.xmotion.y;
6119 rectangle_info.x=(ssize_t) event.xmotion.x;
6120 rectangle_info.y=(ssize_t) event.xmotion.y;
6121 break;
6122 }
6123 coordinate_info[number_coordinates].x=event.xbutton.x;
6124 coordinate_info[number_coordinates].y=event.xbutton.y;
6125 number_coordinates++;
6126 if (number_coordinates < (int) max_coordinates)
6127 break;
6128 max_coordinates<<=1;
6129 coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6130 max_coordinates,sizeof(*coordinate_info));
6131 if (coordinate_info == (XPoint *) NULL)
6132 (void) ThrowMagickException(exception,GetMagickModule(),
6133 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6134 break;
6135 }
6136 default:
6137 break;
6138 }
6139 /*
6140 Check boundary conditions.
6141 */
6142 if (line_info.x2 < 0)
6143 line_info.x2=0;
6144 else
6145 if (line_info.x2 > (int) windows->image.width)
6146 line_info.x2=(short) windows->image.width;
6147 if (line_info.y2 < 0)
6148 line_info.y2=0;
6149 else
6150 if (line_info.y2 > (int) windows->image.height)
6151 line_info.y2=(short) windows->image.height;
6152 distance=(unsigned int)
6153 (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6154 ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6155 if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6156 ((state & ExitState) != 0))
6157 {
6158 if (rectangle_info.x < 0)
6159 rectangle_info.x=0;
6160 else
6161 if (rectangle_info.x > (ssize_t) windows->image.width)
6162 rectangle_info.x=(ssize_t) windows->image.width;
6163 if ((int) rectangle_info.x < x)
6164 rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6165 else
6166 {
6167 rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6168 rectangle_info.x=(ssize_t) x;
6169 }
6170 if (rectangle_info.y < 0)
6171 rectangle_info.y=0;
6172 else
6173 if (rectangle_info.y > (ssize_t) windows->image.height)
6174 rectangle_info.y=(ssize_t) windows->image.height;
6175 if ((int) rectangle_info.y < y)
6176 rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6177 else
6178 {
6179 rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6180 rectangle_info.y=(ssize_t) y;
6181 }
6182 }
6183 } while ((state & ExitState) == 0);
6184 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6185 if ((element == PointElement) || (element == PolygonElement) ||
6186 (element == FillPolygonElement))
6187 {
6188 /*
6189 Determine polygon bounding box.
6190 */
6191 rectangle_info.x=(ssize_t) coordinate_info->x;
6192 rectangle_info.y=(ssize_t) coordinate_info->y;
6193 x=coordinate_info->x;
6194 y=coordinate_info->y;
6195 for (i=1; i < number_coordinates; i++)
6196 {
6197 if (coordinate_info[i].x > x)
6198 x=coordinate_info[i].x;
6199 if (coordinate_info[i].y > y)
6200 y=coordinate_info[i].y;
6201 if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6202 rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6203 if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6204 rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6205 }
6206 rectangle_info.width=(size_t) (x-rectangle_info.x);
6207 rectangle_info.height=(size_t) (y-rectangle_info.y);
6208 for (i=0; i < number_coordinates; i++)
6209 {
6210 coordinate_info[i].x-=rectangle_info.x;
6211 coordinate_info[i].y-=rectangle_info.y;
6212 }
6213 }
6214 else
6215 if (distance <= 9)
6216 continue;
6217 else
6218 if ((element == RectangleElement) ||
6219 (element == CircleElement) || (element == EllipseElement))
6220 {
6221 rectangle_info.width--;
6222 rectangle_info.height--;
6223 }
6224 /*
6225 Drawing is relative to image configuration.
6226 */
6227 draw_info.x=(int) rectangle_info.x;
6228 draw_info.y=(int) rectangle_info.y;
6229 (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6230 image,exception);
6231 width=(unsigned int) (*image)->columns;
6232 height=(unsigned int) (*image)->rows;
6233 x=0;
6234 y=0;
6235 if (windows->image.crop_geometry != (char *) NULL)
6236 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6237 draw_info.x+=windows->image.x-((int) line_width/2);
6238 if (draw_info.x < 0)
6239 draw_info.x=0;
6240 draw_info.x=(int) width*draw_info.x/windows->image.ximage->width;
6241 draw_info.y+=windows->image.y-((int) line_width/2);
6242 if (draw_info.y < 0)
6243 draw_info.y=0;
6244 draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6245 draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6246 if (draw_info.width > (unsigned int) (*image)->columns)
6247 draw_info.width=(unsigned int) (*image)->columns;
6248 draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6249 if (draw_info.height > (unsigned int) (*image)->rows)
6250 draw_info.height=(unsigned int) (*image)->rows;
6251 (void) FormatLocaleString(draw_info.geometry,MagickPathExtent,"%ux%u%+d%+d",
6252 width*draw_info.width/(unsigned int) windows->image.ximage->width,
6253 height*draw_info.height/(unsigned int) windows->image.ximage->height,
6254 draw_info.x+x,draw_info.y+y);
6255 /*
6256 Initialize drawing attributes.
6257 */
6258 draw_info.degrees=0.0;
6259 draw_info.element=element;
6260 draw_info.stipple=stipple;
6261 draw_info.line_width=line_width;
6262 draw_info.line_info=line_info;
6263 if (line_info.x1 > (int) (line_width/2))
6264 draw_info.line_info.x1=(short) line_width/2;
6265 if (line_info.y1 > (int) (line_width/2))
6266 draw_info.line_info.y1=(short) line_width/2;
6267 draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+
6268 ((int) line_width/2));
6269 draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+
6270 ((int) line_width/2));
6271 if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6272 {
6273 draw_info.line_info.x2=(-draw_info.line_info.x2);
6274 draw_info.line_info.y2=(-draw_info.line_info.y2);
6275 }
6276 if (draw_info.line_info.x2 < 0)
6277 {
6278 draw_info.line_info.x2=(-draw_info.line_info.x2);
6279 Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6280 }
6281 if (draw_info.line_info.y2 < 0)
6282 {
6283 draw_info.line_info.y2=(-draw_info.line_info.y2);
6284 Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6285 }
6286 draw_info.rectangle_info=rectangle_info;
6287 if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6288 draw_info.rectangle_info.x=(ssize_t) line_width/2;
6289 if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6290 draw_info.rectangle_info.y=(ssize_t) line_width/2;
6291 draw_info.number_coordinates=(unsigned int) number_coordinates;
6292 draw_info.coordinate_info=coordinate_info;
6293 windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6294 /*
6295 Draw element on image.
6296 */
6297 XSetCursorState(display,windows,MagickTrue);
6298 XCheckRefreshWindows(display,windows);
6299 status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6300 XSetCursorState(display,windows,MagickFalse);
6301 /*
6302 Update image colormap and return to image drawing.
6303 */
6304 XConfigureImageColormap(display,resource_info,windows,*image,exception);
6305 (void) XConfigureImage(display,resource_info,windows,*image,exception);
6306 }
6307 XSetCursorState(display,windows,MagickFalse);
6308 coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6309 return(status != 0 ? MagickTrue : MagickFalse);
6310}
6311
6312/*
6313%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6314% %
6315% %
6316% %
6317+ X D r a w P a n R e c t a n g l e %
6318% %
6319% %
6320% %
6321%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6322%
6323% XDrawPanRectangle() draws a rectangle in the pan window. The pan window
6324% displays a zoom image and the rectangle shows which portion of the image is
6325% displayed in the Image window.
6326%
6327% The format of the XDrawPanRectangle method is:
6328%
6329% XDrawPanRectangle(Display *display,XWindows *windows)
6330%
6331% A description of each parameter follows:
6332%
6333% o display: Specifies a connection to an X server; returned from
6334% XOpenDisplay.
6335%
6336% o windows: Specifies a pointer to a XWindows structure.
6337%
6338*/
6339static void XDrawPanRectangle(Display *display,XWindows *windows)
6340{
6341 double
6342 scale_factor;
6343
6345 highlight_info;
6346
6347 /*
6348 Determine dimensions of the panning rectangle.
6349 */
6350 scale_factor=(double) windows->pan.width/windows->image.ximage->width;
6351 highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6352 highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6353 scale_factor=(double)
6354 windows->pan.height/windows->image.ximage->height;
6355 highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6356 highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6357 /*
6358 Display the panning rectangle.
6359 */
6360 (void) XClearWindow(display,windows->pan.id);
6361 XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6362 &highlight_info);
6363}
6364
6365/*
6366%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6367% %
6368% %
6369% %
6370+ X I m a g e C a c h e %
6371% %
6372% %
6373% %
6374%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6375%
6376% XImageCache() handles the creation, manipulation, and destruction of the
6377% image cache (undo and redo buffers).
6378%
6379% The format of the XImageCache method is:
6380%
6381% void XImageCache(Display *display,XResourceInfo *resource_info,
6382% XWindows *windows,const DisplayCommand command,Image **image,
6383% ExceptionInfo *exception)
6384%
6385% A description of each parameter follows:
6386%
6387% o display: Specifies a connection to an X server; returned from
6388% XOpenDisplay.
6389%
6390% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6391%
6392% o windows: Specifies a pointer to a XWindows structure.
6393%
6394% o command: Specifies a command to perform.
6395%
6396% o image: the image; XImageCache may transform the image and return a new
6397% image pointer.
6398%
6399% o exception: return any errors or warnings in this structure.
6400%
6401*/
6402static void XImageCache(Display *display,XResourceInfo *resource_info,
6403 XWindows *windows,const DisplayCommand command,Image **image,
6404 ExceptionInfo *exception)
6405{
6406 Image
6407 *cache_image;
6408
6409 static Image
6410 *redo_image = (Image *) NULL,
6411 *undo_image = (Image *) NULL;
6412
6413 switch (command)
6414 {
6415 case FreeBuffersCommand:
6416 {
6417 /*
6418 Free memory from the undo and redo cache.
6419 */
6420 while (undo_image != (Image *) NULL)
6421 {
6422 cache_image=undo_image;
6423 undo_image=GetPreviousImageInList(undo_image);
6424 cache_image->list=DestroyImage(cache_image->list);
6425 cache_image=DestroyImage(cache_image);
6426 }
6427 undo_image=NewImageList();
6428 if (redo_image != (Image *) NULL)
6429 redo_image=DestroyImage(redo_image);
6430 redo_image=NewImageList();
6431 return;
6432 }
6433 case UndoCommand:
6434 {
6435 char
6436 image_geometry[MagickPathExtent];
6437
6438 /*
6439 Undo the last image transformation.
6440 */
6441 if (undo_image == (Image *) NULL)
6442 {
6443 (void) XBell(display,0);
6444 return;
6445 }
6446 cache_image=undo_image;
6447 undo_image=GetPreviousImageInList(undo_image);
6448 windows->image.window_changes.width=(int) cache_image->columns;
6449 windows->image.window_changes.height=(int) cache_image->rows;
6450 (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
6451 windows->image.ximage->width,windows->image.ximage->height);
6452 (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6453 exception);
6454 if (windows->image.crop_geometry != (char *) NULL)
6455 windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6456 windows->image.crop_geometry);
6457 windows->image.crop_geometry=cache_image->geometry;
6458 if (redo_image != (Image *) NULL)
6459 redo_image=DestroyImage(redo_image);
6460 redo_image=(*image);
6461 *image=cache_image->list;
6462 cache_image=DestroyImage(cache_image);
6463 if (windows->image.orphan != MagickFalse)
6464 return;
6465 XConfigureImageColormap(display,resource_info,windows,*image,exception);
6466 (void) XConfigureImage(display,resource_info,windows,*image,exception);
6467 return;
6468 }
6469 case CutCommand:
6470 case PasteCommand:
6471 case ApplyCommand:
6472 case HalfSizeCommand:
6473 case OriginalSizeCommand:
6474 case DoubleSizeCommand:
6475 case ResizeCommand:
6476 case TrimCommand:
6477 case CropCommand:
6478 case ChopCommand:
6479 case FlipCommand:
6480 case FlopCommand:
6481 case RotateRightCommand:
6482 case RotateLeftCommand:
6483 case RotateCommand:
6484 case ShearCommand:
6485 case RollCommand:
6486 case NegateCommand:
6487 case ContrastStretchCommand:
6488 case SigmoidalContrastCommand:
6489 case NormalizeCommand:
6490 case EqualizeCommand:
6491 case HueCommand:
6492 case SaturationCommand:
6493 case BrightnessCommand:
6494 case GammaCommand:
6495 case SpiffCommand:
6496 case DullCommand:
6497 case GrayscaleCommand:
6498 case MapCommand:
6499 case QuantizeCommand:
6500 case DespeckleCommand:
6501 case EmbossCommand:
6502 case ReduceNoiseCommand:
6503 case AddNoiseCommand:
6504 case SharpenCommand:
6505 case BlurCommand:
6506 case ThresholdCommand:
6507 case EdgeDetectCommand:
6508 case SpreadCommand:
6509 case ShadeCommand:
6510 case RaiseCommand:
6511 case SegmentCommand:
6512 case SolarizeCommand:
6513 case SepiaToneCommand:
6514 case SwirlCommand:
6515 case ImplodeCommand:
6516 case VignetteCommand:
6517 case WaveCommand:
6518 case OilPaintCommand:
6519 case CharcoalDrawCommand:
6520 case AnnotateCommand:
6521 case AddBorderCommand:
6522 case AddFrameCommand:
6523 case CompositeCommand:
6524 case CommentCommand:
6525 case LaunchCommand:
6526 case RegionOfInterestCommand:
6527 case SaveToUndoBufferCommand:
6528 case RedoCommand:
6529 {
6530 Image
6531 *previous_image;
6532
6533 size_t
6534 bytes;
6535
6536 bytes=(*image)->columns*(*image)->rows*sizeof(PixelInfo);
6537 if (undo_image != (Image *) NULL)
6538 {
6539 /*
6540 Ensure the undo cache has enough memory available.
6541 */
6542 previous_image=undo_image;
6543 while (previous_image != (Image *) NULL)
6544 {
6545 bytes+=previous_image->list->columns*previous_image->list->rows*
6546 sizeof(PixelInfo);
6547 if (bytes <= (resource_info->undo_cache << 20))
6548 {
6549 previous_image=GetPreviousImageInList(previous_image);
6550 continue;
6551 }
6552 bytes-=previous_image->list->columns*previous_image->list->rows*
6553 sizeof(PixelInfo);
6554 if (previous_image == undo_image)
6555 undo_image=NewImageList();
6556 else
6557 previous_image->next->previous=NewImageList();
6558 break;
6559 }
6560 while (previous_image != (Image *) NULL)
6561 {
6562 /*
6563 Delete any excess memory from undo cache.
6564 */
6565 cache_image=previous_image;
6566 previous_image=GetPreviousImageInList(previous_image);
6567 cache_image->list=DestroyImage(cache_image->list);
6568 cache_image=DestroyImage(cache_image);
6569 }
6570 }
6571 if (bytes > (resource_info->undo_cache << 20))
6572 break;
6573 /*
6574 Save image before transformations are applied.
6575 */
6576 cache_image=AcquireImage((ImageInfo *) NULL,exception);
6577 if (cache_image == (Image *) NULL)
6578 break;
6579 XSetCursorState(display,windows,MagickTrue);
6580 XCheckRefreshWindows(display,windows);
6581 cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6582 XSetCursorState(display,windows,MagickFalse);
6583 if (cache_image->list == (Image *) NULL)
6584 {
6585 cache_image=DestroyImage(cache_image);
6586 break;
6587 }
6588 cache_image->columns=(size_t) windows->image.ximage->width;
6589 cache_image->rows=(size_t) windows->image.ximage->height;
6590 cache_image->geometry=windows->image.crop_geometry;
6591 if (windows->image.crop_geometry != (char *) NULL)
6592 {
6593 cache_image->geometry=AcquireString((char *) NULL);
6594 (void) CopyMagickString(cache_image->geometry,
6595 windows->image.crop_geometry,MagickPathExtent);
6596 }
6597 if (undo_image == (Image *) NULL)
6598 {
6599 undo_image=cache_image;
6600 break;
6601 }
6602 undo_image->next=cache_image;
6603 undo_image->next->previous=undo_image;
6604 undo_image=undo_image->next;
6605 break;
6606 }
6607 default:
6608 break;
6609 }
6610 if (command == RedoCommand)
6611 {
6612 /*
6613 Redo the last image transformation.
6614 */
6615 if (redo_image == (Image *) NULL)
6616 {
6617 (void) XBell(display,0);
6618 return;
6619 }
6620 windows->image.window_changes.width=(int) redo_image->columns;
6621 windows->image.window_changes.height=(int) redo_image->rows;
6622 if (windows->image.crop_geometry != (char *) NULL)
6623 windows->image.crop_geometry=(char *)
6624 RelinquishMagickMemory(windows->image.crop_geometry);
6625 windows->image.crop_geometry=redo_image->geometry;
6626 *image=DestroyImage(*image);
6627 *image=redo_image;
6628 redo_image=NewImageList();
6629 if (windows->image.orphan != MagickFalse)
6630 return;
6631 XConfigureImageColormap(display,resource_info,windows,*image,exception);
6632 (void) XConfigureImage(display,resource_info,windows,*image,exception);
6633 return;
6634 }
6635 if (command != InfoCommand)
6636 return;
6637 /*
6638 Display image info.
6639 */
6640 XSetCursorState(display,windows,MagickTrue);
6641 XCheckRefreshWindows(display,windows);
6642 XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6643 XSetCursorState(display,windows,MagickFalse);
6644}
6645
6646/*
6647%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6648% %
6649% %
6650% %
6651+ X I m a g e W i n d o w C o m m a n d %
6652% %
6653% %
6654% %
6655%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6656%
6657% XImageWindowCommand() makes a transform to the image or Image window as
6658% specified by a user menu button or keyboard command.
6659%
6660% The format of the XImageWindowCommand method is:
6661%
6662% DisplayCommand XImageWindowCommand(Display *display,
6663% XResourceInfo *resource_info,XWindows *windows,
6664% const MagickStatusType state,KeySym key_symbol,Image **image,
6665% ExceptionInfo *exception)
6666%
6667% A description of each parameter follows:
6668%
6669% o nexus: Method XImageWindowCommand returns an image when the
6670% user chooses 'Open Image' from the command menu. Otherwise a null
6671% image is returned.
6672%
6673% o display: Specifies a connection to an X server; returned from
6674% XOpenDisplay.
6675%
6676% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6677%
6678% o windows: Specifies a pointer to a XWindows structure.
6679%
6680% o state: key mask.
6681%
6682% o key_symbol: Specifies a command to perform.
6683%
6684% o image: the image; XImageWIndowCommand may transform the image and
6685% return a new image pointer.
6686%
6687% o exception: return any errors or warnings in this structure.
6688%
6689*/
6690static DisplayCommand XImageWindowCommand(Display *display,
6691 XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6692 KeySym key_symbol,Image **image,ExceptionInfo *exception)
6693{
6694 static char
6695 delta[MagickPathExtent] = "";
6696
6697 static const char
6698 Digits[] = "01234567890";
6699
6700 static KeySym
6701 last_symbol = XK_0;
6702
6703 if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6704 {
6705 if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6706 {
6707 *delta='\0';
6708 resource_info->quantum=1;
6709 }
6710 last_symbol=key_symbol;
6711 delta[strlen(delta)+1]='\0';
6712 delta[strlen(delta)]=Digits[key_symbol-XK_0];
6713 resource_info->quantum=StringToLong(delta);
6714 return(NullCommand);
6715 }
6716 last_symbol=key_symbol;
6717 if (resource_info->immutable)
6718 {
6719 /*
6720 Virtual image window has a restricted command set.
6721 */
6722 switch (key_symbol)
6723 {
6724 case XK_question:
6725 return(InfoCommand);
6726 case XK_p:
6727 case XK_Print:
6728 return(PrintCommand);
6729 case XK_space:
6730 return(NextCommand);
6731 case XK_q:
6732 case XK_Escape:
6733 return(QuitCommand);
6734 default:
6735 break;
6736 }
6737 return(NullCommand);
6738 }
6739 switch ((int) key_symbol)
6740 {
6741 case XK_o:
6742 {
6743 if ((state & ControlMask) == 0)
6744 break;
6745 return(OpenCommand);
6746 }
6747 case XK_space:
6748 return(NextCommand);
6749 case XK_BackSpace:
6750 return(FormerCommand);
6751 case XK_s:
6752 {
6753 if ((state & Mod1Mask) != 0)
6754 return(SwirlCommand);
6755 if ((state & ControlMask) == 0)
6756 return(ShearCommand);
6757 return(SaveCommand);
6758 }
6759 case XK_p:
6760 case XK_Print:
6761 {
6762 if ((state & Mod1Mask) != 0)
6763 return(OilPaintCommand);
6764 if ((state & Mod4Mask) != 0)
6765 return(ColorCommand);
6766 if ((state & ControlMask) == 0)
6767 return(NullCommand);
6768 return(PrintCommand);
6769 }
6770 case XK_d:
6771 {
6772 if ((state & Mod4Mask) != 0)
6773 return(DrawCommand);
6774 if ((state & ControlMask) == 0)
6775 return(NullCommand);
6776 return(DeleteCommand);
6777 }
6778 case XK_Select:
6779 {
6780 if ((state & ControlMask) == 0)
6781 return(NullCommand);
6782 return(SelectCommand);
6783 }
6784 case XK_n:
6785 {
6786 if ((state & ControlMask) == 0)
6787 return(NullCommand);
6788 return(NewCommand);
6789 }
6790 case XK_q:
6791 case XK_Escape:
6792 return(QuitCommand);
6793 case XK_z:
6794 case XK_Undo:
6795 {
6796 if ((state & ControlMask) == 0)
6797 return(NullCommand);
6798 return(UndoCommand);
6799 }
6800 case XK_r:
6801 case XK_Redo:
6802 {
6803 if ((state & ControlMask) == 0)
6804 return(RollCommand);
6805 return(RedoCommand);
6806 }
6807 case XK_x:
6808 {
6809 if ((state & ControlMask) == 0)
6810 return(NullCommand);
6811 return(CutCommand);
6812 }
6813 case XK_c:
6814 {
6815 if ((state & Mod1Mask) != 0)
6816 return(CharcoalDrawCommand);
6817 if ((state & ControlMask) == 0)
6818 return(CropCommand);
6819 return(CopyCommand);
6820 }
6821 case XK_v:
6822 case XK_Insert:
6823 {
6824 if ((state & Mod4Mask) != 0)
6825 return(CompositeCommand);
6826 if ((state & ControlMask) == 0)
6827 return(FlipCommand);
6828 return(PasteCommand);
6829 }
6830 case XK_less:
6831 return(HalfSizeCommand);
6832 case XK_minus:
6833 return(OriginalSizeCommand);
6834 case XK_greater:
6835 return(DoubleSizeCommand);
6836 case XK_percent:
6837 return(ResizeCommand);
6838 case XK_at:
6839 return(RefreshCommand);
6840 case XK_bracketleft:
6841 return(ChopCommand);
6842 case XK_h:
6843 return(FlopCommand);
6844 case XK_slash:
6845 return(RotateRightCommand);
6846 case XK_backslash:
6847 return(RotateLeftCommand);
6848 case XK_asterisk:
6849 return(RotateCommand);
6850 case XK_t:
6851 return(TrimCommand);
6852 case XK_H:
6853 return(HueCommand);
6854 case XK_S:
6855 return(SaturationCommand);
6856 case XK_L:
6857 return(BrightnessCommand);
6858 case XK_G:
6859 return(GammaCommand);
6860 case XK_C:
6861 return(SpiffCommand);
6862 case XK_Z:
6863 return(DullCommand);
6864 case XK_N:
6865 return(NormalizeCommand);
6866 case XK_equal:
6867 return(EqualizeCommand);
6868 case XK_asciitilde:
6869 return(NegateCommand);
6870 case XK_period:
6871 return(GrayscaleCommand);
6872 case XK_numbersign:
6873 return(QuantizeCommand);
6874 case XK_F2:
6875 return(DespeckleCommand);
6876 case XK_F3:
6877 return(EmbossCommand);
6878 case XK_F4:
6879 return(ReduceNoiseCommand);
6880 case XK_F5:
6881 return(AddNoiseCommand);
6882 case XK_F6:
6883 return(SharpenCommand);
6884 case XK_F7:
6885 return(BlurCommand);
6886 case XK_F8:
6887 return(ThresholdCommand);
6888 case XK_F9:
6889 return(EdgeDetectCommand);
6890 case XK_F10:
6891 return(SpreadCommand);
6892 case XK_F11:
6893 return(ShadeCommand);
6894 case XK_F12:
6895 return(RaiseCommand);
6896 case XK_F13:
6897 return(SegmentCommand);
6898 case XK_i:
6899 {
6900 if ((state & Mod1Mask) == 0)
6901 return(NullCommand);
6902 return(ImplodeCommand);
6903 }
6904 case XK_w:
6905 {
6906 if ((state & Mod1Mask) == 0)
6907 return(NullCommand);
6908 return(WaveCommand);
6909 }
6910 case XK_m:
6911 {
6912 if ((state & Mod4Mask) == 0)
6913 return(NullCommand);
6914 return(MatteCommand);
6915 }
6916 case XK_b:
6917 {
6918 if ((state & Mod4Mask) == 0)
6919 return(NullCommand);
6920 return(AddBorderCommand);
6921 }
6922 case XK_f:
6923 {
6924 if ((state & Mod4Mask) == 0)
6925 return(NullCommand);
6926 return(AddFrameCommand);
6927 }
6928 case XK_exclam:
6929 {
6930 if ((state & Mod4Mask) == 0)
6931 return(NullCommand);
6932 return(CommentCommand);
6933 }
6934 case XK_a:
6935 {
6936 if ((state & Mod1Mask) != 0)
6937 return(ApplyCommand);
6938 if ((state & Mod4Mask) != 0)
6939 return(AnnotateCommand);
6940 if ((state & ControlMask) == 0)
6941 return(NullCommand);
6942 return(RegionOfInterestCommand);
6943 }
6944 case XK_question:
6945 return(InfoCommand);
6946 case XK_plus:
6947 return(ZoomCommand);
6948 case XK_P:
6949 {
6950 if ((state & ShiftMask) == 0)
6951 return(NullCommand);
6952 return(ShowPreviewCommand);
6953 }
6954 case XK_Execute:
6955 return(LaunchCommand);
6956 case XK_F1:
6957 return(HelpCommand);
6958 case XK_Find:
6959 return(BrowseDocumentationCommand);
6960 case XK_Menu:
6961 {
6962 (void) XMapRaised(display,windows->command.id);
6963 return(NullCommand);
6964 }
6965 case XK_Next:
6966 case XK_Prior:
6967 case XK_Home:
6968 case XK_KP_Home:
6969 {
6970 XTranslateImage(display,windows,*image,key_symbol);
6971 return(NullCommand);
6972 }
6973 case XK_Up:
6974 case XK_KP_Up:
6975 case XK_Down:
6976 case XK_KP_Down:
6977 case XK_Left:
6978 case XK_KP_Left:
6979 case XK_Right:
6980 case XK_KP_Right:
6981 {
6982 if ((state & Mod1Mask) != 0)
6983 {
6985 crop_info;
6986
6987 /*
6988 Trim one pixel from edge of image.
6989 */
6990 crop_info.x=0;
6991 crop_info.y=0;
6992 crop_info.width=(size_t) windows->image.ximage->width;
6993 crop_info.height=(size_t) windows->image.ximage->height;
6994 if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
6995 {
6996 if (resource_info->quantum >= (int) crop_info.height)
6997 resource_info->quantum=(int) crop_info.height-1;
6998 crop_info.height-=(size_t) resource_info->quantum;
6999 }
7000 if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7001 {
7002 if (resource_info->quantum >= ((int) crop_info.height-crop_info.y))
7003 resource_info->quantum=(int) crop_info.height-crop_info.y-1;
7004 crop_info.y+=resource_info->quantum;
7005 crop_info.height-=(size_t) resource_info->quantum;
7006 }
7007 if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7008 {
7009 if (resource_info->quantum >= (int) crop_info.width)
7010 resource_info->quantum=(int) crop_info.width-1;
7011 crop_info.width-=(size_t) resource_info->quantum;
7012 }
7013 if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7014 {
7015 if (resource_info->quantum >= ((int) crop_info.width-crop_info.x))
7016 resource_info->quantum=(int) crop_info.width-crop_info.x-1;
7017 crop_info.x+=resource_info->quantum;
7018 crop_info.width-=(size_t) resource_info->quantum;
7019 }
7020 if ((windows->image.x+(int) windows->image.width) > (int) crop_info.width)
7021 windows->image.x=(int) (crop_info.width-windows->image.width);
7022 if ((windows->image.y+(int) windows->image.height) > (int) crop_info.height)
7023 windows->image.y=(int) (crop_info.height-windows->image.height);
7024 XSetCropGeometry(display,windows,&crop_info,*image);
7025 windows->image.window_changes.width=(int) crop_info.width;
7026 windows->image.window_changes.height=(int) crop_info.height;
7027 (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7028 (void) XConfigureImage(display,resource_info,windows,*image,
7029 exception);
7030 return(NullCommand);
7031 }
7032 XTranslateImage(display,windows,*image,key_symbol);
7033 return(NullCommand);
7034 }
7035 default:
7036 return(NullCommand);
7037 }
7038 return(NullCommand);
7039}
7040
7041/*
7042%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7043% %
7044% %
7045% %
7046+ X M a g i c k C o m m a n d %
7047% %
7048% %
7049% %
7050%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7051%
7052% XMagickCommand() makes a transform to the image or Image window as
7053% specified by a user menu button or keyboard command.
7054%
7055% The format of the XMagickCommand method is:
7056%
7057% Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7058% XWindows *windows,const DisplayCommand command,Image **image,
7059% ExceptionInfo *exception)
7060%
7061% A description of each parameter follows:
7062%
7063% o display: Specifies a connection to an X server; returned from
7064% XOpenDisplay.
7065%
7066% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7067%
7068% o windows: Specifies a pointer to a XWindows structure.
7069%
7070% o command: Specifies a command to perform.
7071%
7072% o image: the image; XMagickCommand may transform the image and return a
7073% new image pointer.
7074%
7075% o exception: return any errors or warnings in this structure.
7076%
7077*/
7078static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7079 XWindows *windows,const DisplayCommand command,Image **image,
7080 ExceptionInfo *exception)
7081{
7082 char
7083 filename[MagickPathExtent],
7084 geometry[MagickPathExtent],
7085 modulate_factors[MagickPathExtent];
7086
7088 geometry_info;
7089
7090 Image
7091 *nexus;
7092
7093 ImageInfo
7094 *image_info;
7095
7096 int
7097 x,
7098 y;
7099
7100 MagickStatusType
7101 flags,
7102 status;
7103
7105 quantize_info;
7106
7108 page_geometry;
7109
7110 int
7111 i;
7112
7113 static char
7114 color[MagickPathExtent] = "gray";
7115
7116 unsigned int
7117 height,
7118 width;
7119
7120 /*
7121 Process user command.
7122 */
7123 XCheckRefreshWindows(display,windows);
7124 XImageCache(display,resource_info,windows,command,image,exception);
7125 nexus=NewImageList();
7126 windows->image.window_changes.width=windows->image.ximage->width;
7127 windows->image.window_changes.height=windows->image.ximage->height;
7128 image_info=CloneImageInfo(resource_info->image_info);
7129 SetGeometryInfo(&geometry_info);
7130 GetQuantizeInfo(&quantize_info);
7131 switch (command)
7132 {
7133 case OpenCommand:
7134 {
7135 /*
7136 Load image.
7137 */
7138 nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7139 break;
7140 }
7141 case NextCommand:
7142 {
7143 /*
7144 Display next image.
7145 */
7146 for (i=0; i < resource_info->quantum; i++)
7147 XClientMessage(display,windows->image.id,windows->im_protocols,
7148 windows->im_next_image,CurrentTime);
7149 break;
7150 }
7151 case FormerCommand:
7152 {
7153 /*
7154 Display former image.
7155 */
7156 for (i=0; i < resource_info->quantum; i++)
7157 XClientMessage(display,windows->image.id,windows->im_protocols,
7158 windows->im_former_image,CurrentTime);
7159 break;
7160 }
7161 case SelectCommand:
7162 {
7163 int
7164 status;
7165
7166 /*
7167 Select image.
7168 */
7169 if (*resource_info->home_directory == '\0')
7170 (void) CopyMagickString(resource_info->home_directory,".",
7171 MagickPathExtent);
7172 status=chdir(resource_info->home_directory);
7173 if (status == -1)
7174 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7175 "UnableToOpenFile","%s",resource_info->home_directory);
7176 nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7177 break;
7178 }
7179 case SaveCommand:
7180 {
7181 /*
7182 Save image.
7183 */
7184 status=XSaveImage(display,resource_info,windows,*image,exception);
7185 if (status == MagickFalse)
7186 {
7187 char
7188 message[MagickPathExtent];
7189
7190 (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
7191 exception->reason != (char *) NULL ? exception->reason : "",
7192 exception->description != (char *) NULL ? exception->description :
7193 "");
7194 XNoticeWidget(display,windows,"Unable to save file:",message);
7195 break;
7196 }
7197 break;
7198 }
7199 case PrintCommand:
7200 {
7201 /*
7202 Print image.
7203 */
7204 status=XPrintImage(display,resource_info,windows,*image,exception);
7205 if (status == MagickFalse)
7206 {
7207 char
7208 message[MagickPathExtent];
7209
7210 (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
7211 exception->reason != (char *) NULL ? exception->reason : "",
7212 exception->description != (char *) NULL ? exception->description :
7213 "");
7214 XNoticeWidget(display,windows,"Unable to print file:",message);
7215 break;
7216 }
7217 break;
7218 }
7219 case DeleteCommand:
7220 {
7221 static char
7222 filename[MagickPathExtent] = "\0";
7223
7224 /*
7225 Delete image file.
7226 */
7227 XFileBrowserWidget(display,windows,"Delete",filename);
7228 if (*filename == '\0')
7229 break;
7230 status=ShredFile(filename);
7231 if (remove_utf8(filename) < 0)
7232 status=MagickTrue;
7233 if (status != MagickFalse)
7234 XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7235 break;
7236 }
7237 case NewCommand:
7238 {
7239 int
7240 status;
7241
7242 static char
7243 color[MagickPathExtent] = "gray",
7244 geometry[MagickPathExtent] = "640x480";
7245
7246 static const char
7247 *format = "gradient";
7248
7249 /*
7250 Query user for canvas geometry.
7251 */
7252 status=XDialogWidget(display,windows,"New","Enter image geometry:",
7253 geometry);
7254 if (*geometry == '\0')
7255 break;
7256 if (status == 0)
7257 format="xc";
7258 XColorBrowserWidget(display,windows,"Select",color);
7259 if (*color == '\0')
7260 break;
7261 /*
7262 Create canvas.
7263 */
7264 (void) FormatLocaleString(image_info->filename,MagickPathExtent,
7265 "%s:%s",format,color);
7266 (void) CloneString(&image_info->size,geometry);
7267 nexus=ReadImage(image_info,exception);
7268 CatchException(exception);
7269 XClientMessage(display,windows->image.id,windows->im_protocols,
7270 windows->im_next_image,CurrentTime);
7271 break;
7272 }
7273 case VisualDirectoryCommand:
7274 {
7275 /*
7276 Visual Image directory.
7277 */
7278 nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7279 break;
7280 }
7281 case QuitCommand:
7282 {
7283 /*
7284 exit program.
7285 */
7286 if (resource_info->confirm_exit == MagickFalse)
7287 XClientMessage(display,windows->image.id,windows->im_protocols,
7288 windows->im_exit,CurrentTime);
7289 else
7290 {
7291 int
7292 status;
7293
7294 /*
7295 Confirm program exit.
7296 */
7297 status=XConfirmWidget(display,windows,"Do you really want to exit",
7298 resource_info->client_name);
7299 if (status > 0)
7300 XClientMessage(display,windows->image.id,windows->im_protocols,
7301 windows->im_exit,CurrentTime);
7302 }
7303 break;
7304 }
7305 case CutCommand:
7306 {
7307 /*
7308 Cut image.
7309 */
7310 (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7311 break;
7312 }
7313 case CopyCommand:
7314 {
7315 /*
7316 Copy image.
7317 */
7318 (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7319 exception);
7320 break;
7321 }
7322 case PasteCommand:
7323 {
7324 /*
7325 Paste image.
7326 */
7327 status=XPasteImage(display,resource_info,windows,*image,exception);
7328 if (status == MagickFalse)
7329 {
7330 XNoticeWidget(display,windows,"Unable to paste X image",
7331 (*image)->filename);
7332 break;
7333 }
7334 break;
7335 }
7336 case HalfSizeCommand:
7337 {
7338 /*
7339 Half image size.
7340 */
7341 windows->image.window_changes.width=windows->image.ximage->width/2;
7342 windows->image.window_changes.height=windows->image.ximage->height/2;
7343 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7344 break;
7345 }
7346 case OriginalSizeCommand:
7347 {
7348 /*
7349 Original image size.
7350 */
7351 windows->image.window_changes.width=(int) (*image)->columns;
7352 windows->image.window_changes.height=(int) (*image)->rows;
7353 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7354 break;
7355 }
7356 case DoubleSizeCommand:
7357 {
7358 /*
7359 Double the image size.
7360 */
7361 windows->image.window_changes.width=windows->image.ximage->width << 1;
7362 windows->image.window_changes.height=windows->image.ximage->height << 1;
7363 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7364 break;
7365 }
7366 case ResizeCommand:
7367 {
7368 int
7369 status;
7370
7371 size_t
7372 height,
7373 width;
7374
7375 ssize_t
7376 x,
7377 y;
7378
7379 /*
7380 Resize image.
7381 */
7382 width=(size_t) windows->image.ximage->width;
7383 height=(size_t) windows->image.ximage->height;
7384 x=0;
7385 y=0;
7386 (void) FormatLocaleString(geometry,MagickPathExtent,"%.20gx%.20g+0+0",
7387 (double) width,(double) height);
7388 status=XDialogWidget(display,windows,"Resize",
7389 "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7390 if (*geometry == '\0')
7391 break;
7392 if (status == 0)
7393 (void) ConcatenateMagickString(geometry,"!",MagickPathExtent);
7394 (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7395 windows->image.window_changes.width=(int) width;
7396 windows->image.window_changes.height=(int) height;
7397 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7398 break;
7399 }
7400 case ApplyCommand:
7401 {
7402 char
7403 image_geometry[MagickPathExtent];
7404
7405 if ((windows->image.crop_geometry == (char *) NULL) &&
7406 ((int) (*image)->columns == windows->image.ximage->width) &&
7407 ((int) (*image)->rows == windows->image.ximage->height))
7408 break;
7409 /*
7410 Apply size transforms to image.
7411 */
7412 XSetCursorState(display,windows,MagickTrue);
7413 XCheckRefreshWindows(display,windows);
7414 /*
7415 Crop and/or scale displayed image.
7416 */
7417 (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
7418 windows->image.ximage->width,windows->image.ximage->height);
7419 (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7420 exception);
7421 if (windows->image.crop_geometry != (char *) NULL)
7422 windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7423 windows->image.crop_geometry);
7424 windows->image.x=0;
7425 windows->image.y=0;
7426 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7427 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7428 break;
7429 }
7430 case RefreshCommand:
7431 {
7432 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7433 break;
7434 }
7435 case RestoreCommand:
7436 {
7437 /*
7438 Restore Image window to its original size.
7439 */
7440 if ((windows->image.width == (unsigned int) (*image)->columns) &&
7441 (windows->image.height == (unsigned int) (*image)->rows) &&
7442 (windows->image.crop_geometry == (char *) NULL))
7443 {
7444 (void) XBell(display,0);
7445 break;
7446 }
7447 windows->image.window_changes.width=(int) (*image)->columns;
7448 windows->image.window_changes.height=(int) (*image)->rows;
7449 if (windows->image.crop_geometry != (char *) NULL)
7450 {
7451 windows->image.crop_geometry=(char *)
7452 RelinquishMagickMemory(windows->image.crop_geometry);
7453 windows->image.crop_geometry=(char *) NULL;
7454 windows->image.x=0;
7455 windows->image.y=0;
7456 }
7457 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7458 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7459 break;
7460 }
7461 case CropCommand:
7462 {
7463 /*
7464 Crop image.
7465 */
7466 (void) XCropImage(display,resource_info,windows,*image,CropMode,
7467 exception);
7468 break;
7469 }
7470 case ChopCommand:
7471 {
7472 /*
7473 Chop image.
7474 */
7475 status=XChopImage(display,resource_info,windows,image,exception);
7476 if (status == MagickFalse)
7477 {
7478 XNoticeWidget(display,windows,"Unable to cut X image",
7479 (*image)->filename);
7480 break;
7481 }
7482 break;
7483 }
7484 case FlopCommand:
7485 {
7486 Image
7487 *flop_image;
7488
7489 /*
7490 Flop image scanlines.
7491 */
7492 XSetCursorState(display,windows,MagickTrue);
7493 XCheckRefreshWindows(display,windows);
7494 flop_image=FlopImage(*image,exception);
7495 if (flop_image != (Image *) NULL)
7496 {
7497 *image=DestroyImage(*image);
7498 *image=flop_image;
7499 }
7500 CatchException(exception);
7501 XSetCursorState(display,windows,MagickFalse);
7502 if (windows->image.crop_geometry != (char *) NULL)
7503 {
7504 /*
7505 Flop crop geometry.
7506 */
7507 width=(unsigned int) (*image)->columns;
7508 height=(unsigned int) (*image)->rows;
7509 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7510 &width,&height);
7511 (void) FormatLocaleString(windows->image.crop_geometry,
7512 MagickPathExtent,"%ux%u%+d%+d",width,height,(int) (*image)->columns-
7513 (int) width-x,y);
7514 }
7515 if (windows->image.orphan != MagickFalse)
7516 break;
7517 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7518 break;
7519 }
7520 case FlipCommand:
7521 {
7522 Image
7523 *flip_image;
7524
7525 /*
7526 Flip image scanlines.
7527 */
7528 XSetCursorState(display,windows,MagickTrue);
7529 XCheckRefreshWindows(display,windows);
7530 flip_image=FlipImage(*image,exception);
7531 if (flip_image != (Image *) NULL)
7532 {
7533 *image=DestroyImage(*image);
7534 *image=flip_image;
7535 }
7536 CatchException(exception);
7537 XSetCursorState(display,windows,MagickFalse);
7538 if (windows->image.crop_geometry != (char *) NULL)
7539 {
7540 /*
7541 Flip crop geometry.
7542 */
7543 width=(unsigned int) (*image)->columns;
7544 height=(unsigned int) (*image)->rows;
7545 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7546 &width,&height);
7547 (void) FormatLocaleString(windows->image.crop_geometry,
7548 MagickPathExtent,"%ux%u%+d%+d",width,height,x,(int) (*image)->rows-
7549 (int) height-y);
7550 }
7551 if (windows->image.orphan != MagickFalse)
7552 break;
7553 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7554 break;
7555 }
7556 case RotateRightCommand:
7557 {
7558 /*
7559 Rotate image 90 degrees clockwise.
7560 */
7561 status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7562 if (status == MagickFalse)
7563 {
7564 XNoticeWidget(display,windows,"Unable to rotate X image",
7565 (*image)->filename);
7566 break;
7567 }
7568 break;
7569 }
7570 case RotateLeftCommand:
7571 {
7572 /*
7573 Rotate image 90 degrees counter-clockwise.
7574 */
7575 status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7576 if (status == MagickFalse)
7577 {
7578 XNoticeWidget(display,windows,"Unable to rotate X image",
7579 (*image)->filename);
7580 break;
7581 }
7582 break;
7583 }
7584 case RotateCommand:
7585 {
7586 /*
7587 Rotate image.
7588 */
7589 status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7590 if (status == MagickFalse)
7591 {
7592 XNoticeWidget(display,windows,"Unable to rotate X image",
7593 (*image)->filename);
7594 break;
7595 }
7596 break;
7597 }
7598 case ShearCommand:
7599 {
7600 Image
7601 *shear_image;
7602
7603 static char
7604 geometry[MagickPathExtent] = "45.0x45.0";
7605
7606 /*
7607 Query user for shear color and geometry.
7608 */
7609 XColorBrowserWidget(display,windows,"Select",color);
7610 if (*color == '\0')
7611 break;
7612 (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7613 geometry);
7614 if (*geometry == '\0')
7615 break;
7616 /*
7617 Shear image.
7618 */
7619 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7620 exception);
7621 XSetCursorState(display,windows,MagickTrue);
7622 XCheckRefreshWindows(display,windows);
7623 (void) QueryColorCompliance(color,AllCompliance,
7624 &(*image)->background_color,exception);
7625 flags=ParseGeometry(geometry,&geometry_info);
7626 if ((flags & SigmaValue) == 0)
7627 geometry_info.sigma=geometry_info.rho;
7628 shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7629 exception);
7630 if (shear_image != (Image *) NULL)
7631 {
7632 *image=DestroyImage(*image);
7633 *image=shear_image;
7634 }
7635 CatchException(exception);
7636 XSetCursorState(display,windows,MagickFalse);
7637 if (windows->image.orphan != MagickFalse)
7638 break;
7639 windows->image.window_changes.width=(int) (*image)->columns;
7640 windows->image.window_changes.height=(int) (*image)->rows;
7641 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7642 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7643 break;
7644 }
7645 case RollCommand:
7646 {
7647 Image
7648 *roll_image;
7649
7650 static char
7651 geometry[MagickPathExtent] = "+2+2";
7652
7653 /*
7654 Query user for the roll geometry.
7655 */
7656 (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7657 geometry);
7658 if (*geometry == '\0')
7659 break;
7660 /*
7661 Roll image.
7662 */
7663 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7664 exception);
7665 XSetCursorState(display,windows,MagickTrue);
7666 XCheckRefreshWindows(display,windows);
7667 (void) ParsePageGeometry(*image,geometry,&page_geometry,
7668 exception);
7669 roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7670 exception);
7671 if (roll_image != (Image *) NULL)
7672 {
7673 *image=DestroyImage(*image);
7674 *image=roll_image;
7675 }
7676 CatchException(exception);
7677 XSetCursorState(display,windows,MagickFalse);
7678 if (windows->image.orphan != MagickFalse)
7679 break;
7680 windows->image.window_changes.width=(int) (*image)->columns;
7681 windows->image.window_changes.height=(int) (*image)->rows;
7682 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7683 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7684 break;
7685 }
7686 case TrimCommand:
7687 {
7688 static char
7689 fuzz[MagickPathExtent];
7690
7691 /*
7692 Query user for the fuzz factor.
7693 */
7694 (void) FormatLocaleString(fuzz,MagickPathExtent,"%g%%",100.0*
7695 (*image)->fuzz/((double) QuantumRange+1.0));
7696 (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7697 if (*fuzz == '\0')
7698 break;
7699 (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7700 /*
7701 Trim image.
7702 */
7703 status=XTrimImage(display,resource_info,windows,*image,exception);
7704 if (status == MagickFalse)
7705 {
7706 XNoticeWidget(display,windows,"Unable to trim X image",
7707 (*image)->filename);
7708 break;
7709 }
7710 break;
7711 }
7712 case HueCommand:
7713 {
7714 static char
7715 hue_percent[MagickPathExtent] = "110";
7716
7717 /*
7718 Query user for percent hue change.
7719 */
7720 (void) XDialogWidget(display,windows,"Apply",
7721 "Enter percent change in image hue (0-200):",hue_percent);
7722 if (*hue_percent == '\0')
7723 break;
7724 /*
7725 Vary the image hue.
7726 */
7727 XSetCursorState(display,windows,MagickTrue);
7728 XCheckRefreshWindows(display,windows);
7729 (void) CopyMagickString(modulate_factors,"100.0/100.0/",MagickPathExtent);
7730 (void) ConcatenateMagickString(modulate_factors,hue_percent,
7731 MagickPathExtent);
7732 (void) ModulateImage(*image,modulate_factors,exception);
7733 XSetCursorState(display,windows,MagickFalse);
7734 if (windows->image.orphan != MagickFalse)
7735 break;
7736 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7737 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7738 break;
7739 }
7740 case SaturationCommand:
7741 {
7742 static char
7743 saturation_percent[MagickPathExtent] = "110";
7744
7745 /*
7746 Query user for percent saturation change.
7747 */
7748 (void) XDialogWidget(display,windows,"Apply",
7749 "Enter percent change in color saturation (0-200):",saturation_percent);
7750 if (*saturation_percent == '\0')
7751 break;
7752 /*
7753 Vary color saturation.
7754 */
7755 XSetCursorState(display,windows,MagickTrue);
7756 XCheckRefreshWindows(display,windows);
7757 (void) CopyMagickString(modulate_factors,"100.0/",MagickPathExtent);
7758 (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7759 MagickPathExtent);
7760 (void) ModulateImage(*image,modulate_factors,exception);
7761 XSetCursorState(display,windows,MagickFalse);
7762 if (windows->image.orphan != MagickFalse)
7763 break;
7764 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7765 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7766 break;
7767 }
7768 case BrightnessCommand:
7769 {
7770 static char
7771 brightness_percent[MagickPathExtent] = "110";
7772
7773 /*
7774 Query user for percent brightness change.
7775 */
7776 (void) XDialogWidget(display,windows,"Apply",
7777 "Enter percent change in color brightness (0-200):",brightness_percent);
7778 if (*brightness_percent == '\0')
7779 break;
7780 /*
7781 Vary the color brightness.
7782 */
7783 XSetCursorState(display,windows,MagickTrue);
7784 XCheckRefreshWindows(display,windows);
7785 (void) CopyMagickString(modulate_factors,brightness_percent,
7786 MagickPathExtent);
7787 (void) ModulateImage(*image,modulate_factors,exception);
7788 XSetCursorState(display,windows,MagickFalse);
7789 if (windows->image.orphan != MagickFalse)
7790 break;
7791 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7792 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7793 break;
7794 }
7795 case GammaCommand:
7796 {
7797 static char
7798 factor[MagickPathExtent] = "1.6";
7799
7800 /*
7801 Query user for gamma value.
7802 */
7803 (void) XDialogWidget(display,windows,"Gamma",
7804 "Enter gamma value (e.g. 1.2):",factor);
7805 if (*factor == '\0')
7806 break;
7807 /*
7808 Gamma correct image.
7809 */
7810 XSetCursorState(display,windows,MagickTrue);
7811 XCheckRefreshWindows(display,windows);
7812 (void) GammaImage(*image,strtod(factor,(char **) NULL),exception);
7813 XSetCursorState(display,windows,MagickFalse);
7814 if (windows->image.orphan != MagickFalse)
7815 break;
7816 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7817 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7818 break;
7819 }
7820 case SpiffCommand:
7821 {
7822 /*
7823 Sharpen the image contrast.
7824 */
7825 XSetCursorState(display,windows,MagickTrue);
7826 XCheckRefreshWindows(display,windows);
7827 (void) ContrastImage(*image,MagickTrue,exception);
7828 XSetCursorState(display,windows,MagickFalse);
7829 if (windows->image.orphan != MagickFalse)
7830 break;
7831 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7832 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7833 break;
7834 }
7835 case DullCommand:
7836 {
7837 /*
7838 Dull the image contrast.
7839 */
7840 XSetCursorState(display,windows,MagickTrue);
7841 XCheckRefreshWindows(display,windows);
7842 (void) ContrastImage(*image,MagickFalse,exception);
7843 XSetCursorState(display,windows,MagickFalse);
7844 if (windows->image.orphan != MagickFalse)
7845 break;
7846 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7847 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7848 break;
7849 }
7850 case ContrastStretchCommand:
7851 {
7852 double
7853 black_point,
7854 white_point;
7855
7856 static char
7857 levels[MagickPathExtent] = "1%";
7858
7859 /*
7860 Query user for gamma value.
7861 */
7862 (void) XDialogWidget(display,windows,"Contrast Stretch",
7863 "Enter black and white points:",levels);
7864 if (*levels == '\0')
7865 break;
7866 /*
7867 Contrast stretch image.
7868 */
7869 XSetCursorState(display,windows,MagickTrue);
7870 XCheckRefreshWindows(display,windows);
7871 flags=ParseGeometry(levels,&geometry_info);
7872 black_point=geometry_info.rho;
7873 white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7874 if ((flags & PercentValue) != 0)
7875 {
7876 black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7877 white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7878 }
7879 white_point=(double) (*image)->columns*(*image)->rows-white_point;
7880 (void) ContrastStretchImage(*image,black_point,white_point,
7881 exception);
7882 XSetCursorState(display,windows,MagickFalse);
7883 if (windows->image.orphan != MagickFalse)
7884 break;
7885 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7886 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7887 break;
7888 }
7889 case SigmoidalContrastCommand:
7890 {
7892 geometry_info;
7893
7894 MagickStatusType
7895 flags;
7896
7897 static char
7898 levels[MagickPathExtent] = "3x50%";
7899
7900 /*
7901 Query user for gamma value.
7902 */
7903 (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7904 "Enter contrast and midpoint:",levels);
7905 if (*levels == '\0')
7906 break;
7907 /*
7908 Contrast stretch image.
7909 */
7910 XSetCursorState(display,windows,MagickTrue);
7911 XCheckRefreshWindows(display,windows);
7912 flags=ParseGeometry(levels,&geometry_info);
7913 if ((flags & SigmaValue) == 0)
7914 geometry_info.sigma=1.0*(double) QuantumRange/2.0;
7915 if ((flags & PercentValue) != 0)
7916 geometry_info.sigma=1.0*(double) QuantumRange*geometry_info.sigma/100.0;
7917 (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7918 geometry_info.sigma,exception);
7919 XSetCursorState(display,windows,MagickFalse);
7920 if (windows->image.orphan != MagickFalse)
7921 break;
7922 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7923 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7924 break;
7925 }
7926 case NormalizeCommand:
7927 {
7928 /*
7929 Perform histogram normalization on the image.
7930 */
7931 XSetCursorState(display,windows,MagickTrue);
7932 XCheckRefreshWindows(display,windows);
7933 (void) NormalizeImage(*image,exception);
7934 XSetCursorState(display,windows,MagickFalse);
7935 if (windows->image.orphan != MagickFalse)
7936 break;
7937 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7938 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7939 break;
7940 }
7941 case EqualizeCommand:
7942 {
7943 /*
7944 Perform histogram equalization on the image.
7945 */
7946 XSetCursorState(display,windows,MagickTrue);
7947 XCheckRefreshWindows(display,windows);
7948 (void) EqualizeImage(*image,exception);
7949 XSetCursorState(display,windows,MagickFalse);
7950 if (windows->image.orphan != MagickFalse)
7951 break;
7952 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7953 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7954 break;
7955 }
7956 case NegateCommand:
7957 {
7958 /*
7959 Negate colors in image.
7960 */
7961 XSetCursorState(display,windows,MagickTrue);
7962 XCheckRefreshWindows(display,windows);
7963 (void) NegateImage(*image,MagickFalse,exception);
7964 XSetCursorState(display,windows,MagickFalse);
7965 if (windows->image.orphan != MagickFalse)
7966 break;
7967 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7968 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7969 break;
7970 }
7971 case GrayscaleCommand:
7972 {
7973 /*
7974 Convert image to grayscale.
7975 */
7976 XSetCursorState(display,windows,MagickTrue);
7977 XCheckRefreshWindows(display,windows);
7978 (void) SetImageType(*image,(*image)->alpha_trait == UndefinedPixelTrait ?
7979 GrayscaleType : GrayscaleAlphaType,exception);
7980 XSetCursorState(display,windows,MagickFalse);
7981 if (windows->image.orphan != MagickFalse)
7982 break;
7983 XConfigureImageColormap(display,resource_info,windows,*image,exception);
7984 (void) XConfigureImage(display,resource_info,windows,*image,exception);
7985 break;
7986 }
7987 case MapCommand:
7988 {
7989 Image
7990 *affinity_image;
7991
7992 static char
7993 filename[MagickPathExtent] = "\0";
7994
7995 /*
7996 Request image file name from user.
7997 */
7998 XFileBrowserWidget(display,windows,"Map",filename);
7999 if (*filename == '\0')
8000 break;
8001 /*
8002 Map image.
8003 */
8004 XSetCursorState(display,windows,MagickTrue);
8005 XCheckRefreshWindows(display,windows);
8006 (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
8007 affinity_image=ReadImage(image_info,exception);
8008 if (affinity_image != (Image *) NULL)
8009 {
8010 (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8011 affinity_image=DestroyImage(affinity_image);
8012 }
8013 CatchException(exception);
8014 XSetCursorState(display,windows,MagickFalse);
8015 if (windows->image.orphan != MagickFalse)
8016 break;
8017 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8018 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8019 break;
8020 }
8021 case QuantizeCommand:
8022 {
8023 int
8024 status;
8025
8026 static char
8027 colors[MagickPathExtent] = "256";
8028
8029 /*
8030 Query user for maximum number of colors.
8031 */
8032 status=XDialogWidget(display,windows,"Quantize",
8033 "Maximum number of colors:",colors);
8034 if (*colors == '\0')
8035 break;
8036 /*
8037 Color reduce the image.
8038 */
8039 XSetCursorState(display,windows,MagickTrue);
8040 XCheckRefreshWindows(display,windows);
8041 quantize_info.number_colors=StringToUnsignedLong(colors);
8042 quantize_info.dither_method=status != 0 ? RiemersmaDitherMethod :
8043 NoDitherMethod;
8044 (void) QuantizeImage(&quantize_info,*image,exception);
8045 XSetCursorState(display,windows,MagickFalse);
8046 if (windows->image.orphan != MagickFalse)
8047 break;
8048 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8049 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8050 break;
8051 }
8052 case DespeckleCommand:
8053 {
8054 Image
8055 *despeckle_image;
8056
8057 /*
8058 Despeckle image.
8059 */
8060 XSetCursorState(display,windows,MagickTrue);
8061 XCheckRefreshWindows(display,windows);
8062 despeckle_image=DespeckleImage(*image,exception);
8063 if (despeckle_image != (Image *) NULL)
8064 {
8065 *image=DestroyImage(*image);
8066 *image=despeckle_image;
8067 }
8068 CatchException(exception);
8069 XSetCursorState(display,windows,MagickFalse);
8070 if (windows->image.orphan != MagickFalse)
8071 break;
8072 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8073 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8074 break;
8075 }
8076 case EmbossCommand:
8077 {
8078 Image
8079 *emboss_image;
8080
8081 static char
8082 radius[MagickPathExtent] = "0.0x1.0";
8083
8084 /*
8085 Query user for emboss radius.
8086 */
8087 (void) XDialogWidget(display,windows,"Emboss",
8088 "Enter the emboss radius and standard deviation:",radius);
8089 if (*radius == '\0')
8090 break;
8091 /*
8092 Reduce noise in the image.
8093 */
8094 XSetCursorState(display,windows,MagickTrue);
8095 XCheckRefreshWindows(display,windows);
8096 flags=ParseGeometry(radius,&geometry_info);
8097 if ((flags & SigmaValue) == 0)
8098 geometry_info.sigma=1.0;
8099 emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8100 exception);
8101 if (emboss_image != (Image *) NULL)
8102 {
8103 *image=DestroyImage(*image);
8104 *image=emboss_image;
8105 }
8106 CatchException(exception);
8107 XSetCursorState(display,windows,MagickFalse);
8108 if (windows->image.orphan != MagickFalse)
8109 break;
8110 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8111 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8112 break;
8113 }
8114 case ReduceNoiseCommand:
8115 {
8116 Image
8117 *noise_image;
8118
8119 static char
8120 radius[MagickPathExtent] = "0";
8121
8122 /*
8123 Query user for noise radius.
8124 */
8125 (void) XDialogWidget(display,windows,"Reduce Noise",
8126 "Enter the noise radius:",radius);
8127 if (*radius == '\0')
8128 break;
8129 /*
8130 Reduce noise in the image.
8131 */
8132 XSetCursorState(display,windows,MagickTrue);
8133 XCheckRefreshWindows(display,windows);
8134 flags=ParseGeometry(radius,&geometry_info);
8135 noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8136 geometry_info.rho,(size_t) geometry_info.rho,exception);
8137 if (noise_image != (Image *) NULL)
8138 {
8139 *image=DestroyImage(*image);
8140 *image=noise_image;
8141 }
8142 CatchException(exception);
8143 XSetCursorState(display,windows,MagickFalse);
8144 if (windows->image.orphan != MagickFalse)
8145 break;
8146 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8147 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8148 break;
8149 }
8150 case AddNoiseCommand:
8151 {
8152 char
8153 **noises;
8154
8155 Image
8156 *noise_image;
8157
8158 static char
8159 noise_type[MagickPathExtent] = "Gaussian";
8160
8161 /*
8162 Add noise to the image.
8163 */
8164 noises=GetCommandOptions(MagickNoiseOptions);
8165 if (noises == (char **) NULL)
8166 break;
8167 XListBrowserWidget(display,windows,&windows->widget,
8168 (const char **) noises,"Add Noise",
8169 "Select a type of noise to add to your image:",noise_type);
8170 noises=DestroyStringList(noises);
8171 if (*noise_type == '\0')
8172 break;
8173 XSetCursorState(display,windows,MagickTrue);
8174 XCheckRefreshWindows(display,windows);
8175 noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8176 MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8177 if (noise_image != (Image *) NULL)
8178 {
8179 *image=DestroyImage(*image);
8180 *image=noise_image;
8181 }
8182 CatchException(exception);
8183 XSetCursorState(display,windows,MagickFalse);
8184 if (windows->image.orphan != MagickFalse)
8185 break;
8186 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8187 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8188 break;
8189 }
8190 case SharpenCommand:
8191 {
8192 Image
8193 *sharp_image;
8194
8195 static char
8196 radius[MagickPathExtent] = "0.0x1.0";
8197
8198 /*
8199 Query user for sharpen radius.
8200 */
8201 (void) XDialogWidget(display,windows,"Sharpen",
8202 "Enter the sharpen radius and standard deviation:",radius);
8203 if (*radius == '\0')
8204 break;
8205 /*
8206 Sharpen image scanlines.
8207 */
8208 XSetCursorState(display,windows,MagickTrue);
8209 XCheckRefreshWindows(display,windows);
8210 flags=ParseGeometry(radius,&geometry_info);
8211 sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8212 exception);
8213 if (sharp_image != (Image *) NULL)
8214 {
8215 *image=DestroyImage(*image);
8216 *image=sharp_image;
8217 }
8218 CatchException(exception);
8219 XSetCursorState(display,windows,MagickFalse);
8220 if (windows->image.orphan != MagickFalse)
8221 break;
8222 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8223 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8224 break;
8225 }
8226 case BlurCommand:
8227 {
8228 Image
8229 *blur_image;
8230
8231 static char
8232 radius[MagickPathExtent] = "0.0x1.0";
8233
8234 /*
8235 Query user for blur radius.
8236 */
8237 (void) XDialogWidget(display,windows,"Blur",
8238 "Enter the blur radius and standard deviation:",radius);
8239 if (*radius == '\0')
8240 break;
8241 /*
8242 Blur an image.
8243 */
8244 XSetCursorState(display,windows,MagickTrue);
8245 XCheckRefreshWindows(display,windows);
8246 flags=ParseGeometry(radius,&geometry_info);
8247 blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8248 exception);
8249 if (blur_image != (Image *) NULL)
8250 {
8251 *image=DestroyImage(*image);
8252 *image=blur_image;
8253 }
8254 CatchException(exception);
8255 XSetCursorState(display,windows,MagickFalse);
8256 if (windows->image.orphan != MagickFalse)
8257 break;
8258 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8259 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8260 break;
8261 }
8262 case ThresholdCommand:
8263 {
8264 double
8265 threshold;
8266
8267 static char
8268 factor[MagickPathExtent] = "128";
8269
8270 /*
8271 Query user for threshold value.
8272 */
8273 (void) XDialogWidget(display,windows,"Threshold",
8274 "Enter threshold value:",factor);
8275 if (*factor == '\0')
8276 break;
8277 /*
8278 Gamma correct image.
8279 */
8280 XSetCursorState(display,windows,MagickTrue);
8281 XCheckRefreshWindows(display,windows);
8282 threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8283 (void) BilevelImage(*image,threshold,exception);
8284 XSetCursorState(display,windows,MagickFalse);
8285 if (windows->image.orphan != MagickFalse)
8286 break;
8287 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8288 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8289 break;
8290 }
8291 case EdgeDetectCommand:
8292 {
8293 Image
8294 *edge_image;
8295
8296 static char
8297 radius[MagickPathExtent] = "0";
8298
8299 /*
8300 Query user for edge factor.
8301 */
8302 (void) XDialogWidget(display,windows,"Detect Edges",
8303 "Enter the edge detect radius:",radius);
8304 if (*radius == '\0')
8305 break;
8306 /*
8307 Detect edge in image.
8308 */
8309 XSetCursorState(display,windows,MagickTrue);
8310 XCheckRefreshWindows(display,windows);
8311 flags=ParseGeometry(radius,&geometry_info);
8312 edge_image=EdgeImage(*image,geometry_info.rho,exception);
8313 if (edge_image != (Image *) NULL)
8314 {
8315 *image=DestroyImage(*image);
8316 *image=edge_image;
8317 }
8318 CatchException(exception);
8319 XSetCursorState(display,windows,MagickFalse);
8320 if (windows->image.orphan != MagickFalse)
8321 break;
8322 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8323 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8324 break;
8325 }
8326 case SpreadCommand:
8327 {
8328 Image
8329 *spread_image;
8330
8331 static char
8332 amount[MagickPathExtent] = "2";
8333
8334 /*
8335 Query user for spread amount.
8336 */
8337 (void) XDialogWidget(display,windows,"Spread",
8338 "Enter the displacement amount:",amount);
8339 if (*amount == '\0')
8340 break;
8341 /*
8342 Displace image pixels by a random amount.
8343 */
8344 XSetCursorState(display,windows,MagickTrue);
8345 XCheckRefreshWindows(display,windows);
8346 flags=ParseGeometry(amount,&geometry_info);
8347 spread_image=EdgeImage(*image,geometry_info.rho,exception);
8348 if (spread_image != (Image *) NULL)
8349 {
8350 *image=DestroyImage(*image);
8351 *image=spread_image;
8352 }
8353 CatchException(exception);
8354 XSetCursorState(display,windows,MagickFalse);
8355 if (windows->image.orphan != MagickFalse)
8356 break;
8357 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8358 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8359 break;
8360 }
8361 case ShadeCommand:
8362 {
8363 Image
8364 *shade_image;
8365
8366 int
8367 status;
8368
8369 static char
8370 geometry[MagickPathExtent] = "30x30";
8371
8372 /*
8373 Query user for the shade geometry.
8374 */
8375 status=XDialogWidget(display,windows,"Shade",
8376 "Enter the azimuth and elevation of the light source:",geometry);
8377 if (*geometry == '\0')
8378 break;
8379 /*
8380 Shade image pixels.
8381 */
8382 XSetCursorState(display,windows,MagickTrue);
8383 XCheckRefreshWindows(display,windows);
8384 flags=ParseGeometry(geometry,&geometry_info);
8385 if ((flags & SigmaValue) == 0)
8386 geometry_info.sigma=1.0;
8387 shade_image=ShadeImage(*image,status != 0 ? MagickTrue : MagickFalse,
8388 geometry_info.rho,geometry_info.sigma,exception);
8389 if (shade_image != (Image *) NULL)
8390 {
8391 *image=DestroyImage(*image);
8392 *image=shade_image;
8393 }
8394 CatchException(exception);
8395 XSetCursorState(display,windows,MagickFalse);
8396 if (windows->image.orphan != MagickFalse)
8397 break;
8398 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8399 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8400 break;
8401 }
8402 case RaiseCommand:
8403 {
8404 static char
8405 bevel_width[MagickPathExtent] = "10";
8406
8407 /*
8408 Query user for bevel width.
8409 */
8410 (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8411 if (*bevel_width == '\0')
8412 break;
8413 /*
8414 Raise an image.
8415 */
8416 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8417 exception);
8418 XSetCursorState(display,windows,MagickTrue);
8419 XCheckRefreshWindows(display,windows);
8420 (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8421 exception);
8422 (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8423 XSetCursorState(display,windows,MagickFalse);
8424 if (windows->image.orphan != MagickFalse)
8425 break;
8426 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8427 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8428 break;
8429 }
8430 case SegmentCommand:
8431 {
8432 static char
8433 threshold[MagickPathExtent] = "1.0x1.5";
8434
8435 /*
8436 Query user for smoothing threshold.
8437 */
8438 (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8439 threshold);
8440 if (*threshold == '\0')
8441 break;
8442 /*
8443 Segment an image.
8444 */
8445 XSetCursorState(display,windows,MagickTrue);
8446 XCheckRefreshWindows(display,windows);
8447 flags=ParseGeometry(threshold,&geometry_info);
8448 if ((flags & SigmaValue) == 0)
8449 geometry_info.sigma=1.0;
8450 (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8451 geometry_info.sigma,exception);
8452 XSetCursorState(display,windows,MagickFalse);
8453 if (windows->image.orphan != MagickFalse)
8454 break;
8455 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8456 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8457 break;
8458 }
8459 case SepiaToneCommand:
8460 {
8461 double
8462 threshold;
8463
8464 Image
8465 *sepia_image;
8466
8467 static char
8468 factor[MagickPathExtent] = "80%";
8469
8470 /*
8471 Query user for sepia-tone factor.
8472 */
8473 (void) XDialogWidget(display,windows,"Sepia Tone",
8474 "Enter the sepia tone factor (0 - 99.9%):",factor);
8475 if (*factor == '\0')
8476 break;
8477 /*
8478 Sepia tone image pixels.
8479 */
8480 XSetCursorState(display,windows,MagickTrue);
8481 XCheckRefreshWindows(display,windows);
8482 threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8483 sepia_image=SepiaToneImage(*image,threshold,exception);
8484 if (sepia_image != (Image *) NULL)
8485 {
8486 *image=DestroyImage(*image);
8487 *image=sepia_image;
8488 }
8489 CatchException(exception);
8490 XSetCursorState(display,windows,MagickFalse);
8491 if (windows->image.orphan != MagickFalse)
8492 break;
8493 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8494 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8495 break;
8496 }
8497 case SolarizeCommand:
8498 {
8499 double
8500 threshold;
8501
8502 static char
8503 factor[MagickPathExtent] = "60%";
8504
8505 /*
8506 Query user for solarize factor.
8507 */
8508 (void) XDialogWidget(display,windows,"Solarize",
8509 "Enter the solarize factor (0 - 99.9%):",factor);
8510 if (*factor == '\0')
8511 break;
8512 /*
8513 Solarize image pixels.
8514 */
8515 XSetCursorState(display,windows,MagickTrue);
8516 XCheckRefreshWindows(display,windows);
8517 threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8518 (void) SolarizeImage(*image,threshold,exception);
8519 XSetCursorState(display,windows,MagickFalse);
8520 if (windows->image.orphan != MagickFalse)
8521 break;
8522 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8523 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8524 break;
8525 }
8526 case SwirlCommand:
8527 {
8528 Image
8529 *swirl_image;
8530
8531 static char
8532 degrees[MagickPathExtent] = "60";
8533
8534 /*
8535 Query user for swirl angle.
8536 */
8537 (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8538 degrees);
8539 if (*degrees == '\0')
8540 break;
8541 /*
8542 Swirl image pixels about the center.
8543 */
8544 XSetCursorState(display,windows,MagickTrue);
8545 XCheckRefreshWindows(display,windows);
8546 flags=ParseGeometry(degrees,&geometry_info);
8547 swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8548 exception);
8549 if (swirl_image != (Image *) NULL)
8550 {
8551 *image=DestroyImage(*image);
8552 *image=swirl_image;
8553 }
8554 CatchException(exception);
8555 XSetCursorState(display,windows,MagickFalse);
8556 if (windows->image.orphan != MagickFalse)
8557 break;
8558 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8559 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8560 break;
8561 }
8562 case ImplodeCommand:
8563 {
8564 Image
8565 *implode_image;
8566
8567 static char
8568 factor[MagickPathExtent] = "0.3";
8569
8570 /*
8571 Query user for implode factor.
8572 */
8573 (void) XDialogWidget(display,windows,"Implode",
8574 "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8575 if (*factor == '\0')
8576 break;
8577 /*
8578 Implode image pixels about the center.
8579 */
8580 XSetCursorState(display,windows,MagickTrue);
8581 XCheckRefreshWindows(display,windows);
8582 flags=ParseGeometry(factor,&geometry_info);
8583 implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8584 exception);
8585 if (implode_image != (Image *) NULL)
8586 {
8587 *image=DestroyImage(*image);
8588 *image=implode_image;
8589 }
8590 CatchException(exception);
8591 XSetCursorState(display,windows,MagickFalse);
8592 if (windows->image.orphan != MagickFalse)
8593 break;
8594 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8595 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8596 break;
8597 }
8598 case VignetteCommand:
8599 {
8600 Image
8601 *vignette_image;
8602
8603 static char
8604 geometry[MagickPathExtent] = "0x20";
8605
8606 /*
8607 Query user for the vignette geometry.
8608 */
8609 (void) XDialogWidget(display,windows,"Vignette",
8610 "Enter the radius, sigma, and x and y offsets:",geometry);
8611 if (*geometry == '\0')
8612 break;
8613 /*
8614 Soften the edges of the image in vignette style
8615 */
8616 XSetCursorState(display,windows,MagickTrue);
8617 XCheckRefreshWindows(display,windows);
8618 flags=ParseGeometry(geometry,&geometry_info);
8619 if ((flags & SigmaValue) == 0)
8620 geometry_info.sigma=1.0;
8621 if ((flags & XiValue) == 0)
8622 geometry_info.xi=0.1*(*image)->columns;
8623 if ((flags & PsiValue) == 0)
8624 geometry_info.psi=0.1*(*image)->rows;
8625 vignette_image=VignetteImage(*image,geometry_info.rho,0.0,(ssize_t)
8626 ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-0.5),
8627 exception);
8628 if (vignette_image != (Image *) NULL)
8629 {
8630 *image=DestroyImage(*image);
8631 *image=vignette_image;
8632 }
8633 CatchException(exception);
8634 XSetCursorState(display,windows,MagickFalse);
8635 if (windows->image.orphan != MagickFalse)
8636 break;
8637 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8638 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8639 break;
8640 }
8641 case WaveCommand:
8642 {
8643 Image
8644 *wave_image;
8645
8646 static char
8647 geometry[MagickPathExtent] = "25x150";
8648
8649 /*
8650 Query user for the wave geometry.
8651 */
8652 (void) XDialogWidget(display,windows,"Wave",
8653 "Enter the amplitude and length of the wave:",geometry);
8654 if (*geometry == '\0')
8655 break;
8656 /*
8657 Alter an image along a sine wave.
8658 */
8659 XSetCursorState(display,windows,MagickTrue);
8660 XCheckRefreshWindows(display,windows);
8661 flags=ParseGeometry(geometry,&geometry_info);
8662 if ((flags & SigmaValue) == 0)
8663 geometry_info.sigma=1.0;
8664 wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8665 (*image)->interpolate,exception);
8666 if (wave_image != (Image *) NULL)
8667 {
8668 *image=DestroyImage(*image);
8669 *image=wave_image;
8670 }
8671 CatchException(exception);
8672 XSetCursorState(display,windows,MagickFalse);
8673 if (windows->image.orphan != MagickFalse)
8674 break;
8675 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8676 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8677 break;
8678 }
8679 case OilPaintCommand:
8680 {
8681 Image
8682 *paint_image;
8683
8684 static char
8685 radius[MagickPathExtent] = "0";
8686
8687 /*
8688 Query user for circular neighborhood radius.
8689 */
8690 (void) XDialogWidget(display,windows,"Oil Paint",
8691 "Enter the mask radius:",radius);
8692 if (*radius == '\0')
8693 break;
8694 /*
8695 OilPaint image scanlines.
8696 */
8697 XSetCursorState(display,windows,MagickTrue);
8698 XCheckRefreshWindows(display,windows);
8699 flags=ParseGeometry(radius,&geometry_info);
8700 paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8701 exception);
8702 if (paint_image != (Image *) NULL)
8703 {
8704 *image=DestroyImage(*image);
8705 *image=paint_image;
8706 }
8707 CatchException(exception);
8708 XSetCursorState(display,windows,MagickFalse);
8709 if (windows->image.orphan != MagickFalse)
8710 break;
8711 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8712 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8713 break;
8714 }
8715 case CharcoalDrawCommand:
8716 {
8717 Image
8718 *charcoal_image;
8719
8720 static char
8721 radius[MagickPathExtent] = "0x1";
8722
8723 /*
8724 Query user for charcoal radius.
8725 */
8726 (void) XDialogWidget(display,windows,"Charcoal Draw",
8727 "Enter the charcoal radius and sigma:",radius);
8728 if (*radius == '\0')
8729 break;
8730 /*
8731 Charcoal the image.
8732 */
8733 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8734 exception);
8735 XSetCursorState(display,windows,MagickTrue);
8736 XCheckRefreshWindows(display,windows);
8737 flags=ParseGeometry(radius,&geometry_info);
8738 if ((flags & SigmaValue) == 0)
8739 geometry_info.sigma=geometry_info.rho;
8740 charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8741 exception);
8742 if (charcoal_image != (Image *) NULL)
8743 {
8744 *image=DestroyImage(*image);
8745 *image=charcoal_image;
8746 }
8747 CatchException(exception);
8748 XSetCursorState(display,windows,MagickFalse);
8749 if (windows->image.orphan != MagickFalse)
8750 break;
8751 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8752 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8753 break;
8754 }
8755 case AnnotateCommand:
8756 {
8757 /*
8758 Annotate the image with text.
8759 */
8760 status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8761 if (status == MagickFalse)
8762 {
8763 XNoticeWidget(display,windows,"Unable to annotate X image",
8764 (*image)->filename);
8765 break;
8766 }
8767 break;
8768 }
8769 case DrawCommand:
8770 {
8771 /*
8772 Draw image.
8773 */
8774 status=XDrawEditImage(display,resource_info,windows,image,exception);
8775 if (status == MagickFalse)
8776 {
8777 XNoticeWidget(display,windows,"Unable to draw on the X image",
8778 (*image)->filename);
8779 break;
8780 }
8781 break;
8782 }
8783 case ColorCommand:
8784 {
8785 /*
8786 Color edit.
8787 */
8788 status=XColorEditImage(display,resource_info,windows,image,exception);
8789 if (status == MagickFalse)
8790 {
8791 XNoticeWidget(display,windows,"Unable to pixel edit X image",
8792 (*image)->filename);
8793 break;
8794 }
8795 break;
8796 }
8797 case MatteCommand:
8798 {
8799 /*
8800 Matte edit.
8801 */
8802 status=XMatteEditImage(display,resource_info,windows,image,exception);
8803 if (status == MagickFalse)
8804 {
8805 XNoticeWidget(display,windows,"Unable to matte edit X image",
8806 (*image)->filename);
8807 break;
8808 }
8809 break;
8810 }
8811 case CompositeCommand:
8812 {
8813 /*
8814 Composite image.
8815 */
8816 status=XCompositeImage(display,resource_info,windows,*image,
8817 exception);
8818 if (status == MagickFalse)
8819 {
8820 XNoticeWidget(display,windows,"Unable to composite X image",
8821 (*image)->filename);
8822 break;
8823 }
8824 break;
8825 }
8826 case AddBorderCommand:
8827 {
8828 Image
8829 *border_image;
8830
8831 static char
8832 geometry[MagickPathExtent] = "6x6";
8833
8834 /*
8835 Query user for border color and geometry.
8836 */
8837 XColorBrowserWidget(display,windows,"Select",color);
8838 if (*color == '\0')
8839 break;
8840 (void) XDialogWidget(display,windows,"Add Border",
8841 "Enter border geometry:",geometry);
8842 if (*geometry == '\0')
8843 break;
8844 /*
8845 Add a border to the image.
8846 */
8847 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8848 exception);
8849 XSetCursorState(display,windows,MagickTrue);
8850 XCheckRefreshWindows(display,windows);
8851 (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8852 exception);
8853 (void) ParsePageGeometry(*image,geometry,&page_geometry,
8854 exception);
8855 border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8856 exception);
8857 if (border_image != (Image *) NULL)
8858 {
8859 *image=DestroyImage(*image);
8860 *image=border_image;
8861 }
8862 CatchException(exception);
8863 XSetCursorState(display,windows,MagickFalse);
8864 if (windows->image.orphan != MagickFalse)
8865 break;
8866 windows->image.window_changes.width=(int) (*image)->columns;
8867 windows->image.window_changes.height=(int) (*image)->rows;
8868 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8869 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8870 break;
8871 }
8872 case AddFrameCommand:
8873 {
8874 FrameInfo
8875 frame_info;
8876
8877 Image
8878 *frame_image;
8879
8880 static char
8881 geometry[MagickPathExtent] = "6x6";
8882
8883 /*
8884 Query user for frame color and geometry.
8885 */
8886 XColorBrowserWidget(display,windows,"Select",color);
8887 if (*color == '\0')
8888 break;
8889 (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8890 geometry);
8891 if (*geometry == '\0')
8892 break;
8893 /*
8894 Surround image with an ornamental border.
8895 */
8896 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8897 exception);
8898 XSetCursorState(display,windows,MagickTrue);
8899 XCheckRefreshWindows(display,windows);
8900 (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8901 exception);
8902 (void) ParsePageGeometry(*image,geometry,&page_geometry,
8903 exception);
8904 frame_info.width=page_geometry.width;
8905 frame_info.height=page_geometry.height;
8906 frame_info.outer_bevel=page_geometry.x;
8907 frame_info.inner_bevel=page_geometry.y;
8908 frame_info.x=(ssize_t) frame_info.width;
8909 frame_info.y=(ssize_t) frame_info.height;
8910 frame_info.width=(*image)->columns+2*frame_info.width;
8911 frame_info.height=(*image)->rows+2*frame_info.height;
8912 frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8913 if (frame_image != (Image *) NULL)
8914 {
8915 *image=DestroyImage(*image);
8916 *image=frame_image;
8917 }
8918 CatchException(exception);
8919 XSetCursorState(display,windows,MagickFalse);
8920 if (windows->image.orphan != MagickFalse)
8921 break;
8922 windows->image.window_changes.width=(int) (*image)->columns;
8923 windows->image.window_changes.height=(int) (*image)->rows;
8924 XConfigureImageColormap(display,resource_info,windows,*image,exception);
8925 (void) XConfigureImage(display,resource_info,windows,*image,exception);
8926 break;
8927 }
8928 case CommentCommand:
8929 {
8930 const char
8931 *value;
8932
8933 FILE
8934 *file;
8935
8936 int
8937 unique_file;
8938
8939 /*
8940 Edit image comment.
8941 */
8942 unique_file=AcquireUniqueFileResource(image_info->filename);
8943 if (unique_file == -1)
8944 {
8945 XNoticeWidget(display,windows,"Unable to edit image comment",
8946 image_info->filename);
8947 break;
8948 }
8949 value=GetImageProperty(*image,"comment",exception);
8950 if (value == (char *) NULL)
8951 unique_file=close(unique_file)-1;
8952 else
8953 {
8954 const char
8955 *p;
8956
8957 file=fdopen(unique_file,"w");
8958 if (file == (FILE *) NULL)
8959 {
8960 XNoticeWidget(display,windows,"Unable to edit image comment",
8961 image_info->filename);
8962 break;
8963 }
8964 for (p=value; *p != '\0'; p++)
8965 (void) fputc((int) *p,file);
8966 (void) fputc('\n',file);
8967 (void) fclose(file);
8968 }
8969 XSetCursorState(display,windows,MagickTrue);
8970 XCheckRefreshWindows(display,windows);
8971 status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8972 exception);
8973 if (status == MagickFalse)
8974 XNoticeWidget(display,windows,"Unable to edit image comment",
8975 (char *) NULL);
8976 else
8977 {
8978 char
8979 *comment;
8980
8981 comment=FileToString(image_info->filename,~0UL,exception);
8982 if (comment != (char *) NULL)
8983 {
8984 (void) SetImageProperty(*image,"comment",comment,exception);
8985 (*image)->taint=MagickTrue;
8986 }
8987 }
8988 (void) RelinquishUniqueFileResource(image_info->filename);
8989 XSetCursorState(display,windows,MagickFalse);
8990 break;
8991 }
8992 case LaunchCommand:
8993 {
8994 /*
8995 Launch program.
8996 */
8997 XSetCursorState(display,windows,MagickTrue);
8998 XCheckRefreshWindows(display,windows);
8999 (void) AcquireUniqueFilename(filename);
9000 (void) FormatLocaleString((*image)->filename,MagickPathExtent,"launch:%s",
9001 filename);
9002 status=WriteImage(image_info,*image,exception);
9003 if (status == MagickFalse)
9004 XNoticeWidget(display,windows,"Unable to launch image editor",
9005 (char *) NULL);
9006 else
9007 {
9008 nexus=ReadImage(resource_info->image_info,exception);
9009 CatchException(exception);
9010 XClientMessage(display,windows->image.id,windows->im_protocols,
9011 windows->im_next_image,CurrentTime);
9012 }
9013 (void) RelinquishUniqueFileResource(filename);
9014 XSetCursorState(display,windows,MagickFalse);
9015 break;
9016 }
9017 case RegionOfInterestCommand:
9018 {
9019 /*
9020 Apply an image processing technique to a region of interest.
9021 */
9022 (void) XROIImage(display,resource_info,windows,image,exception);
9023 break;
9024 }
9025 case InfoCommand:
9026 break;
9027 case ZoomCommand:
9028 {
9029 /*
9030 Zoom image.
9031 */
9032 if (windows->magnify.mapped != MagickFalse)
9033 (void) XRaiseWindow(display,windows->magnify.id);
9034 else
9035 {
9036 /*
9037 Make magnify image.
9038 */
9039 XSetCursorState(display,windows,MagickTrue);
9040 (void) XMapRaised(display,windows->magnify.id);
9041 XSetCursorState(display,windows,MagickFalse);
9042 }
9043 break;
9044 }
9045 case ShowPreviewCommand:
9046 {
9047 char
9048 **previews;
9049
9050 Image
9051 *preview_image;
9052
9053 PreviewType
9054 preview;
9055
9056 static char
9057 preview_type[MagickPathExtent] = "Gamma";
9058
9059 /*
9060 Select preview type from menu.
9061 */
9062 previews=GetCommandOptions(MagickPreviewOptions);
9063 if (previews == (char **) NULL)
9064 break;
9065 XListBrowserWidget(display,windows,&windows->widget,
9066 (const char **) previews,"Preview",
9067 "Select an enhancement, effect, or F/X:",preview_type);
9068 previews=DestroyStringList(previews);
9069 if (*preview_type == '\0')
9070 break;
9071 /*
9072 Show image preview.
9073 */
9074 XSetCursorState(display,windows,MagickTrue);
9075 XCheckRefreshWindows(display,windows);
9076 preview=(PreviewType) ParseCommandOption(MagickPreviewOptions,
9077 MagickFalse,preview_type);
9078 (void) FormatImageProperty(*image,"group","%.20g",(double)
9079 windows->image.id);
9080 (void) DeleteImageProperty(*image,"label");
9081 (void) SetImageProperty(*image,"label","Preview",exception);
9082 preview_image=PreviewImage(*image,preview,exception);
9083 if (preview_image == (Image *) NULL)
9084 break;
9085 (void) AcquireUniqueFilename(filename);
9086 (void) FormatLocaleString(preview_image->filename,MagickPathExtent,
9087 "show:%s",filename);
9088 status=WriteImage(image_info,preview_image,exception);
9089 (void) RelinquishUniqueFileResource(filename);
9090 preview_image=DestroyImage(preview_image);
9091 if (status == MagickFalse)
9092 XNoticeWidget(display,windows,"Unable to show image preview",
9093 (*image)->filename);
9094 XDelay(display,1500);
9095 XSetCursorState(display,windows,MagickFalse);
9096 break;
9097 }
9098 case ShowHistogramCommand:
9099 {
9100 Image
9101 *histogram_image;
9102
9103 /*
9104 Show image histogram.
9105 */
9106 XSetCursorState(display,windows,MagickTrue);
9107 XCheckRefreshWindows(display,windows);
9108 (void) DeleteImageProperty(*image,"label");
9109 (void) FormatImageProperty(*image,"group","%.20g",(double)
9110 windows->image.id);
9111 (void) SetImageProperty(*image,"label","Histogram",exception);
9112 (void) AcquireUniqueFilename(filename);
9113 (void) FormatLocaleString((*image)->filename,MagickPathExtent,
9114 "histogram:%s",filename);
9115 status=WriteImage(image_info,*image,exception);
9116 (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9117 histogram_image=ReadImage(image_info,exception);
9118 (void) RelinquishUniqueFileResource(filename);
9119 if (histogram_image == (Image *) NULL)
9120 break;
9121 (void) FormatLocaleString(histogram_image->filename,MagickPathExtent,
9122 "show:%s",filename);
9123 status=WriteImage(image_info,histogram_image,exception);
9124 histogram_image=DestroyImage(histogram_image);
9125 if (status == MagickFalse)
9126 XNoticeWidget(display,windows,"Unable to show histogram",
9127 (*image)->filename);
9128 XDelay(display,1500);
9129 XSetCursorState(display,windows,MagickFalse);
9130 break;
9131 }
9132 case ShowMatteCommand:
9133 {
9134 Image
9135 *matte_image;
9136
9137 if ((*image)->alpha_trait == UndefinedPixelTrait)
9138 {
9139 XNoticeWidget(display,windows,
9140 "Image does not have any matte information",(*image)->filename);
9141 break;
9142 }
9143 /*
9144 Show image matte.
9145 */
9146 XSetCursorState(display,windows,MagickTrue);
9147 XCheckRefreshWindows(display,windows);
9148 (void) FormatImageProperty(*image,"group","%.20g",(double)
9149 windows->image.id);
9150 (void) DeleteImageProperty(*image,"label");
9151 (void) SetImageProperty(*image,"label","Matte",exception);
9152 (void) AcquireUniqueFilename(filename);
9153 (void) FormatLocaleString((*image)->filename,MagickPathExtent,"matte:%s",
9154 filename);
9155 status=WriteImage(image_info,*image,exception);
9156 (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9157 matte_image=ReadImage(image_info,exception);
9158 (void) RelinquishUniqueFileResource(filename);
9159 if (matte_image == (Image *) NULL)
9160 break;
9161 (void) FormatLocaleString(matte_image->filename,MagickPathExtent,
9162 "show:%s",filename);
9163 status=WriteImage(image_info,matte_image,exception);
9164 matte_image=DestroyImage(matte_image);
9165 if (status == MagickFalse)
9166 XNoticeWidget(display,windows,"Unable to show matte",
9167 (*image)->filename);
9168 XDelay(display,1500);
9169 XSetCursorState(display,windows,MagickFalse);
9170 break;
9171 }
9172 case BackgroundCommand:
9173 {
9174 /*
9175 Background image.
9176 */
9177 status=XBackgroundImage(display,resource_info,windows,image,exception);
9178 if (status == MagickFalse)
9179 break;
9180 nexus=CloneImage(*image,0,0,MagickTrue,exception);
9181 if (nexus != (Image *) NULL)
9182 XClientMessage(display,windows->image.id,windows->im_protocols,
9183 windows->im_next_image,CurrentTime);
9184 break;
9185 }
9186 case SlideShowCommand:
9187 {
9188 static char
9189 delay[MagickPathExtent] = "5";
9190
9191 /*
9192 Display next image after pausing.
9193 */
9194 (void) XDialogWidget(display,windows,"Slide Show",
9195 "Pause how many 1/100ths of a second between images:",delay);
9196 if (*delay == '\0')
9197 break;
9198 resource_info->delay=StringToUnsignedLong(delay);
9199 XClientMessage(display,windows->image.id,windows->im_protocols,
9200 windows->im_next_image,CurrentTime);
9201 break;
9202 }
9203 case PreferencesCommand:
9204 {
9205 /*
9206 Set user preferences.
9207 */
9208 status=XPreferencesWidget(display,resource_info,windows);
9209 if (status == MagickFalse)
9210 break;
9211 nexus=CloneImage(*image,0,0,MagickTrue,exception);
9212 if (nexus != (Image *) NULL)
9213 XClientMessage(display,windows->image.id,windows->im_protocols,
9214 windows->im_next_image,CurrentTime);
9215 break;
9216 }
9217 case HelpCommand:
9218 {
9219 /*
9220 User requested help.
9221 */
9222 XTextViewHelp(display,resource_info,windows,MagickFalse,
9223 "Help Viewer - Display",DisplayHelp);
9224 break;
9225 }
9226 case BrowseDocumentationCommand:
9227 {
9228 Atom
9229 mozilla_atom;
9230
9231 Window
9232 mozilla_window,
9233 root_window;
9234
9235 /*
9236 Browse the ImageMagick documentation.
9237 */
9238 root_window=XRootWindow(display,XDefaultScreen(display));
9239 mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9240 mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9241 if (mozilla_window != (Window) NULL)
9242 {
9243 char
9244 command[MagickPathExtent];
9245
9246 /*
9247 Display documentation using Netscape remote control.
9248 */
9249 (void) FormatLocaleString(command,MagickPathExtent,
9250 "openurl(%s,new-tab)",MagickAuthoritativeURL);
9251 mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9252 (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9253 8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9254 XSetCursorState(display,windows,MagickFalse);
9255 break;
9256 }
9257 XSetCursorState(display,windows,MagickTrue);
9258 XCheckRefreshWindows(display,windows);
9259 status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9260 exception);
9261 if (status == MagickFalse)
9262 XNoticeWidget(display,windows,"Unable to browse documentation",
9263 (char *) NULL);
9264 XDelay(display,1500);
9265 XSetCursorState(display,windows,MagickFalse);
9266 break;
9267 }
9268 case VersionCommand:
9269 {
9270 XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9271 GetMagickCopyright());
9272 break;
9273 }
9274 case SaveToUndoBufferCommand:
9275 break;
9276 default:
9277 {
9278 (void) XBell(display,0);
9279 break;
9280 }
9281 }
9282 image_info=DestroyImageInfo(image_info);
9283 return(nexus);
9284}
9285
9286/*
9287%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9288% %
9289% %
9290% %
9291+ X M a g n i f y I m a g e %
9292% %
9293% %
9294% %
9295%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9296%
9297% XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9298% The magnified portion is displayed in a separate window.
9299%
9300% The format of the XMagnifyImage method is:
9301%
9302% void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9303% ExceptionInfo *exception)
9304%
9305% A description of each parameter follows:
9306%
9307% o display: Specifies a connection to an X server; returned from
9308% XOpenDisplay.
9309%
9310% o windows: Specifies a pointer to a XWindows structure.
9311%
9312% o event: Specifies a pointer to a XEvent structure. If it is NULL,
9313% the entire image is refreshed.
9314%
9315% o exception: return any errors or warnings in this structure.
9316%
9317*/
9318static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9319 ExceptionInfo *exception)
9320{
9321 char
9322 text[MagickPathExtent];
9323
9324 int
9325 x,
9326 y;
9327
9328 size_t
9329 state;
9330
9331 /*
9332 Update magnified image until the mouse button is released.
9333 */
9334 (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9335 state=DefaultState;
9336 x=event->xbutton.x;
9337 y=event->xbutton.y;
9338 windows->magnify.x=(int) windows->image.x+x;
9339 windows->magnify.y=(int) windows->image.y+y;
9340 do
9341 {
9342 /*
9343 Map and unmap Info widget as text cursor crosses its boundaries.
9344 */
9345 if (windows->info.mapped != MagickFalse)
9346 {
9347 if ((x < (windows->info.x+(int) windows->info.width)) &&
9348 (y < (windows->info.y+(int) windows->info.height)))
9349 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9350 }
9351 else
9352 if ((x > (windows->info.x+(int) windows->info.width)) ||
9353 (y > (windows->info.y+(int) windows->info.height)))
9354 (void) XMapWindow(display,windows->info.id);
9355 if (windows->info.mapped != MagickFalse)
9356 {
9357 /*
9358 Display pointer position.
9359 */
9360 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
9361 windows->magnify.x,windows->magnify.y);
9362 XInfoWidget(display,windows,text);
9363 }
9364 /*
9365 Wait for next event.
9366 */
9367 XScreenEvent(display,windows,event,exception);
9368 switch (event->type)
9369 {
9370 case ButtonPress:
9371 break;
9372 case ButtonRelease:
9373 {
9374 /*
9375 User has finished magnifying image.
9376 */
9377 x=event->xbutton.x;
9378 y=event->xbutton.y;
9379 state|=ExitState;
9380 break;
9381 }
9382 case Expose:
9383 break;
9384 case MotionNotify:
9385 {
9386 x=event->xmotion.x;
9387 y=event->xmotion.y;
9388 break;
9389 }
9390 default:
9391 break;
9392 }
9393 /*
9394 Check boundary conditions.
9395 */
9396 if (x < 0)
9397 x=0;
9398 else
9399 if (x >= (int) windows->image.width)
9400 x=(int) windows->image.width-1;
9401 if (y < 0)
9402 y=0;
9403 else
9404 if (y >= (int) windows->image.height)
9405 y=(int) windows->image.height-1;
9406 } while ((state & ExitState) == 0);
9407 /*
9408 Display magnified image.
9409 */
9410 XSetCursorState(display,windows,MagickFalse);
9411}
9412
9413/*
9414%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9415% %
9416% %
9417% %
9418+ X M a g n i f y W i n d o w C o m m a n d %
9419% %
9420% %
9421% %
9422%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9423%
9424% XMagnifyWindowCommand() moves the image within an Magnify window by one
9425% pixel as specified by the key symbol.
9426%
9427% The format of the XMagnifyWindowCommand method is:
9428%
9429% void XMagnifyWindowCommand(Display *display,XWindows *windows,
9430% const MagickStatusType state,const KeySym key_symbol,
9431% ExceptionInfo *exception)
9432%
9433% A description of each parameter follows:
9434%
9435% o display: Specifies a connection to an X server; returned from
9436% XOpenDisplay.
9437%
9438% o windows: Specifies a pointer to a XWindows structure.
9439%
9440% o state: key mask.
9441%
9442% o key_symbol: Specifies a KeySym which indicates which side of the image
9443% to trim.
9444%
9445% o exception: return any errors or warnings in this structure.
9446%
9447*/
9448static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9449 const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9450{
9451 unsigned int
9452 quantum;
9453
9454 /*
9455 User specified a magnify factor or position.
9456 */
9457 quantum=1;
9458 if ((state & Mod1Mask) != 0)
9459 quantum=10;
9460 switch ((int) key_symbol)
9461 {
9462 case QuitCommand:
9463 {
9464 (void) XWithdrawWindow(display,windows->magnify.id,
9465 windows->magnify.screen);
9466 break;
9467 }
9468 case XK_Home:
9469 case XK_KP_Home:
9470 {
9471 windows->magnify.x=(int) windows->image.width/2;
9472 windows->magnify.y=(int) windows->image.height/2;
9473 break;
9474 }
9475 case XK_Left:
9476 case XK_KP_Left:
9477 {
9478 if (windows->magnify.x > 0)
9479 windows->magnify.x-=(int) quantum;
9480 break;
9481 }
9482 case XK_Up:
9483 case XK_KP_Up:
9484 {
9485 if (windows->magnify.y > 0)
9486 windows->magnify.y-=(int) quantum;
9487 break;
9488 }
9489 case XK_Right:
9490 case XK_KP_Right:
9491 {
9492 if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9493 windows->magnify.x+=(int) quantum;
9494 break;
9495 }
9496 case XK_Down:
9497 case XK_KP_Down:
9498 {
9499 if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9500 windows->magnify.y+=(int) quantum;
9501 break;
9502 }
9503 case XK_0:
9504 case XK_1:
9505 case XK_2:
9506 case XK_3:
9507 case XK_4:
9508 case XK_5:
9509 case XK_6:
9510 case XK_7:
9511 case XK_8:
9512 case XK_9:
9513 {
9514 windows->magnify.data=(key_symbol-XK_0);
9515 break;
9516 }
9517 case XK_KP_0:
9518 case XK_KP_1:
9519 case XK_KP_2:
9520 case XK_KP_3:
9521 case XK_KP_4:
9522 case XK_KP_5:
9523 case XK_KP_6:
9524 case XK_KP_7:
9525 case XK_KP_8:
9526 case XK_KP_9:
9527 {
9528 windows->magnify.data=(key_symbol-XK_KP_0);
9529 break;
9530 }
9531 default:
9532 break;
9533 }
9534 XMakeMagnifyImage(display,windows,exception);
9535}
9536
9537/*
9538%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9539% %
9540% %
9541% %
9542+ X M a k e P a n I m a g e %
9543% %
9544% %
9545% %
9546%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9547%
9548% XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9549% icon window.
9550%
9551% The format of the XMakePanImage method is:
9552%
9553% void XMakePanImage(Display *display,XResourceInfo *resource_info,
9554% XWindows *windows,Image *image,ExceptionInfo *exception)
9555%
9556% A description of each parameter follows:
9557%
9558% o display: Specifies a connection to an X server; returned from
9559% XOpenDisplay.
9560%
9561% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9562%
9563% o windows: Specifies a pointer to a XWindows structure.
9564%
9565% o image: the image.
9566%
9567% o exception: return any errors or warnings in this structure.
9568%
9569*/
9570static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9571 XWindows *windows,Image *image,ExceptionInfo *exception)
9572{
9573 MagickStatusType
9574 status;
9575
9576 /*
9577 Create and display image for panning icon.
9578 */
9579 XSetCursorState(display,windows,MagickTrue);
9580 XCheckRefreshWindows(display,windows);
9581 windows->pan.x=(int) windows->image.x;
9582 windows->pan.y=(int) windows->image.y;
9583 status=XMakeImage(display,resource_info,&windows->pan,image,
9584 windows->pan.width,windows->pan.height,exception);
9585 if (status == MagickFalse)
9586 ThrowXWindowException(ResourceLimitError,
9587 "MemoryAllocationFailed",image->filename);
9588 (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9589 windows->pan.pixmap);
9590 (void) XClearWindow(display,windows->pan.id);
9591 XDrawPanRectangle(display,windows);
9592 XSetCursorState(display,windows,MagickFalse);
9593}
9594
9595/*
9596%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9597% %
9598% %
9599% %
9600+ X M a t t a E d i t I m a g e %
9601% %
9602% %
9603% %
9604%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9605%
9606% XMatteEditImage() allows the user to interactively change the Matte channel
9607% of an image. If the image is PseudoClass it is promoted to DirectClass
9608% before the matte information is stored.
9609%
9610% The format of the XMatteEditImage method is:
9611%
9612% MagickBooleanType XMatteEditImage(Display *display,
9613% XResourceInfo *resource_info,XWindows *windows,Image **image,
9614% ExceptionInfo *exception)
9615%
9616% A description of each parameter follows:
9617%
9618% o display: Specifies a connection to an X server; returned from
9619% XOpenDisplay.
9620%
9621% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9622%
9623% o windows: Specifies a pointer to a XWindows structure.
9624%
9625% o image: the image; returned from ReadImage.
9626%
9627% o exception: return any errors or warnings in this structure.
9628%
9629*/
9630static MagickBooleanType XMatteEditImage(Display *display,
9631 XResourceInfo *resource_info,XWindows *windows,Image **image,
9632 ExceptionInfo *exception)
9633{
9634 const char
9635 *const MatteEditMenu[] =
9636 {
9637 "Method",
9638 "Border Color",
9639 "Fuzz",
9640 "Matte Value",
9641 "Undo",
9642 "Help",
9643 "Dismiss",
9644 (char *) NULL
9645 };
9646
9647 static char
9648 matte[MagickPathExtent] = "0";
9649
9650 static const ModeType
9651 MatteEditCommands[] =
9652 {
9653 MatteEditMethod,
9654 MatteEditBorderCommand,
9655 MatteEditFuzzCommand,
9656 MatteEditValueCommand,
9657 MatteEditUndoCommand,
9658 MatteEditHelpCommand,
9659 MatteEditDismissCommand
9660 };
9661
9662 static PaintMethod
9663 method = PointMethod;
9664
9665 static XColor
9666 border_color = { 0, 0, 0, 0, 0, 0 };
9667
9668 char
9669 command[MagickPathExtent],
9670 text[MagickPathExtent];
9671
9672 Cursor
9673 cursor;
9674
9675 int
9676 entry,
9677 id,
9678 x,
9679 x_offset,
9680 y,
9681 y_offset;
9682
9683 int
9684 i;
9685
9686 Quantum
9687 *q;
9688
9689 unsigned int
9690 height,
9691 width;
9692
9693 size_t
9694 state;
9695
9696 XEvent
9697 event;
9698
9699 /*
9700 Map Command widget.
9701 */
9702 (void) CloneString(&windows->command.name,"Matte Edit");
9703 windows->command.data=4;
9704 (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9705 (void) XMapRaised(display,windows->command.id);
9706 XClientMessage(display,windows->image.id,windows->im_protocols,
9707 windows->im_update_widget,CurrentTime);
9708 /*
9709 Make cursor.
9710 */
9711 cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9712 resource_info->background_color,resource_info->foreground_color);
9713 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9714 /*
9715 Track pointer until button 1 is pressed.
9716 */
9717 XQueryPosition(display,windows->image.id,&x,&y);
9718 (void) XSelectInput(display,windows->image.id,
9719 windows->image.attributes.event_mask | PointerMotionMask);
9720 state=DefaultState;
9721 do
9722 {
9723 if (windows->info.mapped != MagickFalse)
9724 {
9725 /*
9726 Display pointer position.
9727 */
9728 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
9729 x+windows->image.x,y+windows->image.y);
9730 XInfoWidget(display,windows,text);
9731 }
9732 /*
9733 Wait for next event.
9734 */
9735 XScreenEvent(display,windows,&event,exception);
9736 if (event.xany.window == windows->command.id)
9737 {
9738 /*
9739 Select a command from the Command widget.
9740 */
9741 id=XCommandWidget(display,windows,MatteEditMenu,&event);
9742 if (id < 0)
9743 {
9744 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9745 continue;
9746 }
9747 switch (MatteEditCommands[id])
9748 {
9749 case MatteEditMethod:
9750 {
9751 char
9752 **methods;
9753
9754 /*
9755 Select a method from the pop-up menu.
9756 */
9757 methods=GetCommandOptions(MagickMethodOptions);
9758 if (methods == (char **) NULL)
9759 break;
9760 entry=XMenuWidget(display,windows,MatteEditMenu[id],
9761 (const char **) methods,command);
9762 if (entry >= 0)
9763 method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9764 MagickFalse,methods[entry]);
9765 methods=DestroyStringList(methods);
9766 break;
9767 }
9768 case MatteEditBorderCommand:
9769 {
9770 const char
9771 *ColorMenu[MaxNumberPens];
9772
9773 int
9774 pen_number;
9775
9776 /*
9777 Initialize menu selections.
9778 */
9779 for (i=0; i < (int) (MaxNumberPens-2); i++)
9780 ColorMenu[i]=resource_info->pen_colors[i];
9781 ColorMenu[MaxNumberPens-2]="Browser...";
9782 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9783 /*
9784 Select a pen color from the pop-up menu.
9785 */
9786 pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9787 (const char **) ColorMenu,command);
9788 if (pen_number < 0)
9789 break;
9790 if (pen_number == (MaxNumberPens-2))
9791 {
9792 static char
9793 color_name[MagickPathExtent] = "gray";
9794
9795 /*
9796 Select a pen color from a dialog.
9797 */
9798 resource_info->pen_colors[pen_number]=color_name;
9799 XColorBrowserWidget(display,windows,"Select",color_name);
9800 if (*color_name == '\0')
9801 break;
9802 }
9803 /*
9804 Set border color.
9805 */
9806 (void) XParseColor(display,windows->map_info->colormap,
9807 resource_info->pen_colors[pen_number],&border_color);
9808 break;
9809 }
9810 case MatteEditFuzzCommand:
9811 {
9812 const char
9813 *const FuzzMenu[] =
9814 {
9815 "0%",
9816 "2%",
9817 "5%",
9818 "10%",
9819 "15%",
9820 "Dialog...",
9821 (char *) NULL,
9822 };
9823
9824 static char
9825 fuzz[MagickPathExtent];
9826
9827 /*
9828 Select a command from the pop-up menu.
9829 */
9830 entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9831 command);
9832 if (entry < 0)
9833 break;
9834 if (entry != 5)
9835 {
9836 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9837 QuantumRange+1.0);
9838 break;
9839 }
9840 (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
9841 (void) XDialogWidget(display,windows,"Ok",
9842 "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9843 if (*fuzz == '\0')
9844 break;
9845 (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
9846 (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9847 1.0);
9848 break;
9849 }
9850 case MatteEditValueCommand:
9851 {
9852 const char
9853 *const MatteMenu[] =
9854 {
9855 "Opaque",
9856 "Transparent",
9857 "Dialog...",
9858 (char *) NULL,
9859 };
9860
9861 static char
9862 message[MagickPathExtent];
9863
9864 /*
9865 Select a command from the pop-up menu.
9866 */
9867 entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9868 command);
9869 if (entry < 0)
9870 break;
9871 if (entry != 2)
9872 {
9873 (void) FormatLocaleString(matte,MagickPathExtent,"%g",
9874 (double) OpaqueAlpha);
9875 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9876 (void) FormatLocaleString(matte,MagickPathExtent,"%g",
9877 (double) TransparentAlpha);
9878 break;
9879 }
9880 (void) FormatLocaleString(message,MagickPathExtent,
9881 "Enter matte value (0 - " "%g" "):",(double) QuantumRange);
9882 (void) XDialogWidget(display,windows,"Matte",message,matte);
9883 if (*matte == '\0')
9884 break;
9885 break;
9886 }
9887 case MatteEditUndoCommand:
9888 {
9889 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9890 image,exception);
9891 break;
9892 }
9893 case MatteEditHelpCommand:
9894 {
9895 XTextViewHelp(display,resource_info,windows,MagickFalse,
9896 "Help Viewer - Matte Edit",ImageMatteEditHelp);
9897 break;
9898 }
9899 case MatteEditDismissCommand:
9900 {
9901 /*
9902 Prematurely exit.
9903 */
9904 state|=EscapeState;
9905 state|=ExitState;
9906 break;
9907 }
9908 default:
9909 break;
9910 }
9911 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9912 continue;
9913 }
9914 switch (event.type)
9915 {
9916 case ButtonPress:
9917 {
9918 if (event.xbutton.button != Button1)
9919 break;
9920 if ((event.xbutton.window != windows->image.id) &&
9921 (event.xbutton.window != windows->magnify.id))
9922 break;
9923 /*
9924 Update matte data.
9925 */
9926 x=event.xbutton.x;
9927 y=event.xbutton.y;
9928 (void) XMagickCommand(display,resource_info,windows,
9929 SaveToUndoBufferCommand,image,exception);
9930 state|=UpdateConfigurationState;
9931 break;
9932 }
9933 case ButtonRelease:
9934 {
9935 if (event.xbutton.button != Button1)
9936 break;
9937 if ((event.xbutton.window != windows->image.id) &&
9938 (event.xbutton.window != windows->magnify.id))
9939 break;
9940 /*
9941 Update colormap information.
9942 */
9943 x=event.xbutton.x;
9944 y=event.xbutton.y;
9945 XConfigureImageColormap(display,resource_info,windows,*image,exception);
9946 (void) XConfigureImage(display,resource_info,windows,*image,exception);
9947 XInfoWidget(display,windows,text);
9948 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9949 state&=(unsigned int) (~UpdateConfigurationState);
9950 break;
9951 }
9952 case Expose:
9953 break;
9954 case KeyPress:
9955 {
9956 char
9957 command[MagickPathExtent];
9958
9959 KeySym
9960 key_symbol;
9961
9962 if (event.xkey.window == windows->magnify.id)
9963 {
9964 Window
9965 window;
9966
9967 window=windows->magnify.id;
9968 while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9969 }
9970 if (event.xkey.window != windows->image.id)
9971 break;
9972 /*
9973 Respond to a user key press.
9974 */
9975 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9976 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9977 switch ((int) key_symbol)
9978 {
9979 case XK_Escape:
9980 case XK_F20:
9981 {
9982 /*
9983 Prematurely exit.
9984 */
9985 state|=ExitState;
9986 break;
9987 }
9988 case XK_F1:
9989 case XK_Help:
9990 {
9991 XTextViewHelp(display,resource_info,windows,MagickFalse,
9992 "Help Viewer - Matte Edit",ImageMatteEditHelp);
9993 break;
9994 }
9995 default:
9996 {
9997 (void) XBell(display,0);
9998 break;
9999 }
10000 }
10001 break;
10002 }
10003 case MotionNotify:
10004 {
10005 /*
10006 Map and unmap Info widget as cursor crosses its boundaries.
10007 */
10008 x=event.xmotion.x;
10009 y=event.xmotion.y;
10010 if (windows->info.mapped != MagickFalse)
10011 {
10012 if ((x < (windows->info.x+(int) windows->info.width)) &&
10013 (y < (windows->info.y+(int) windows->info.height)))
10014 (void) XWithdrawWindow(display,windows->info.id,
10015 windows->info.screen);
10016 }
10017 else
10018 if ((x > (windows->info.x+(int) windows->info.width)) ||
10019 (y > (windows->info.y+(int) windows->info.height)))
10020 (void) XMapWindow(display,windows->info.id);
10021 break;
10022 }
10023 default:
10024 break;
10025 }
10026 if (event.xany.window == windows->magnify.id)
10027 {
10028 x=windows->magnify.x-windows->image.x;
10029 y=windows->magnify.y-windows->image.y;
10030 }
10031 x_offset=x;
10032 y_offset=y;
10033 if ((state & UpdateConfigurationState) != 0)
10034 {
10035 CacheView
10036 *image_view;
10037
10038 int
10039 x,
10040 y;
10041
10042 /*
10043 Matte edit is relative to image configuration.
10044 */
10045 (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10046 MagickTrue);
10047 XPutPixel(windows->image.ximage,x_offset,y_offset,
10048 windows->pixel_info->background_color.pixel);
10049 width=(unsigned int) (*image)->columns;
10050 height=(unsigned int) (*image)->rows;
10051 x=0;
10052 y=0;
10053 if (windows->image.crop_geometry != (char *) NULL)
10054 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10055 &height);
10056 x_offset=((int) width*(windows->image.x+x_offset)/
10057 windows->image.ximage->width+x);
10058 y_offset=((int) height*(windows->image.y+y_offset)/
10059 windows->image.ximage->height+y);
10060 if ((x_offset < 0) || (y_offset < 0))
10061 continue;
10062 if ((x_offset >= (int) (*image)->columns) ||
10063 (y_offset >= (int) (*image)->rows))
10064 continue;
10065 if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10066 return(MagickFalse);
10067 if ((*image)->alpha_trait == UndefinedPixelTrait)
10068 (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
10069 image_view=AcquireAuthenticCacheView(*image,exception);
10070 switch (method)
10071 {
10072 case PointMethod:
10073 default:
10074 {
10075 /*
10076 Update matte information using point algorithm.
10077 */
10078 q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10079 (ssize_t) y_offset,1,1,exception);
10080 if (q == (Quantum *) NULL)
10081 break;
10082 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10083 (void) SyncCacheViewAuthenticPixels(image_view,exception);
10084 break;
10085 }
10086 case ReplaceMethod:
10087 {
10088 PixelInfo
10089 pixel,
10090 target;
10091
10092 /*
10093 Update matte information using replace algorithm.
10094 */
10095 (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
10096 x_offset,(ssize_t) y_offset,&target,exception);
10097 for (y=0; y < (int) (*image)->rows; y++)
10098 {
10099 q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10100 (*image)->columns,1,exception);
10101 if (q == (Quantum *) NULL)
10102 break;
10103 for (x=0; x < (int) (*image)->columns; x++)
10104 {
10105 GetPixelInfoPixel(*image,q,&pixel);
10106 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10107 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10108 q+=GetPixelChannels(*image);
10109 }
10110 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10111 break;
10112 }
10113 break;
10114 }
10115 case FloodfillMethod:
10116 case FillToBorderMethod:
10117 {
10118 ChannelType
10119 channel_mask;
10120
10121 DrawInfo
10122 *draw_info;
10123
10124 PixelInfo
10125 target;
10126
10127 /*
10128 Update matte information using floodfill algorithm.
10129 */
10130 (void) GetOneVirtualPixelInfo(*image,
10131 GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10132 y_offset,&target,exception);
10133 if (method == FillToBorderMethod)
10134 {
10135 target.red=(double) ScaleShortToQuantum(
10136 border_color.red);
10137 target.green=(double) ScaleShortToQuantum(
10138 border_color.green);
10139 target.blue=(double) ScaleShortToQuantum(
10140 border_color.blue);
10141 }
10142 draw_info=CloneDrawInfo(resource_info->image_info,
10143 (DrawInfo *) NULL);
10144 draw_info->fill.alpha=(double) ClampToQuantum(
10145 StringToDouble(matte,(char **) NULL));
10146 channel_mask=SetImageChannelMask(*image,AlphaChannel);
10147 (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10148 x_offset,(ssize_t) y_offset,
10149 method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
10150 (void) SetPixelChannelMask(*image,channel_mask);
10151 draw_info=DestroyDrawInfo(draw_info);
10152 break;
10153 }
10154 case ResetMethod:
10155 {
10156 /*
10157 Update matte information using reset algorithm.
10158 */
10159 if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10160 return(MagickFalse);
10161 for (y=0; y < (int) (*image)->rows; y++)
10162 {
10163 q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10164 (*image)->columns,1,exception);
10165 if (q == (Quantum *) NULL)
10166 break;
10167 for (x=0; x < (int) (*image)->columns; x++)
10168 {
10169 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10170 q+=GetPixelChannels(*image);
10171 }
10172 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10173 break;
10174 }
10175 if (StringToLong(matte) == (long) OpaqueAlpha)
10176 (*image)->alpha_trait=UndefinedPixelTrait;
10177 break;
10178 }
10179 }
10180 image_view=DestroyCacheView(image_view);
10181 state&=(unsigned int) (~UpdateConfigurationState);
10182 }
10183 } while ((state & ExitState) == 0);
10184 (void) XSelectInput(display,windows->image.id,
10185 windows->image.attributes.event_mask);
10186 XSetCursorState(display,windows,MagickFalse);
10187 (void) XFreeCursor(display,cursor);
10188 return(MagickTrue);
10189}
10190
10191/*
10192%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10193% %
10194% %
10195% %
10196+ X O p e n I m a g e %
10197% %
10198% %
10199% %
10200%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10201%
10202% XOpenImage() loads an image from a file.
10203%
10204% The format of the XOpenImage method is:
10205%
10206% Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10207% XWindows *windows,const unsigned int command)
10208%
10209% A description of each parameter follows:
10210%
10211% o display: Specifies a connection to an X server; returned from
10212% XOpenDisplay.
10213%
10214% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10215%
10216% o windows: Specifies a pointer to a XWindows structure.
10217%
10218% o command: A value other than zero indicates that the file is selected
10219% from the command line argument list.
10220%
10221*/
10222static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10223 XWindows *windows,const MagickBooleanType command)
10224{
10225 const MagickInfo
10226 *magick_info;
10227
10229 *exception;
10230
10231 Image
10232 *nexus;
10233
10234 ImageInfo
10235 *image_info;
10236
10237 static char
10238 filename[MagickPathExtent] = "\0";
10239
10240 /*
10241 Request file name from user.
10242 */
10243 if (command == MagickFalse)
10244 XFileBrowserWidget(display,windows,"Open",filename);
10245 else
10246 {
10247 char
10248 **filelist,
10249 **files;
10250
10251 int
10252 count,
10253 status;
10254
10255 int
10256 i,
10257 j;
10258
10259 /*
10260 Select next image from the command line.
10261 */
10262 status=XGetCommand(display,windows->image.id,&files,&count);
10263 if (status == 0)
10264 {
10265 ThrowXWindowException(XServerError,"UnableToGetProperty","...");
10266 return((Image *) NULL);
10267 }
10268 filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10269 if (filelist == (char **) NULL)
10270 {
10271 ThrowXWindowException(ResourceLimitError,
10272 "MemoryAllocationFailed","...");
10273 (void) XFreeStringList(files);
10274 return((Image *) NULL);
10275 }
10276 j=0;
10277 for (i=1; i < count; i++)
10278 if (*files[i] != '-')
10279 filelist[j++]=files[i];
10280 filelist[j]=(char *) NULL;
10281 XListBrowserWidget(display,windows,&windows->widget,
10282 (const char **) filelist,"Load","Select Image to Load:",filename);
10283 filelist=(char **) RelinquishMagickMemory(filelist);
10284 (void) XFreeStringList(files);
10285 }
10286 if (*filename == '\0')
10287 return((Image *) NULL);
10288 image_info=CloneImageInfo(resource_info->image_info);
10289 (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10290 (void *) NULL);
10291 (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
10292 exception=AcquireExceptionInfo();
10293 (void) SetImageInfo(image_info,0,exception);
10294 if (LocaleCompare(image_info->magick,"X") == 0)
10295 {
10296 char
10297 seconds[MagickPathExtent];
10298
10299 /*
10300 User may want to delay the X server screen grab.
10301 */
10302 (void) CopyMagickString(seconds,"0",MagickPathExtent);
10303 (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10304 seconds);
10305 if (*seconds == '\0')
10306 return((Image *) NULL);
10307 XDelay(display,(size_t) (1000*StringToLong(seconds)));
10308 }
10309 magick_info=GetMagickInfo(image_info->magick,exception);
10310 if ((magick_info != (const MagickInfo *) NULL) &&
10311 GetMagickRawSupport(magick_info) == MagickTrue)
10312 {
10313 char
10314 geometry[MagickPathExtent];
10315
10316 /*
10317 Request image size from the user.
10318 */
10319 (void) CopyMagickString(geometry,"512x512",MagickPathExtent);
10320 if (image_info->size != (char *) NULL)
10321 (void) CopyMagickString(geometry,image_info->size,MagickPathExtent);
10322 (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10323 geometry);
10324 (void) CloneString(&image_info->size,geometry);
10325 }
10326 /*
10327 Load the image.
10328 */
10329 XSetCursorState(display,windows,MagickTrue);
10330 XCheckRefreshWindows(display,windows);
10331 (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
10332 nexus=ReadImage(image_info,exception);
10333 CatchException(exception);
10334 XSetCursorState(display,windows,MagickFalse);
10335 if (nexus != (Image *) NULL)
10336 XClientMessage(display,windows->image.id,windows->im_protocols,
10337 windows->im_next_image,CurrentTime);
10338 else
10339 {
10340 char
10341 *text,
10342 **textlist;
10343
10344 /*
10345 Unknown image format.
10346 */
10347 text=FileToString(filename,~0UL,exception);
10348 if (text == (char *) NULL)
10349 return((Image *) NULL);
10350 textlist=StringToList(text);
10351 if (textlist != (char **) NULL)
10352 {
10353 char
10354 title[MagickPathExtent];
10355
10356 int
10357 i;
10358
10359 (void) FormatLocaleString(title,MagickPathExtent,
10360 "Unknown format: %s",filename);
10361 XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10362 (const char **) textlist);
10363 for (i=0; textlist[i] != (char *) NULL; i++)
10364 textlist[i]=DestroyString(textlist[i]);
10365 textlist=(char **) RelinquishMagickMemory(textlist);
10366 }
10367 text=DestroyString(text);
10368 }
10369 exception=DestroyExceptionInfo(exception);
10370 image_info=DestroyImageInfo(image_info);
10371 return(nexus);
10372}
10373
10374/*
10375%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10376% %
10377% %
10378% %
10379+ X P a n I m a g e %
10380% %
10381% %
10382% %
10383%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10384%
10385% XPanImage() pans the image until the mouse button is released.
10386%
10387% The format of the XPanImage method is:
10388%
10389% void XPanImage(Display *display,XWindows *windows,XEvent *event,
10390% ExceptionInfo *exception)
10391%
10392% A description of each parameter follows:
10393%
10394% o display: Specifies a connection to an X server; returned from
10395% XOpenDisplay.
10396%
10397% o windows: Specifies a pointer to a XWindows structure.
10398%
10399% o event: Specifies a pointer to a XEvent structure. If it is NULL,
10400% the entire image is refreshed.
10401%
10402% o exception: return any errors or warnings in this structure.
10403%
10404*/
10405static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10406 ExceptionInfo *exception)
10407{
10408 char
10409 text[MagickPathExtent];
10410
10411 Cursor
10412 cursor;
10413
10414 double
10415 x_factor,
10416 y_factor;
10417
10419 pan_info;
10420
10421 size_t
10422 state;
10423
10424 /*
10425 Define cursor.
10426 */
10427 if ((windows->image.ximage->width > (int) windows->image.width) &&
10428 (windows->image.ximage->height > (int) windows->image.height))
10429 cursor=XCreateFontCursor(display,XC_fleur);
10430 else
10431 if (windows->image.ximage->width > (int) windows->image.width)
10432 cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10433 else
10434 if (windows->image.ximage->height > (int) windows->image.height)
10435 cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10436 else
10437 cursor=XCreateFontCursor(display,XC_arrow);
10438 (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10439 /*
10440 Pan image as pointer moves until the mouse button is released.
10441 */
10442 x_factor=(double) windows->image.ximage->width/windows->pan.width;
10443 y_factor=(double) windows->image.ximage->height/windows->pan.height;
10444 pan_info.width=windows->pan.width*windows->image.width/
10445 (unsigned int) windows->image.ximage->width;
10446 pan_info.height=windows->pan.height*windows->image.height/
10447 (unsigned int) windows->image.ximage->height;
10448 pan_info.x=0;
10449 pan_info.y=0;
10450 state=UpdateConfigurationState;
10451 do
10452 {
10453 switch (event->type)
10454 {
10455 case ButtonPress:
10456 {
10457 /*
10458 User choose an initial pan location.
10459 */
10460 pan_info.x=(ssize_t) event->xbutton.x;
10461 pan_info.y=(ssize_t) event->xbutton.y;
10462 state|=UpdateConfigurationState;
10463 break;
10464 }
10465 case ButtonRelease:
10466 {
10467 /*
10468 User has finished panning the image.
10469 */
10470 pan_info.x=(ssize_t) event->xbutton.x;
10471 pan_info.y=(ssize_t) event->xbutton.y;
10472 state|=UpdateConfigurationState | ExitState;
10473 break;
10474 }
10475 case MotionNotify:
10476 {
10477 pan_info.x=(ssize_t) event->xmotion.x;
10478 pan_info.y=(ssize_t) event->xmotion.y;
10479 state|=UpdateConfigurationState;
10480 }
10481 default:
10482 break;
10483 }
10484 if ((state & UpdateConfigurationState) != 0)
10485 {
10486 /*
10487 Check boundary conditions.
10488 */
10489 if (pan_info.x < (ssize_t) (pan_info.width/2))
10490 pan_info.x=0;
10491 else
10492 pan_info.x=(x_factor*(pan_info.x-((int) pan_info.width/2)));
10493 if (pan_info.x < 0)
10494 pan_info.x=0;
10495 else
10496 if ((int) (pan_info.x+windows->image.width) >
10497 windows->image.ximage->width)
10498 pan_info.x=windows->image.ximage->width-(int) windows->image.width;
10499 if (pan_info.y < (ssize_t) (pan_info.height/2))
10500 pan_info.y=0;
10501 else
10502 pan_info.y=(y_factor*(pan_info.y-((int) pan_info.height/2)));
10503 if (pan_info.y < 0)
10504 pan_info.y=0;
10505 else
10506 if ((int) (pan_info.y+windows->image.height) >
10507 windows->image.ximage->height)
10508 pan_info.y=windows->image.ximage->height-(int)
10509 windows->image.height;
10510 if ((windows->image.x != (int) pan_info.x) ||
10511 (windows->image.y != (int) pan_info.y))
10512 {
10513 /*
10514 Display image pan offset.
10515 */
10516 windows->image.x=(int) pan_info.x;
10517 windows->image.y=(int) pan_info.y;
10518 (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
10519 windows->image.width,windows->image.height,windows->image.x,
10520 windows->image.y);
10521 XInfoWidget(display,windows,text);
10522 /*
10523 Refresh Image window.
10524 */
10525 XDrawPanRectangle(display,windows);
10526 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10527 }
10528 state&=(unsigned int) (~UpdateConfigurationState);
10529 }
10530 /*
10531 Wait for next event.
10532 */
10533 if ((state & ExitState) == 0)
10534 XScreenEvent(display,windows,event,exception);
10535 } while ((state & ExitState) == 0);
10536 /*
10537 Restore cursor.
10538 */
10539 (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10540 (void) XFreeCursor(display,cursor);
10541 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10542}
10543
10544/*
10545%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10546% %
10547% %
10548% %
10549+ X P a s t e I m a g e %
10550% %
10551% %
10552% %
10553%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10554%
10555% XPasteImage() pastes an image previously saved with XCropImage in the X
10556% window image at a location the user chooses with the pointer.
10557%
10558% The format of the XPasteImage method is:
10559%
10560% MagickBooleanType XPasteImage(Display *display,
10561% XResourceInfo *resource_info,XWindows *windows,Image *image,
10562% ExceptionInfo *exception)
10563%
10564% A description of each parameter follows:
10565%
10566% o display: Specifies a connection to an X server; returned from
10567% XOpenDisplay.
10568%
10569% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10570%
10571% o windows: Specifies a pointer to a XWindows structure.
10572%
10573% o image: the image; returned from ReadImage.
10574%
10575% o exception: return any errors or warnings in this structure.
10576%
10577*/
10578static MagickBooleanType XPasteImage(Display *display,
10579 XResourceInfo *resource_info,XWindows *windows,Image *image,
10580 ExceptionInfo *exception)
10581{
10582 const char
10583 *const PasteMenu[] =
10584 {
10585 "Operator",
10586 "Help",
10587 "Dismiss",
10588 (char *) NULL
10589 };
10590
10591 static const ModeType
10592 PasteCommands[] =
10593 {
10594 PasteOperatorsCommand,
10595 PasteHelpCommand,
10596 PasteDismissCommand
10597 };
10598
10599 static CompositeOperator
10600 compose = CopyCompositeOp;
10601
10602 char
10603 text[MagickPathExtent];
10604
10605 Cursor
10606 cursor;
10607
10608 Image
10609 *paste_image;
10610
10611 int
10612 entry,
10613 id,
10614 x,
10615 y;
10616
10617 double
10618 scale_factor;
10619
10621 highlight_info,
10622 paste_info;
10623
10624 unsigned int
10625 height,
10626 width;
10627
10628 size_t
10629 state;
10630
10631 XEvent
10632 event;
10633
10634 /*
10635 Copy image.
10636 */
10637 if (resource_info->copy_image == (Image *) NULL)
10638 return(MagickFalse);
10639 paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10640 if (paste_image == (Image *) NULL)
10641 return(MagickFalse);
10642 /*
10643 Map Command widget.
10644 */
10645 (void) CloneString(&windows->command.name,"Paste");
10646 windows->command.data=1;
10647 (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10648 (void) XMapRaised(display,windows->command.id);
10649 XClientMessage(display,windows->image.id,windows->im_protocols,
10650 windows->im_update_widget,CurrentTime);
10651 /*
10652 Track pointer until button 1 is pressed.
10653 */
10654 XSetCursorState(display,windows,MagickFalse);
10655 XQueryPosition(display,windows->image.id,&x,&y);
10656 (void) XSelectInput(display,windows->image.id,
10657 windows->image.attributes.event_mask | PointerMotionMask);
10658 paste_info.x=(ssize_t) windows->image.x+x;
10659 paste_info.y=(ssize_t) windows->image.y+y;
10660 paste_info.width=0;
10661 paste_info.height=0;
10662 cursor=XCreateFontCursor(display,XC_ul_angle);
10663 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10664 state=DefaultState;
10665 do
10666 {
10667 if (windows->info.mapped != MagickFalse)
10668 {
10669 /*
10670 Display pointer position.
10671 */
10672 (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
10673 (long) paste_info.x,(long) paste_info.y);
10674 XInfoWidget(display,windows,text);
10675 }
10676 highlight_info=paste_info;
10677 highlight_info.x=paste_info.x-windows->image.x;
10678 highlight_info.y=paste_info.y-windows->image.y;
10679 XHighlightRectangle(display,windows->image.id,
10680 windows->image.highlight_context,&highlight_info);
10681 /*
10682 Wait for next event.
10683 */
10684 XScreenEvent(display,windows,&event,exception);
10685 XHighlightRectangle(display,windows->image.id,
10686 windows->image.highlight_context,&highlight_info);
10687 if (event.xany.window == windows->command.id)
10688 {
10689 /*
10690 Select a command from the Command widget.
10691 */
10692 id=XCommandWidget(display,windows,PasteMenu,&event);
10693 if (id < 0)
10694 continue;
10695 switch (PasteCommands[id])
10696 {
10697 case PasteOperatorsCommand:
10698 {
10699 char
10700 command[MagickPathExtent],
10701 **operators;
10702
10703 /*
10704 Select a command from the pop-up menu.
10705 */
10706 operators=GetCommandOptions(MagickComposeOptions);
10707 if (operators == (char **) NULL)
10708 break;
10709 entry=XMenuWidget(display,windows,PasteMenu[id],
10710 (const char **) operators,command);
10711 if (entry >= 0)
10712 compose=(CompositeOperator) ParseCommandOption(
10713 MagickComposeOptions,MagickFalse,operators[entry]);
10714 operators=DestroyStringList(operators);
10715 break;
10716 }
10717 case PasteHelpCommand:
10718 {
10719 XTextViewHelp(display,resource_info,windows,MagickFalse,
10720 "Help Viewer - Image Composite",ImagePasteHelp);
10721 break;
10722 }
10723 case PasteDismissCommand:
10724 {
10725 /*
10726 Prematurely exit.
10727 */
10728 state|=EscapeState;
10729 state|=ExitState;
10730 break;
10731 }
10732 default:
10733 break;
10734 }
10735 continue;
10736 }
10737 switch (event.type)
10738 {
10739 case ButtonPress:
10740 {
10741 if (resource_info->debug != MagickFalse)
10742 (void) LogMagickEvent(X11Event,GetMagickModule(),
10743 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10744 event.xbutton.button,event.xbutton.x,event.xbutton.y);
10745 if (event.xbutton.button != Button1)
10746 break;
10747 if (event.xbutton.window != windows->image.id)
10748 break;
10749 /*
10750 Paste rectangle is relative to image configuration.
10751 */
10752 width=(unsigned int) image->columns;
10753 height=(unsigned int) image->rows;
10754 x=0;
10755 y=0;
10756 if (windows->image.crop_geometry != (char *) NULL)
10757 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10758 &width,&height);
10759 scale_factor=(double) windows->image.ximage->width/width;
10760 paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10761 scale_factor=(double) windows->image.ximage->height/height;
10762 paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10763 (void) XCheckDefineCursor(display,windows->image.id,cursor);
10764 paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10765 paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10766 break;
10767 }
10768 case ButtonRelease:
10769 {
10770 if (resource_info->debug != MagickFalse)
10771 (void) LogMagickEvent(X11Event,GetMagickModule(),
10772 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10773 event.xbutton.button,event.xbutton.x,event.xbutton.y);
10774 if (event.xbutton.button != Button1)
10775 break;
10776 if (event.xbutton.window != windows->image.id)
10777 break;
10778 if ((paste_info.width != 0) && (paste_info.height != 0))
10779 {
10780 /*
10781 User has selected the location of the paste image.
10782 */
10783 paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10784 paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10785 state|=ExitState;
10786 }
10787 break;
10788 }
10789 case Expose:
10790 break;
10791 case KeyPress:
10792 {
10793 char
10794 command[MagickPathExtent];
10795
10796 KeySym
10797 key_symbol;
10798
10799 int
10800 length;
10801
10802 if (event.xkey.window != windows->image.id)
10803 break;
10804 /*
10805 Respond to a user key press.
10806 */
10807 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10808 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10809 *(command+length)='\0';
10810 if (resource_info->debug != MagickFalse)
10811 (void) LogMagickEvent(X11Event,GetMagickModule(),
10812 "Key press: 0x%lx (%s)",(long) key_symbol,command);
10813 switch ((int) key_symbol)
10814 {
10815 case XK_Escape:
10816 case XK_F20:
10817 {
10818 /*
10819 Prematurely exit.
10820 */
10821 paste_image=DestroyImage(paste_image);
10822 state|=EscapeState;
10823 state|=ExitState;
10824 break;
10825 }
10826 case XK_F1:
10827 case XK_Help:
10828 {
10829 (void) XSetFunction(display,windows->image.highlight_context,
10830 GXcopy);
10831 XTextViewHelp(display,resource_info,windows,MagickFalse,
10832 "Help Viewer - Image Composite",ImagePasteHelp);
10833 (void) XSetFunction(display,windows->image.highlight_context,
10834 GXinvert);
10835 break;
10836 }
10837 default:
10838 {
10839 (void) XBell(display,0);
10840 break;
10841 }
10842 }
10843 break;
10844 }
10845 case MotionNotify:
10846 {
10847 /*
10848 Map and unmap Info widget as text cursor crosses its boundaries.
10849 */
10850 x=event.xmotion.x;
10851 y=event.xmotion.y;
10852 if (windows->info.mapped != MagickFalse)
10853 {
10854 if ((x < (windows->info.x+(int) windows->info.width)) &&
10855 (y < (windows->info.y+(int) windows->info.height)))
10856 (void) XWithdrawWindow(display,windows->info.id,
10857 windows->info.screen);
10858 }
10859 else
10860 if ((x > (windows->info.x+(int) windows->info.width)) ||
10861 (y > (windows->info.y+(int) windows->info.height)))
10862 (void) XMapWindow(display,windows->info.id);
10863 paste_info.x=(ssize_t) windows->image.x+x;
10864 paste_info.y=(ssize_t) windows->image.y+y;
10865 break;
10866 }
10867 default:
10868 {
10869 if (resource_info->debug != MagickFalse)
10870 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10871 event.type);
10872 break;
10873 }
10874 }
10875 } while ((state & ExitState) == 0);
10876 (void) XSelectInput(display,windows->image.id,
10877 windows->image.attributes.event_mask);
10878 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10879 XSetCursorState(display,windows,MagickFalse);
10880 (void) XFreeCursor(display,cursor);
10881 if ((state & EscapeState) != 0)
10882 return(MagickTrue);
10883 /*
10884 Image pasting is relative to image configuration.
10885 */
10886 XSetCursorState(display,windows,MagickTrue);
10887 XCheckRefreshWindows(display,windows);
10888 width=(unsigned int) image->columns;
10889 height=(unsigned int) image->rows;
10890 x=0;
10891 y=0;
10892 if (windows->image.crop_geometry != (char *) NULL)
10893 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10894 scale_factor=(double) width/windows->image.ximage->width;
10895 paste_info.x+=x;
10896 paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10897 paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10898 scale_factor=(double) height/windows->image.ximage->height;
10899 paste_info.y+=y;
10900 paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10901 paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10902 /*
10903 Paste image with X Image window.
10904 */
10905 (void) CompositeImage(image,paste_image,compose,MagickTrue,paste_info.x,
10906 paste_info.y,exception);
10907 paste_image=DestroyImage(paste_image);
10908 XSetCursorState(display,windows,MagickFalse);
10909 /*
10910 Update image colormap.
10911 */
10912 XConfigureImageColormap(display,resource_info,windows,image,exception);
10913 (void) XConfigureImage(display,resource_info,windows,image,exception);
10914 return(MagickTrue);
10915}
10916
10917/*
10918%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10919% %
10920% %
10921% %
10922+ X P r i n t I m a g e %
10923% %
10924% %
10925% %
10926%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10927%
10928% XPrintImage() prints an image to a Postscript printer.
10929%
10930% The format of the XPrintImage method is:
10931%
10932% MagickBooleanType XPrintImage(Display *display,
10933% XResourceInfo *resource_info,XWindows *windows,Image *image,
10934% ExceptionInfo *exception)
10935%
10936% A description of each parameter follows:
10937%
10938% o display: Specifies a connection to an X server; returned from
10939% XOpenDisplay.
10940%
10941% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10942%
10943% o windows: Specifies a pointer to a XWindows structure.
10944%
10945% o image: the image.
10946%
10947% o exception: return any errors or warnings in this structure.
10948%
10949*/
10950static MagickBooleanType XPrintImage(Display *display,
10951 XResourceInfo *resource_info,XWindows *windows,Image *image,
10952 ExceptionInfo *exception)
10953{
10954 char
10955 filename[MagickPathExtent],
10956 geometry[MagickPathExtent];
10957
10958 const char
10959 *const PageSizes[] =
10960 {
10961 "Letter",
10962 "Tabloid",
10963 "Ledger",
10964 "Legal",
10965 "Statement",
10966 "Executive",
10967 "A3",
10968 "A4",
10969 "A5",
10970 "B4",
10971 "B5",
10972 "Folio",
10973 "Quarto",
10974 "10x14",
10975 (char *) NULL
10976 };
10977
10978 Image
10979 *print_image;
10980
10981 ImageInfo
10982 *image_info;
10983
10984 MagickStatusType
10985 status;
10986
10987 /*
10988 Request Postscript page geometry from user.
10989 */
10990 image_info=CloneImageInfo(resource_info->image_info);
10991 (void) FormatLocaleString(geometry,MagickPathExtent,"Letter");
10992 if (image_info->page != (char *) NULL)
10993 (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
10994 XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10995 "Select Postscript Page Geometry:",geometry);
10996 if (*geometry == '\0')
10997 return(MagickTrue);
10998 image_info->page=GetPageGeometry(geometry);
10999 /*
11000 Apply image transforms.
11001 */
11002 XSetCursorState(display,windows,MagickTrue);
11003 XCheckRefreshWindows(display,windows);
11004 print_image=CloneImage(image,0,0,MagickTrue,exception);
11005 if (print_image == (Image *) NULL)
11006 return(MagickFalse);
11007 (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
11008 windows->image.ximage->width,windows->image.ximage->height);
11009 (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11010 exception);
11011 /*
11012 Print image.
11013 */
11014 (void) AcquireUniqueFilename(filename);
11015 (void) FormatLocaleString(print_image->filename,MagickPathExtent,"print:%s",
11016 filename);
11017 status=WriteImage(image_info,print_image,exception);
11018 (void) RelinquishUniqueFileResource(filename);
11019 print_image=DestroyImage(print_image);
11020 image_info=DestroyImageInfo(image_info);
11021 XSetCursorState(display,windows,MagickFalse);
11022 return(status != 0 ? MagickTrue : MagickFalse);
11023}
11024
11025/*
11026%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11027% %
11028% %
11029% %
11030+ X R O I I m a g e %
11031% %
11032% %
11033% %
11034%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11035%
11036% XROIImage() applies an image processing technique to a region of interest.
11037%
11038% The format of the XROIImage method is:
11039%
11040% MagickBooleanType XROIImage(Display *display,
11041% XResourceInfo *resource_info,XWindows *windows,Image **image,
11042% ExceptionInfo *exception)
11043%
11044% A description of each parameter follows:
11045%
11046% o display: Specifies a connection to an X server; returned from
11047% XOpenDisplay.
11048%
11049% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11050%
11051% o windows: Specifies a pointer to a XWindows structure.
11052%
11053% o image: the image; returned from ReadImage.
11054%
11055% o exception: return any errors or warnings in this structure.
11056%
11057*/
11058static MagickBooleanType XROIImage(Display *display,
11059 XResourceInfo *resource_info,XWindows *windows,Image **image,
11060 ExceptionInfo *exception)
11061{
11062#define ApplyMenus 7
11063
11064 const char
11065 *const ROIMenu[] =
11066 {
11067 "Help",
11068 "Dismiss",
11069 (char *) NULL
11070 },
11071 *const ApplyMenu[] =
11072 {
11073 "File",
11074 "Edit",
11075 "Transform",
11076 "Enhance",
11077 "Effects",
11078 "F/X",
11079 "Miscellany",
11080 "Help",
11081 "Dismiss",
11082 (char *) NULL
11083 },
11084 *const FileMenu[] =
11085 {
11086 "Save...",
11087 "Print...",
11088 (char *) NULL
11089 },
11090 *const EditMenu[] =
11091 {
11092 "Undo",
11093 "Redo",
11094 (char *) NULL
11095 },
11096 *const TransformMenu[] =
11097 {
11098 "Flop",
11099 "Flip",
11100 "Rotate Right",
11101 "Rotate Left",
11102 (char *) NULL
11103 },
11104 *const EnhanceMenu[] =
11105 {
11106 "Hue...",
11107 "Saturation...",
11108 "Brightness...",
11109 "Gamma...",
11110 "Spiff",
11111 "Dull",
11112 "Contrast Stretch...",
11113 "Sigmoidal Contrast...",
11114 "Normalize",
11115 "Equalize",
11116 "Negate",
11117 "Grayscale",
11118 "Map...",
11119 "Quantize...",
11120 (char *) NULL
11121 },
11122 *const EffectsMenu[] =
11123 {
11124 "Despeckle",
11125 "Emboss",
11126 "Reduce Noise",
11127 "Add Noise",
11128 "Sharpen...",
11129 "Blur...",
11130 "Threshold...",
11131 "Edge Detect...",
11132 "Spread...",
11133 "Shade...",
11134 "Raise...",
11135 "Segment...",
11136 (char *) NULL
11137 },
11138 *const FXMenu[] =
11139 {
11140 "Solarize...",
11141 "Sepia Tone...",
11142 "Swirl...",
11143 "Implode...",
11144 "Vignette...",
11145 "Wave...",
11146 "Oil Paint...",
11147 "Charcoal Draw...",
11148 (char *) NULL
11149 },
11150 *const MiscellanyMenu[] =
11151 {
11152 "Image Info",
11153 "Zoom Image",
11154 "Show Preview...",
11155 "Show Histogram",
11156 "Show Matte",
11157 (char *) NULL
11158 };
11159
11160 const char
11161 *const *Menus[ApplyMenus] =
11162 {
11163 FileMenu,
11164 EditMenu,
11165 TransformMenu,
11166 EnhanceMenu,
11167 EffectsMenu,
11168 FXMenu,
11169 MiscellanyMenu
11170 };
11171
11172 static const DisplayCommand
11173 ApplyCommands[] =
11174 {
11175 NullCommand,
11176 NullCommand,
11177 NullCommand,
11178 NullCommand,
11179 NullCommand,
11180 NullCommand,
11181 NullCommand,
11182 HelpCommand,
11183 QuitCommand
11184 },
11185 FileCommands[] =
11186 {
11187 SaveCommand,
11188 PrintCommand
11189 },
11190 EditCommands[] =
11191 {
11192 UndoCommand,
11193 RedoCommand
11194 },
11195 TransformCommands[] =
11196 {
11197 FlopCommand,
11198 FlipCommand,
11199 RotateRightCommand,
11200 RotateLeftCommand
11201 },
11202 EnhanceCommands[] =
11203 {
11204 HueCommand,
11205 SaturationCommand,
11206 BrightnessCommand,
11207 GammaCommand,
11208 SpiffCommand,
11209 DullCommand,
11210 ContrastStretchCommand,
11211 SigmoidalContrastCommand,
11212 NormalizeCommand,
11213 EqualizeCommand,
11214 NegateCommand,
11215 GrayscaleCommand,
11216 MapCommand,
11217 QuantizeCommand
11218 },
11219 EffectsCommands[] =
11220 {
11221 DespeckleCommand,
11222 EmbossCommand,
11223 ReduceNoiseCommand,
11224 AddNoiseCommand,
11225 SharpenCommand,
11226 BlurCommand,
11227 ThresholdCommand,
11228 EdgeDetectCommand,
11229 SpreadCommand,
11230 ShadeCommand,
11231 RaiseCommand,
11232 SegmentCommand
11233 },
11234 FXCommands[] =
11235 {
11236 SolarizeCommand,
11237 SepiaToneCommand,
11238 SwirlCommand,
11239 ImplodeCommand,
11240 VignetteCommand,
11241 WaveCommand,
11242 OilPaintCommand,
11243 CharcoalDrawCommand
11244 },
11245 MiscellanyCommands[] =
11246 {
11247 InfoCommand,
11248 ZoomCommand,
11249 ShowPreviewCommand,
11250 ShowHistogramCommand,
11251 ShowMatteCommand
11252 },
11253 ROICommands[] =
11254 {
11255 ROIHelpCommand,
11256 ROIDismissCommand
11257 };
11258
11259 static const DisplayCommand
11260 *Commands[ApplyMenus] =
11261 {
11262 FileCommands,
11263 EditCommands,
11264 TransformCommands,
11265 EnhanceCommands,
11266 EffectsCommands,
11267 FXCommands,
11268 MiscellanyCommands
11269 };
11270
11271 char
11272 command[MagickPathExtent],
11273 text[MagickPathExtent];
11274
11275 DisplayCommand
11276 display_command;
11277
11278 Cursor
11279 cursor;
11280
11281 Image
11282 *roi_image;
11283
11284 int
11285 entry,
11286 id,
11287 x,
11288 y;
11289
11290 double
11291 scale_factor;
11292
11293 MagickProgressMonitor
11294 progress_monitor;
11295
11297 crop_info,
11298 highlight_info,
11299 roi_info;
11300
11301 unsigned int
11302 height,
11303 width;
11304
11305 size_t
11306 state;
11307
11308 XEvent
11309 event;
11310
11311 /*
11312 Map Command widget.
11313 */
11314 (void) CloneString(&windows->command.name,"ROI");
11315 windows->command.data=0;
11316 (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11317 (void) XMapRaised(display,windows->command.id);
11318 XClientMessage(display,windows->image.id,windows->im_protocols,
11319 windows->im_update_widget,CurrentTime);
11320 /*
11321 Track pointer until button 1 is pressed.
11322 */
11323 XQueryPosition(display,windows->image.id,&x,&y);
11324 (void) XSelectInput(display,windows->image.id,
11325 windows->image.attributes.event_mask | PointerMotionMask);
11326 roi_info.x=(ssize_t) windows->image.x+x;
11327 roi_info.y=(ssize_t) windows->image.y+y;
11328 roi_info.width=0;
11329 roi_info.height=0;
11330 cursor=XCreateFontCursor(display,XC_fleur);
11331 state=DefaultState;
11332 do
11333 {
11334 if (windows->info.mapped != MagickFalse)
11335 {
11336 /*
11337 Display pointer position.
11338 */
11339 (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
11340 (long) roi_info.x,(long) roi_info.y);
11341 XInfoWidget(display,windows,text);
11342 }
11343 /*
11344 Wait for next event.
11345 */
11346 XScreenEvent(display,windows,&event,exception);
11347 if (event.xany.window == windows->command.id)
11348 {
11349 /*
11350 Select a command from the Command widget.
11351 */
11352 id=XCommandWidget(display,windows,ROIMenu,&event);
11353 if (id < 0)
11354 continue;
11355 switch (ROICommands[id])
11356 {
11357 case ROIHelpCommand:
11358 {
11359 XTextViewHelp(display,resource_info,windows,MagickFalse,
11360 "Help Viewer - Region of Interest",ImageROIHelp);
11361 break;
11362 }
11363 case ROIDismissCommand:
11364 {
11365 /*
11366 Prematurely exit.
11367 */
11368 state|=EscapeState;
11369 state|=ExitState;
11370 break;
11371 }
11372 default:
11373 break;
11374 }
11375 continue;
11376 }
11377 switch (event.type)
11378 {
11379 case ButtonPress:
11380 {
11381 if (event.xbutton.button != Button1)
11382 break;
11383 if (event.xbutton.window != windows->image.id)
11384 break;
11385 /*
11386 Note first corner of region of interest rectangle-- exit loop.
11387 */
11388 (void) XCheckDefineCursor(display,windows->image.id,cursor);
11389 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11390 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11391 state|=ExitState;
11392 break;
11393 }
11394 case ButtonRelease:
11395 break;
11396 case Expose:
11397 break;
11398 case KeyPress:
11399 {
11400 KeySym
11401 key_symbol;
11402
11403 if (event.xkey.window != windows->image.id)
11404 break;
11405 /*
11406 Respond to a user key press.
11407 */
11408 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11409 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11410 switch ((int) key_symbol)
11411 {
11412 case XK_Escape:
11413 case XK_F20:
11414 {
11415 /*
11416 Prematurely exit.
11417 */
11418 state|=EscapeState;
11419 state|=ExitState;
11420 break;
11421 }
11422 case XK_F1:
11423 case XK_Help:
11424 {
11425 XTextViewHelp(display,resource_info,windows,MagickFalse,
11426 "Help Viewer - Region of Interest",ImageROIHelp);
11427 break;
11428 }
11429 default:
11430 {
11431 (void) XBell(display,0);
11432 break;
11433 }
11434 }
11435 break;
11436 }
11437 case MotionNotify:
11438 {
11439 /*
11440 Map and unmap Info widget as text cursor crosses its boundaries.
11441 */
11442 x=event.xmotion.x;
11443 y=event.xmotion.y;
11444 if (windows->info.mapped != MagickFalse)
11445 {
11446 if ((x < (windows->info.x+(int) windows->info.width)) &&
11447 (y < (windows->info.y+(int) windows->info.height)))
11448 (void) XWithdrawWindow(display,windows->info.id,
11449 windows->info.screen);
11450 }
11451 else
11452 if ((x > (windows->info.x+(int) windows->info.width)) ||
11453 (y > (windows->info.y+(int) windows->info.height)))
11454 (void) XMapWindow(display,windows->info.id);
11455 roi_info.x=(ssize_t) windows->image.x+x;
11456 roi_info.y=(ssize_t) windows->image.y+y;
11457 break;
11458 }
11459 default:
11460 break;
11461 }
11462 } while ((state & ExitState) == 0);
11463 (void) XSelectInput(display,windows->image.id,
11464 windows->image.attributes.event_mask);
11465 if ((state & EscapeState) != 0)
11466 {
11467 /*
11468 User want to exit without region of interest.
11469 */
11470 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11471 (void) XFreeCursor(display,cursor);
11472 return(MagickTrue);
11473 }
11474 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11475 do
11476 {
11477 /*
11478 Size rectangle as pointer moves until the mouse button is released.
11479 */
11480 x=(int) roi_info.x;
11481 y=(int) roi_info.y;
11482 roi_info.width=0;
11483 roi_info.height=0;
11484 state=DefaultState;
11485 do
11486 {
11487 highlight_info=roi_info;
11488 highlight_info.x=roi_info.x-windows->image.x;
11489 highlight_info.y=roi_info.y-windows->image.y;
11490 if ((highlight_info.width > 3) && (highlight_info.height > 3))
11491 {
11492 /*
11493 Display info and draw region of interest rectangle.
11494 */
11495 if (windows->info.mapped == MagickFalse)
11496 (void) XMapWindow(display,windows->info.id);
11497 (void) FormatLocaleString(text,MagickPathExtent,
11498 " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11499 roi_info.height,(double) roi_info.x,(double) roi_info.y);
11500 XInfoWidget(display,windows,text);
11501 XHighlightRectangle(display,windows->image.id,
11502 windows->image.highlight_context,&highlight_info);
11503 }
11504 else
11505 if (windows->info.mapped != MagickFalse)
11506 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11507 /*
11508 Wait for next event.
11509 */
11510 XScreenEvent(display,windows,&event,exception);
11511 if ((highlight_info.width > 3) && (highlight_info.height > 3))
11512 XHighlightRectangle(display,windows->image.id,
11513 windows->image.highlight_context,&highlight_info);
11514 switch (event.type)
11515 {
11516 case ButtonPress:
11517 {
11518 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11519 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11520 break;
11521 }
11522 case ButtonRelease:
11523 {
11524 /*
11525 User has committed to region of interest rectangle.
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 XSetCursorState(display,windows,MagickFalse);
11530 state|=ExitState;
11531 if (LocaleCompare(windows->command.name,"Apply") == 0)
11532 break;
11533 (void) CloneString(&windows->command.name,"Apply");
11534 windows->command.data=ApplyMenus;
11535 (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11536 break;
11537 }
11538 case Expose:
11539 break;
11540 case MotionNotify:
11541 {
11542 roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11543 roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11544 }
11545 default:
11546 break;
11547 }
11548 if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11549 ((state & ExitState) != 0))
11550 {
11551 /*
11552 Check boundary conditions.
11553 */
11554 if (roi_info.x < 0)
11555 roi_info.x=0;
11556 else
11557 if (roi_info.x > (ssize_t) windows->image.ximage->width)
11558 roi_info.x=(ssize_t) windows->image.ximage->width;
11559 if ((int) roi_info.x < x)
11560 roi_info.width=(unsigned int) (x-roi_info.x);
11561 else
11562 {
11563 roi_info.width=(unsigned int) (roi_info.x-x);
11564 roi_info.x=(ssize_t) x;
11565 }
11566 if (roi_info.y < 0)
11567 roi_info.y=0;
11568 else
11569 if (roi_info.y > (ssize_t) windows->image.ximage->height)
11570 roi_info.y=(ssize_t) windows->image.ximage->height;
11571 if ((int) roi_info.y < y)
11572 roi_info.height=(unsigned int) (y-roi_info.y);
11573 else
11574 {
11575 roi_info.height=(unsigned int) (roi_info.y-y);
11576 roi_info.y=(ssize_t) y;
11577 }
11578 }
11579 } while ((state & ExitState) == 0);
11580 /*
11581 Wait for user to grab a corner of the rectangle or press return.
11582 */
11583 state=DefaultState;
11584 display_command=NullCommand;
11585 crop_info.x=0;
11586 crop_info.y=0;
11587 (void) XMapWindow(display,windows->info.id);
11588 do
11589 {
11590 if (windows->info.mapped != MagickFalse)
11591 {
11592 /*
11593 Display pointer position.
11594 */
11595 (void) FormatLocaleString(text,MagickPathExtent,
11596 " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11597 roi_info.height,(double) roi_info.x,(double) roi_info.y);
11598 XInfoWidget(display,windows,text);
11599 }
11600 highlight_info=roi_info;
11601 highlight_info.x=roi_info.x-windows->image.x;
11602 highlight_info.y=roi_info.y-windows->image.y;
11603 if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11604 {
11605 state|=EscapeState;
11606 state|=ExitState;
11607 break;
11608 }
11609 if ((state & UpdateRegionState) != 0)
11610 {
11611 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11612 switch (display_command)
11613 {
11614 case UndoCommand:
11615 case RedoCommand:
11616 {
11617 (void) XMagickCommand(display,resource_info,windows,
11618 display_command,image,exception);
11619 break;
11620 }
11621 default:
11622 {
11623 /*
11624 Region of interest is relative to image configuration.
11625 */
11626 progress_monitor=SetImageProgressMonitor(*image,
11627 (MagickProgressMonitor) NULL,(*image)->client_data);
11628 crop_info=roi_info;
11629 width=(unsigned int) (*image)->columns;
11630 height=(unsigned int) (*image)->rows;
11631 x=0;
11632 y=0;
11633 if (windows->image.crop_geometry != (char *) NULL)
11634 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11635 &width,&height);
11636 scale_factor=(double) width/windows->image.ximage->width;
11637 crop_info.x+=x;
11638 crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11639 crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11640 scale_factor=(double)
11641 height/windows->image.ximage->height;
11642 crop_info.y+=y;
11643 crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11644 crop_info.height=(unsigned int)
11645 (scale_factor*crop_info.height+0.5);
11646 roi_image=CropImage(*image,&crop_info,exception);
11647 (void) SetImageProgressMonitor(*image,progress_monitor,
11648 (*image)->client_data);
11649 if (roi_image == (Image *) NULL)
11650 continue;
11651 /*
11652 Apply image processing technique to the region of interest.
11653 */
11654 windows->image.orphan=MagickTrue;
11655 (void) XMagickCommand(display,resource_info,windows,
11656 display_command,&roi_image,exception);
11657 progress_monitor=SetImageProgressMonitor(*image,
11658 (MagickProgressMonitor) NULL,(*image)->client_data);
11659 (void) XMagickCommand(display,resource_info,windows,
11660 SaveToUndoBufferCommand,image,exception);
11661 windows->image.orphan=MagickFalse;
11662 (void) CompositeImage(*image,roi_image,CopyCompositeOp,
11663 MagickTrue,crop_info.x,crop_info.y,exception);
11664 roi_image=DestroyImage(roi_image);
11665 (void) SetImageProgressMonitor(*image,progress_monitor,
11666 (*image)->client_data);
11667 break;
11668 }
11669 }
11670 if (display_command != InfoCommand)
11671 {
11672 XConfigureImageColormap(display,resource_info,windows,*image,
11673 exception);
11674 (void) XConfigureImage(display,resource_info,windows,*image,
11675 exception);
11676 }
11677 XCheckRefreshWindows(display,windows);
11678 XInfoWidget(display,windows,text);
11679 (void) XSetFunction(display,windows->image.highlight_context,
11680 GXinvert);
11681 state&=(unsigned int) (~UpdateRegionState);
11682 }
11683 XHighlightRectangle(display,windows->image.id,
11684 windows->image.highlight_context,&highlight_info);
11685 XScreenEvent(display,windows,&event,exception);
11686 if (event.xany.window == windows->command.id)
11687 {
11688 /*
11689 Select a command from the Command widget.
11690 */
11691 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11692 display_command=NullCommand;
11693 id=XCommandWidget(display,windows,ApplyMenu,&event);
11694 if (id >= 0)
11695 {
11696 (void) CopyMagickString(command,ApplyMenu[id],MagickPathExtent);
11697 display_command=ApplyCommands[id];
11698 if (id < ApplyMenus)
11699 {
11700 /*
11701 Select a command from a pop-up menu.
11702 */
11703 entry=XMenuWidget(display,windows,ApplyMenu[id],
11704 (const char **) Menus[id],command);
11705 if (entry >= 0)
11706 {
11707 (void) CopyMagickString(command,Menus[id][entry],
11708 MagickPathExtent);
11709 display_command=Commands[id][entry];
11710 }
11711 }
11712 }
11713 (void) XSetFunction(display,windows->image.highlight_context,
11714 GXinvert);
11715 XHighlightRectangle(display,windows->image.id,
11716 windows->image.highlight_context,&highlight_info);
11717 if (display_command == HelpCommand)
11718 {
11719 (void) XSetFunction(display,windows->image.highlight_context,
11720 GXcopy);
11721 XTextViewHelp(display,resource_info,windows,MagickFalse,
11722 "Help Viewer - Region of Interest",ImageROIHelp);
11723 (void) XSetFunction(display,windows->image.highlight_context,
11724 GXinvert);
11725 continue;
11726 }
11727 if (display_command == QuitCommand)
11728 {
11729 /*
11730 exit.
11731 */
11732 state|=EscapeState;
11733 state|=ExitState;
11734 continue;
11735 }
11736 if (display_command != NullCommand)
11737 state|=UpdateRegionState;
11738 continue;
11739 }
11740 XHighlightRectangle(display,windows->image.id,
11741 windows->image.highlight_context,&highlight_info);
11742 switch (event.type)
11743 {
11744 case ButtonPress:
11745 {
11746 x=windows->image.x;
11747 y=windows->image.y;
11748 if (event.xbutton.button != Button1)
11749 break;
11750 if (event.xbutton.window != windows->image.id)
11751 break;
11752 x=windows->image.x+event.xbutton.x;
11753 y=windows->image.y+event.xbutton.y;
11754 if ((x < (int) (roi_info.x+RoiDelta)) &&
11755 (x > (int) (roi_info.x-RoiDelta)) &&
11756 (y < (int) (roi_info.y+RoiDelta)) &&
11757 (y > (int) (roi_info.y-RoiDelta)))
11758 {
11759 roi_info.x=roi_info.x+(int) roi_info.width;
11760 roi_info.y=roi_info.y+(int) roi_info.height;
11761 state|=UpdateConfigurationState;
11762 break;
11763 }
11764 if ((x < (int) (roi_info.x+RoiDelta)) &&
11765 (x > (int) (roi_info.x-RoiDelta)) &&
11766 (y < (roi_info.y+(int) roi_info.height+RoiDelta)) &&
11767 (y > (roi_info.y+(int) roi_info.height-RoiDelta)))
11768 {
11769 roi_info.x=roi_info.x+(int) roi_info.width;
11770 state|=UpdateConfigurationState;
11771 break;
11772 }
11773 if ((x < (roi_info.x+(int) roi_info.width+RoiDelta)) &&
11774 (x > (roi_info.x+(int) roi_info.width-RoiDelta)) &&
11775 (y < (int) (roi_info.y+RoiDelta)) &&
11776 (y > (int) (roi_info.y-RoiDelta)))
11777 {
11778 roi_info.y=roi_info.y+(int) roi_info.height;
11779 state|=UpdateConfigurationState;
11780 break;
11781 }
11782 if ((x < (roi_info.x+(int) roi_info.width+RoiDelta)) &&
11783 (x > (roi_info.x+(int) roi_info.width-RoiDelta)) &&
11784 (y < (roi_info.y+(int) roi_info.height+RoiDelta)) &&
11785 (y > (roi_info.y+(int) roi_info.height-RoiDelta)))
11786 {
11787 state|=UpdateConfigurationState;
11788 break;
11789 }
11790 magick_fallthrough;
11791 }
11792 case ButtonRelease:
11793 {
11794 if (event.xbutton.window == windows->pan.id)
11795 if ((highlight_info.x != crop_info.x-windows->image.x) ||
11796 (highlight_info.y != crop_info.y-windows->image.y))
11797 XHighlightRectangle(display,windows->image.id,
11798 windows->image.highlight_context,&highlight_info);
11799 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11800 event.xbutton.time);
11801 break;
11802 }
11803 case Expose:
11804 {
11805 if (event.xexpose.window == windows->image.id)
11806 if (event.xexpose.count == 0)
11807 {
11808 event.xexpose.x=(int) highlight_info.x;
11809 event.xexpose.y=(int) highlight_info.y;
11810 event.xexpose.width=(int) highlight_info.width;
11811 event.xexpose.height=(int) highlight_info.height;
11812 XRefreshWindow(display,&windows->image,&event);
11813 }
11814 if (event.xexpose.window == windows->info.id)
11815 if (event.xexpose.count == 0)
11816 XInfoWidget(display,windows,text);
11817 break;
11818 }
11819 case KeyPress:
11820 {
11821 KeySym
11822 key_symbol;
11823
11824 if (event.xkey.window != windows->image.id)
11825 break;
11826 /*
11827 Respond to a user key press.
11828 */
11829 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11830 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11831 switch ((int) key_symbol)
11832 {
11833 case XK_Shift_L:
11834 case XK_Shift_R:
11835 break;
11836 case XK_Escape:
11837 case XK_F20:
11838 {
11839 state|=EscapeState;
11840 magick_fallthrough;
11841 }
11842 case XK_Return:
11843 {
11844 state|=ExitState;
11845 break;
11846 }
11847 case XK_Home:
11848 case XK_KP_Home:
11849 {
11850 roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11851 roi_info.y=(ssize_t) (windows->image.height/2L-
11852 roi_info.height/2L);
11853 break;
11854 }
11855 case XK_Left:
11856 case XK_KP_Left:
11857 {
11858 roi_info.x--;
11859 break;
11860 }
11861 case XK_Up:
11862 case XK_KP_Up:
11863 case XK_Next:
11864 {
11865 roi_info.y--;
11866 break;
11867 }
11868 case XK_Right:
11869 case XK_KP_Right:
11870 {
11871 roi_info.x++;
11872 break;
11873 }
11874 case XK_Prior:
11875 case XK_Down:
11876 case XK_KP_Down:
11877 {
11878 roi_info.y++;
11879 break;
11880 }
11881 case XK_F1:
11882 case XK_Help:
11883 {
11884 (void) XSetFunction(display,windows->image.highlight_context,
11885 GXcopy);
11886 XTextViewHelp(display,resource_info,windows,MagickFalse,
11887 "Help Viewer - Region of Interest",ImageROIHelp);
11888 (void) XSetFunction(display,windows->image.highlight_context,
11889 GXinvert);
11890 break;
11891 }
11892 default:
11893 {
11894 display_command=XImageWindowCommand(display,resource_info,windows,
11895 event.xkey.state,key_symbol,image,exception);
11896 if (display_command != NullCommand)
11897 state|=UpdateRegionState;
11898 break;
11899 }
11900 }
11901 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11902 event.xkey.time);
11903 break;
11904 }
11905 case KeyRelease:
11906 break;
11907 case MotionNotify:
11908 {
11909 if (event.xbutton.window != windows->image.id)
11910 break;
11911 /*
11912 Map and unmap Info widget as text cursor crosses its boundaries.
11913 */
11914 x=event.xmotion.x;
11915 y=event.xmotion.y;
11916 if (windows->info.mapped != MagickFalse)
11917 {
11918 if ((x < (windows->info.x+(int) windows->info.width)) &&
11919 (y < (windows->info.y+(int) windows->info.height)))
11920 (void) XWithdrawWindow(display,windows->info.id,
11921 windows->info.screen);
11922 }
11923 else
11924 if ((x > (windows->info.x+(int) windows->info.width)) ||
11925 (y > (windows->info.y+(int) windows->info.height)))
11926 (void) XMapWindow(display,windows->info.id);
11927 roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11928 roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11929 break;
11930 }
11931 case SelectionRequest:
11932 {
11933 XSelectionEvent
11934 notify;
11935
11936 XSelectionRequestEvent
11937 *request;
11938
11939 /*
11940 Set primary selection.
11941 */
11942 (void) FormatLocaleString(text,MagickPathExtent,
11943 "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11944 roi_info.height,(double) roi_info.x,(double) roi_info.y);
11945 request=(&(event.xselectionrequest));
11946 (void) XChangeProperty(request->display,request->requestor,
11947 request->property,request->target,8,PropModeReplace,
11948 (unsigned char *) text,(int) strlen(text));
11949 notify.type=SelectionNotify;
11950 notify.display=request->display;
11951 notify.requestor=request->requestor;
11952 notify.selection=request->selection;
11953 notify.target=request->target;
11954 notify.time=request->time;
11955 if (request->property == None)
11956 notify.property=request->target;
11957 else
11958 notify.property=request->property;
11959 (void) XSendEvent(request->display,request->requestor,False,0,
11960 (XEvent *) &notify);
11961 }
11962 default:
11963 break;
11964 }
11965 if ((state & UpdateConfigurationState) != 0)
11966 {
11967 (void) XPutBackEvent(display,&event);
11968 (void) XCheckDefineCursor(display,windows->image.id,cursor);
11969 break;
11970 }
11971 } while ((state & ExitState) == 0);
11972 } while ((state & ExitState) == 0);
11973 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11974 XSetCursorState(display,windows,MagickFalse);
11975 if ((state & EscapeState) != 0)
11976 return(MagickTrue);
11977 return(MagickTrue);
11978}
11979
11980/*
11981%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11982% %
11983% %
11984% %
11985+ X R o t a t e I m a g e %
11986% %
11987% %
11988% %
11989%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11990%
11991% XRotateImage() rotates the X image. If the degrees parameter if zero, the
11992% rotation angle is computed from the slope of a line drawn by the user.
11993%
11994% The format of the XRotateImage method is:
11995%
11996% MagickBooleanType XRotateImage(Display *display,
11997% XResourceInfo *resource_info,XWindows *windows,double degrees,
11998% Image **image,ExceptionInfo *exception)
11999%
12000% A description of each parameter follows:
12001%
12002% o display: Specifies a connection to an X server; returned from
12003% XOpenDisplay.
12004%
12005% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12006%
12007% o windows: Specifies a pointer to a XWindows structure.
12008%
12009% o degrees: Specifies the number of degrees to rotate the image.
12010%
12011% o image: the image.
12012%
12013% o exception: return any errors or warnings in this structure.
12014%
12015*/
12016static MagickBooleanType XRotateImage(Display *display,
12017 XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12018 ExceptionInfo *exception)
12019{
12020 const char
12021 *const RotateMenu[] =
12022 {
12023 "Pixel Color",
12024 "Direction",
12025 "Help",
12026 "Dismiss",
12027 (char *) NULL
12028 };
12029
12030 static ModeType
12031 direction = HorizontalRotateCommand;
12032
12033 static const ModeType
12034 DirectionCommands[] =
12035 {
12036 HorizontalRotateCommand,
12037 VerticalRotateCommand
12038 },
12039 RotateCommands[] =
12040 {
12041 RotateColorCommand,
12042 RotateDirectionCommand,
12043 RotateHelpCommand,
12044 RotateDismissCommand
12045 };
12046
12047 static unsigned int
12048 pen_id = 0;
12049
12050 char
12051 command[MagickPathExtent],
12052 text[MagickPathExtent];
12053
12054 Image
12055 *rotate_image;
12056
12057 int
12058 id,
12059 x,
12060 y;
12061
12062 double
12063 normalized_degrees;
12064
12065 int
12066 i;
12067
12068 unsigned int
12069 height,
12070 rotations,
12071 width;
12072
12073 if (degrees == 0.0)
12074 {
12075 unsigned int
12076 distance;
12077
12078 size_t
12079 state;
12080
12081 XEvent
12082 event;
12083
12084 XSegment
12085 rotate_info;
12086
12087 /*
12088 Map Command widget.
12089 */
12090 (void) CloneString(&windows->command.name,"Rotate");
12091 windows->command.data=2;
12092 (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12093 (void) XMapRaised(display,windows->command.id);
12094 XClientMessage(display,windows->image.id,windows->im_protocols,
12095 windows->im_update_widget,CurrentTime);
12096 /*
12097 Wait for first button press.
12098 */
12099 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12100 XQueryPosition(display,windows->image.id,&x,&y);
12101 rotate_info.x1=x;
12102 rotate_info.y1=y;
12103 rotate_info.x2=x;
12104 rotate_info.y2=y;
12105 state=DefaultState;
12106 do
12107 {
12108 XHighlightLine(display,windows->image.id,
12109 windows->image.highlight_context,&rotate_info);
12110 /*
12111 Wait for next event.
12112 */
12113 XScreenEvent(display,windows,&event,exception);
12114 XHighlightLine(display,windows->image.id,
12115 windows->image.highlight_context,&rotate_info);
12116 if (event.xany.window == windows->command.id)
12117 {
12118 /*
12119 Select a command from the Command widget.
12120 */
12121 id=XCommandWidget(display,windows,RotateMenu,&event);
12122 if (id < 0)
12123 continue;
12124 (void) XSetFunction(display,windows->image.highlight_context,
12125 GXcopy);
12126 switch (RotateCommands[id])
12127 {
12128 case RotateColorCommand:
12129 {
12130 const char
12131 *ColorMenu[MaxNumberPens];
12132
12133 int
12134 pen_number;
12135
12136 XColor
12137 color;
12138
12139 /*
12140 Initialize menu selections.
12141 */
12142 for (i=0; i < (int) (MaxNumberPens-2); i++)
12143 ColorMenu[i]=resource_info->pen_colors[i];
12144 ColorMenu[MaxNumberPens-2]="Browser...";
12145 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12146 /*
12147 Select a pen color from the pop-up menu.
12148 */
12149 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12150 (const char **) ColorMenu,command);
12151 if (pen_number < 0)
12152 break;
12153 if (pen_number == (MaxNumberPens-2))
12154 {
12155 static char
12156 color_name[MagickPathExtent] = "gray";
12157
12158 /*
12159 Select a pen color from a dialog.
12160 */
12161 resource_info->pen_colors[pen_number]=color_name;
12162 XColorBrowserWidget(display,windows,"Select",color_name);
12163 if (*color_name == '\0')
12164 break;
12165 }
12166 /*
12167 Set pen color.
12168 */
12169 (void) XParseColor(display,windows->map_info->colormap,
12170 resource_info->pen_colors[pen_number],&color);
12171 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12172 (unsigned int) MaxColors,&color);
12173 windows->pixel_info->pen_colors[pen_number]=color;
12174 pen_id=(unsigned int) pen_number;
12175 break;
12176 }
12177 case RotateDirectionCommand:
12178 {
12179 const char
12180 *Directions[] =
12181 {
12182 "horizontal",
12183 "vertical",
12184 (char *) NULL,
12185 };
12186
12187 /*
12188 Select a command from the pop-up menu.
12189 */
12190 id=XMenuWidget(display,windows,RotateMenu[id],
12191 Directions,command);
12192 if (id >= 0)
12193 direction=DirectionCommands[id];
12194 break;
12195 }
12196 case RotateHelpCommand:
12197 {
12198 XTextViewHelp(display,resource_info,windows,MagickFalse,
12199 "Help Viewer - Image Rotation",ImageRotateHelp);
12200 break;
12201 }
12202 case RotateDismissCommand:
12203 {
12204 /*
12205 Prematurely exit.
12206 */
12207 state|=EscapeState;
12208 state|=ExitState;
12209 break;
12210 }
12211 default:
12212 break;
12213 }
12214 (void) XSetFunction(display,windows->image.highlight_context,
12215 GXinvert);
12216 continue;
12217 }
12218 switch (event.type)
12219 {
12220 case ButtonPress:
12221 {
12222 if (event.xbutton.button != Button1)
12223 break;
12224 if (event.xbutton.window != windows->image.id)
12225 break;
12226 /*
12227 exit loop.
12228 */
12229 (void) XSetFunction(display,windows->image.highlight_context,
12230 GXcopy);
12231 rotate_info.x1=event.xbutton.x;
12232 rotate_info.y1=event.xbutton.y;
12233 state|=ExitState;
12234 break;
12235 }
12236 case ButtonRelease:
12237 break;
12238 case Expose:
12239 break;
12240 case KeyPress:
12241 {
12242 char
12243 command[MagickPathExtent];
12244
12245 KeySym
12246 key_symbol;
12247
12248 if (event.xkey.window != windows->image.id)
12249 break;
12250 /*
12251 Respond to a user key press.
12252 */
12253 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12254 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12255 switch ((int) key_symbol)
12256 {
12257 case XK_Escape:
12258 case XK_F20:
12259 {
12260 /*
12261 Prematurely exit.
12262 */
12263 state|=EscapeState;
12264 state|=ExitState;
12265 break;
12266 }
12267 case XK_F1:
12268 case XK_Help:
12269 {
12270 (void) XSetFunction(display,windows->image.highlight_context,
12271 GXcopy);
12272 XTextViewHelp(display,resource_info,windows,MagickFalse,
12273 "Help Viewer - Image Rotation",ImageRotateHelp);
12274 (void) XSetFunction(display,windows->image.highlight_context,
12275 GXinvert);
12276 break;
12277 }
12278 default:
12279 {
12280 (void) XBell(display,0);
12281 break;
12282 }
12283 }
12284 break;
12285 }
12286 case MotionNotify:
12287 {
12288 rotate_info.x1=event.xmotion.x;
12289 rotate_info.y1=event.xmotion.y;
12290 }
12291 }
12292 rotate_info.x2=rotate_info.x1;
12293 rotate_info.y2=rotate_info.y1;
12294 if (direction == HorizontalRotateCommand)
12295 rotate_info.x2+=32;
12296 else
12297 rotate_info.y2-=32;
12298 } while ((state & ExitState) == 0);
12299 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12300 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12301 if ((state & EscapeState) != 0)
12302 return(MagickTrue);
12303 /*
12304 Draw line as pointer moves until the mouse button is released.
12305 */
12306 distance=0;
12307 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12308 state=DefaultState;
12309 do
12310 {
12311 if (distance > 9)
12312 {
12313 /*
12314 Display info and draw rotation line.
12315 */
12316 if (windows->info.mapped == MagickFalse)
12317 (void) XMapWindow(display,windows->info.id);
12318 (void) FormatLocaleString(text,MagickPathExtent," %g",
12319 direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12320 XInfoWidget(display,windows,text);
12321 XHighlightLine(display,windows->image.id,
12322 windows->image.highlight_context,&rotate_info);
12323 }
12324 else
12325 if (windows->info.mapped != MagickFalse)
12326 (void) XWithdrawWindow(display,windows->info.id,
12327 windows->info.screen);
12328 /*
12329 Wait for next event.
12330 */
12331 XScreenEvent(display,windows,&event,exception);
12332 if (distance > 9)
12333 XHighlightLine(display,windows->image.id,
12334 windows->image.highlight_context,&rotate_info);
12335 switch (event.type)
12336 {
12337 case ButtonPress:
12338 break;
12339 case ButtonRelease:
12340 {
12341 /*
12342 User has committed to rotation line.
12343 */
12344 rotate_info.x2=event.xbutton.x;
12345 rotate_info.y2=event.xbutton.y;
12346 state|=ExitState;
12347 break;
12348 }
12349 case Expose:
12350 break;
12351 case MotionNotify:
12352 {
12353 rotate_info.x2=event.xmotion.x;
12354 rotate_info.y2=event.xmotion.y;
12355 }
12356 default:
12357 break;
12358 }
12359 /*
12360 Check boundary conditions.
12361 */
12362 if (rotate_info.x2 < 0)
12363 rotate_info.x2=0;
12364 else
12365 if (rotate_info.x2 > (int) windows->image.width)
12366 rotate_info.x2=(short) windows->image.width;
12367 if (rotate_info.y2 < 0)
12368 rotate_info.y2=0;
12369 else
12370 if (rotate_info.y2 > (int) windows->image.height)
12371 rotate_info.y2=(short) windows->image.height;
12372 /*
12373 Compute rotation angle from the slope of the line.
12374 */
12375 degrees=0.0;
12376 distance=(unsigned int)
12377 (((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12378 ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1)));
12379 if (distance > 9)
12380 degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12381 rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12382 } while ((state & ExitState) == 0);
12383 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12384 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12385 if (distance <= 9)
12386 return(MagickTrue);
12387 }
12388 if (direction == VerticalRotateCommand)
12389 degrees-=90.0;
12390 if (degrees == 0.0)
12391 return(MagickTrue);
12392 /*
12393 Rotate image.
12394 */
12395 normalized_degrees=degrees;
12396 while (normalized_degrees < -45.0)
12397 normalized_degrees+=360.0;
12398 for (rotations=0; normalized_degrees > 45.0; rotations++)
12399 normalized_degrees-=90.0;
12400 if (normalized_degrees != 0.0)
12401 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12402 exception);
12403 XSetCursorState(display,windows,MagickTrue);
12404 XCheckRefreshWindows(display,windows);
12405 (*image)->background_color.red=(double) ScaleShortToQuantum(
12406 windows->pixel_info->pen_colors[pen_id].red);
12407 (*image)->background_color.green=(double) ScaleShortToQuantum(
12408 windows->pixel_info->pen_colors[pen_id].green);
12409 (*image)->background_color.blue=(double) ScaleShortToQuantum(
12410 windows->pixel_info->pen_colors[pen_id].blue);
12411 rotate_image=RotateImage(*image,degrees,exception);
12412 XSetCursorState(display,windows,MagickFalse);
12413 if (rotate_image == (Image *) NULL)
12414 return(MagickFalse);
12415 *image=DestroyImage(*image);
12416 *image=rotate_image;
12417 if (windows->image.crop_geometry != (char *) NULL)
12418 {
12419 /*
12420 Rotate crop geometry.
12421 */
12422 width=(unsigned int) (*image)->columns;
12423 height=(unsigned int) (*image)->rows;
12424 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12425 switch (rotations % 4)
12426 {
12427 default:
12428 case 0:
12429 break;
12430 case 1:
12431 {
12432 /*
12433 Rotate 90 degrees.
12434 */
12435 (void) FormatLocaleString(windows->image.crop_geometry,
12436 MagickPathExtent,"%ux%u%+d%+d",height,width,(int) (*image)->columns-
12437 (int) height-y,x);
12438 break;
12439 }
12440 case 2:
12441 {
12442 /*
12443 Rotate 180 degrees.
12444 */
12445 (void) FormatLocaleString(windows->image.crop_geometry,
12446 MagickPathExtent,"%ux%u%+d%+d",width,height,(int) width-x,(int)
12447 height-y);
12448 break;
12449 }
12450 case 3:
12451 {
12452 /*
12453 Rotate 270 degrees.
12454 */
12455 (void) FormatLocaleString(windows->image.crop_geometry,
12456 MagickPathExtent,"%ux%u%+d%+d",height,width,y,(int) (*image)->rows-
12457 (int) width-x);
12458 break;
12459 }
12460 }
12461 }
12462 if (windows->image.orphan != MagickFalse)
12463 return(MagickTrue);
12464 if (normalized_degrees != 0.0)
12465 {
12466 /*
12467 Update image colormap.
12468 */
12469 windows->image.window_changes.width=(int) (*image)->columns;
12470 windows->image.window_changes.height=(int) (*image)->rows;
12471 if (windows->image.crop_geometry != (char *) NULL)
12472 {
12473 /*
12474 Obtain dimensions of image from crop geometry.
12475 */
12476 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12477 &width,&height);
12478 windows->image.window_changes.width=(int) width;
12479 windows->image.window_changes.height=(int) height;
12480 }
12481 XConfigureImageColormap(display,resource_info,windows,*image,exception);
12482 }
12483 else
12484 if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12485 {
12486 windows->image.window_changes.width=windows->image.ximage->height;
12487 windows->image.window_changes.height=windows->image.ximage->width;
12488 }
12489 /*
12490 Update image configuration.
12491 */
12492 (void) XConfigureImage(display,resource_info,windows,*image,exception);
12493 return(MagickTrue);
12494}
12495
12496/*
12497%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12498% %
12499% %
12500% %
12501+ X S a v e I m a g e %
12502% %
12503% %
12504% %
12505%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12506%
12507% XSaveImage() saves an image to a file.
12508%
12509% The format of the XSaveImage method is:
12510%
12511% MagickBooleanType XSaveImage(Display *display,
12512% XResourceInfo *resource_info,XWindows *windows,Image *image,
12513% ExceptionInfo *exception)
12514%
12515% A description of each parameter follows:
12516%
12517% o display: Specifies a connection to an X server; returned from
12518% XOpenDisplay.
12519%
12520% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12521%
12522% o windows: Specifies a pointer to a XWindows structure.
12523%
12524% o image: the image.
12525%
12526% o exception: return any errors or warnings in this structure.
12527%
12528*/
12529static MagickBooleanType XSaveImage(Display *display,
12530 XResourceInfo *resource_info,XWindows *windows,Image *image,
12531 ExceptionInfo *exception)
12532{
12533 char
12534 filename[MagickPathExtent],
12535 geometry[MagickPathExtent];
12536
12537 Image
12538 *save_image;
12539
12540 ImageInfo
12541 *image_info;
12542
12543 MagickStatusType
12544 status;
12545
12546 /*
12547 Request file name from user.
12548 */
12549 if (resource_info->write_filename != (char *) NULL)
12550 (void) CopyMagickString(filename,resource_info->write_filename,
12551 MagickPathExtent);
12552 else
12553 {
12554 char
12555 path[MagickPathExtent];
12556
12557 int
12558 status;
12559
12560 GetPathComponent(image->filename,HeadPath,path);
12561 GetPathComponent(image->filename,TailPath,filename);
12562 if (*path != '\0')
12563 {
12564 status=chdir(path);
12565 if (status == -1)
12566 (void) ThrowMagickException(exception,GetMagickModule(),
12567 FileOpenError,"UnableToOpenFile","%s",path);
12568 }
12569 }
12570 XFileBrowserWidget(display,windows,"Save",filename);
12571 if (*filename == '\0')
12572 return(MagickTrue);
12573 if (IsPathAccessible(filename) != MagickFalse)
12574 {
12575 int
12576 status;
12577
12578 /*
12579 File exists-- seek user's permission before overwriting.
12580 */
12581 status=XConfirmWidget(display,windows,"Overwrite",filename);
12582 if (status <= 0)
12583 return(MagickTrue);
12584 }
12585 image_info=CloneImageInfo(resource_info->image_info);
12586 (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
12587 (void) SetImageInfo(image_info,1,exception);
12588 if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12589 (LocaleCompare(image_info->magick,"JPG") == 0))
12590 {
12591 char
12592 quality[MagickPathExtent];
12593
12594 int
12595 status;
12596
12597 /*
12598 Request JPEG quality from user.
12599 */
12600 (void) FormatLocaleString(quality,MagickPathExtent,"%.20g",(double)
12601 image->quality);
12602 status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12603 quality);
12604 if (*quality == '\0')
12605 return(MagickTrue);
12606 image->quality=StringToUnsignedLong(quality);
12607 image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12608 }
12609 if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12610 (LocaleCompare(image_info->magick,"PDF") == 0) ||
12611 (LocaleCompare(image_info->magick,"PS") == 0) ||
12612 (LocaleCompare(image_info->magick,"PS2") == 0))
12613 {
12614 char
12615 geometry[MagickPathExtent];
12616
12617 const char
12618 *const PageSizes[] =
12619 {
12620 "Letter",
12621 "Tabloid",
12622 "Ledger",
12623 "Legal",
12624 "Statement",
12625 "Executive",
12626 "A3",
12627 "A4",
12628 "A5",
12629 "B4",
12630 "B5",
12631 "Folio",
12632 "Quarto",
12633 "10x14",
12634 (char *) NULL
12635 };
12636
12637 /*
12638 Request page geometry from user.
12639 */
12640 (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12641 if (LocaleCompare(image_info->magick,"PDF") == 0)
12642 (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12643 if (image_info->page != (char *) NULL)
12644 (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
12645 XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12646 "Select page geometry:",geometry);
12647 if (*geometry != '\0')
12648 image_info->page=GetPageGeometry(geometry);
12649 }
12650 /*
12651 Apply image transforms.
12652 */
12653 XSetCursorState(display,windows,MagickTrue);
12654 XCheckRefreshWindows(display,windows);
12655 save_image=CloneImage(image,0,0,MagickTrue,exception);
12656 if (save_image == (Image *) NULL)
12657 return(MagickFalse);
12658 (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
12659 windows->image.ximage->width,windows->image.ximage->height);
12660 (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12661 exception);
12662 /*
12663 Write image.
12664 */
12665 (void) CopyMagickString(save_image->filename,filename,MagickPathExtent);
12666 status=WriteImage(image_info,save_image,exception);
12667 if (status != MagickFalse)
12668 image->taint=MagickFalse;
12669 save_image=DestroyImage(save_image);
12670 image_info=DestroyImageInfo(image_info);
12671 XSetCursorState(display,windows,MagickFalse);
12672 return(status != 0 ? MagickTrue : MagickFalse);
12673}
12674
12675/*
12676%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12677% %
12678% %
12679% %
12680+ X S c r e e n E v e n t %
12681% %
12682% %
12683% %
12684%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12685%
12686% XScreenEvent() handles global events associated with the Pan and Magnify
12687% windows.
12688%
12689% The format of the XScreenEvent function is:
12690%
12691% void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12692% ExceptionInfo *exception)
12693%
12694% A description of each parameter follows:
12695%
12696% o display: Specifies a pointer to the Display structure; returned from
12697% XOpenDisplay.
12698%
12699% o windows: Specifies a pointer to a XWindows structure.
12700%
12701% o event: Specifies a pointer to a X11 XEvent structure.
12702%
12703% o exception: return any errors or warnings in this structure.
12704%
12705*/
12706
12707#if defined(__cplusplus) || defined(c_plusplus)
12708extern "C" {
12709#endif
12710
12711static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12712{
12713 XWindows
12714 *windows;
12715
12716 windows=(XWindows *) data;
12717 if ((event->type == ClientMessage) &&
12718 (event->xclient.window == windows->image.id))
12719 return(MagickFalse);
12720 return(MagickTrue);
12721}
12722
12723#if defined(__cplusplus) || defined(c_plusplus)
12724}
12725#endif
12726
12727static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12728 ExceptionInfo *exception)
12729{
12730 int
12731 x,
12732 y;
12733
12734 (void) XIfEvent(display,event,XPredicate,(char *) windows);
12735 if (event->xany.window == windows->command.id)
12736 return;
12737 switch (event->type)
12738 {
12739 case ButtonPress:
12740 case ButtonRelease:
12741 {
12742 if ((event->xbutton.button == Button3) &&
12743 (event->xbutton.state & Mod1Mask))
12744 {
12745 /*
12746 Convert Alt-Button3 to Button2.
12747 */
12748 event->xbutton.button=Button2;
12749 event->xbutton.state&=(unsigned int) (~Mod1Mask);
12750 }
12751 if (event->xbutton.window == windows->backdrop.id)
12752 {
12753 (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12754 event->xbutton.time);
12755 break;
12756 }
12757 if (event->xbutton.window == windows->pan.id)
12758 {
12759 XPanImage(display,windows,event,exception);
12760 break;
12761 }
12762 if (event->xbutton.window == windows->image.id)
12763 if (event->xbutton.button == Button2)
12764 {
12765 /*
12766 Update magnified image.
12767 */
12768 x=event->xbutton.x;
12769 y=event->xbutton.y;
12770 if (x < 0)
12771 x=0;
12772 else
12773 if (x >= (int) windows->image.width)
12774 x=(int) (windows->image.width-1);
12775 windows->magnify.x=(int) windows->image.x+x;
12776 if (y < 0)
12777 y=0;
12778 else
12779 if (y >= (int) windows->image.height)
12780 y=(int) (windows->image.height-1);
12781 windows->magnify.y=windows->image.y+y;
12782 if (windows->magnify.mapped == MagickFalse)
12783 (void) XMapRaised(display,windows->magnify.id);
12784 XMakeMagnifyImage(display,windows,exception);
12785 if (event->type == ButtonRelease)
12786 (void) XWithdrawWindow(display,windows->info.id,
12787 windows->info.screen);
12788 break;
12789 }
12790 break;
12791 }
12792 case ClientMessage:
12793 {
12794 /*
12795 If client window delete message, exit.
12796 */
12797 if (event->xclient.message_type != windows->wm_protocols)
12798 break;
12799 if (*event->xclient.data.l != (long) windows->wm_delete_window)
12800 break;
12801 if (event->xclient.window == windows->magnify.id)
12802 {
12803 (void) XWithdrawWindow(display,windows->magnify.id,
12804 windows->magnify.screen);
12805 break;
12806 }
12807 break;
12808 }
12809 case ConfigureNotify:
12810 {
12811 if (event->xconfigure.window == windows->magnify.id)
12812 {
12813 unsigned int
12814 magnify;
12815
12816 /*
12817 Magnify window has a new configuration.
12818 */
12819 windows->magnify.width=(unsigned int) event->xconfigure.width;
12820 windows->magnify.height=(unsigned int) event->xconfigure.height;
12821 if (windows->magnify.mapped == MagickFalse)
12822 break;
12823 magnify=1;
12824 while ((int) magnify <= event->xconfigure.width)
12825 magnify<<=1;
12826 while ((int) magnify <= event->xconfigure.height)
12827 magnify<<=1;
12828 magnify>>=1;
12829 if (((int) magnify != event->xconfigure.width) ||
12830 ((int) magnify != event->xconfigure.height))
12831 {
12832 XWindowChanges
12833 window_changes;
12834
12835 window_changes.width=(int) magnify;
12836 window_changes.height=(int) magnify;
12837 (void) XReconfigureWMWindow(display,windows->magnify.id,
12838 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12839 &window_changes);
12840 break;
12841 }
12842 XMakeMagnifyImage(display,windows,exception);
12843 break;
12844 }
12845 break;
12846 }
12847 case Expose:
12848 {
12849 if (event->xexpose.window == windows->image.id)
12850 {
12851 XRefreshWindow(display,&windows->image,event);
12852 break;
12853 }
12854 if (event->xexpose.window == windows->pan.id)
12855 if (event->xexpose.count == 0)
12856 {
12857 XDrawPanRectangle(display,windows);
12858 break;
12859 }
12860 if (event->xexpose.window == windows->magnify.id)
12861 if (event->xexpose.count == 0)
12862 {
12863 XMakeMagnifyImage(display,windows,exception);
12864 break;
12865 }
12866 break;
12867 }
12868 case KeyPress:
12869 {
12870 char
12871 command[MagickPathExtent];
12872
12873 KeySym
12874 key_symbol;
12875
12876 if (event->xkey.window != windows->magnify.id)
12877 break;
12878 /*
12879 Respond to a user key press.
12880 */
12881 (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12882 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12883 XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12884 exception);
12885 break;
12886 }
12887 case MapNotify:
12888 {
12889 if (event->xmap.window == windows->magnify.id)
12890 {
12891 windows->magnify.mapped=MagickTrue;
12892 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12893 break;
12894 }
12895 if (event->xmap.window == windows->info.id)
12896 {
12897 windows->info.mapped=MagickTrue;
12898 break;
12899 }
12900 break;
12901 }
12902 case MotionNotify:
12903 {
12904 while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12905 if (event->xmotion.window == windows->image.id)
12906 if (windows->magnify.mapped != MagickFalse)
12907 {
12908 /*
12909 Update magnified image.
12910 */
12911 x=event->xmotion.x;
12912 y=event->xmotion.y;
12913 if (x < 0)
12914 x=0;
12915 else
12916 if (x >= (int) windows->image.width)
12917 x=(int) (windows->image.width-1);
12918 windows->magnify.x=(int) windows->image.x+x;
12919 if (y < 0)
12920 y=0;
12921 else
12922 if (y >= (int) windows->image.height)
12923 y=(int) (windows->image.height-1);
12924 windows->magnify.y=windows->image.y+y;
12925 XMakeMagnifyImage(display,windows,exception);
12926 }
12927 break;
12928 }
12929 case UnmapNotify:
12930 {
12931 if (event->xunmap.window == windows->magnify.id)
12932 {
12933 windows->magnify.mapped=MagickFalse;
12934 break;
12935 }
12936 if (event->xunmap.window == windows->info.id)
12937 {
12938 windows->info.mapped=MagickFalse;
12939 break;
12940 }
12941 break;
12942 }
12943 default:
12944 break;
12945 }
12946}
12947
12948/*
12949%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12950% %
12951% %
12952% %
12953+ X S e t C r o p G e o m e t r y %
12954% %
12955% %
12956% %
12957%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12958%
12959% XSetCropGeometry() accepts a cropping geometry relative to the Image window
12960% and translates it to a cropping geometry relative to the image.
12961%
12962% The format of the XSetCropGeometry method is:
12963%
12964% void XSetCropGeometry(Display *display,XWindows *windows,
12965% RectangleInfo *crop_info,Image *image)
12966%
12967% A description of each parameter follows:
12968%
12969% o display: Specifies a connection to an X server; returned from
12970% XOpenDisplay.
12971%
12972% o windows: Specifies a pointer to a XWindows structure.
12973%
12974% o crop_info: A pointer to a RectangleInfo that defines a region of the
12975% Image window to crop.
12976%
12977% o image: the image.
12978%
12979*/
12980static void XSetCropGeometry(Display *display,XWindows *windows,
12981 RectangleInfo *crop_info,Image *image)
12982{
12983 char
12984 text[MagickPathExtent];
12985
12986 int
12987 x,
12988 y;
12989
12990 double
12991 scale_factor;
12992
12993 unsigned int
12994 height,
12995 width;
12996
12997 if (windows->info.mapped != MagickFalse)
12998 {
12999 /*
13000 Display info on cropping rectangle.
13001 */
13002 (void) FormatLocaleString(text,MagickPathExtent," %.20gx%.20g%+.20g%+.20g",
13003 (double) crop_info->width,(double) crop_info->height,(double)
13004 crop_info->x,(double) crop_info->y);
13005 XInfoWidget(display,windows,text);
13006 }
13007 /*
13008 Cropping geometry is relative to any previous crop geometry.
13009 */
13010 x=0;
13011 y=0;
13012 width=(unsigned int) image->columns;
13013 height=(unsigned int) image->rows;
13014 if (windows->image.crop_geometry != (char *) NULL)
13015 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13016 else
13017 windows->image.crop_geometry=AcquireString((char *) NULL);
13018 /*
13019 Define the crop geometry string from the cropping rectangle.
13020 */
13021 scale_factor=(double) width/windows->image.ximage->width;
13022 if (crop_info->x > 0)
13023 x+=(int) (scale_factor*crop_info->x+0.5);
13024 width=(unsigned int) (scale_factor*crop_info->width+0.5);
13025 if (width == 0)
13026 width=1;
13027 scale_factor=(double) height/windows->image.ximage->height;
13028 if (crop_info->y > 0)
13029 y+=(int) (scale_factor*crop_info->y+0.5);
13030 height=(unsigned int) (scale_factor*crop_info->height+0.5);
13031 if (height == 0)
13032 height=1;
13033 (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
13034 "%ux%u%+d%+d",width,height,x,y);
13035}
13036
13037/*
13038%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13039% %
13040% %
13041% %
13042+ X T i l e I m a g e %
13043% %
13044% %
13045% %
13046%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13047%
13048% XTileImage() loads or deletes a selected tile from a visual image directory.
13049% The load or delete command is chosen from a menu.
13050%
13051% The format of the XTileImage method is:
13052%
13053% Image *XTileImage(Display *display,XResourceInfo *resource_info,
13054% XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13055%
13056% A description of each parameter follows:
13057%
13058% o tile_image: XTileImage reads or deletes the tile image
13059% and returns it. A null image is returned if an error occurs.
13060%
13061% o display: Specifies a connection to an X server; returned from
13062% XOpenDisplay.
13063%
13064% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13065%
13066% o windows: Specifies a pointer to a XWindows structure.
13067%
13068% o image: the image; returned from ReadImage.
13069%
13070% o event: Specifies a pointer to a XEvent structure. If it is NULL,
13071% the entire image is refreshed.
13072%
13073% o exception: return any errors or warnings in this structure.
13074%
13075*/
13076static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13077 XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13078{
13079 const char
13080 *const VerbMenu[] =
13081 {
13082 "Load",
13083 "Next",
13084 "Former",
13085 "Delete",
13086 "Update",
13087 (char *) NULL,
13088 };
13089
13090 static const ModeType
13091 TileCommands[] =
13092 {
13093 TileLoadCommand,
13094 TileNextCommand,
13095 TileFormerCommand,
13096 TileDeleteCommand,
13097 TileUpdateCommand
13098 };
13099
13100 char
13101 command[MagickPathExtent],
13102 filename[MagickPathExtent];
13103
13104 Image
13105 *tile_image;
13106
13107 int
13108 id,
13109 status,
13110 tile,
13111 x,
13112 y;
13113
13114 double
13115 scale_factor;
13116
13117 char
13118 *p,
13119 *q;
13120
13121 int
13122 i;
13123
13124 unsigned int
13125 height,
13126 width;
13127
13128 /*
13129 Tile image is relative to montage image configuration.
13130 */
13131 x=0;
13132 y=0;
13133 width=(unsigned int) image->columns;
13134 height=(unsigned int) image->rows;
13135 if (windows->image.crop_geometry != (char *) NULL)
13136 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13137 scale_factor=(double) width/windows->image.ximage->width;
13138 event->xbutton.x+=windows->image.x;
13139 event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13140 scale_factor=(double) height/windows->image.ximage->height;
13141 event->xbutton.y+=windows->image.y;
13142 event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13143 /*
13144 Determine size and location of each tile in the visual image directory.
13145 */
13146 width=(unsigned int) image->columns;
13147 height=(unsigned int) image->rows;
13148 x=0;
13149 y=0;
13150 (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13151 tile=((event->xbutton.y-y)/(int) height)*(((int) image->columns-x)/(int)
13152 width)+(event->xbutton.x-x)/(int) width;
13153 if (tile < 0)
13154 {
13155 /*
13156 Button press is outside any tile.
13157 */
13158 (void) XBell(display,0);
13159 return((Image *) NULL);
13160 }
13161 /*
13162 Determine file name from the tile directory.
13163 */
13164 p=image->directory;
13165 for (i=tile; (i != 0) && (*p != '\0'); )
13166 {
13167 if (*p == '\xff')
13168 i--;
13169 p++;
13170 }
13171 if (*p == '\0')
13172 {
13173 /*
13174 Button press is outside any tile.
13175 */
13176 (void) XBell(display,0);
13177 return((Image *) NULL);
13178 }
13179 /*
13180 Select a command from the pop-up menu.
13181 */
13182 id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13183 if (id < 0)
13184 return((Image *) NULL);
13185 q=p;
13186 while ((*q != '\xff') && (*q != '\0'))
13187 q++;
13188 (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13189 /*
13190 Perform command for the selected tile.
13191 */
13192 XSetCursorState(display,windows,MagickTrue);
13193 XCheckRefreshWindows(display,windows);
13194 tile_image=NewImageList();
13195 switch (TileCommands[id])
13196 {
13197 case TileLoadCommand:
13198 {
13199 /*
13200 Load tile image.
13201 */
13202 XCheckRefreshWindows(display,windows);
13203 (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13204 MagickPathExtent);
13205 (void) CopyMagickString(resource_info->image_info->filename,filename,
13206 MagickPathExtent);
13207 tile_image=ReadImage(resource_info->image_info,exception);
13208 CatchException(exception);
13209 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13210 break;
13211 }
13212 case TileNextCommand:
13213 {
13214 /*
13215 Display next image.
13216 */
13217 XClientMessage(display,windows->image.id,windows->im_protocols,
13218 windows->im_next_image,CurrentTime);
13219 break;
13220 }
13221 case TileFormerCommand:
13222 {
13223 /*
13224 Display former image.
13225 */
13226 XClientMessage(display,windows->image.id,windows->im_protocols,
13227 windows->im_former_image,CurrentTime);
13228 break;
13229 }
13230 case TileDeleteCommand:
13231 {
13232 /*
13233 Delete tile image.
13234 */
13235 if (IsPathAccessible(filename) == MagickFalse)
13236 {
13237 XNoticeWidget(display,windows,"Image file does not exist:",filename);
13238 break;
13239 }
13240 status=XConfirmWidget(display,windows,"Really delete tile",filename);
13241 if (status <= 0)
13242 break;
13243 status=ShredFile(filename) == MagickFalse ? 0 : 1;
13244 status|=remove_utf8(filename);
13245 if (status != MagickFalse)
13246 {
13247 XNoticeWidget(display,windows,"Unable to delete image file:",
13248 filename);
13249 break;
13250 }
13251 magick_fallthrough;
13252 }
13253 case TileUpdateCommand:
13254 {
13255 int
13256 x_offset,
13257 y_offset;
13258
13259 PixelInfo
13260 pixel;
13261
13262 int
13263 j;
13264
13265 Quantum
13266 *s;
13267
13268 /*
13269 Ensure all the images exist.
13270 */
13271 tile=0;
13272 GetPixelInfo(image,&pixel);
13273 for (p=image->directory; *p != '\0'; p++)
13274 {
13275 CacheView
13276 *image_view;
13277
13278 q=p;
13279 while ((*q != '\xff') && (*q != '\0'))
13280 q++;
13281 (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13282 p=q;
13283 if (IsPathAccessible(filename) != MagickFalse)
13284 {
13285 tile++;
13286 continue;
13287 }
13288 /*
13289 Overwrite tile with background color.
13290 */
13291 x_offset=((int) width*(tile % (((int) image->columns-x)/(int) width))+
13292 x);
13293 y_offset=((int) height*(tile/(((int) image->columns-x)/(int) width))+
13294 y);
13295 image_view=AcquireAuthenticCacheView(image,exception);
13296 (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
13297 for (i=0; i < (int) height; i++)
13298 {
13299 s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13300 y_offset+i,width,1,exception);
13301 if (s == (Quantum *) NULL)
13302 break;
13303 for (j=0; j < (int) width; j++)
13304 {
13305 SetPixelViaPixelInfo(image,&pixel,s);
13306 s+=GetPixelChannels(image);
13307 }
13308 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13309 break;
13310 }
13311 image_view=DestroyCacheView(image_view);
13312 tile++;
13313 }
13314 windows->image.window_changes.width=(int) image->columns;
13315 windows->image.window_changes.height=(int) image->rows;
13316 XConfigureImageColormap(display,resource_info,windows,image,exception);
13317 (void) XConfigureImage(display,resource_info,windows,image,exception);
13318 break;
13319 }
13320 default:
13321 break;
13322 }
13323 XSetCursorState(display,windows,MagickFalse);
13324 return(tile_image);
13325}
13326
13327/*
13328%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13329% %
13330% %
13331% %
13332+ X T r a n s l a t e I m a g e %
13333% %
13334% %
13335% %
13336%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13337%
13338% XTranslateImage() translates the image within an Image window by one pixel
13339% as specified by the key symbol. If the image has a montage string the
13340% translation is respect to the width and height contained within the string.
13341%
13342% The format of the XTranslateImage method is:
13343%
13344% void XTranslateImage(Display *display,XWindows *windows,
13345% Image *image,const KeySym key_symbol)
13346%
13347% A description of each parameter follows:
13348%
13349% o display: Specifies a connection to an X server; returned from
13350% XOpenDisplay.
13351%
13352% o windows: Specifies a pointer to a XWindows structure.
13353%
13354% o image: the image.
13355%
13356% o key_symbol: Specifies a KeySym which indicates which side of the image
13357% to trim.
13358%
13359*/
13360static void XTranslateImage(Display *display,XWindows *windows,
13361 Image *image,const KeySym key_symbol)
13362{
13363 char
13364 text[MagickPathExtent];
13365
13366 int
13367 x,
13368 y;
13369
13370 unsigned int
13371 x_offset,
13372 y_offset;
13373
13374 /*
13375 User specified a pan position offset.
13376 */
13377 x_offset=windows->image.width;
13378 y_offset=windows->image.height;
13379 if (image->montage != (char *) NULL)
13380 (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13381 switch ((int) key_symbol)
13382 {
13383 case XK_Home:
13384 case XK_KP_Home:
13385 {
13386 windows->image.x=(int) windows->image.width/2;
13387 windows->image.y=(int) windows->image.height/2;
13388 break;
13389 }
13390 case XK_Left:
13391 case XK_KP_Left:
13392 {
13393 windows->image.x-=(int) x_offset;
13394 break;
13395 }
13396 case XK_Next:
13397 case XK_Up:
13398 case XK_KP_Up:
13399 {
13400 windows->image.y-=(int) y_offset;
13401 break;
13402 }
13403 case XK_Right:
13404 case XK_KP_Right:
13405 {
13406 windows->image.x+=(int) x_offset;
13407 break;
13408 }
13409 case XK_Prior:
13410 case XK_Down:
13411 case XK_KP_Down:
13412 {
13413 windows->image.y+=(int) y_offset;
13414 break;
13415 }
13416 default:
13417 return;
13418 }
13419 /*
13420 Check boundary conditions.
13421 */
13422 if (windows->image.x < 0)
13423 windows->image.x=0;
13424 else
13425 if ((windows->image.x+(int) windows->image.width) > windows->image.ximage->width)
13426 windows->image.x=windows->image.ximage->width-(int) windows->image.width;
13427 if (windows->image.y < 0)
13428 windows->image.y=0;
13429 else
13430 if ((windows->image.y+(int) windows->image.height) > windows->image.ximage->height)
13431 windows->image.y=windows->image.ximage->height-(int)
13432 windows->image.height;
13433 /*
13434 Refresh Image window.
13435 */
13436 (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
13437 windows->image.width,windows->image.height,windows->image.x,
13438 windows->image.y);
13439 XInfoWidget(display,windows,text);
13440 XCheckRefreshWindows(display,windows);
13441 XDrawPanRectangle(display,windows);
13442 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13443 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13444}
13445
13446/*
13447%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13448% %
13449% %
13450% %
13451+ X T r i m I m a g e %
13452% %
13453% %
13454% %
13455%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13456%
13457% XTrimImage() trims the edges from the Image window.
13458%
13459% The format of the XTrimImage method is:
13460%
13461% MagickBooleanType XTrimImage(Display *display,
13462% XResourceInfo *resource_info,XWindows *windows,Image *image,
13463% ExceptionInfo *exception)
13464%
13465% A description of each parameter follows:
13466%
13467% o display: Specifies a connection to an X server; returned from
13468% XOpenDisplay.
13469%
13470% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13471%
13472% o windows: Specifies a pointer to a XWindows structure.
13473%
13474% o image: the image.
13475%
13476% o exception: return any errors or warnings in this structure.
13477%
13478*/
13479static MagickBooleanType XTrimImage(Display *display,
13480 XResourceInfo *resource_info,XWindows *windows,Image *image,
13481 ExceptionInfo *exception)
13482{
13484 trim_info;
13485
13486 int
13487 x,
13488 y;
13489
13490 size_t
13491 background,
13492 pixel;
13493
13494 /*
13495 Trim edges from image.
13496 */
13497 XSetCursorState(display,windows,MagickTrue);
13498 XCheckRefreshWindows(display,windows);
13499 /*
13500 Crop the left edge.
13501 */
13502 background=XGetPixel(windows->image.ximage,0,0);
13503 trim_info.width=(size_t) windows->image.ximage->width;
13504 for (x=0; x < windows->image.ximage->width; x++)
13505 {
13506 for (y=0; y < windows->image.ximage->height; y++)
13507 {
13508 pixel=XGetPixel(windows->image.ximage,x,y);
13509 if (pixel != background)
13510 break;
13511 }
13512 if (y < windows->image.ximage->height)
13513 break;
13514 }
13515 trim_info.x=(ssize_t) x;
13516 if (trim_info.x == (ssize_t) windows->image.ximage->width)
13517 {
13518 XSetCursorState(display,windows,MagickFalse);
13519 return(MagickFalse);
13520 }
13521 /*
13522 Crop the right edge.
13523 */
13524 background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13525 for (x=windows->image.ximage->width-1; x != 0; x--)
13526 {
13527 for (y=0; y < windows->image.ximage->height; y++)
13528 {
13529 pixel=XGetPixel(windows->image.ximage,x,y);
13530 if (pixel != background)
13531 break;
13532 }
13533 if (y < windows->image.ximage->height)
13534 break;
13535 }
13536 trim_info.width=(size_t) (x-trim_info.x+1);
13537 /*
13538 Crop the top edge.
13539 */
13540 background=XGetPixel(windows->image.ximage,0,0);
13541 trim_info.height=(size_t) windows->image.ximage->height;
13542 for (y=0; y < windows->image.ximage->height; y++)
13543 {
13544 for (x=0; x < windows->image.ximage->width; x++)
13545 {
13546 pixel=XGetPixel(windows->image.ximage,x,y);
13547 if (pixel != background)
13548 break;
13549 }
13550 if (x < windows->image.ximage->width)
13551 break;
13552 }
13553 trim_info.y=(ssize_t) y;
13554 /*
13555 Crop the bottom edge.
13556 */
13557 background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13558 for (y=windows->image.ximage->height-1; y != 0; y--)
13559 {
13560 for (x=0; x < windows->image.ximage->width; x++)
13561 {
13562 pixel=XGetPixel(windows->image.ximage,x,y);
13563 if (pixel != background)
13564 break;
13565 }
13566 if (x < windows->image.ximage->width)
13567 break;
13568 }
13569 trim_info.height=(size_t) (y-trim_info.y+1);
13570 if (((unsigned int) trim_info.width != windows->image.width) ||
13571 ((unsigned int) trim_info.height != windows->image.height))
13572 {
13573 /*
13574 Reconfigure Image window as defined by the trimming rectangle.
13575 */
13576 XSetCropGeometry(display,windows,&trim_info,image);
13577 windows->image.window_changes.width=(int) trim_info.width;
13578 windows->image.window_changes.height=(int) trim_info.height;
13579 (void) XConfigureImage(display,resource_info,windows,image,exception);
13580 }
13581 XSetCursorState(display,windows,MagickFalse);
13582 return(MagickTrue);
13583}
13584
13585/*
13586%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13587% %
13588% %
13589% %
13590+ X V i s u a l D i r e c t o r y I m a g e %
13591% %
13592% %
13593% %
13594%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13595%
13596% XVisualDirectoryImage() creates a Visual Image Directory.
13597%
13598% The format of the XVisualDirectoryImage method is:
13599%
13600% Image *XVisualDirectoryImage(Display *display,
13601% XResourceInfo *resource_info,XWindows *windows,
13602% ExceptionInfo *exception)
13603%
13604% A description of each parameter follows:
13605%
13606% o display: Specifies a connection to an X server; returned from
13607% XOpenDisplay.
13608%
13609% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13610%
13611% o windows: Specifies a pointer to a XWindows structure.
13612%
13613% o exception: return any errors or warnings in this structure.
13614%
13615*/
13616static Image *XVisualDirectoryImage(Display *display,
13617 XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13618{
13619#define TileImageTag "Scale/Image"
13620#define XClientName "montage"
13621
13622 char
13623 **filelist;
13624
13625 Image
13626 *images,
13627 *montage_image,
13628 *next_image,
13629 *thumbnail_image;
13630
13631 ImageInfo
13632 *read_info;
13633
13634 int
13635 number_files;
13636
13637 MagickBooleanType
13638 backdrop;
13639
13640 MagickStatusType
13641 status;
13642
13644 *montage_info;
13645
13647 geometry;
13648
13649 int
13650 i;
13651
13652 static char
13653 filename[MagickPathExtent] = "\0",
13654 filenames[MagickPathExtent] = "*";
13655
13656 XResourceInfo
13657 background_resources;
13658
13659 /*
13660 Request file name from user.
13661 */
13662 XFileBrowserWidget(display,windows,"Directory",filenames);
13663 if (*filenames == '\0')
13664 return((Image *) NULL);
13665 /*
13666 Expand the filenames.
13667 */
13668 filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13669 if (filelist == (char **) NULL)
13670 {
13671 ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13672 filenames);
13673 return((Image *) NULL);
13674 }
13675 number_files=1;
13676 filelist[0]=filenames;
13677 status=ExpandFilenames(&number_files,&filelist);
13678 if ((status == MagickFalse) || (number_files == 0))
13679 {
13680 if (number_files == 0)
13681 ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13682 else
13683 ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13684 filenames);
13685 return((Image *) NULL);
13686 }
13687 /*
13688 Set image background resources.
13689 */
13690 background_resources=(*resource_info);
13691 background_resources.window_id=AcquireString("");
13692 (void) FormatLocaleString(background_resources.window_id,MagickPathExtent,
13693 "0x%lx",windows->image.id);
13694 background_resources.backdrop=MagickTrue;
13695 /*
13696 Read each image and convert them to a tile.
13697 */
13698 backdrop=((windows->visual_info->klass == TrueColor) ||
13699 (windows->visual_info->klass == DirectColor)) ? MagickTrue : MagickFalse;
13700 read_info=CloneImageInfo(resource_info->image_info);
13701 (void) SetImageOption(read_info,"jpeg:size","120x120");
13702 (void) CloneString(&read_info->size,DefaultTileGeometry);
13703 (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13704 (void *) NULL);
13705 images=NewImageList();
13706 XSetCursorState(display,windows,MagickTrue);
13707 XCheckRefreshWindows(display,windows);
13708 for (i=0; i < (int) number_files; i++)
13709 {
13710 (void) CopyMagickString(read_info->filename,filelist[i],MagickPathExtent);
13711 filelist[i]=DestroyString(filelist[i]);
13712 *read_info->magick='\0';
13713 next_image=ReadImage(read_info,exception);
13714 CatchException(exception);
13715 if (next_image != (Image *) NULL)
13716 {
13717 (void) DeleteImageProperty(next_image,"label");
13718 (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13719 read_info,next_image,DefaultTileLabel,exception),exception);
13720 (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13721 exception);
13722 thumbnail_image=ThumbnailImage(next_image,geometry.width,
13723 geometry.height,exception);
13724 if (thumbnail_image != (Image *) NULL)
13725 {
13726 next_image=DestroyImage(next_image);
13727 next_image=thumbnail_image;
13728 }
13729 if (backdrop)
13730 {
13731 (void) XDisplayBackgroundImage(display,&background_resources,
13732 next_image,exception);
13733 XSetCursorState(display,windows,MagickTrue);
13734 }
13735 AppendImageToList(&images,next_image);
13736 if (images->progress_monitor != (MagickProgressMonitor) NULL)
13737 {
13738 MagickBooleanType
13739 proceed;
13740
13741 proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13742 (MagickSizeType) number_files);
13743 if (proceed == MagickFalse)
13744 break;
13745 }
13746 }
13747 }
13748 filelist=(char **) RelinquishMagickMemory(filelist);
13749 if (images == (Image *) NULL)
13750 {
13751 read_info=DestroyImageInfo(read_info);
13752 XSetCursorState(display,windows,MagickFalse);
13753 ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13754 return((Image *) NULL);
13755 }
13756 /*
13757 Create the Visual Image Directory.
13758 */
13759 montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13760 montage_info->pointsize=10;
13761 if (resource_info->font != (char *) NULL)
13762 (void) CloneString(&montage_info->font,resource_info->font);
13763 (void) CopyMagickString(montage_info->filename,filename,MagickPathExtent);
13764 montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13765 images),exception);
13766 images=DestroyImageList(images);
13767 montage_info=DestroyMontageInfo(montage_info);
13768 read_info=DestroyImageInfo(read_info);
13769 XSetCursorState(display,windows,MagickFalse);
13770 if (montage_image == (Image *) NULL)
13771 return(montage_image);
13772 XClientMessage(display,windows->image.id,windows->im_protocols,
13773 windows->im_next_image,CurrentTime);
13774 return(montage_image);
13775}
13776
13777/*
13778%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13779% %
13780% %
13781% %
13782% X D i s p l a y B a c k g r o u n d I m a g e %
13783% %
13784% %
13785% %
13786%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13787%
13788% XDisplayBackgroundImage() displays an image in the background of a window.
13789%
13790% The format of the XDisplayBackgroundImage method is:
13791%
13792% MagickBooleanType XDisplayBackgroundImage(Display *display,
13793% XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13794%
13795% A description of each parameter follows:
13796%
13797% o display: Specifies a connection to an X server; returned from
13798% XOpenDisplay.
13799%
13800% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13801%
13802% o image: the image.
13803%
13804% o exception: return any errors or warnings in this structure.
13805%
13806*/
13807MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13808 XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13809{
13810 char
13811 geometry[MagickPathExtent],
13812 visual_type[MagickPathExtent];
13813
13814 int
13815 height,
13816 status,
13817 width;
13818
13820 geometry_info;
13821
13822 static XPixelInfo
13823 pixel;
13824
13825 static XStandardColormap
13826 *map_info;
13827
13828 static XVisualInfo
13829 *visual_info = (XVisualInfo *) NULL;
13830
13831 static XWindowInfo
13832 window_info;
13833
13834 size_t
13835 delay;
13836
13837 Window
13838 root_window;
13839
13840 XGCValues
13841 context_values;
13842
13843 XResourceInfo
13844 resources;
13845
13846 XWindowAttributes
13847 window_attributes;
13848
13849 /*
13850 Determine target window.
13851 */
13852 assert(image != (Image *) NULL);
13853 assert(image->signature == MagickCoreSignature);
13854 if (IsEventLogging() != MagickFalse)
13855 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13856 resources=(*resource_info);
13857 window_info.id=(Window) NULL;
13858 root_window=XRootWindow(display,XDefaultScreen(display));
13859 if (LocaleCompare(resources.window_id,"root") == 0)
13860 window_info.id=root_window;
13861 else
13862 {
13863 if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13864 window_info.id=XWindowByID(display,root_window,
13865 (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13866 if (window_info.id == (Window) NULL)
13867 window_info.id=XWindowByName(display,root_window,resources.window_id);
13868 }
13869 if (window_info.id == (Window) NULL)
13870 {
13871 ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13872 resources.window_id);
13873 return(MagickFalse);
13874 }
13875 /*
13876 Determine window visual id.
13877 */
13878 window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13879 window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13880 (void) CopyMagickString(visual_type,"default",MagickPathExtent);
13881 status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13882 if (status != 0)
13883 (void) FormatLocaleString(visual_type,MagickPathExtent,"0x%lx",
13884 XVisualIDFromVisual(window_attributes.visual));
13885 if (visual_info == (XVisualInfo *) NULL)
13886 {
13887 /*
13888 Allocate standard colormap.
13889 */
13890 map_info=XAllocStandardColormap();
13891 if (map_info == (XStandardColormap *) NULL)
13892 ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13893 image->filename);
13894 map_info->colormap=(Colormap) NULL;
13895 pixel.pixels=(unsigned long *) NULL;
13896 /*
13897 Initialize visual info.
13898 */
13899 resources.map_type=(char *) NULL;
13900 resources.visual_type=visual_type;
13901 visual_info=XBestVisualInfo(display,map_info,&resources);
13902 if (visual_info == (XVisualInfo *) NULL)
13903 ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13904 resources.visual_type);
13905 /*
13906 Initialize window info.
13907 */
13908 window_info.ximage=(XImage *) NULL;
13909 window_info.matte_image=(XImage *) NULL;
13910 window_info.pixmap=(Pixmap) NULL;
13911 window_info.matte_pixmap=(Pixmap) NULL;
13912 }
13913 /*
13914 Free previous root colors.
13915 */
13916 if (window_info.id == root_window)
13917 (void) XDestroyWindowColors(display,root_window);
13918 /*
13919 Initialize Standard Colormap.
13920 */
13921 resources.colormap=SharedColormap;
13922 XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13923 exception);
13924 /*
13925 Graphic context superclass.
13926 */
13927 context_values.background=pixel.foreground_color.pixel;
13928 context_values.foreground=pixel.background_color.pixel;
13929 pixel.annotate_context=XCreateGC(display,window_info.id,
13930 (size_t) (GCBackground | GCForeground),&context_values);
13931 if (pixel.annotate_context == (GC) NULL)
13932 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13933 image->filename);
13934 /*
13935 Initialize Image window attributes.
13936 */
13937 window_info.name=AcquireString("\0");
13938 window_info.icon_name=AcquireString("\0");
13939 XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13940 &resources,&window_info);
13941 /*
13942 Create the X image.
13943 */
13944 window_info.width=(unsigned int) image->columns;
13945 window_info.height=(unsigned int) image->rows;
13946 if ((image->columns != window_info.width) ||
13947 (image->rows != window_info.height))
13948 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13949 image->filename);
13950 (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>",
13951 window_attributes.width,window_attributes.height);
13952 geometry_info.width=window_info.width;
13953 geometry_info.height=window_info.height;
13954 geometry_info.x=(ssize_t) window_info.x;
13955 geometry_info.y=(ssize_t) window_info.y;
13956 (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13957 &geometry_info.width,&geometry_info.height);
13958 window_info.width=(unsigned int) geometry_info.width;
13959 window_info.height=(unsigned int) geometry_info.height;
13960 window_info.x=(int) geometry_info.x;
13961 window_info.y=(int) geometry_info.y;
13962 status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13963 window_info.height,exception) == MagickFalse ? 0 : 1;
13964 if (status == MagickFalse)
13965 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13966 image->filename);
13967 window_info.x=0;
13968 window_info.y=0;
13969 if (resource_info->debug != MagickFalse)
13970 {
13971 (void) LogMagickEvent(X11Event,GetMagickModule(),
13972 "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13973 (double) image->columns,(double) image->rows);
13974 if (image->colors != 0)
13975 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13976 image->colors);
13977 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13978 }
13979 /*
13980 Adjust image dimensions as specified by backdrop or geometry options.
13981 */
13982 width=(int) window_info.width;
13983 height=(int) window_info.height;
13984 if (resources.backdrop != MagickFalse)
13985 {
13986 /*
13987 Center image on window.
13988 */
13989 window_info.x=(window_attributes.width/2)-(window_info.ximage->width/2);
13990 window_info.y=(window_attributes.height/2)-(window_info.ximage->height/2);
13991 width=window_attributes.width;
13992 height=window_attributes.height;
13993 }
13994 if ((resources.image_geometry != (char *) NULL) &&
13995 (*resources.image_geometry != '\0'))
13996 {
13997 char
13998 default_geometry[MagickPathExtent];
13999
14000 int
14001 flags,
14002 gravity;
14003
14004 XSizeHints
14005 *size_hints;
14006
14007 /*
14008 User specified geometry.
14009 */
14010 size_hints=XAllocSizeHints();
14011 if (size_hints == (XSizeHints *) NULL)
14012 ThrowXWindowFatalException(ResourceLimitFatalError,
14013 "MemoryAllocationFailed",image->filename);
14014 size_hints->flags=0L;
14015 (void) FormatLocaleString(default_geometry,MagickPathExtent,"%dx%d",
14016 width,height);
14017 flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
14018 default_geometry,window_info.border_width,size_hints,&window_info.x,
14019 &window_info.y,&width,&height,&gravity);
14020 if (flags & (XValue | YValue))
14021 {
14022 width=window_attributes.width;
14023 height=window_attributes.height;
14024 }
14025 (void) XFree((void *) size_hints);
14026 }
14027 /*
14028 Create the X pixmap.
14029 */
14030 window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14031 (unsigned int) height,window_info.depth);
14032 if (window_info.pixmap == (Pixmap) NULL)
14033 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14034 image->filename);
14035 /*
14036 Display pixmap on the window.
14037 */
14038 if (((unsigned int) width > window_info.width) ||
14039 ((unsigned int) height > window_info.height))
14040 (void) XFillRectangle(display,window_info.pixmap,
14041 window_info.annotate_context,0,0,(unsigned int) width,
14042 (unsigned int) height);
14043 (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14044 window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14045 window_info.width,(unsigned int) window_info.height);
14046 (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14047 (void) XClearWindow(display,window_info.id);
14048 delay=1000*image->delay/(size_t) MagickMax(image->ticks_per_second,1L);
14049 XDelay(display,delay == 0UL ? 10UL : delay);
14050 (void) XSync(display,MagickFalse);
14051 return(window_info.id == root_window ? MagickTrue : MagickFalse);
14052}
14053
14054/*
14055%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14056% %
14057% %
14058% %
14059+ X D i s p l a y I m a g e %
14060% %
14061% %
14062% %
14063%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14064%
14065% XDisplayImage() displays an image via X11. A new image is created and
14066% returned if the user interactively transforms the displayed image.
14067%
14068% The format of the XDisplayImage method is:
14069%
14070% Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14071% char **argv,int argc,Image **image,size_t *state,
14072% ExceptionInfo *exception)
14073%
14074% A description of each parameter follows:
14075%
14076% o nexus: Method XDisplayImage returns an image when the
14077% user chooses 'Open Image' from the command menu or picks a tile
14078% from the image directory. Otherwise a null image is returned.
14079%
14080% o display: Specifies a connection to an X server; returned from
14081% XOpenDisplay.
14082%
14083% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14084%
14085% o argv: Specifies the application's argument list.
14086%
14087% o argc: Specifies the number of arguments.
14088%
14089% o image: Specifies an address to an address of an Image structure;
14090%
14091% o exception: return any errors or warnings in this structure.
14092%
14093*/
14094MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14095 char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14096{
14097#define MagnifySize 256 /* must be a power of 2 */
14098#define MagickMenus 10
14099#define MagickTitle "Commands"
14100
14101 const char
14102 *const CommandMenu[] =
14103 {
14104 "File",
14105 "Edit",
14106 "View",
14107 "Transform",
14108 "Enhance",
14109 "Effects",
14110 "F/X",
14111 "Image Edit",
14112 "Miscellany",
14113 "Help",
14114 (char *) NULL
14115 },
14116 *const FileMenu[] =
14117 {
14118 "Open...",
14119 "Next",
14120 "Former",
14121 "Select...",
14122 "Save...",
14123 "Print...",
14124 "Delete...",
14125 "New...",
14126 "Visual Directory...",
14127 "Quit",
14128 (char *) NULL
14129 },
14130 *const EditMenu[] =
14131 {
14132 "Undo",
14133 "Redo",
14134 "Cut",
14135 "Copy",
14136 "Paste",
14137 (char *) NULL
14138 },
14139 *const ViewMenu[] =
14140 {
14141 "Half Size",
14142 "Original Size",
14143 "Double Size",
14144 "Resize...",
14145 "Apply",
14146 "Refresh",
14147 "Restore",
14148 (char *) NULL
14149 },
14150 *const TransformMenu[] =
14151 {
14152 "Crop",
14153 "Chop",
14154 "Flop",
14155 "Flip",
14156 "Rotate Right",
14157 "Rotate Left",
14158 "Rotate...",
14159 "Shear...",
14160 "Roll...",
14161 "Trim Edges",
14162 (char *) NULL
14163 },
14164 *const EnhanceMenu[] =
14165 {
14166 "Hue...",
14167 "Saturation...",
14168 "Brightness...",
14169 "Gamma...",
14170 "Spiff",
14171 "Dull",
14172 "Contrast Stretch...",
14173 "Sigmoidal Contrast...",
14174 "Normalize",
14175 "Equalize",
14176 "Negate",
14177 "Grayscale",
14178 "Map...",
14179 "Quantize...",
14180 (char *) NULL
14181 },
14182 *const EffectsMenu[] =
14183 {
14184 "Despeckle",
14185 "Emboss",
14186 "Reduce Noise",
14187 "Add Noise...",
14188 "Sharpen...",
14189 "Blur...",
14190 "Threshold...",
14191 "Edge Detect...",
14192 "Spread...",
14193 "Shade...",
14194 "Raise...",
14195 "Segment...",
14196 (char *) NULL
14197 },
14198 *const FXMenu[] =
14199 {
14200 "Solarize...",
14201 "Sepia Tone...",
14202 "Swirl...",
14203 "Implode...",
14204 "Vignette...",
14205 "Wave...",
14206 "Oil Paint...",
14207 "Charcoal Draw...",
14208 (char *) NULL
14209 },
14210 *const ImageEditMenu[] =
14211 {
14212 "Annotate...",
14213 "Draw...",
14214 "Color...",
14215 "Matte...",
14216 "Composite...",
14217 "Add Border...",
14218 "Add Frame...",
14219 "Comment...",
14220 "Launch...",
14221 "Region of Interest...",
14222 (char *) NULL
14223 },
14224 *const MiscellanyMenu[] =
14225 {
14226 "Image Info",
14227 "Zoom Image",
14228 "Show Preview...",
14229 "Show Histogram",
14230 "Show Matte",
14231 "Background...",
14232 "Slide Show...",
14233 "Preferences...",
14234 (char *) NULL
14235 },
14236 *const HelpMenu[] =
14237 {
14238 "Overview",
14239 "Browse Documentation",
14240 "About Display",
14241 (char *) NULL
14242 },
14243 *const ShortCutsMenu[] =
14244 {
14245 "Next",
14246 "Former",
14247 "Open...",
14248 "Save...",
14249 "Print...",
14250 "Undo",
14251 "Restore",
14252 "Image Info",
14253 "Quit",
14254 (char *) NULL
14255 },
14256 *const VirtualMenu[] =
14257 {
14258 "Image Info",
14259 "Print",
14260 "Next",
14261 "Quit",
14262 (char *) NULL
14263 };
14264
14265 const char
14266 *const *Menus[MagickMenus] =
14267 {
14268 FileMenu,
14269 EditMenu,
14270 ViewMenu,
14271 TransformMenu,
14272 EnhanceMenu,
14273 EffectsMenu,
14274 FXMenu,
14275 ImageEditMenu,
14276 MiscellanyMenu,
14277 HelpMenu
14278 };
14279
14280 static DisplayCommand
14281 CommandMenus[] =
14282 {
14283 NullCommand,
14284 NullCommand,
14285 NullCommand,
14286 NullCommand,
14287 NullCommand,
14288 NullCommand,
14289 NullCommand,
14290 NullCommand,
14291 NullCommand,
14292 NullCommand,
14293 },
14294 FileCommands[] =
14295 {
14296 OpenCommand,
14297 NextCommand,
14298 FormerCommand,
14299 SelectCommand,
14300 SaveCommand,
14301 PrintCommand,
14302 DeleteCommand,
14303 NewCommand,
14304 VisualDirectoryCommand,
14305 QuitCommand
14306 },
14307 EditCommands[] =
14308 {
14309 UndoCommand,
14310 RedoCommand,
14311 CutCommand,
14312 CopyCommand,
14313 PasteCommand
14314 },
14315 ViewCommands[] =
14316 {
14317 HalfSizeCommand,
14318 OriginalSizeCommand,
14319 DoubleSizeCommand,
14320 ResizeCommand,
14321 ApplyCommand,
14322 RefreshCommand,
14323 RestoreCommand
14324 },
14325 TransformCommands[] =
14326 {
14327 CropCommand,
14328 ChopCommand,
14329 FlopCommand,
14330 FlipCommand,
14331 RotateRightCommand,
14332 RotateLeftCommand,
14333 RotateCommand,
14334 ShearCommand,
14335 RollCommand,
14336 TrimCommand
14337 },
14338 EnhanceCommands[] =
14339 {
14340 HueCommand,
14341 SaturationCommand,
14342 BrightnessCommand,
14343 GammaCommand,
14344 SpiffCommand,
14345 DullCommand,
14346 ContrastStretchCommand,
14347 SigmoidalContrastCommand,
14348 NormalizeCommand,
14349 EqualizeCommand,
14350 NegateCommand,
14351 GrayscaleCommand,
14352 MapCommand,
14353 QuantizeCommand
14354 },
14355 EffectsCommands[] =
14356 {
14357 DespeckleCommand,
14358 EmbossCommand,
14359 ReduceNoiseCommand,
14360 AddNoiseCommand,
14361 SharpenCommand,
14362 BlurCommand,
14363 ThresholdCommand,
14364 EdgeDetectCommand,
14365 SpreadCommand,
14366 ShadeCommand,
14367 RaiseCommand,
14368 SegmentCommand
14369 },
14370 FXCommands[] =
14371 {
14372 SolarizeCommand,
14373 SepiaToneCommand,
14374 SwirlCommand,
14375 ImplodeCommand,
14376 VignetteCommand,
14377 WaveCommand,
14378 OilPaintCommand,
14379 CharcoalDrawCommand
14380 },
14381 ImageEditCommands[] =
14382 {
14383 AnnotateCommand,
14384 DrawCommand,
14385 ColorCommand,
14386 MatteCommand,
14387 CompositeCommand,
14388 AddBorderCommand,
14389 AddFrameCommand,
14390 CommentCommand,
14391 LaunchCommand,
14392 RegionOfInterestCommand
14393 },
14394 MiscellanyCommands[] =
14395 {
14396 InfoCommand,
14397 ZoomCommand,
14398 ShowPreviewCommand,
14399 ShowHistogramCommand,
14400 ShowMatteCommand,
14401 BackgroundCommand,
14402 SlideShowCommand,
14403 PreferencesCommand
14404 },
14405 HelpCommands[] =
14406 {
14407 HelpCommand,
14408 BrowseDocumentationCommand,
14409 VersionCommand
14410 },
14411 ShortCutsCommands[] =
14412 {
14413 NextCommand,
14414 FormerCommand,
14415 OpenCommand,
14416 SaveCommand,
14417 PrintCommand,
14418 UndoCommand,
14419 RestoreCommand,
14420 InfoCommand,
14421 QuitCommand
14422 },
14423 VirtualCommands[] =
14424 {
14425 InfoCommand,
14426 PrintCommand,
14427 NextCommand,
14428 QuitCommand
14429 };
14430
14431 static DisplayCommand
14432 *Commands[MagickMenus] =
14433 {
14434 FileCommands,
14435 EditCommands,
14436 ViewCommands,
14437 TransformCommands,
14438 EnhanceCommands,
14439 EffectsCommands,
14440 FXCommands,
14441 ImageEditCommands,
14442 MiscellanyCommands,
14443 HelpCommands
14444 };
14445
14446 char
14447 command[MagickPathExtent],
14448 *directory,
14449 geometry[MagickPathExtent],
14450 resource_name[MagickPathExtent];
14451
14452 DisplayCommand
14453 display_command;
14454
14455 Image
14456 *display_image,
14457 *nexus;
14458
14459 int
14460 entry,
14461 id;
14462
14463 KeySym
14464 key_symbol;
14465
14466 MagickStatusType
14467 context_mask,
14468 status;
14469
14471 geometry_info;
14472
14473 int
14474 i;
14475
14476 static char
14477 working_directory[MagickPathExtent];
14478
14479 static XPoint
14480 vid_info;
14481
14482 static XWindowInfo
14483 *magick_windows[MaxXWindows];
14484
14485 static unsigned int
14486 number_windows;
14487
14488 struct stat
14489 attributes;
14490
14491 time_t
14492 timer,
14493 timestamp,
14494 update_time;
14495
14496 unsigned int
14497 height,
14498 width;
14499
14500 size_t
14501 delay;
14502
14503 WarningHandler
14504 warning_handler;
14505
14506 Window
14507 root_window;
14508
14509 XClassHint
14510 *class_hints;
14511
14512 XEvent
14513 event;
14514
14515 XFontStruct
14516 *font_info;
14517
14518 XGCValues
14519 context_values;
14520
14521 XPixelInfo
14522 *icon_pixel,
14523 *pixel;
14524
14525 XResourceInfo
14526 *icon_resources;
14527
14528 XStandardColormap
14529 *icon_map,
14530 *map_info;
14531
14532 XVisualInfo
14533 *icon_visual,
14534 *visual_info;
14535
14536 XWindowChanges
14537 window_changes;
14538
14539 XWindows
14540 *windows;
14541
14542 XWMHints
14543 *manager_hints;
14544
14545 assert(image != (Image **) NULL);
14546 assert((*image)->signature == MagickCoreSignature);
14547 if (IsEventLogging() != MagickFalse)
14548 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14549 display_image=(*image);
14550 warning_handler=(WarningHandler) NULL;
14551 windows=XSetWindows((XWindows *) ~0);
14552 if (windows != (XWindows *) NULL)
14553 {
14554 int
14555 status;
14556
14557 if (*working_directory == '\0')
14558 (void) CopyMagickString(working_directory,".",MagickPathExtent);
14559 status=chdir(working_directory);
14560 if (status == -1)
14561 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14562 "UnableToOpenFile","%s",working_directory);
14563 warning_handler=resource_info->display_warnings ?
14564 SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14565 warning_handler=resource_info->display_warnings ?
14566 SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14567 }
14568 else
14569 {
14570 /*
14571 Allocate windows structure.
14572 */
14573 resource_info->colors=display_image->colors;
14574 windows=XSetWindows(XInitializeWindows(display,resource_info));
14575 if (windows == (XWindows *) NULL)
14576 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14577 (*image)->filename);
14578 /*
14579 Initialize window id's.
14580 */
14581 number_windows=0;
14582 magick_windows[number_windows++]=(&windows->icon);
14583 magick_windows[number_windows++]=(&windows->backdrop);
14584 magick_windows[number_windows++]=(&windows->image);
14585 magick_windows[number_windows++]=(&windows->info);
14586 magick_windows[number_windows++]=(&windows->command);
14587 magick_windows[number_windows++]=(&windows->widget);
14588 magick_windows[number_windows++]=(&windows->popup);
14589 magick_windows[number_windows++]=(&windows->magnify);
14590 magick_windows[number_windows++]=(&windows->pan);
14591 for (i=0; i < (int) number_windows; i++)
14592 magick_windows[i]->id=(Window) NULL;
14593 vid_info.x=0;
14594 vid_info.y=0;
14595 }
14596 /*
14597 Initialize font info.
14598 */
14599 if (windows->font_info != (XFontStruct *) NULL)
14600 (void) XFreeFont(display,windows->font_info);
14601 windows->font_info=XBestFont(display,resource_info,MagickFalse);
14602 if (windows->font_info == (XFontStruct *) NULL)
14603 ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14604 resource_info->font);
14605 /*
14606 Initialize Standard Colormap.
14607 */
14608 map_info=windows->map_info;
14609 icon_map=windows->icon_map;
14610 visual_info=windows->visual_info;
14611 icon_visual=windows->icon_visual;
14612 pixel=windows->pixel_info;
14613 icon_pixel=windows->icon_pixel;
14614 font_info=windows->font_info;
14615 icon_resources=windows->icon_resources;
14616 class_hints=windows->class_hints;
14617 manager_hints=windows->manager_hints;
14618 root_window=XRootWindow(display,visual_info->screen);
14619 nexus=NewImageList();
14620 if (resource_info->debug != MagickFalse)
14621 {
14622 (void) LogMagickEvent(X11Event,GetMagickModule(),
14623 "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14624 (double) display_image->scene,(double) display_image->columns,
14625 (double) display_image->rows);
14626 if (display_image->colors != 0)
14627 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14628 display_image->colors);
14629 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14630 display_image->magick);
14631 }
14632 XMakeStandardColormap(display,visual_info,resource_info,display_image,
14633 map_info,pixel,exception);
14634 display_image->taint=MagickFalse;
14635 /*
14636 Initialize graphic context.
14637 */
14638 windows->context.id=(Window) NULL;
14639 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14640 resource_info,&windows->context);
14641 (void) CloneString(&class_hints->res_name,resource_info->client_name);
14642 (void) CloneString(&class_hints->res_class,resource_info->client_name);
14643 class_hints->res_class[0]=(char) LocaleToUppercase((int)
14644 class_hints->res_class[0]);
14645 manager_hints->flags=InputHint | StateHint;
14646 manager_hints->input=MagickFalse;
14647 manager_hints->initial_state=WithdrawnState;
14648 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14649 &windows->context);
14650 if (resource_info->debug != MagickFalse)
14651 (void) LogMagickEvent(X11Event,GetMagickModule(),
14652 "Window id: 0x%lx (context)",windows->context.id);
14653 context_values.background=pixel->background_color.pixel;
14654 context_values.font=font_info->fid;
14655 context_values.foreground=pixel->foreground_color.pixel;
14656 context_values.graphics_exposures=MagickFalse;
14657 context_mask=(MagickStatusType)
14658 (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14659 if (pixel->annotate_context != (GC) NULL)
14660 (void) XFreeGC(display,pixel->annotate_context);
14661 pixel->annotate_context=XCreateGC(display,windows->context.id,
14662 context_mask,&context_values);
14663 if (pixel->annotate_context == (GC) NULL)
14664 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14665 display_image->filename);
14666 context_values.background=pixel->depth_color.pixel;
14667 if (pixel->widget_context != (GC) NULL)
14668 (void) XFreeGC(display,pixel->widget_context);
14669 pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14670 &context_values);
14671 if (pixel->widget_context == (GC) NULL)
14672 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14673 display_image->filename);
14674 context_values.background=pixel->foreground_color.pixel;
14675 context_values.foreground=pixel->background_color.pixel;
14676 context_values.plane_mask=context_values.background ^
14677 context_values.foreground;
14678 if (pixel->highlight_context != (GC) NULL)
14679 (void) XFreeGC(display,pixel->highlight_context);
14680 pixel->highlight_context=XCreateGC(display,windows->context.id,
14681 (size_t) (context_mask | GCPlaneMask),&context_values);
14682 if (pixel->highlight_context == (GC) NULL)
14683 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14684 display_image->filename);
14685 (void) XDestroyWindow(display,windows->context.id);
14686 /*
14687 Initialize icon window.
14688 */
14689 XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14690 icon_resources,&windows->icon);
14691 windows->icon.geometry=resource_info->icon_geometry;
14692 XBestIconSize(display,&windows->icon,display_image);
14693 windows->icon.attributes.colormap=XDefaultColormap(display,
14694 icon_visual->screen);
14695 windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14696 manager_hints->flags=InputHint | StateHint;
14697 manager_hints->input=MagickFalse;
14698 manager_hints->initial_state=IconicState;
14699 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14700 &windows->icon);
14701 if (resource_info->debug != MagickFalse)
14702 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14703 windows->icon.id);
14704 /*
14705 Initialize graphic context for icon window.
14706 */
14707 if (icon_pixel->annotate_context != (GC) NULL)
14708 (void) XFreeGC(display,icon_pixel->annotate_context);
14709 context_values.background=icon_pixel->background_color.pixel;
14710 context_values.foreground=icon_pixel->foreground_color.pixel;
14711 icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14712 (size_t) (GCBackground | GCForeground),&context_values);
14713 if (icon_pixel->annotate_context == (GC) NULL)
14714 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14715 display_image->filename);
14716 windows->icon.annotate_context=icon_pixel->annotate_context;
14717 /*
14718 Initialize Image window.
14719 */
14720 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14721 &windows->image);
14722 windows->image.shape=MagickTrue; /* non-rectangular shape hint */
14723 if (resource_info->use_shared_memory == MagickFalse)
14724 windows->image.shared_memory=MagickFalse;
14725 if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14726 {
14727 char
14728 *title;
14729
14730 title=InterpretImageProperties(resource_info->image_info,display_image,
14731 resource_info->title,exception);
14732 (void) CloneString(&windows->image.name,title);
14733 (void) CloneString(&windows->image.icon_name,title);
14734 title=DestroyString(title);
14735 }
14736 else
14737 {
14738 char
14739 filename[MagickPathExtent],
14740 window_name[MagickPathExtent];
14741
14742 /*
14743 Window name is the base of the filename.
14744 */
14745 GetPathComponent(display_image->magick_filename,TailPath,filename);
14746 if (display_image->scene == 0)
14747 (void) FormatLocaleString(window_name,MagickPathExtent,"%s: %s",
14748 MagickPackageName,filename);
14749 else
14750 (void) FormatLocaleString(window_name,MagickPathExtent,
14751 "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14752 (double) display_image->scene,(double) GetImageListLength(
14753 display_image));
14754 (void) CloneString(&windows->image.name,window_name);
14755 (void) CloneString(&windows->image.icon_name,filename);
14756 }
14757 if (resource_info->immutable)
14758 windows->image.immutable=MagickTrue;
14759 windows->image.use_pixmap=resource_info->use_pixmap;
14760 windows->image.geometry=resource_info->image_geometry;
14761 (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
14762 XDisplayWidth(display,visual_info->screen),
14763 XDisplayHeight(display,visual_info->screen));
14764 geometry_info.width=display_image->columns;
14765 geometry_info.height=display_image->rows;
14766 geometry_info.x=0;
14767 geometry_info.y=0;
14768 (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14769 &geometry_info.width,&geometry_info.height);
14770 windows->image.width=(unsigned int) geometry_info.width;
14771 windows->image.height=(unsigned int) geometry_info.height;
14772 windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14773 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14774 KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14775 PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14776 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14777 resource_info,&windows->backdrop);
14778 if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14779 {
14780 /*
14781 Initialize backdrop window.
14782 */
14783 windows->backdrop.x=0;
14784 windows->backdrop.y=0;
14785 (void) CloneString(&windows->backdrop.name,"Backdrop");
14786 windows->backdrop.flags=(size_t) (USSize | USPosition);
14787 windows->backdrop.width=(unsigned int)
14788 XDisplayWidth(display,visual_info->screen);
14789 windows->backdrop.height=(unsigned int)
14790 XDisplayHeight(display,visual_info->screen);
14791 windows->backdrop.border_width=0;
14792 windows->backdrop.immutable=MagickTrue;
14793 windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14794 ButtonReleaseMask;
14795 windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14796 StructureNotifyMask;
14797 manager_hints->flags=IconWindowHint | InputHint | StateHint;
14798 manager_hints->icon_window=windows->icon.id;
14799 manager_hints->input=MagickTrue;
14800 manager_hints->initial_state=resource_info->iconic ? IconicState :
14801 NormalState;
14802 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14803 &windows->backdrop);
14804 if (resource_info->debug != MagickFalse)
14805 (void) LogMagickEvent(X11Event,GetMagickModule(),
14806 "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14807 (void) XMapWindow(display,windows->backdrop.id);
14808 (void) XClearWindow(display,windows->backdrop.id);
14809 if (windows->image.id != (Window) NULL)
14810 {
14811 (void) XDestroyWindow(display,windows->image.id);
14812 windows->image.id=(Window) NULL;
14813 }
14814 /*
14815 Position image in the center the backdrop.
14816 */
14817 windows->image.flags|=USPosition;
14818 windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14819 ((int) windows->image.width/2);
14820 windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14821 ((int) windows->image.height/2);
14822 }
14823 manager_hints->flags=IconWindowHint | InputHint | StateHint;
14824 manager_hints->icon_window=windows->icon.id;
14825 manager_hints->input=MagickTrue;
14826 manager_hints->initial_state=resource_info->iconic ? IconicState :
14827 NormalState;
14828 if (windows->group_leader.id != (Window) NULL)
14829 {
14830 /*
14831 Follow the leader.
14832 */
14833 manager_hints->flags|=WindowGroupHint;
14834 manager_hints->window_group=windows->group_leader.id;
14835 (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14836 if (resource_info->debug != MagickFalse)
14837 (void) LogMagickEvent(X11Event,GetMagickModule(),
14838 "Window id: 0x%lx (group leader)",windows->group_leader.id);
14839 }
14840 XMakeWindow(display,
14841 (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14842 argv,argc,class_hints,manager_hints,&windows->image);
14843 (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14844 XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14845 if (windows->group_leader.id != (Window) NULL)
14846 (void) XSetTransientForHint(display,windows->image.id,
14847 windows->group_leader.id);
14848 if (resource_info->debug != MagickFalse)
14849 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14850 windows->image.id);
14851 /*
14852 Initialize Info widget.
14853 */
14854 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14855 &windows->info);
14856 (void) CloneString(&windows->info.name,"Info");
14857 (void) CloneString(&windows->info.icon_name,"Info");
14858 windows->info.border_width=1;
14859 windows->info.x=2;
14860 windows->info.y=2;
14861 windows->info.flags|=PPosition;
14862 windows->info.attributes.win_gravity=UnmapGravity;
14863 windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14864 StructureNotifyMask;
14865 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14866 manager_hints->input=MagickFalse;
14867 manager_hints->initial_state=NormalState;
14868 manager_hints->window_group=windows->image.id;
14869 XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14870 &windows->info);
14871 windows->info.highlight_stipple=XCreateBitmapFromData(display,
14872 windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14873 windows->info.shadow_stipple=XCreateBitmapFromData(display,
14874 windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14875 (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14876 if (windows->image.mapped != MagickFalse)
14877 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14878 if (resource_info->debug != MagickFalse)
14879 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14880 windows->info.id);
14881 /*
14882 Initialize Command widget.
14883 */
14884 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14885 resource_info,&windows->command);
14886 windows->command.data=MagickMenus;
14887 (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14888 (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.command",
14889 resource_info->client_name);
14890 windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14891 resource_name,"geometry",(char *) NULL);
14892 (void) CloneString(&windows->command.name,MagickTitle);
14893 windows->command.border_width=0;
14894 windows->command.flags|=PPosition;
14895 windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14896 ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14897 OwnerGrabButtonMask | StructureNotifyMask;
14898 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14899 manager_hints->input=MagickTrue;
14900 manager_hints->initial_state=NormalState;
14901 manager_hints->window_group=windows->image.id;
14902 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14903 &windows->command);
14904 windows->command.highlight_stipple=XCreateBitmapFromData(display,
14905 windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14906 HighlightHeight);
14907 windows->command.shadow_stipple=XCreateBitmapFromData(display,
14908 windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14909 (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14910 if (windows->command.mapped != MagickFalse)
14911 (void) XMapRaised(display,windows->command.id);
14912 if (resource_info->debug != MagickFalse)
14913 (void) LogMagickEvent(X11Event,GetMagickModule(),
14914 "Window id: 0x%lx (command)",windows->command.id);
14915 /*
14916 Initialize Widget window.
14917 */
14918 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14919 resource_info,&windows->widget);
14920 (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.widget",
14921 resource_info->client_name);
14922 windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14923 resource_name,"geometry",(char *) NULL);
14924 windows->widget.border_width=0;
14925 windows->widget.flags|=PPosition;
14926 windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14927 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14928 KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14929 StructureNotifyMask;
14930 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14931 manager_hints->input=MagickTrue;
14932 manager_hints->initial_state=NormalState;
14933 manager_hints->window_group=windows->image.id;
14934 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14935 &windows->widget);
14936 windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14937 windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14938 windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14939 windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14940 (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14941 if (resource_info->debug != MagickFalse)
14942 (void) LogMagickEvent(X11Event,GetMagickModule(),
14943 "Window id: 0x%lx (widget)",windows->widget.id);
14944 /*
14945 Initialize popup window.
14946 */
14947 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14948 resource_info,&windows->popup);
14949 windows->popup.border_width=0;
14950 windows->popup.flags|=PPosition;
14951 windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14952 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14953 KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14954 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14955 manager_hints->input=MagickTrue;
14956 manager_hints->initial_state=NormalState;
14957 manager_hints->window_group=windows->image.id;
14958 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14959 &windows->popup);
14960 windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14961 windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14962 windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14963 windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14964 (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14965 if (resource_info->debug != MagickFalse)
14966 (void) LogMagickEvent(X11Event,GetMagickModule(),
14967 "Window id: 0x%lx (pop up)",windows->popup.id);
14968 /*
14969 Initialize Magnify window and cursor.
14970 */
14971 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14972 resource_info,&windows->magnify);
14973 if (resource_info->use_shared_memory == MagickFalse)
14974 windows->magnify.shared_memory=MagickFalse;
14975 (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.magnify",
14976 resource_info->client_name);
14977 windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14978 resource_name,"geometry",(char *) NULL);
14979 (void) FormatLocaleString(windows->magnify.name,MagickPathExtent,
14980 "Magnify %uX",resource_info->magnify);
14981 if (windows->magnify.cursor != (Cursor) NULL)
14982 (void) XFreeCursor(display,windows->magnify.cursor);
14983 windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14984 map_info->colormap,resource_info->background_color,
14985 resource_info->foreground_color);
14986 if (windows->magnify.cursor == (Cursor) NULL)
14987 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14988 display_image->filename);
14989 windows->magnify.width=MagnifySize;
14990 windows->magnify.height=MagnifySize;
14991 windows->magnify.flags|=PPosition;
14992 windows->magnify.min_width=MagnifySize;
14993 windows->magnify.min_height=MagnifySize;
14994 windows->magnify.width_inc=MagnifySize;
14995 windows->magnify.height_inc=MagnifySize;
14996 windows->magnify.data=resource_info->magnify;
14997 windows->magnify.attributes.cursor=windows->magnify.cursor;
14998 windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14999 ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
15000 StructureNotifyMask;
15001 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15002 manager_hints->input=MagickTrue;
15003 manager_hints->initial_state=NormalState;
15004 manager_hints->window_group=windows->image.id;
15005 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15006 &windows->magnify);
15007 if (resource_info->debug != MagickFalse)
15008 (void) LogMagickEvent(X11Event,GetMagickModule(),
15009 "Window id: 0x%lx (magnify)",windows->magnify.id);
15010 (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
15011 /*
15012 Initialize panning window.
15013 */
15014 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
15015 resource_info,&windows->pan);
15016 (void) CloneString(&windows->pan.name,"Pan Icon");
15017 windows->pan.width=windows->icon.width;
15018 windows->pan.height=windows->icon.height;
15019 (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.pan",
15020 resource_info->client_name);
15021 windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
15022 resource_name,"geometry",(char *) NULL);
15023 (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15024 &windows->pan.width,&windows->pan.height);
15025 windows->pan.flags|=PPosition;
15026 windows->pan.immutable=MagickTrue;
15027 windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15028 ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15029 StructureNotifyMask;
15030 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15031 manager_hints->input=MagickFalse;
15032 manager_hints->initial_state=NormalState;
15033 manager_hints->window_group=windows->image.id;
15034 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15035 &windows->pan);
15036 if (resource_info->debug != MagickFalse)
15037 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15038 windows->pan.id);
15039 (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15040 if (windows->info.mapped != MagickFalse)
15041 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15042 if ((windows->image.mapped == MagickFalse) ||
15043 (windows->backdrop.id != (Window) NULL))
15044 (void) XMapWindow(display,windows->image.id);
15045 /*
15046 Set our progress monitor and warning handlers.
15047 */
15048 if (warning_handler == (WarningHandler) NULL)
15049 {
15050 warning_handler=resource_info->display_warnings ?
15051 SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15052 warning_handler=resource_info->display_warnings ?
15053 SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15054 }
15055 /*
15056 Initialize Image and Magnify X images.
15057 */
15058 windows->image.x=0;
15059 windows->image.y=0;
15060 windows->magnify.shape=MagickFalse;
15061 width=(unsigned int) display_image->columns;
15062 height=(unsigned int) display_image->rows;
15063 if ((display_image->columns != width) || (display_image->rows != height))
15064 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15065 display_image->filename);
15066 status=XMakeImage(display,resource_info,&windows->image,display_image,
15067 width,height,exception);
15068 if (status == MagickFalse)
15069 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15070 display_image->filename);
15071 status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15072 windows->magnify.width,windows->magnify.height,exception);
15073 if (status == MagickFalse)
15074 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15075 display_image->filename);
15076 if (windows->magnify.mapped != MagickFalse)
15077 (void) XMapRaised(display,windows->magnify.id);
15078 if (windows->pan.mapped != MagickFalse)
15079 (void) XMapRaised(display,windows->pan.id);
15080 windows->image.window_changes.width=(int) display_image->columns;
15081 windows->image.window_changes.height=(int) display_image->rows;
15082 (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15083 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15084 (void) XSync(display,MagickFalse);
15085 /*
15086 Respond to events.
15087 */
15088 delay=display_image->delay/(size_t)
15089 MagickMax(display_image->ticks_per_second,1L);
15090 timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15091 update_time=0;
15092 if (resource_info->update != MagickFalse)
15093 {
15094 MagickBooleanType
15095 status;
15096
15097 /*
15098 Determine when file data was last modified.
15099 */
15100 status=GetPathAttributes(display_image->filename,&attributes);
15101 if (status != MagickFalse)
15102 update_time=attributes.st_mtime;
15103 }
15104 *state&=(unsigned int) (~FormerImageState);
15105 *state&=(unsigned int) (~MontageImageState);
15106 *state&=(unsigned int) (~NextImageState);
15107 do
15108 {
15109 /*
15110 Handle a window event.
15111 */
15112 if (windows->image.mapped != MagickFalse)
15113 if ((display_image->delay != 0) || (resource_info->update != 0))
15114 {
15115 if (timer < GetMagickTime())
15116 {
15117 if (resource_info->update == MagickFalse)
15118 *state|=NextImageState | ExitState;
15119 else
15120 {
15121 MagickBooleanType
15122 status;
15123
15124 /*
15125 Determine if image file was modified.
15126 */
15127 status=GetPathAttributes(display_image->filename,&attributes);
15128 if (status != MagickFalse)
15129 if (update_time != attributes.st_mtime)
15130 {
15131 /*
15132 Redisplay image.
15133 */
15134 (void) FormatLocaleString(
15135 resource_info->image_info->filename,MagickPathExtent,
15136 "%s:%s",display_image->magick,
15137 display_image->filename);
15138 nexus=ReadImage(resource_info->image_info,exception);
15139 if (nexus != (Image *) NULL)
15140 *state|=NextImageState | ExitState;
15141 }
15142 delay=display_image->delay/(size_t) MagickMax(
15143 display_image->ticks_per_second,1L);
15144 timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15145 }
15146 }
15147 if (XEventsQueued(display,QueuedAfterFlush) == 0)
15148 {
15149 /*
15150 Do not block if delay > 0.
15151 */
15152 XDelay(display,SuspendTime << 2);
15153 continue;
15154 }
15155 }
15156 timestamp=GetMagickTime();
15157 (void) XNextEvent(display,&event);
15158 if ((windows->image.stasis == MagickFalse) ||
15159 (windows->magnify.stasis == MagickFalse))
15160 {
15161 if ((GetMagickTime()-timestamp) > 0)
15162 {
15163 windows->image.stasis=MagickTrue;
15164 windows->magnify.stasis=MagickTrue;
15165 }
15166 }
15167 if (event.xany.window == windows->command.id)
15168 {
15169 /*
15170 Select a command from the Command widget.
15171 */
15172 id=XCommandWidget(display,windows,CommandMenu,&event);
15173 if (id < 0)
15174 continue;
15175 (void) CopyMagickString(command,CommandMenu[id],MagickPathExtent);
15176 display_command=CommandMenus[id];
15177 if (id < MagickMenus)
15178 {
15179 /*
15180 Select a command from a pop-up menu.
15181 */
15182 entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15183 command);
15184 if (entry < 0)
15185 continue;
15186 (void) CopyMagickString(command,Menus[id][entry],MagickPathExtent);
15187 display_command=Commands[id][entry];
15188 }
15189 if (display_command != NullCommand)
15190 nexus=XMagickCommand(display,resource_info,windows,display_command,
15191 &display_image,exception);
15192 continue;
15193 }
15194 switch (event.type)
15195 {
15196 case ButtonPress:
15197 {
15198 if (resource_info->debug != MagickFalse)
15199 (void) LogMagickEvent(X11Event,GetMagickModule(),
15200 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15201 event.xbutton.button,event.xbutton.x,event.xbutton.y);
15202 if ((event.xbutton.button == Button3) &&
15203 (event.xbutton.state & Mod1Mask))
15204 {
15205 /*
15206 Convert Alt-Button3 to Button2.
15207 */
15208 event.xbutton.button=Button2;
15209 event.xbutton.state&=(unsigned int) (~Mod1Mask);
15210 }
15211 if (event.xbutton.window == windows->backdrop.id)
15212 {
15213 (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15214 event.xbutton.time);
15215 break;
15216 }
15217 if (event.xbutton.window == windows->image.id)
15218 {
15219 switch (event.xbutton.button)
15220 {
15221 case Button1:
15222 {
15223 if (resource_info->immutable)
15224 {
15225 /*
15226 Select a command from the Virtual menu.
15227 */
15228 entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15229 command);
15230 if (entry >= 0)
15231 nexus=XMagickCommand(display,resource_info,windows,
15232 VirtualCommands[entry],&display_image,exception);
15233 break;
15234 }
15235 /*
15236 Map/unmap Command widget.
15237 */
15238 if (windows->command.mapped != MagickFalse)
15239 (void) XWithdrawWindow(display,windows->command.id,
15240 windows->command.screen);
15241 else
15242 {
15243 (void) XCommandWidget(display,windows,CommandMenu,
15244 (XEvent *) NULL);
15245 (void) XMapRaised(display,windows->command.id);
15246 }
15247 break;
15248 }
15249 case Button2:
15250 {
15251 /*
15252 User pressed the image magnify button.
15253 */
15254 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15255 &display_image,exception);
15256 XMagnifyImage(display,windows,&event,exception);
15257 break;
15258 }
15259 case Button3:
15260 {
15261 if (resource_info->immutable)
15262 {
15263 /*
15264 Select a command from the Virtual menu.
15265 */
15266 entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15267 command);
15268 if (entry >= 0)
15269 nexus=XMagickCommand(display,resource_info,windows,
15270 VirtualCommands[entry],&display_image,exception);
15271 break;
15272 }
15273 if (display_image->montage != (char *) NULL)
15274 {
15275 /*
15276 Open or delete a tile from a visual image directory.
15277 */
15278 nexus=XTileImage(display,resource_info,windows,
15279 display_image,&event,exception);
15280 if (nexus != (Image *) NULL)
15281 *state|=MontageImageState | NextImageState | ExitState;
15282 vid_info.x=(short int) windows->image.x;
15283 vid_info.y=(short int) windows->image.y;
15284 break;
15285 }
15286 /*
15287 Select a command from the Short Cuts menu.
15288 */
15289 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15290 command);
15291 if (entry >= 0)
15292 nexus=XMagickCommand(display,resource_info,windows,
15293 ShortCutsCommands[entry],&display_image,exception);
15294 break;
15295 }
15296 case Button4:
15297 {
15298 /*
15299 Wheel up.
15300 */
15301 XTranslateImage(display,windows,*image,XK_Up);
15302 break;
15303 }
15304 case Button5:
15305 {
15306 /*
15307 Wheel down.
15308 */
15309 XTranslateImage(display,windows,*image,XK_Down);
15310 break;
15311 }
15312 default:
15313 break;
15314 }
15315 break;
15316 }
15317 if (event.xbutton.window == windows->magnify.id)
15318 {
15319 const char
15320 *const MagnifyMenu[] =
15321 {
15322 "2",
15323 "4",
15324 "5",
15325 "6",
15326 "7",
15327 "8",
15328 "9",
15329 "3",
15330 (char *) NULL,
15331 };
15332
15333 int
15334 factor;
15335
15336 static KeySym
15337 MagnifyCommands[] =
15338 {
15339 XK_2,
15340 XK_4,
15341 XK_5,
15342 XK_6,
15343 XK_7,
15344 XK_8,
15345 XK_9,
15346 XK_3
15347 };
15348
15349 /*
15350 Select a magnify factor from the pop-up menu.
15351 */
15352 factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15353 if (factor >= 0)
15354 XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15355 exception);
15356 break;
15357 }
15358 if (event.xbutton.window == windows->pan.id)
15359 {
15360 switch (event.xbutton.button)
15361 {
15362 case Button4:
15363 {
15364 /*
15365 Wheel up.
15366 */
15367 XTranslateImage(display,windows,*image,XK_Up);
15368 break;
15369 }
15370 case Button5:
15371 {
15372 /*
15373 Wheel down.
15374 */
15375 XTranslateImage(display,windows,*image,XK_Down);
15376 break;
15377 }
15378 default:
15379 {
15380 XPanImage(display,windows,&event,exception);
15381 break;
15382 }
15383 }
15384 break;
15385 }
15386 delay=display_image->delay/(size_t)
15387 MagickMax(display_image->ticks_per_second,1L);
15388 timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15389 break;
15390 }
15391 case ButtonRelease:
15392 {
15393 if (resource_info->debug != MagickFalse)
15394 (void) LogMagickEvent(X11Event,GetMagickModule(),
15395 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15396 event.xbutton.button,event.xbutton.x,event.xbutton.y);
15397 break;
15398 }
15399 case ClientMessage:
15400 {
15401 if (resource_info->debug != MagickFalse)
15402 (void) LogMagickEvent(X11Event,GetMagickModule(),
15403 "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15404 event.xclient.message_type,event.xclient.format,(unsigned long)
15405 event.xclient.data.l[0]);
15406 if (event.xclient.message_type == windows->im_protocols)
15407 {
15408 if (*event.xclient.data.l == (long) windows->im_update_widget)
15409 {
15410 (void) CloneString(&windows->command.name,MagickTitle);
15411 windows->command.data=MagickMenus;
15412 (void) XCommandWidget(display,windows,CommandMenu,
15413 (XEvent *) NULL);
15414 break;
15415 }
15416 if (*event.xclient.data.l == (long) windows->im_update_colormap)
15417 {
15418 /*
15419 Update graphic context and window colormap.
15420 */
15421 for (i=0; i < (int) number_windows; i++)
15422 {
15423 if (magick_windows[i]->id == windows->icon.id)
15424 continue;
15425 context_values.background=pixel->background_color.pixel;
15426 context_values.foreground=pixel->foreground_color.pixel;
15427 (void) XChangeGC(display,magick_windows[i]->annotate_context,
15428 context_mask,&context_values);
15429 (void) XChangeGC(display,magick_windows[i]->widget_context,
15430 context_mask,&context_values);
15431 context_values.background=pixel->foreground_color.pixel;
15432 context_values.foreground=pixel->background_color.pixel;
15433 context_values.plane_mask=context_values.background ^
15434 context_values.foreground;
15435 (void) XChangeGC(display,magick_windows[i]->highlight_context,
15436 (size_t) (context_mask | GCPlaneMask),
15437 &context_values);
15438 magick_windows[i]->attributes.background_pixel=
15439 pixel->background_color.pixel;
15440 magick_windows[i]->attributes.border_pixel=
15441 pixel->border_color.pixel;
15442 magick_windows[i]->attributes.colormap=map_info->colormap;
15443 (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15444 (unsigned long) magick_windows[i]->mask,
15445 &magick_windows[i]->attributes);
15446 }
15447 if (windows->pan.mapped != MagickFalse)
15448 {
15449 (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15450 windows->pan.pixmap);
15451 (void) XClearWindow(display,windows->pan.id);
15452 XDrawPanRectangle(display,windows);
15453 }
15454 if (windows->backdrop.id != (Window) NULL)
15455 (void) XInstallColormap(display,map_info->colormap);
15456 break;
15457 }
15458 if (*event.xclient.data.l == (long) windows->im_former_image)
15459 {
15460 *state|=FormerImageState | ExitState;
15461 break;
15462 }
15463 if (*event.xclient.data.l == (long) windows->im_next_image)
15464 {
15465 *state|=NextImageState | ExitState;
15466 break;
15467 }
15468 if (*event.xclient.data.l == (long) windows->im_retain_colors)
15469 {
15470 *state|=RetainColorsState;
15471 break;
15472 }
15473 if (*event.xclient.data.l == (long) windows->im_exit)
15474 {
15475 *state|=ExitState;
15476 break;
15477 }
15478 break;
15479 }
15480 if (event.xclient.message_type == windows->dnd_protocols)
15481 {
15482 Atom
15483 selection,
15484 type;
15485
15486 int
15487 format,
15488 status;
15489
15490 unsigned char
15491 *data;
15492
15493 unsigned long
15494 after,
15495 length;
15496
15497 /*
15498 Display image named by the Drag-and-Drop selection.
15499 */
15500 if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15501 break;
15502 selection=XInternAtom(display,"DndSelection",MagickFalse);
15503 status=XGetWindowProperty(display,root_window,selection,0L,(long)
15504 MagickPathExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15505 &length,&after,&data);
15506 if ((status != Success) || (length == 0))
15507 break;
15508 if (*event.xclient.data.l == 2)
15509 {
15510 /*
15511 Offix DND.
15512 */
15513 (void) CopyMagickString(resource_info->image_info->filename,
15514 (char *) data,MagickPathExtent);
15515 }
15516 else
15517 {
15518 /*
15519 XDND.
15520 */
15521 if (strncmp((char *) data, "file:", 5) != 0)
15522 {
15523 (void) XFree((void *) data);
15524 break;
15525 }
15526 (void) CopyMagickString(resource_info->image_info->filename,
15527 ((char *) data)+5,MagickPathExtent);
15528 }
15529 nexus=ReadImage(resource_info->image_info,exception);
15530 CatchException(exception);
15531 if (nexus != (Image *) NULL)
15532 *state|=NextImageState | ExitState;
15533 (void) XFree((void *) data);
15534 break;
15535 }
15536 /*
15537 If client window delete message, exit.
15538 */
15539 if (event.xclient.message_type != windows->wm_protocols)
15540 break;
15541 if (*event.xclient.data.l != (long) windows->wm_delete_window)
15542 break;
15543 (void) XWithdrawWindow(display,event.xclient.window,
15544 visual_info->screen);
15545 if (event.xclient.window == windows->image.id)
15546 {
15547 *state|=ExitState;
15548 break;
15549 }
15550 if (event.xclient.window == windows->pan.id)
15551 {
15552 /*
15553 Restore original image size when pan window is deleted.
15554 */
15555 windows->image.window_changes.width=windows->image.ximage->width;
15556 windows->image.window_changes.height=windows->image.ximage->height;
15557 (void) XConfigureImage(display,resource_info,windows,
15558 display_image,exception);
15559 }
15560 break;
15561 }
15562 case ConfigureNotify:
15563 {
15564 if (resource_info->debug != MagickFalse)
15565 (void) LogMagickEvent(X11Event,GetMagickModule(),
15566 "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15567 event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15568 event.xconfigure.y,event.xconfigure.send_event);
15569 if (event.xconfigure.window == windows->image.id)
15570 {
15571 /*
15572 Image window has a new configuration.
15573 */
15574 if (event.xconfigure.send_event != 0)
15575 {
15576 XWindowChanges
15577 window_changes;
15578
15579 /*
15580 Position the transient windows relative of the Image window.
15581 */
15582 if (windows->command.geometry == (char *) NULL)
15583 if (windows->command.mapped == MagickFalse)
15584 {
15585 windows->command.x=event.xconfigure.x-(int)
15586 windows->command.width-25;
15587 windows->command.y=event.xconfigure.y;
15588 XConstrainWindowPosition(display,&windows->command);
15589 window_changes.x=windows->command.x;
15590 window_changes.y=windows->command.y;
15591 (void) XReconfigureWMWindow(display,windows->command.id,
15592 windows->command.screen,(unsigned int) (CWX | CWY),
15593 &window_changes);
15594 }
15595 if (windows->widget.geometry == (char *) NULL)
15596 if (windows->widget.mapped == MagickFalse)
15597 {
15598 windows->widget.x=event.xconfigure.x+
15599 event.xconfigure.width/10;
15600 windows->widget.y=event.xconfigure.y+
15601 event.xconfigure.height/10;
15602 XConstrainWindowPosition(display,&windows->widget);
15603 window_changes.x=windows->widget.x;
15604 window_changes.y=windows->widget.y;
15605 (void) XReconfigureWMWindow(display,windows->widget.id,
15606 windows->widget.screen,(unsigned int) (CWX | CWY),
15607 &window_changes);
15608 }
15609 if (windows->magnify.geometry == (char *) NULL)
15610 if (windows->magnify.mapped == MagickFalse)
15611 {
15612 windows->magnify.x=event.xconfigure.x+
15613 event.xconfigure.width+25;
15614 windows->magnify.y=event.xconfigure.y;
15615 XConstrainWindowPosition(display,&windows->magnify);
15616 window_changes.x=windows->magnify.x;
15617 window_changes.y=windows->magnify.y;
15618 (void) XReconfigureWMWindow(display,windows->magnify.id,
15619 windows->magnify.screen,(unsigned int) (CWX | CWY),
15620 &window_changes);
15621 }
15622 if (windows->pan.geometry == (char *) NULL)
15623 if (windows->pan.mapped == MagickFalse)
15624 {
15625 windows->pan.x=event.xconfigure.x+(int)
15626 event.xconfigure.width+25;
15627 windows->pan.y=event.xconfigure.y+(int)
15628 windows->magnify.height+50;
15629 XConstrainWindowPosition(display,&windows->pan);
15630 window_changes.x=windows->pan.x;
15631 window_changes.y=windows->pan.y;
15632 (void) XReconfigureWMWindow(display,windows->pan.id,
15633 windows->pan.screen,(unsigned int) (CWX | CWY),
15634 &window_changes);
15635 }
15636 }
15637 if ((event.xconfigure.width == (int) windows->image.width) &&
15638 (event.xconfigure.height == (int) windows->image.height))
15639 break;
15640 windows->image.width=(unsigned int) event.xconfigure.width;
15641 windows->image.height=(unsigned int) event.xconfigure.height;
15642 windows->image.x=0;
15643 windows->image.y=0;
15644 if (display_image->montage != (char *) NULL)
15645 {
15646 windows->image.x=vid_info.x;
15647 windows->image.y=vid_info.y;
15648 }
15649 if (windows->image.mapped != MagickFalse &&
15650 windows->image.stasis != MagickFalse)
15651 {
15652 /*
15653 Update image window configuration.
15654 */
15655 windows->image.window_changes.width=event.xconfigure.width;
15656 windows->image.window_changes.height=event.xconfigure.height;
15657 (void) XConfigureImage(display,resource_info,windows,
15658 display_image,exception);
15659 }
15660 /*
15661 Update pan window configuration.
15662 */
15663 if ((event.xconfigure.width < windows->image.ximage->width) ||
15664 (event.xconfigure.height < windows->image.ximage->height))
15665 {
15666 (void) XMapRaised(display,windows->pan.id);
15667 XDrawPanRectangle(display,windows);
15668 }
15669 else
15670 if (windows->pan.mapped != MagickFalse)
15671 (void) XWithdrawWindow(display,windows->pan.id,
15672 windows->pan.screen);
15673 break;
15674 }
15675 if (event.xconfigure.window == windows->magnify.id)
15676 {
15677 unsigned int
15678 magnify;
15679
15680 /*
15681 Magnify window has a new configuration.
15682 */
15683 windows->magnify.width=(unsigned int) event.xconfigure.width;
15684 windows->magnify.height=(unsigned int) event.xconfigure.height;
15685 if (windows->magnify.mapped == MagickFalse)
15686 break;
15687 magnify=1;
15688 while ((int) magnify <= event.xconfigure.width)
15689 magnify<<=1;
15690 while ((int) magnify <= event.xconfigure.height)
15691 magnify<<=1;
15692 magnify>>=1;
15693 if (((int) magnify != event.xconfigure.width) ||
15694 ((int) magnify != event.xconfigure.height))
15695 {
15696 window_changes.width=(int) magnify;
15697 window_changes.height=(int) magnify;
15698 (void) XReconfigureWMWindow(display,windows->magnify.id,
15699 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15700 &window_changes);
15701 break;
15702 }
15703 if (windows->magnify.mapped != MagickFalse &&
15704 windows->magnify.stasis != MagickFalse)
15705 {
15706 status=XMakeImage(display,resource_info,&windows->magnify,
15707 display_image,windows->magnify.width,windows->magnify.height,
15708 exception);
15709 XMakeMagnifyImage(display,windows,exception);
15710 }
15711 break;
15712 }
15713 if (windows->magnify.mapped != MagickFalse &&
15714 (event.xconfigure.window == windows->pan.id))
15715 {
15716 /*
15717 Pan icon window has a new configuration.
15718 */
15719 if (event.xconfigure.send_event != 0)
15720 {
15721 windows->pan.x=event.xconfigure.x;
15722 windows->pan.y=event.xconfigure.y;
15723 }
15724 windows->pan.width=(unsigned int) event.xconfigure.width;
15725 windows->pan.height=(unsigned int) event.xconfigure.height;
15726 break;
15727 }
15728 if (event.xconfigure.window == windows->icon.id)
15729 {
15730 /*
15731 Icon window has a new configuration.
15732 */
15733 windows->icon.width=(unsigned int) event.xconfigure.width;
15734 windows->icon.height=(unsigned int) event.xconfigure.height;
15735 break;
15736 }
15737 break;
15738 }
15739 case DestroyNotify:
15740 {
15741 /*
15742 Group leader has exited.
15743 */
15744 if (resource_info->debug != MagickFalse)
15745 (void) LogMagickEvent(X11Event,GetMagickModule(),
15746 "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15747 if (event.xdestroywindow.window == windows->group_leader.id)
15748 {
15749 *state|=ExitState;
15750 break;
15751 }
15752 break;
15753 }
15754 case EnterNotify:
15755 {
15756 /*
15757 Selectively install colormap.
15758 */
15759 if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15760 if (event.xcrossing.mode != NotifyUngrab)
15761 XInstallColormap(display,map_info->colormap);
15762 break;
15763 }
15764 case Expose:
15765 {
15766 if (resource_info->debug != MagickFalse)
15767 (void) LogMagickEvent(X11Event,GetMagickModule(),
15768 "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15769 event.xexpose.width,event.xexpose.height,event.xexpose.x,
15770 event.xexpose.y);
15771 /*
15772 Refresh windows that are now exposed.
15773 */
15774 if ((event.xexpose.window == windows->image.id) &&
15775 windows->image.mapped != MagickFalse)
15776 {
15777 XRefreshWindow(display,&windows->image,&event);
15778 delay=display_image->delay/(size_t) MagickMax(
15779 display_image->ticks_per_second,1L);
15780 timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15781 break;
15782 }
15783 if ((event.xexpose.window == windows->magnify.id) &&
15784 windows->magnify.mapped != MagickFalse)
15785 {
15786 XMakeMagnifyImage(display,windows,exception);
15787 break;
15788 }
15789 if (event.xexpose.window == windows->pan.id)
15790 {
15791 XDrawPanRectangle(display,windows);
15792 break;
15793 }
15794 if (event.xexpose.window == windows->icon.id)
15795 {
15796 XRefreshWindow(display,&windows->icon,&event);
15797 break;
15798 }
15799 break;
15800 }
15801 case KeyPress:
15802 {
15803 int
15804 length;
15805
15806 /*
15807 Respond to a user key press.
15808 */
15809 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15810 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15811 *(command+length)='\0';
15812 if (resource_info->debug != MagickFalse)
15813 (void) LogMagickEvent(X11Event,GetMagickModule(),
15814 "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15815 key_symbol,command);
15816 if (event.xkey.window == windows->image.id)
15817 {
15818 display_command=XImageWindowCommand(display,resource_info,windows,
15819 event.xkey.state,key_symbol,&display_image,exception);
15820 if (display_command != NullCommand)
15821 nexus=XMagickCommand(display,resource_info,windows,
15822 display_command,&display_image,exception);
15823 }
15824 if (event.xkey.window == windows->magnify.id)
15825 XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15826 exception);
15827 if (event.xkey.window == windows->pan.id)
15828 {
15829 if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15830 (void) XWithdrawWindow(display,windows->pan.id,
15831 windows->pan.screen);
15832 else
15833 if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15834 XTextViewHelp(display,resource_info,windows,MagickFalse,
15835 "Help Viewer - Image Pan",ImagePanHelp);
15836 else
15837 XTranslateImage(display,windows,*image,key_symbol);
15838 }
15839 delay=display_image->delay/(size_t) MagickMax(
15840 display_image->ticks_per_second,1L);
15841 timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15842 break;
15843 }
15844 case KeyRelease:
15845 {
15846 /*
15847 Respond to a user key release.
15848 */
15849 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15850 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15851 if (resource_info->debug != MagickFalse)
15852 (void) LogMagickEvent(X11Event,GetMagickModule(),
15853 "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15854 break;
15855 }
15856 case LeaveNotify:
15857 {
15858 /*
15859 Selectively uninstall colormap.
15860 */
15861 if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15862 if (event.xcrossing.mode != NotifyUngrab)
15863 XUninstallColormap(display,map_info->colormap);
15864 break;
15865 }
15866 case MapNotify:
15867 {
15868 if (resource_info->debug != MagickFalse)
15869 (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15870 event.xmap.window);
15871 if (event.xmap.window == windows->backdrop.id)
15872 {
15873 (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15874 CurrentTime);
15875 windows->backdrop.mapped=MagickTrue;
15876 break;
15877 }
15878 if (event.xmap.window == windows->image.id)
15879 {
15880 if (windows->backdrop.id != (Window) NULL)
15881 (void) XInstallColormap(display,map_info->colormap);
15882 if (LocaleCompare(display_image->magick,"LOGO") == 0)
15883 {
15884 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15885 nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15886 }
15887 if (((int) windows->image.width < windows->image.ximage->width) ||
15888 ((int) windows->image.height < windows->image.ximage->height))
15889 (void) XMapRaised(display,windows->pan.id);
15890 windows->image.mapped=MagickTrue;
15891 break;
15892 }
15893 if (event.xmap.window == windows->magnify.id)
15894 {
15895 XMakeMagnifyImage(display,windows,exception);
15896 windows->magnify.mapped=MagickTrue;
15897 (void) XWithdrawWindow(display,windows->info.id,
15898 windows->info.screen);
15899 break;
15900 }
15901 if (event.xmap.window == windows->pan.id)
15902 {
15903 XMakePanImage(display,resource_info,windows,display_image,
15904 exception);
15905 windows->pan.mapped=MagickTrue;
15906 break;
15907 }
15908 if (event.xmap.window == windows->info.id)
15909 {
15910 windows->info.mapped=MagickTrue;
15911 break;
15912 }
15913 if (event.xmap.window == windows->icon.id)
15914 {
15915 MagickBooleanType
15916 taint;
15917
15918 /*
15919 Create an icon image.
15920 */
15921 taint=display_image->taint;
15922 XMakeStandardColormap(display,icon_visual,icon_resources,
15923 display_image,icon_map,icon_pixel,exception);
15924 (void) XMakeImage(display,icon_resources,&windows->icon,
15925 display_image,windows->icon.width,windows->icon.height,
15926 exception);
15927 display_image->taint=taint;
15928 (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15929 windows->icon.pixmap);
15930 (void) XClearWindow(display,windows->icon.id);
15931 (void) XWithdrawWindow(display,windows->info.id,
15932 windows->info.screen);
15933 windows->icon.mapped=MagickTrue;
15934 break;
15935 }
15936 if (event.xmap.window == windows->command.id)
15937 {
15938 windows->command.mapped=MagickTrue;
15939 break;
15940 }
15941 if (event.xmap.window == windows->popup.id)
15942 {
15943 windows->popup.mapped=MagickTrue;
15944 break;
15945 }
15946 if (event.xmap.window == windows->widget.id)
15947 {
15948 windows->widget.mapped=MagickTrue;
15949 break;
15950 }
15951 break;
15952 }
15953 case MappingNotify:
15954 {
15955 (void) XRefreshKeyboardMapping(&event.xmapping);
15956 break;
15957 }
15958 case NoExpose:
15959 break;
15960 case PropertyNotify:
15961 {
15962 Atom
15963 type;
15964
15965 int
15966 format,
15967 status;
15968
15969 unsigned char
15970 *data;
15971
15972 unsigned long
15973 after,
15974 length;
15975
15976 if (resource_info->debug != MagickFalse)
15977 (void) LogMagickEvent(X11Event,GetMagickModule(),
15978 "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15979 event.xproperty.atom,event.xproperty.state);
15980 if (event.xproperty.atom != windows->im_remote_command)
15981 break;
15982 /*
15983 Display image named by the remote command protocol.
15984 */
15985 status=XGetWindowProperty(display,event.xproperty.window,
15986 event.xproperty.atom,0L,(long) MagickPathExtent,MagickFalse,(Atom)
15987 AnyPropertyType,&type,&format,&length,&after,&data);
15988 if ((status != Success) || (length == 0))
15989 break;
15990 if (LocaleCompare((char *) data,"-quit") == 0)
15991 {
15992 XClientMessage(display,windows->image.id,windows->im_protocols,
15993 windows->im_exit,CurrentTime);
15994 (void) XFree((void *) data);
15995 break;
15996 }
15997 (void) CopyMagickString(resource_info->image_info->filename,
15998 (char *) data,MagickPathExtent);
15999 (void) XFree((void *) data);
16000 nexus=ReadImage(resource_info->image_info,exception);
16001 CatchException(exception);
16002 if (nexus != (Image *) NULL)
16003 *state|=NextImageState | ExitState;
16004 break;
16005 }
16006 case ReparentNotify:
16007 {
16008 if (resource_info->debug != MagickFalse)
16009 (void) LogMagickEvent(X11Event,GetMagickModule(),
16010 "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
16011 event.xreparent.window);
16012 break;
16013 }
16014 case UnmapNotify:
16015 {
16016 if (resource_info->debug != MagickFalse)
16017 (void) LogMagickEvent(X11Event,GetMagickModule(),
16018 "Unmap Notify: 0x%lx",event.xunmap.window);
16019 if (event.xunmap.window == windows->backdrop.id)
16020 {
16021 windows->backdrop.mapped=MagickFalse;
16022 break;
16023 }
16024 if (event.xunmap.window == windows->image.id)
16025 {
16026 windows->image.mapped=MagickFalse;
16027 break;
16028 }
16029 if (event.xunmap.window == windows->magnify.id)
16030 {
16031 windows->magnify.mapped=MagickFalse;
16032 break;
16033 }
16034 if (event.xunmap.window == windows->pan.id)
16035 {
16036 windows->pan.mapped=MagickFalse;
16037 break;
16038 }
16039 if (event.xunmap.window == windows->info.id)
16040 {
16041 windows->info.mapped=MagickFalse;
16042 break;
16043 }
16044 if (event.xunmap.window == windows->icon.id)
16045 {
16046 if (map_info->colormap == icon_map->colormap)
16047 XConfigureImageColormap(display,resource_info,windows,
16048 display_image,exception);
16049 (void) XFreeStandardColormap(display,icon_visual,icon_map,
16050 icon_pixel);
16051 windows->icon.mapped=MagickFalse;
16052 break;
16053 }
16054 if (event.xunmap.window == windows->command.id)
16055 {
16056 windows->command.mapped=MagickFalse;
16057 break;
16058 }
16059 if (event.xunmap.window == windows->popup.id)
16060 {
16061 if (windows->backdrop.id != (Window) NULL)
16062 (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16063 CurrentTime);
16064 windows->popup.mapped=MagickFalse;
16065 break;
16066 }
16067 if (event.xunmap.window == windows->widget.id)
16068 {
16069 if (windows->backdrop.id != (Window) NULL)
16070 (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16071 CurrentTime);
16072 windows->widget.mapped=MagickFalse;
16073 break;
16074 }
16075 break;
16076 }
16077 default:
16078 {
16079 if (resource_info->debug != MagickFalse)
16080 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16081 event.type);
16082 break;
16083 }
16084 }
16085 } while (!(*state & ExitState));
16086 if ((*state & ExitState) == 0)
16087 (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16088 &display_image,exception);
16089 else
16090 if (resource_info->confirm_edit != MagickFalse)
16091 {
16092 /*
16093 Query user if image has changed.
16094 */
16095 if ((resource_info->immutable == MagickFalse) &&
16096 display_image->taint != MagickFalse)
16097 {
16098 int
16099 status;
16100
16101 status=XConfirmWidget(display,windows,"Your image changed.",
16102 "Do you want to save it");
16103 if (status == 0)
16104 *state&=(unsigned int) (~ExitState);
16105 else
16106 if (status > 0)
16107 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16108 &display_image,exception);
16109 }
16110 }
16111 if ((windows->visual_info->klass == GrayScale) ||
16112 (windows->visual_info->klass == PseudoColor) ||
16113 (windows->visual_info->klass == DirectColor))
16114 {
16115 /*
16116 Withdraw pan and Magnify window.
16117 */
16118 if (windows->info.mapped != MagickFalse)
16119 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16120 if (windows->magnify.mapped != MagickFalse)
16121 (void) XWithdrawWindow(display,windows->magnify.id,
16122 windows->magnify.screen);
16123 if (windows->command.mapped != MagickFalse)
16124 (void) XWithdrawWindow(display,windows->command.id,
16125 windows->command.screen);
16126 }
16127 if (windows->pan.mapped != MagickFalse)
16128 (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16129 if (resource_info->backdrop == MagickFalse)
16130 if (windows->backdrop.mapped)
16131 {
16132 (void) XWithdrawWindow(display,windows->backdrop.id,
16133 windows->backdrop.screen);
16134 (void) XDestroyWindow(display,windows->backdrop.id);
16135 windows->backdrop.id=(Window) NULL;
16136 (void) XWithdrawWindow(display,windows->image.id,
16137 windows->image.screen);
16138 (void) XDestroyWindow(display,windows->image.id);
16139 windows->image.id=(Window) NULL;
16140 }
16141 XSetCursorState(display,windows,MagickTrue);
16142 XCheckRefreshWindows(display,windows);
16143 if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16144 *state&=(unsigned int) (~ExitState);
16145 if (*state & ExitState)
16146 {
16147 /*
16148 Free Standard Colormap.
16149 */
16150 (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16151 if (resource_info->map_type == (char *) NULL)
16152 (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16153 /*
16154 Free X resources.
16155 */
16156 if (resource_info->copy_image != (Image *) NULL)
16157 {
16158 resource_info->copy_image=DestroyImage(resource_info->copy_image);
16159 resource_info->copy_image=NewImageList();
16160 }
16161 DestroyXResources();
16162 }
16163 (void) XSync(display,MagickFalse);
16164 /*
16165 Restore our progress monitor and warning handlers.
16166 */
16167 (void) SetErrorHandler(warning_handler);
16168 (void) SetWarningHandler(warning_handler);
16169 /*
16170 Change to home directory.
16171 */
16172 directory=getcwd(working_directory,MagickPathExtent);
16173 (void) directory;
16174 {
16175 int
16176 status;
16177
16178 if (*resource_info->home_directory == '\0')
16179 (void) CopyMagickString(resource_info->home_directory,".",MagickPathExtent);
16180 status=chdir(resource_info->home_directory);
16181 if (status == -1)
16182 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16183 "UnableToOpenFile","%s",resource_info->home_directory);
16184 }
16185 *image=display_image;
16186 return(nexus);
16187}
16188#else
16189
16190/*
16191%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16192% %
16193% %
16194% %
16195+ D i s p l a y I m a g e s %
16196% %
16197% %
16198% %
16199%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16200%
16201% DisplayImages() displays an image sequence to any X window screen. It
16202% returns a value other than 0 if successful. Check the exception member
16203% of image to determine the reason for any failure.
16204%
16205% The format of the DisplayImages method is:
16206%
16207% MagickBooleanType DisplayImages(const ImageInfo *image_info,
16208% Image *images,ExceptionInfo *exception)
16209%
16210% A description of each parameter follows:
16211%
16212% o image_info: the image info.
16213%
16214% o image: the image.
16215%
16216% o exception: return any errors or warnings in this structure.
16217%
16218*/
16219MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16220 Image *image,ExceptionInfo *exception)
16221{
16222 assert(image_info != (const ImageInfo *) NULL);
16223 assert(image_info->signature == MagickCoreSignature);
16224 assert(image != (Image *) NULL);
16225 assert(image->signature == MagickCoreSignature);
16226 (void) image_info;
16227 if (IsEventLogging() != MagickFalse)
16228 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16229 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16230 "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
16231 return(MagickFalse);
16232}
16233
16234/*
16235%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16236% %
16237% %
16238% %
16239+ R e m o t e D i s p l a y C o m m a n d %
16240% %
16241% %
16242% %
16243%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16244%
16245% RemoteDisplayCommand() encourages a remote display program to display the
16246% specified image filename.
16247%
16248% The format of the RemoteDisplayCommand method is:
16249%
16250% MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16251% const char *window,const char *filename,ExceptionInfo *exception)
16252%
16253% A description of each parameter follows:
16254%
16255% o image_info: the image info.
16256%
16257% o window: Specifies the name or id of an X window.
16258%
16259% o filename: the name of the image filename to display.
16260%
16261% o exception: return any errors or warnings in this structure.
16262%
16263*/
16264MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16265 const char *window,const char *filename,ExceptionInfo *exception)
16266{
16267 assert(image_info != (const ImageInfo *) NULL);
16268 assert(image_info->signature == MagickCoreSignature);
16269 assert(filename != (char *) NULL);
16270 if (IsEventLogging() != MagickFalse)
16271 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16272 (void) window;
16273 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16274 "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image_info->filename);
16275 return(MagickFalse);
16276}
16277#endif