MagickCore 7.1.1
Convert, Edit, Or Compose Bitmap Images
Loading...
Searching...
No Matches
display.c
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% DDDD IIIII SSSSS PPPP L AAA Y Y %
7% D D I SS P P L A A Y Y %
8% D D I SSS PPPP L AAAAA Y %
9% D D I SS P L A A Y %
10% DDDD IIIII SSSSS P LLLLL A A Y %
11% %
12% %
13% MagickCore Methods to Interactively Display and Edit an Image %
14% %
15% Software Design %
16% Cristy %
17% July 1992 %
18% %
19% %
20% Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization %
21% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% https://imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
42#include "MagickCore/studio.h"
43#include "MagickCore/artifact.h"
44#include "MagickCore/attribute.h"
45#include "MagickCore/blob.h"
46#include "MagickCore/cache.h"
47#include "MagickCore/cache-private.h"
48#include "MagickCore/channel.h"
49#include "MagickCore/client.h"
50#include "MagickCore/color.h"
51#include "MagickCore/colorspace.h"
52#include "MagickCore/composite.h"
53#include "MagickCore/constitute.h"
54#include "MagickCore/decorate.h"
55#include "MagickCore/delegate.h"
56#include "MagickCore/display.h"
57#include "MagickCore/display-private.h"
58#include "MagickCore/distort.h"
59#include "MagickCore/draw.h"
60#include "MagickCore/effect.h"
61#include "MagickCore/enhance.h"
62#include "MagickCore/exception.h"
63#include "MagickCore/exception-private.h"
64#include "MagickCore/fx.h"
65#include "MagickCore/geometry.h"
66#include "MagickCore/image.h"
67#include "MagickCore/image-private.h"
68#include "MagickCore/list.h"
69#include "MagickCore/locale-private.h"
70#include "MagickCore/log.h"
71#include "MagickCore/magick.h"
72#include "MagickCore/memory_.h"
73#include "MagickCore/monitor.h"
74#include "MagickCore/monitor-private.h"
75#include "MagickCore/montage.h"
76#include "MagickCore/nt-base-private.h"
77#include "MagickCore/option.h"
78#include "MagickCore/paint.h"
79#include "MagickCore/pixel.h"
80#include "MagickCore/pixel-accessor.h"
81#include "MagickCore/property.h"
82#include "MagickCore/quantum.h"
83#include "MagickCore/quantum-private.h"
84#include "MagickCore/resize.h"
85#include "MagickCore/resource_.h"
86#include "MagickCore/shear.h"
87#include "MagickCore/segment.h"
88#include "MagickCore/statistic.h"
89#include "MagickCore/string_.h"
90#include "MagickCore/string-private.h"
91#include "MagickCore/timer-private.h"
92#include "MagickCore/transform.h"
93#include "MagickCore/transform-private.h"
94#include "MagickCore/threshold.h"
95#include "MagickCore/utility.h"
96#include "MagickCore/utility-private.h"
97#include "MagickCore/version.h"
98#include "MagickCore/visual-effects.h"
99#include "MagickCore/widget.h"
100#include "MagickCore/widget-private.h"
101#include "MagickCore/xwindow.h"
102#include "MagickCore/xwindow-private.h"
103
104#if defined(MAGICKCORE_X11_DELEGATE)
105/*
106 Define declarations.
107*/
108#define MaxColors MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
109
110/*
111 Constant declarations.
112*/
113static const unsigned char
114 HighlightBitmap[8] =
115 {
116 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
117 },
118 OpaqueBitmap[8] =
119 {
120 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
121 },
122 ShadowBitmap[8] =
123 {
124 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
125 };
126
127/*
128 Help widget declarations.
129*/
130static const char
131 ImageAnnotateHelp[] =
132 {
133 "In annotate mode, the Command widget has these options:\n"
134 "\n"
135 " Font Name\n"
136 " fixed\n"
137 " variable\n"
138 " 5x8\n"
139 " 6x10\n"
140 " 7x13bold\n"
141 " 8x13bold\n"
142 " 9x15bold\n"
143 " 10x20\n"
144 " 12x24\n"
145 " Browser...\n"
146 " Font Color\n"
147 " black\n"
148 " blue\n"
149 " cyan\n"
150 " green\n"
151 " gray\n"
152 " red\n"
153 " magenta\n"
154 " yellow\n"
155 " white\n"
156 " transparent\n"
157 " Browser...\n"
158 " Font Color\n"
159 " black\n"
160 " blue\n"
161 " cyan\n"
162 " green\n"
163 " gray\n"
164 " red\n"
165 " magenta\n"
166 " yellow\n"
167 " white\n"
168 " transparent\n"
169 " Browser...\n"
170 " Rotate Text\n"
171 " -90\n"
172 " -45\n"
173 " -30\n"
174 " 0\n"
175 " 30\n"
176 " 45\n"
177 " 90\n"
178 " 180\n"
179 " Dialog...\n"
180 " Help\n"
181 " Dismiss\n"
182 "\n"
183 "Choose a font name from the Font Name sub-menu. Additional\n"
184 "font names can be specified with the font browser. You can\n"
185 "change the menu names by setting the X resources font1\n"
186 "through font9.\n"
187 "\n"
188 "Choose a font color from the Font Color sub-menu.\n"
189 "Additional font colors can be specified with the color\n"
190 "browser. You can change the menu colors by setting the X\n"
191 "resources pen1 through pen9.\n"
192 "\n"
193 "If you select the color browser and press Grab, you can\n"
194 "choose the font color by moving the pointer to the desired\n"
195 "color on the screen and press any button.\n"
196 "\n"
197 "If you choose to rotate the text, choose Rotate Text from the\n"
198 "menu and select an angle. Typically you will only want to\n"
199 "rotate one line of text at a time. Depending on the angle you\n"
200 "choose, subsequent lines may end up overwriting each other.\n"
201 "\n"
202 "Choosing a font and its color is optional. The default font\n"
203 "is fixed and the default color is black. However, you must\n"
204 "choose a location to begin entering text and press button 1.\n"
205 "An underscore character will appear at the location of the\n"
206 "pointer. The cursor changes to a pencil to indicate you are\n"
207 "in text mode. To exit immediately, press Dismiss.\n"
208 "\n"
209 "In text mode, any key presses will display the character at\n"
210 "the location of the underscore and advance the underscore\n"
211 "cursor. Enter your text and once completed press Apply to\n"
212 "finish your image annotation. To correct errors press BACK\n"
213 "SPACE. To delete an entire line of text, press DELETE. Any\n"
214 "text that exceeds the boundaries of the image window is\n"
215 "automagically continued onto the next line.\n"
216 "\n"
217 "The actual color you request for the font is saved in the\n"
218 "image. However, the color that appears in your image window\n"
219 "may be different. For example, on a monochrome screen the\n"
220 "text will appear black or white even if you choose the color\n"
221 "red as the font color. However, the image saved to a file\n"
222 "with -write is written with red lettering. To assure the\n"
223 "correct color text in the final image, any PseudoClass image\n"
224 "is promoted to DirectClass (see miff(5)). To force a\n"
225 "PseudoClass image to remain PseudoClass, use -colors.\n"
226 },
227 ImageChopHelp[] =
228 {
229 "In chop mode, the Command widget has these options:\n"
230 "\n"
231 " Direction\n"
232 " horizontal\n"
233 " vertical\n"
234 " Help\n"
235 " Dismiss\n"
236 "\n"
237 "If the you choose the horizontal direction (this the\n"
238 "default), the area of the image between the two horizontal\n"
239 "endpoints of the chop line is removed. Otherwise, the area\n"
240 "of the image between the two vertical endpoints of the chop\n"
241 "line is removed.\n"
242 "\n"
243 "Select a location within the image window to begin your chop,\n"
244 "press and hold any button. Next, move the pointer to\n"
245 "another location in the image. As you move a line will\n"
246 "connect the initial location and the pointer. When you\n"
247 "release the button, the area within the image to chop is\n"
248 "determined by which direction you choose from the Command\n"
249 "widget.\n"
250 "\n"
251 "To cancel the image chopping, move the pointer back to the\n"
252 "starting point of the line and release the button.\n"
253 },
254 ImageColorEditHelp[] =
255 {
256 "In color edit mode, the Command widget has these options:\n"
257 "\n"
258 " Method\n"
259 " point\n"
260 " replace\n"
261 " floodfill\n"
262 " filltoborder\n"
263 " reset\n"
264 " Pixel Color\n"
265 " black\n"
266 " blue\n"
267 " cyan\n"
268 " green\n"
269 " gray\n"
270 " red\n"
271 " magenta\n"
272 " yellow\n"
273 " white\n"
274 " Browser...\n"
275 " Border Color\n"
276 " black\n"
277 " blue\n"
278 " cyan\n"
279 " green\n"
280 " gray\n"
281 " red\n"
282 " magenta\n"
283 " yellow\n"
284 " white\n"
285 " Browser...\n"
286 " Fuzz\n"
287 " 0%\n"
288 " 2%\n"
289 " 5%\n"
290 " 10%\n"
291 " 15%\n"
292 " Dialog...\n"
293 " Undo\n"
294 " Help\n"
295 " Dismiss\n"
296 "\n"
297 "Choose a color editing method from the Method sub-menu\n"
298 "of the Command widget. The point method recolors any pixel\n"
299 "selected with the pointer until the button is released. The\n"
300 "replace method recolors any pixel that matches the color of\n"
301 "the pixel you select with a button press. Floodfill recolors\n"
302 "any pixel that matches the color of the pixel you select with\n"
303 "a button press and is a neighbor. Whereas filltoborder recolors\n"
304 "any neighbor pixel that is not the border color. Finally reset\n"
305 "changes the entire image to the designated color.\n"
306 "\n"
307 "Next, choose a pixel color from the Pixel Color sub-menu.\n"
308 "Additional pixel colors can be specified with the color\n"
309 "browser. You can change the menu colors by setting the X\n"
310 "resources pen1 through pen9.\n"
311 "\n"
312 "Now press button 1 to select a pixel within the image window\n"
313 "to change its color. Additional pixels may be recolored as\n"
314 "prescribed by the method you choose.\n"
315 "\n"
316 "If the Magnify widget is mapped, it can be helpful in positioning\n"
317 "your pointer within the image (refer to button 2).\n"
318 "\n"
319 "The actual color you request for the pixels is saved in the\n"
320 "image. However, the color that appears in your image window\n"
321 "may be different. For example, on a monochrome screen the\n"
322 "pixel will appear black or white even if you choose the\n"
323 "color red as the pixel color. However, the image saved to a\n"
324 "file with -write is written with red pixels. To assure the\n"
325 "correct color text in the final image, any PseudoClass image\n"
326 "is promoted to DirectClass (see miff(5)). To force a\n"
327 "PseudoClass image to remain PseudoClass, use -colors.\n"
328 },
329 ImageCompositeHelp[] =
330 {
331 "First a widget window is displayed requesting you to enter an\n"
332 "image name. Press Composite, Grab or type a file name.\n"
333 "Press Cancel if you choose not to create a composite image.\n"
334 "When you choose Grab, move the pointer to the desired window\n"
335 "and press any button.\n"
336 "\n"
337 "If the Composite image does not have any matte information,\n"
338 "you are informed and the file browser is displayed again.\n"
339 "Enter the name of a mask image. The image is typically\n"
340 "grayscale and the same size as the composite image. If the\n"
341 "image is not grayscale, it is converted to grayscale and the\n"
342 "resulting intensities are used as matte information.\n"
343 "\n"
344 "A small window appears showing the location of the cursor in\n"
345 "the image window. You are now in composite mode. To exit\n"
346 "immediately, press Dismiss. In composite mode, the Command\n"
347 "widget has these options:\n"
348 "\n"
349 " Operators\n"
350 " Over\n"
351 " In\n"
352 " Out\n"
353 " Atop\n"
354 " Xor\n"
355 " Plus\n"
356 " Minus\n"
357 " Add\n"
358 " Subtract\n"
359 " Difference\n"
360 " Multiply\n"
361 " Bumpmap\n"
362 " Copy\n"
363 " CopyRed\n"
364 " CopyGreen\n"
365 " CopyBlue\n"
366 " CopyOpacity\n"
367 " Clear\n"
368 " Dissolve\n"
369 " Displace\n"
370 " Help\n"
371 " Dismiss\n"
372 "\n"
373 "Choose a composite operation from the Operators sub-menu of\n"
374 "the Command widget. How each operator behaves is described\n"
375 "below. Image window is the image currently displayed on\n"
376 "your X server and image is the image obtained with the File\n"
377 "Browser widget.\n"
378 "\n"
379 "Over The result is the union of the two image shapes,\n"
380 " with image obscuring image window in the region of\n"
381 " overlap.\n"
382 "\n"
383 "In The result is simply image cut by the shape of\n"
384 " image window. None of the image data of image\n"
385 " window is in the result.\n"
386 "\n"
387 "Out The resulting image is image with the shape of\n"
388 " image window cut out.\n"
389 "\n"
390 "Atop The result is the same shape as the image window,\n"
391 " with image obscuring image window where the image\n"
392 " shapes overlap. Note this differs from over\n"
393 " because the portion of image outside image window's\n"
394 " shape does not appear in the result.\n"
395 "\n"
396 "Xor The result is the image data from both image and\n"
397 " image window that is outside the overlap region.\n"
398 " The overlap region is blank.\n"
399 "\n"
400 "Plus The result is just the sum of the image data.\n"
401 " Output values are cropped to QuantumRange (no overflow).\n"
402 "\n"
403 "Minus The result of image - image window, with underflow\n"
404 " cropped to zero.\n"
405 "\n"
406 "Add The result of image + image window, with overflow\n"
407 " wrapping around (mod 256).\n"
408 "\n"
409 "Subtract The result of image - image window, with underflow\n"
410 " wrapping around (mod 256). The add and subtract\n"
411 " operators can be used to perform reversible\n"
412 " transformations.\n"
413 "\n"
414 "Difference\n"
415 " The result of abs(image - image window). This\n"
416 " useful for comparing two very similar images.\n"
417 "\n"
418 "Multiply\n"
419 " The result of image * image window. This\n"
420 " useful for the creation of drop-shadows.\n"
421 "\n"
422 "Bumpmap The result of surface normals from image * image\n"
423 " window.\n"
424 "\n"
425 "Copy The resulting image is image window replaced with\n"
426 " image. Here the matte information is ignored.\n"
427 "\n"
428 "CopyRed The red layer of the image window is replace with\n"
429 " the red layer of the image. The other layers are\n"
430 " untouched.\n"
431 "\n"
432 "CopyGreen\n"
433 " The green layer of the image window is replace with\n"
434 " the green layer of the image. The other layers are\n"
435 " untouched.\n"
436 "\n"
437 "CopyBlue The blue layer of the image window is replace with\n"
438 " the blue layer of the image. The other layers are\n"
439 " untouched.\n"
440 "\n"
441 "CopyOpacity\n"
442 " The matte layer of the image window is replace with\n"
443 " the matte layer of the image. The other layers are\n"
444 " untouched.\n"
445 "\n"
446 "The image compositor requires a matte, or alpha channel in\n"
447 "the image for some operations. This extra channel usually\n"
448 "defines a mask which represents a sort of a cookie-cutter\n"
449 "for the image. This the case when matte is opaque (full\n"
450 "coverage) for pixels inside the shape, zero outside, and\n"
451 "between 0 and QuantumRange on the boundary. If image does not\n"
452 "have a matte channel, it is initialized with 0 for any pixel\n"
453 "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
454 "\n"
455 "If you choose Dissolve, the composite operator becomes Over. The\n"
456 "image matte channel percent transparency is initialized to factor.\n"
457 "The image window is initialized to (100-factor). Where factor is the\n"
458 "value you specify in the Dialog widget.\n"
459 "\n"
460 "Displace shifts the image pixels as defined by a displacement\n"
461 "map. With this option, image is used as a displacement map.\n"
462 "Black, within the displacement map, is a maximum positive\n"
463 "displacement. White is a maximum negative displacement and\n"
464 "middle gray is neutral. The displacement is scaled to determine\n"
465 "the pixel shift. By default, the displacement applies in both the\n"
466 "horizontal and vertical directions. However, if you specify a mask,\n"
467 "image is the horizontal X displacement and mask the vertical Y\n"
468 "displacement.\n"
469 "\n"
470 "Note that matte information for image window is not retained\n"
471 "for colormapped X server visuals (e.g. StaticColor,\n"
472 "StaticColor, GrayScale, PseudoColor). Correct compositing\n"
473 "behavior may require a TrueColor or DirectColor visual or a\n"
474 "Standard Colormap.\n"
475 "\n"
476 "Choosing a composite operator is optional. The default\n"
477 "operator is replace. However, you must choose a location to\n"
478 "composite your image and press button 1. Press and hold the\n"
479 "button before releasing and an outline of the image will\n"
480 "appear to help you identify your location.\n"
481 "\n"
482 "The actual colors of the composite image is saved. However,\n"
483 "the color that appears in image window may be different.\n"
484 "For example, on a monochrome screen image window will appear\n"
485 "black or white even though your composited image may have\n"
486 "many colors. If the image is saved to a file it is written\n"
487 "with the correct colors. To assure the correct colors are\n"
488 "saved in the final image, any PseudoClass image is promoted\n"
489 "to DirectClass (see miff(5)). To force a PseudoClass image\n"
490 "to remain PseudoClass, use -colors.\n"
491 },
492 ImageCutHelp[] =
493 {
494 "In cut mode, the Command widget has these options:\n"
495 "\n"
496 " Help\n"
497 " Dismiss\n"
498 "\n"
499 "To define a cut region, press button 1 and drag. The\n"
500 "cut region is defined by a highlighted rectangle that\n"
501 "expands or contracts as it follows the pointer. Once you\n"
502 "are satisfied with the cut region, release the button.\n"
503 "You are now in rectify mode. In rectify mode, the Command\n"
504 "widget has these options:\n"
505 "\n"
506 " Cut\n"
507 " Help\n"
508 " Dismiss\n"
509 "\n"
510 "You can make adjustments by moving the pointer to one of the\n"
511 "cut rectangle corners, pressing a button, and dragging.\n"
512 "Finally, press Cut to commit your copy region. To\n"
513 "exit without cutting the image, press Dismiss.\n"
514 },
515 ImageCopyHelp[] =
516 {
517 "In copy mode, the Command widget has these options:\n"
518 "\n"
519 " Help\n"
520 " Dismiss\n"
521 "\n"
522 "To define a copy region, press button 1 and drag. The\n"
523 "copy region is defined by a highlighted rectangle that\n"
524 "expands or contracts as it follows the pointer. Once you\n"
525 "are satisfied with the copy region, release the button.\n"
526 "You are now in rectify mode. In rectify mode, the Command\n"
527 "widget has these options:\n"
528 "\n"
529 " Copy\n"
530 " Help\n"
531 " Dismiss\n"
532 "\n"
533 "You can make adjustments by moving the pointer to one of the\n"
534 "copy rectangle corners, pressing a button, and dragging.\n"
535 "Finally, press Copy to commit your copy region. To\n"
536 "exit without copying the image, press Dismiss.\n"
537 },
538 ImageCropHelp[] =
539 {
540 "In crop mode, the Command widget has these options:\n"
541 "\n"
542 " Help\n"
543 " Dismiss\n"
544 "\n"
545 "To define a cropping region, press button 1 and drag. The\n"
546 "cropping region is defined by a highlighted rectangle that\n"
547 "expands or contracts as it follows the pointer. Once you\n"
548 "are satisfied with the cropping region, release the button.\n"
549 "You are now in rectify mode. In rectify mode, the Command\n"
550 "widget has these options:\n"
551 "\n"
552 " Crop\n"
553 " Help\n"
554 " Dismiss\n"
555 "\n"
556 "You can make adjustments by moving the pointer to one of the\n"
557 "cropping rectangle corners, pressing a button, and dragging.\n"
558 "Finally, press Crop to commit your cropping region. To\n"
559 "exit without cropping the image, press Dismiss.\n"
560 },
561 ImageDrawHelp[] =
562 {
563 "The cursor changes to a crosshair to indicate you are in\n"
564 "draw mode. To exit immediately, press Dismiss. In draw mode,\n"
565 "the Command widget has these options:\n"
566 "\n"
567 " Element\n"
568 " point\n"
569 " line\n"
570 " rectangle\n"
571 " fill rectangle\n"
572 " circle\n"
573 " fill circle\n"
574 " ellipse\n"
575 " fill ellipse\n"
576 " polygon\n"
577 " fill polygon\n"
578 " Color\n"
579 " black\n"
580 " blue\n"
581 " cyan\n"
582 " green\n"
583 " gray\n"
584 " red\n"
585 " magenta\n"
586 " yellow\n"
587 " white\n"
588 " transparent\n"
589 " Browser...\n"
590 " Stipple\n"
591 " Brick\n"
592 " Diagonal\n"
593 " Scales\n"
594 " Vertical\n"
595 " Wavy\n"
596 " Translucent\n"
597 " Opaque\n"
598 " Open...\n"
599 " Width\n"
600 " 1\n"
601 " 2\n"
602 " 4\n"
603 " 8\n"
604 " 16\n"
605 " Dialog...\n"
606 " Undo\n"
607 " Help\n"
608 " Dismiss\n"
609 "\n"
610 "Choose a drawing primitive from the Element sub-menu.\n"
611 "\n"
612 "Choose a color from the Color sub-menu. Additional\n"
613 "colors can be specified with the color browser.\n"
614 "\n"
615 "If you choose the color browser and press Grab, you can\n"
616 "select the color by moving the pointer to the desired\n"
617 "color on the screen and press any button. The transparent\n"
618 "color updates the image matte channel and is useful for\n"
619 "image compositing.\n"
620 "\n"
621 "Choose a stipple, if appropriate, from the Stipple sub-menu.\n"
622 "Additional stipples can be specified with the file browser.\n"
623 "Stipples obtained from the file browser must be on disk in the\n"
624 "X11 bitmap format.\n"
625 "\n"
626 "Choose a width, if appropriate, from the Width sub-menu. To\n"
627 "choose a specific width select the Dialog widget.\n"
628 "\n"
629 "Choose a point in the Image window and press button 1 and\n"
630 "hold. Next, move the pointer to another location in the\n"
631 "image. As you move, a line connects the initial location and\n"
632 "the pointer. When you release the button, the image is\n"
633 "updated with the primitive you just drew. For polygons, the\n"
634 "image is updated when you press and release the button without\n"
635 "moving the pointer.\n"
636 "\n"
637 "To cancel image drawing, move the pointer back to the\n"
638 "starting point of the line and release the button.\n"
639 },
640 DisplayHelp[] =
641 {
642 "BUTTONS\n"
643 " The effects of each button press is described below. Three\n"
644 " buttons are required. If you have a two button mouse,\n"
645 " button 1 and 3 are returned. Press ALT and button 3 to\n"
646 " simulate button 2.\n"
647 "\n"
648 " 1 Press this button to map or unmap the Command widget.\n"
649 "\n"
650 " 2 Press and drag to define a region of the image to\n"
651 " magnify.\n"
652 "\n"
653 " 3 Press and drag to choose from a select set of commands.\n"
654 " This button behaves differently if the image being\n"
655 " displayed is a visual image directory. Here, choose a\n"
656 " particular tile of the directory and press this button and\n"
657 " drag to select a command from a pop-up menu. Choose from\n"
658 " these menu items:\n"
659 "\n"
660 " Open\n"
661 " Next\n"
662 " Former\n"
663 " Delete\n"
664 " Update\n"
665 "\n"
666 " If you choose Open, the image represented by the tile is\n"
667 " displayed. To return to the visual image directory, choose\n"
668 " Next from the Command widget. Next and Former moves to the\n"
669 " next or former image respectively. Choose Delete to delete\n"
670 " a particular image tile. Finally, choose Update to\n"
671 " synchronize all the image tiles with their respective\n"
672 " images.\n"
673 "\n"
674 "COMMAND WIDGET\n"
675 " The Command widget lists a number of sub-menus and commands.\n"
676 " They are\n"
677 "\n"
678 " File\n"
679 " Open...\n"
680 " Next\n"
681 " Former\n"
682 " Select...\n"
683 " Save...\n"
684 " Print...\n"
685 " Delete...\n"
686 " New...\n"
687 " Visual Directory...\n"
688 " Quit\n"
689 " Edit\n"
690 " Undo\n"
691 " Redo\n"
692 " Cut\n"
693 " Copy\n"
694 " Paste\n"
695 " View\n"
696 " Half Size\n"
697 " Original Size\n"
698 " Double Size\n"
699 " Resize...\n"
700 " Apply\n"
701 " Refresh\n"
702 " Restore\n"
703 " Transform\n"
704 " Crop\n"
705 " Chop\n"
706 " Flop\n"
707 " Flip\n"
708 " Rotate Right\n"
709 " Rotate Left\n"
710 " Rotate...\n"
711 " Shear...\n"
712 " Roll...\n"
713 " Trim Edges\n"
714 " Enhance\n"
715 " Brightness...\n"
716 " Saturation...\n"
717 " Hue...\n"
718 " Gamma...\n"
719 " Sharpen...\n"
720 " Dull\n"
721 " Contrast Stretch...\n"
722 " Sigmoidal Contrast...\n"
723 " Normalize\n"
724 " Equalize\n"
725 " Negate\n"
726 " Grayscale\n"
727 " Map...\n"
728 " Quantize...\n"
729 " Effects\n"
730 " Despeckle\n"
731 " Emboss\n"
732 " Reduce Noise\n"
733 " Add Noise\n"
734 " Sharpen...\n"
735 " Blur...\n"
736 " Threshold...\n"
737 " Edge Detect...\n"
738 " Spread...\n"
739 " Shade...\n"
740 " Painting...\n"
741 " Segment...\n"
742 " F/X\n"
743 " Solarize...\n"
744 " Sepia Tone...\n"
745 " Swirl...\n"
746 " Implode...\n"
747 " Vignette...\n"
748 " Wave...\n"
749 " Oil Painting...\n"
750 " Charcoal Drawing...\n"
751 " Image Edit\n"
752 " Annotate...\n"
753 " Draw...\n"
754 " Color...\n"
755 " Matte...\n"
756 " Composite...\n"
757 " Add Border...\n"
758 " Add Frame...\n"
759 " Comment...\n"
760 " Launch...\n"
761 " Region of Interest...\n"
762 " Miscellany\n"
763 " Image Info\n"
764 " Zoom Image\n"
765 " Show Preview...\n"
766 " Show Histogram\n"
767 " Show Matte\n"
768 " Background...\n"
769 " Slide Show\n"
770 " Preferences...\n"
771 " Help\n"
772 " Overview\n"
773 " Browse Documentation\n"
774 " About Display\n"
775 "\n"
776 " Menu items with a indented triangle have a sub-menu. They\n"
777 " are represented above as the indented items. To access a\n"
778 " sub-menu item, move the pointer to the appropriate menu and\n"
779 " press a button and drag. When you find the desired sub-menu\n"
780 " item, release the button and the command is executed. Move\n"
781 " the pointer away from the sub-menu if you decide not to\n"
782 " execute a particular command.\n"
783 "\n"
784 "KEYBOARD ACCELERATORS\n"
785 " Accelerators are one or two key presses that effect a\n"
786 " particular command. The keyboard accelerators that\n"
787 " display(1) understands is:\n"
788 "\n"
789 " Ctl+O Press to open an image from a file.\n"
790 "\n"
791 " space Press to display the next image.\n"
792 "\n"
793 " If the image is a multi-paged document such as a Postscript\n"
794 " document, you can skip ahead several pages by preceding\n"
795 " this command with a number. For example to display the\n"
796 " third page beyond the current page, press 3<space>.\n"
797 "\n"
798 " backspace Press to display the former image.\n"
799 "\n"
800 " If the image is a multi-paged document such as a Postscript\n"
801 " document, you can skip behind several pages by preceding\n"
802 " this command with a number. For example to display the\n"
803 " third page preceding the current page, press 3<backspace>.\n"
804 "\n"
805 " Ctl+S Press to write the image to a file.\n"
806 "\n"
807 " Ctl+P Press to print the image to a Postscript printer.\n"
808 "\n"
809 " Ctl+D Press to delete an image file.\n"
810 "\n"
811 " Ctl+N Press to create a blank canvas.\n"
812 "\n"
813 " Ctl+Q Press to discard all images and exit program.\n"
814 "\n"
815 " Ctl+Z Press to undo last image transformation.\n"
816 "\n"
817 " Ctl+R Press to redo last image transformation.\n"
818 "\n"
819 " Ctl+X Press to cut a region of the image.\n"
820 "\n"
821 " Ctl+C Press to copy a region of the image.\n"
822 "\n"
823 " Ctl+V Press to paste a region to the image.\n"
824 "\n"
825 " < Press to half the image size.\n"
826 "\n"
827 " - Press to return to the original image size.\n"
828 "\n"
829 " > Press to double the image size.\n"
830 "\n"
831 " % Press to resize the image to a width and height you\n"
832 " specify.\n"
833 "\n"
834 "Cmd-A Press to make any image transformations permanent."
835 "\n"
836 " By default, any image size transformations are applied\n"
837 " to the original image to create the image displayed on\n"
838 " the X server. However, the transformations are not\n"
839 " permanent (i.e. the original image does not change\n"
840 " size only the X image does). For example, if you\n"
841 " press > the X image will appear to double in size,\n"
842 " but the original image will in fact remain the same size.\n"
843 " To force the original image to double in size, press >\n"
844 " followed by Cmd-A.\n"
845 "\n"
846 " @ Press to refresh the image window.\n"
847 "\n"
848 " C Press to cut out a rectangular region of the image.\n"
849 "\n"
850 " [ Press to chop the image.\n"
851 "\n"
852 " H Press to flop image in the horizontal direction.\n"
853 "\n"
854 " V Press to flip image in the vertical direction.\n"
855 "\n"
856 " / Press to rotate the image 90 degrees clockwise.\n"
857 "\n"
858 " \\ Press to rotate the image 90 degrees counter-clockwise.\n"
859 "\n"
860 " * Press to rotate the image the number of degrees you\n"
861 " specify.\n"
862 "\n"
863 " S Press to shear the image the number of degrees you\n"
864 " specify.\n"
865 "\n"
866 " R Press to roll the image.\n"
867 "\n"
868 " T Press to trim the image edges.\n"
869 "\n"
870 " Shft-H Press to vary the image hue.\n"
871 "\n"
872 " Shft-S Press to vary the color saturation.\n"
873 "\n"
874 " Shft-L Press to vary the color brightness.\n"
875 "\n"
876 " Shft-G Press to gamma correct the image.\n"
877 "\n"
878 " Shft-C Press to sharpen the image contrast.\n"
879 "\n"
880 " Shft-Z Press to dull the image contrast.\n"
881 "\n"
882 " = Press to perform histogram equalization on the image.\n"
883 "\n"
884 " Shft-N Press to perform histogram normalization on the image.\n"
885 "\n"
886 " Shft-~ Press to negate the colors of the image.\n"
887 "\n"
888 " . Press to convert the image colors to gray.\n"
889 "\n"
890 " Shft-# Press to set the maximum number of unique colors in the\n"
891 " image.\n"
892 "\n"
893 " F2 Press to reduce the speckles in an image.\n"
894 "\n"
895 " F3 Press to eliminate peak noise from an image.\n"
896 "\n"
897 " F4 Press to add noise to an image.\n"
898 "\n"
899 " F5 Press to sharpen an image.\n"
900 "\n"
901 " F6 Press to delete an image file.\n"
902 "\n"
903 " F7 Press to threshold the image.\n"
904 "\n"
905 " F8 Press to detect edges within an image.\n"
906 "\n"
907 " F9 Press to emboss an image.\n"
908 "\n"
909 " F10 Press to displace pixels by a random amount.\n"
910 "\n"
911 " F11 Press to negate all pixels above the threshold level.\n"
912 "\n"
913 " F12 Press to shade the image using a distant light source.\n"
914 "\n"
915 " F13 Press to lighten or darken image edges to create a 3-D effect.\n"
916 "\n"
917 " F14 Press to segment the image by color.\n"
918 "\n"
919 " Meta-S Press to swirl image pixels about the center.\n"
920 "\n"
921 " Meta-I Press to implode image pixels about the center.\n"
922 "\n"
923 " Meta-W Press to alter an image along a sine wave.\n"
924 "\n"
925 " Meta-P Press to simulate an oil painting.\n"
926 "\n"
927 " Meta-C Press to simulate a charcoal drawing.\n"
928 "\n"
929 " Alt-A Press to annotate the image with text.\n"
930 "\n"
931 " Alt-D Press to draw on an image.\n"
932 "\n"
933 " Alt-P Press to edit an image pixel color.\n"
934 "\n"
935 " Alt-M Press to edit the image matte information.\n"
936 "\n"
937 " Alt-V Press to composite the image with another.\n"
938 "\n"
939 " Alt-B Press to add a border to the image.\n"
940 "\n"
941 " Alt-F Press to add an ornamental border to the image.\n"
942 "\n"
943 " Alt-Shft-!\n"
944 " Press to add an image comment.\n"
945 "\n"
946 " Ctl-A Press to apply image processing techniques to a region\n"
947 " of interest.\n"
948 "\n"
949 " Shft-? Press to display information about the image.\n"
950 "\n"
951 " Shft-+ Press to map the zoom image window.\n"
952 "\n"
953 " Shft-P Press to preview an image enhancement, effect, or f/x.\n"
954 "\n"
955 " F1 Press to display helpful information about display(1).\n"
956 "\n"
957 " Find Press to browse documentation about ImageMagick.\n"
958 "\n"
959 " 1-9 Press to change the level of magnification.\n"
960 "\n"
961 " Use the arrow keys to move the image one pixel up, down,\n"
962 " left, or right within the magnify window. Be sure to first\n"
963 " map the magnify window by pressing button 2.\n"
964 "\n"
965 " Press ALT and one of the arrow keys to trim off one pixel\n"
966 " from any side of the image.\n"
967 },
968 ImageMatteEditHelp[] =
969 {
970 "Matte information within an image is useful for some\n"
971 "operations such as image compositing (See IMAGE\n"
972 "COMPOSITING). This extra channel usually defines a mask\n"
973 "which represents a sort of a cookie-cutter for the image.\n"
974 "This the case when matte is opaque (full coverage) for\n"
975 "pixels inside the shape, zero outside, and between 0 and\n"
976 "QuantumRange on the boundary.\n"
977 "\n"
978 "A small window appears showing the location of the cursor in\n"
979 "the image window. You are now in matte edit mode. To exit\n"
980 "immediately, press Dismiss. In matte edit mode, the Command\n"
981 "widget has these options:\n"
982 "\n"
983 " Method\n"
984 " point\n"
985 " replace\n"
986 " floodfill\n"
987 " filltoborder\n"
988 " reset\n"
989 " Border Color\n"
990 " black\n"
991 " blue\n"
992 " cyan\n"
993 " green\n"
994 " gray\n"
995 " red\n"
996 " magenta\n"
997 " yellow\n"
998 " white\n"
999 " Browser...\n"
1000 " Fuzz\n"
1001 " 0%\n"
1002 " 2%\n"
1003 " 5%\n"
1004 " 10%\n"
1005 " 15%\n"
1006 " Dialog...\n"
1007 " Matte\n"
1008 " Opaque\n"
1009 " Transparent\n"
1010 " Dialog...\n"
1011 " Undo\n"
1012 " Help\n"
1013 " Dismiss\n"
1014 "\n"
1015 "Choose a matte editing method from the Method sub-menu of\n"
1016 "the Command widget. The point method changes the matte value\n"
1017 "of any pixel selected with the pointer until the button is\n"
1018 "is released. The replace method changes the matte value of\n"
1019 "any pixel that matches the color of the pixel you select with\n"
1020 "a button press. Floodfill changes the matte value of any pixel\n"
1021 "that matches the color of the pixel you select with a button\n"
1022 "press and is a neighbor. Whereas filltoborder changes the matte\n"
1023 "value any neighbor pixel that is not the border color. Finally\n"
1024 "reset changes the entire image to the designated matte value.\n"
1025 "\n"
1026 "Choose Matte Value and pick Opaque or Transparent. For other values\n"
1027 "select the Dialog entry. Here a dialog appears requesting a matte\n"
1028 "value. The value you select is assigned as the opacity value of the\n"
1029 "selected pixel or pixels.\n"
1030 "\n"
1031 "Now, press any button to select a pixel within the image\n"
1032 "window to change its matte value.\n"
1033 "\n"
1034 "If the Magnify widget is mapped, it can be helpful in positioning\n"
1035 "your pointer within the image (refer to button 2).\n"
1036 "\n"
1037 "Matte information is only valid in a DirectClass image.\n"
1038 "Therefore, any PseudoClass image is promoted to DirectClass\n"
1039 "(see miff(5)). Note that matte information for PseudoClass\n"
1040 "is not retained for colormapped X server visuals (e.g.\n"
1041 "StaticColor, StaticColor, GrayScale, PseudoColor) unless you\n"
1042 "immediately save your image to a file (refer to Write).\n"
1043 "Correct matte editing behavior may require a TrueColor or\n"
1044 "DirectColor visual or a Standard Colormap.\n"
1045 },
1046 ImagePanHelp[] =
1047 {
1048 "When an image exceeds the width or height of the X server\n"
1049 "screen, display maps a small panning icon. The rectangle\n"
1050 "within the panning icon shows the area that is currently\n"
1051 "displayed in the image window. To pan about the image,\n"
1052 "press any button and drag the pointer within the panning\n"
1053 "icon. The pan rectangle moves with the pointer and the\n"
1054 "image window is updated to reflect the location of the\n"
1055 "rectangle within the panning icon. When you have selected\n"
1056 "the area of the image you wish to view, release the button.\n"
1057 "\n"
1058 "Use the arrow keys to pan the image one pixel up, down,\n"
1059 "left, or right within the image window.\n"
1060 "\n"
1061 "The panning icon is withdrawn if the image becomes smaller\n"
1062 "than the dimensions of the X server screen.\n"
1063 },
1064 ImagePasteHelp[] =
1065 {
1066 "A small window appears showing the location of the cursor in\n"
1067 "the image window. You are now in paste mode. To exit\n"
1068 "immediately, press Dismiss. In paste mode, the Command\n"
1069 "widget has these options:\n"
1070 "\n"
1071 " Operators\n"
1072 " over\n"
1073 " in\n"
1074 " out\n"
1075 " atop\n"
1076 " xor\n"
1077 " plus\n"
1078 " minus\n"
1079 " add\n"
1080 " subtract\n"
1081 " difference\n"
1082 " replace\n"
1083 " Help\n"
1084 " Dismiss\n"
1085 "\n"
1086 "Choose a composite operation from the Operators sub-menu of\n"
1087 "the Command widget. How each operator behaves is described\n"
1088 "below. Image window is the image currently displayed on\n"
1089 "your X server and image is the image obtained with the File\n"
1090 "Browser widget.\n"
1091 "\n"
1092 "Over The result is the union of the two image shapes,\n"
1093 " with image obscuring image window in the region of\n"
1094 " overlap.\n"
1095 "\n"
1096 "In The result is simply image cut by the shape of\n"
1097 " image window. None of the image data of image\n"
1098 " window is in the result.\n"
1099 "\n"
1100 "Out The resulting image is image with the shape of\n"
1101 " image window cut out.\n"
1102 "\n"
1103 "Atop The result is the same shape as the image window,\n"
1104 " with image obscuring image window where the image\n"
1105 " shapes overlap. Note this differs from over\n"
1106 " because the portion of image outside image window's\n"
1107 " shape does not appear in the result.\n"
1108 "\n"
1109 "Xor The result is the image data from both image and\n"
1110 " image window that is outside the overlap region.\n"
1111 " The overlap region is blank.\n"
1112 "\n"
1113 "Plus The result is just the sum of the image data.\n"
1114 " Output values are cropped to QuantumRange (no overflow).\n"
1115 " This operation is independent of the matte\n"
1116 " channels.\n"
1117 "\n"
1118 "Minus The result of image - image window, with underflow\n"
1119 " cropped to zero.\n"
1120 "\n"
1121 "Add The result of image + image window, with overflow\n"
1122 " wrapping around (mod 256).\n"
1123 "\n"
1124 "Subtract The result of image - image window, with underflow\n"
1125 " wrapping around (mod 256). The add and subtract\n"
1126 " operators can be used to perform reversible\n"
1127 " transformations.\n"
1128 "\n"
1129 "Difference\n"
1130 " The result of abs(image - image window). This\n"
1131 " useful for comparing two very similar images.\n"
1132 "\n"
1133 "Copy The resulting image is image window replaced with\n"
1134 " image. Here the matte information is ignored.\n"
1135 "\n"
1136 "CopyRed The red layer of the image window is replace with\n"
1137 " the red layer of the image. The other layers are\n"
1138 " untouched.\n"
1139 "\n"
1140 "CopyGreen\n"
1141 " The green layer of the image window is replace with\n"
1142 " the green layer of the image. The other layers are\n"
1143 " untouched.\n"
1144 "\n"
1145 "CopyBlue The blue layer of the image window is replace with\n"
1146 " the blue layer of the image. The other layers are\n"
1147 " untouched.\n"
1148 "\n"
1149 "CopyOpacity\n"
1150 " The matte layer of the image window is replace with\n"
1151 " the matte layer of the image. The other layers are\n"
1152 " untouched.\n"
1153 "\n"
1154 "The image compositor requires a matte, or alpha channel in\n"
1155 "the image for some operations. This extra channel usually\n"
1156 "defines a mask which represents a sort of a cookie-cutter\n"
1157 "for the image. This the case when matte is opaque (full\n"
1158 "coverage) for pixels inside the shape, zero outside, and\n"
1159 "between 0 and QuantumRange on the boundary. If image does not\n"
1160 "have a matte channel, it is initialized with 0 for any pixel\n"
1161 "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
1162 "\n"
1163 "Note that matte information for image window is not retained\n"
1164 "for colormapped X server visuals (e.g. StaticColor,\n"
1165 "StaticColor, GrayScale, PseudoColor). Correct compositing\n"
1166 "behavior may require a TrueColor or DirectColor visual or a\n"
1167 "Standard Colormap.\n"
1168 "\n"
1169 "Choosing a composite operator is optional. The default\n"
1170 "operator is replace. However, you must choose a location to\n"
1171 "paste your image and press button 1. Press and hold the\n"
1172 "button before releasing and an outline of the image will\n"
1173 "appear to help you identify your location.\n"
1174 "\n"
1175 "The actual colors of the pasted image is saved. However,\n"
1176 "the color that appears in image window may be different.\n"
1177 "For example, on a monochrome screen image window will appear\n"
1178 "black or white even though your pasted image may have\n"
1179 "many colors. If the image is saved to a file it is written\n"
1180 "with the correct colors. To assure the correct colors are\n"
1181 "saved in the final image, any PseudoClass image is promoted\n"
1182 "to DirectClass (see miff(5)). To force a PseudoClass image\n"
1183 "to remain PseudoClass, use -colors.\n"
1184 },
1185 ImageROIHelp[] =
1186 {
1187 "In region of interest mode, the Command widget has these\n"
1188 "options:\n"
1189 "\n"
1190 " Help\n"
1191 " Dismiss\n"
1192 "\n"
1193 "To define a region of interest, press button 1 and drag.\n"
1194 "The region of interest is defined by a highlighted rectangle\n"
1195 "that expands or contracts as it follows the pointer. Once\n"
1196 "you are satisfied with the region of interest, release the\n"
1197 "button. You are now in apply mode. In apply mode the\n"
1198 "Command widget has these options:\n"
1199 "\n"
1200 " File\n"
1201 " Save...\n"
1202 " Print...\n"
1203 " Edit\n"
1204 " Undo\n"
1205 " Redo\n"
1206 " Transform\n"
1207 " Flop\n"
1208 " Flip\n"
1209 " Rotate Right\n"
1210 " Rotate Left\n"
1211 " Enhance\n"
1212 " Hue...\n"
1213 " Saturation...\n"
1214 " Brightness...\n"
1215 " Gamma...\n"
1216 " Spiff\n"
1217 " Dull\n"
1218 " Contrast Stretch\n"
1219 " Sigmoidal Contrast...\n"
1220 " Normalize\n"
1221 " Equalize\n"
1222 " Negate\n"
1223 " Grayscale\n"
1224 " Map...\n"
1225 " Quantize...\n"
1226 " Effects\n"
1227 " Despeckle\n"
1228 " Emboss\n"
1229 " Reduce Noise\n"
1230 " Sharpen...\n"
1231 " Blur...\n"
1232 " Threshold...\n"
1233 " Edge Detect...\n"
1234 " Spread...\n"
1235 " Shade...\n"
1236 " Raise...\n"
1237 " Segment...\n"
1238 " F/X\n"
1239 " Solarize...\n"
1240 " Sepia Tone...\n"
1241 " Swirl...\n"
1242 " Implode...\n"
1243 " Vignette...\n"
1244 " Wave...\n"
1245 " Oil Painting...\n"
1246 " Charcoal Drawing...\n"
1247 " Miscellany\n"
1248 " Image Info\n"
1249 " Zoom Image\n"
1250 " Show Preview...\n"
1251 " Show Histogram\n"
1252 " Show Matte\n"
1253 " Help\n"
1254 " Dismiss\n"
1255 "\n"
1256 "You can make adjustments to the region of interest by moving\n"
1257 "the pointer to one of the rectangle corners, pressing a\n"
1258 "button, and dragging. Finally, choose an image processing\n"
1259 "technique from the Command widget. You can choose more than\n"
1260 "one image processing technique to apply to an area.\n"
1261 "Alternatively, you can move the region of interest before\n"
1262 "applying another image processing technique. To exit, press\n"
1263 "Dismiss.\n"
1264 },
1265 ImageRotateHelp[] =
1266 {
1267 "In rotate mode, the Command widget has these options:\n"
1268 "\n"
1269 " Pixel Color\n"
1270 " black\n"
1271 " blue\n"
1272 " cyan\n"
1273 " green\n"
1274 " gray\n"
1275 " red\n"
1276 " magenta\n"
1277 " yellow\n"
1278 " white\n"
1279 " Browser...\n"
1280 " Direction\n"
1281 " horizontal\n"
1282 " vertical\n"
1283 " Help\n"
1284 " Dismiss\n"
1285 "\n"
1286 "Choose a background color from the Pixel Color sub-menu.\n"
1287 "Additional background colors can be specified with the color\n"
1288 "browser. You can change the menu colors by setting the X\n"
1289 "resources pen1 through pen9.\n"
1290 "\n"
1291 "If you choose the color browser and press Grab, you can\n"
1292 "select the background color by moving the pointer to the\n"
1293 "desired color on the screen and press any button.\n"
1294 "\n"
1295 "Choose a point in the image window and press this button and\n"
1296 "hold. Next, move the pointer to another location in the\n"
1297 "image. As you move a line connects the initial location and\n"
1298 "the pointer. When you release the button, the degree of\n"
1299 "image rotation is determined by the slope of the line you\n"
1300 "just drew. The slope is relative to the direction you\n"
1301 "choose from the Direction sub-menu of the Command widget.\n"
1302 "\n"
1303 "To cancel the image rotation, move the pointer back to the\n"
1304 "starting point of the line and release the button.\n"
1305 };
1306
1307/*
1308 Enumeration declarations.
1309*/
1310typedef enum
1311{
1312 CopyMode,
1313 CropMode,
1314 CutMode
1315} ClipboardMode;
1316
1317typedef enum
1318{
1319 OpenCommand,
1320 NextCommand,
1321 FormerCommand,
1322 SelectCommand,
1323 SaveCommand,
1324 PrintCommand,
1325 DeleteCommand,
1326 NewCommand,
1327 VisualDirectoryCommand,
1328 QuitCommand,
1329 UndoCommand,
1330 RedoCommand,
1331 CutCommand,
1332 CopyCommand,
1333 PasteCommand,
1334 HalfSizeCommand,
1335 OriginalSizeCommand,
1336 DoubleSizeCommand,
1337 ResizeCommand,
1338 ApplyCommand,
1339 RefreshCommand,
1340 RestoreCommand,
1341 CropCommand,
1342 ChopCommand,
1343 FlopCommand,
1344 FlipCommand,
1345 RotateRightCommand,
1346 RotateLeftCommand,
1347 RotateCommand,
1348 ShearCommand,
1349 RollCommand,
1350 TrimCommand,
1351 HueCommand,
1352 SaturationCommand,
1353 BrightnessCommand,
1354 GammaCommand,
1355 SpiffCommand,
1356 DullCommand,
1357 ContrastStretchCommand,
1358 SigmoidalContrastCommand,
1359 NormalizeCommand,
1360 EqualizeCommand,
1361 NegateCommand,
1362 GrayscaleCommand,
1363 MapCommand,
1364 QuantizeCommand,
1365 DespeckleCommand,
1366 EmbossCommand,
1367 ReduceNoiseCommand,
1368 AddNoiseCommand,
1369 SharpenCommand,
1370 BlurCommand,
1371 ThresholdCommand,
1372 EdgeDetectCommand,
1373 SpreadCommand,
1374 ShadeCommand,
1375 RaiseCommand,
1376 SegmentCommand,
1377 SolarizeCommand,
1378 SepiaToneCommand,
1379 SwirlCommand,
1380 ImplodeCommand,
1381 VignetteCommand,
1382 WaveCommand,
1383 OilPaintCommand,
1384 CharcoalDrawCommand,
1385 AnnotateCommand,
1386 DrawCommand,
1387 ColorCommand,
1388 MatteCommand,
1389 CompositeCommand,
1390 AddBorderCommand,
1391 AddFrameCommand,
1392 CommentCommand,
1393 LaunchCommand,
1394 RegionOfInterestCommand,
1395 ROIHelpCommand,
1396 ROIDismissCommand,
1397 InfoCommand,
1398 ZoomCommand,
1399 ShowPreviewCommand,
1400 ShowHistogramCommand,
1401 ShowMatteCommand,
1402 BackgroundCommand,
1403 SlideShowCommand,
1404 PreferencesCommand,
1405 HelpCommand,
1406 BrowseDocumentationCommand,
1407 VersionCommand,
1408 SaveToUndoBufferCommand,
1409 FreeBuffersCommand,
1410 NullCommand
1411} DisplayCommand;
1412
1413typedef enum
1414{
1415 AnnotateNameCommand,
1416 AnnotateFontColorCommand,
1417 AnnotateBackgroundColorCommand,
1418 AnnotateRotateCommand,
1419 AnnotateHelpCommand,
1420 AnnotateDismissCommand,
1421 TextHelpCommand,
1422 TextApplyCommand,
1423 ChopDirectionCommand,
1424 ChopHelpCommand,
1425 ChopDismissCommand,
1426 HorizontalChopCommand,
1427 VerticalChopCommand,
1428 ColorEditMethodCommand,
1429 ColorEditColorCommand,
1430 ColorEditBorderCommand,
1431 ColorEditFuzzCommand,
1432 ColorEditUndoCommand,
1433 ColorEditHelpCommand,
1434 ColorEditDismissCommand,
1435 CompositeOperatorsCommand,
1436 CompositeDissolveCommand,
1437 CompositeDisplaceCommand,
1438 CompositeHelpCommand,
1439 CompositeDismissCommand,
1440 CropHelpCommand,
1441 CropDismissCommand,
1442 RectifyCopyCommand,
1443 RectifyHelpCommand,
1444 RectifyDismissCommand,
1445 DrawElementCommand,
1446 DrawColorCommand,
1447 DrawStippleCommand,
1448 DrawWidthCommand,
1449 DrawUndoCommand,
1450 DrawHelpCommand,
1451 DrawDismissCommand,
1452 MatteEditMethod,
1453 MatteEditBorderCommand,
1454 MatteEditFuzzCommand,
1455 MatteEditValueCommand,
1456 MatteEditUndoCommand,
1457 MatteEditHelpCommand,
1458 MatteEditDismissCommand,
1459 PasteOperatorsCommand,
1460 PasteHelpCommand,
1461 PasteDismissCommand,
1462 RotateColorCommand,
1463 RotateDirectionCommand,
1464 RotateCropCommand,
1465 RotateSharpenCommand,
1466 RotateHelpCommand,
1467 RotateDismissCommand,
1468 HorizontalRotateCommand,
1469 VerticalRotateCommand,
1470 TileLoadCommand,
1471 TileNextCommand,
1472 TileFormerCommand,
1473 TileDeleteCommand,
1474 TileUpdateCommand
1475} ModeType;
1476
1477/*
1478 Stipples.
1479*/
1480#define BricksWidth 20
1481#define BricksHeight 20
1482#define DiagonalWidth 16
1483#define DiagonalHeight 16
1484#define HighlightWidth 8
1485#define HighlightHeight 8
1486#define OpaqueWidth 8
1487#define OpaqueHeight 8
1488#define ScalesWidth 16
1489#define ScalesHeight 16
1490#define ShadowWidth 8
1491#define ShadowHeight 8
1492#define VerticalWidth 16
1493#define VerticalHeight 16
1494#define WavyWidth 16
1495#define WavyHeight 16
1496
1497/*
1498 Constant declaration.
1499*/
1500static const int
1501 RoiDelta = 8;
1502
1503static const unsigned char
1504 BricksBitmap[] =
1505 {
1506 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1507 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1508 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1509 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1510 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1511 },
1512 DiagonalBitmap[] =
1513 {
1514 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1515 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1516 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1517 },
1518 ScalesBitmap[] =
1519 {
1520 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1521 0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1522 0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1523 },
1524 VerticalBitmap[] =
1525 {
1526 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1527 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1528 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1529 },
1530 WavyBitmap[] =
1531 {
1532 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1533 0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1534 0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1535 };
1536
1537/*
1538 Function prototypes.
1539*/
1540static DisplayCommand
1541 XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1542 const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1543
1544static Image
1545 *XMagickCommand(Display *,XResourceInfo *,XWindows *,const DisplayCommand,
1546 Image **,ExceptionInfo *),
1547 *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1548 *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1549 ExceptionInfo *),
1550 *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1551 ExceptionInfo *);
1552
1553static MagickBooleanType
1554 XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1555 ExceptionInfo *),
1556 XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1557 ExceptionInfo *),
1558 XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1559 ExceptionInfo *),
1560 XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1561 ExceptionInfo *),
1562 XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1563 ExceptionInfo *),
1564 XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1565 ExceptionInfo *),
1566 XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1567 XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1568 ExceptionInfo *),
1569 XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1570 ExceptionInfo *),
1571 XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1572 XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1573 XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1574 ExceptionInfo *),
1575 XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1576 XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1577 XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1578
1579static void
1580 XDrawPanRectangle(Display *,XWindows *),
1581 XImageCache(Display *,XResourceInfo *,XWindows *,const DisplayCommand,Image **,
1582 ExceptionInfo *),
1583 XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1584 XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1585 XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1586 XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1587 const KeySym,ExceptionInfo *),
1588 XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1589 XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1590 XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1591
1592/*
1593%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1594% %
1595% %
1596% %
1597% D i s p l a y I m a g e s %
1598% %
1599% %
1600% %
1601%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1602%
1603% DisplayImages() displays an image sequence to any X window screen. It
1604% returns a value other than 0 if successful. Check the exception member
1605% of image to determine the reason for any failure.
1606%
1607% The format of the DisplayImages method is:
1608%
1609% MagickBooleanType DisplayImages(const ImageInfo *image_info,
1610% Image *images,ExceptionInfo *exception)
1611%
1612% A description of each parameter follows:
1613%
1614% o image_info: the image info.
1615%
1616% o image: the image.
1617%
1618% o exception: return any errors or warnings in this structure.
1619%
1620*/
1621MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1622 Image *images,ExceptionInfo *exception)
1623{
1624 char
1625 *argv[1];
1626
1627 Display
1628 *display;
1629
1630 Image
1631 *image;
1632
1633 size_t
1634 state;
1635
1636 ssize_t
1637 i;
1638
1639 XrmDatabase
1640 resource_database;
1641
1642 XResourceInfo
1643 resource_info;
1644
1645 assert(image_info != (const ImageInfo *) NULL);
1646 assert(image_info->signature == MagickCoreSignature);
1647 assert(images != (Image *) NULL);
1648 assert(images->signature == MagickCoreSignature);
1649 if (IsEventLogging() != MagickFalse)
1650 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1651 display=XOpenDisplay(image_info->server_name);
1652 if (display == (Display *) NULL)
1653 {
1654 (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1655 "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1656 return(MagickFalse);
1657 }
1658 if (exception->severity != UndefinedException)
1659 CatchException(exception);
1660 (void) XSetErrorHandler(XError);
1661 resource_database=XGetResourceDatabase(display,GetClientName());
1662 (void) memset(&resource_info,0,sizeof(resource_info));
1663 XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1664 if (image_info->page != (char *) NULL)
1665 resource_info.image_geometry=AcquireString(image_info->page);
1666 resource_info.immutable=MagickTrue;
1667 argv[0]=AcquireString(GetClientName());
1668 state=DefaultState;
1669 for (i=0; (state & ExitState) == 0; i++)
1670 {
1671 if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1672 break;
1673 image=GetImageFromList(images,i % (ssize_t) GetImageListLength(images));
1674 (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1675 }
1676 (void) SetErrorHandler((ErrorHandler) NULL);
1677 (void) SetWarningHandler((WarningHandler) NULL);
1678 argv[0]=DestroyString(argv[0]);
1679 (void) XCloseDisplay(display);
1680 XDestroyResourceInfo(&resource_info);
1681 if (exception->severity != UndefinedException)
1682 return(MagickFalse);
1683 return(MagickTrue);
1684}
1685
1686/*
1687%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1688% %
1689% %
1690% %
1691% R e m o t e D i s p l a y C o m m a n d %
1692% %
1693% %
1694% %
1695%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1696%
1697% RemoteDisplayCommand() encourages a remote display program to display the
1698% specified image filename.
1699%
1700% The format of the RemoteDisplayCommand method is:
1701%
1702% MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1703% const char *window,const char *filename,ExceptionInfo *exception)
1704%
1705% A description of each parameter follows:
1706%
1707% o image_info: the image info.
1708%
1709% o window: Specifies the name or id of an X window.
1710%
1711% o filename: the name of the image filename to display.
1712%
1713% o exception: return any errors or warnings in this structure.
1714%
1715*/
1716MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1717 const char *window,const char *filename,ExceptionInfo *exception)
1718{
1719 Display
1720 *display;
1721
1722 MagickStatusType
1723 status;
1724
1725 assert(image_info != (const ImageInfo *) NULL);
1726 assert(image_info->signature == MagickCoreSignature);
1727 assert(filename != (char *) NULL);
1728 if (IsEventLogging() != MagickFalse)
1729 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1730 display=XOpenDisplay(image_info->server_name);
1731 if (display == (Display *) NULL)
1732 {
1733 (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1734 "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1735 return(MagickFalse);
1736 }
1737 (void) XSetErrorHandler(XError);
1738 status=XRemoteCommand(display,window,filename);
1739 (void) XCloseDisplay(display);
1740 return(status != 0 ? MagickTrue : MagickFalse);
1741}
1742
1743/*
1744%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1745% %
1746% %
1747% %
1748+ X A n n o t a t e E d i t I m a g e %
1749% %
1750% %
1751% %
1752%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1753%
1754% XAnnotateEditImage() annotates the image with text.
1755%
1756% The format of the XAnnotateEditImage method is:
1757%
1758% MagickBooleanType XAnnotateEditImage(Display *display,
1759% XResourceInfo *resource_info,XWindows *windows,Image *image,
1760% ExceptionInfo *exception)
1761%
1762% A description of each parameter follows:
1763%
1764% o display: Specifies a connection to an X server; returned from
1765% XOpenDisplay.
1766%
1767% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1768%
1769% o windows: Specifies a pointer to a XWindows structure.
1770%
1771% o image: the image; returned from ReadImage.
1772%
1773*/
1774
1775static MagickBooleanType XAnnotateEditImage(Display *display,
1776 XResourceInfo *resource_info,XWindows *windows,Image *image,
1777 ExceptionInfo *exception)
1778{
1779 const char
1780 *const AnnotateMenu[] =
1781 {
1782 "Font Name",
1783 "Font Color",
1784 "Box Color",
1785 "Rotate Text",
1786 "Help",
1787 "Dismiss",
1788 (char *) NULL
1789 },
1790 *const TextMenu[] =
1791 {
1792 "Help",
1793 "Apply",
1794 (char *) NULL
1795 };
1796
1797 static const ModeType
1798 AnnotateCommands[] =
1799 {
1800 AnnotateNameCommand,
1801 AnnotateFontColorCommand,
1802 AnnotateBackgroundColorCommand,
1803 AnnotateRotateCommand,
1804 AnnotateHelpCommand,
1805 AnnotateDismissCommand
1806 },
1807 TextCommands[] =
1808 {
1809 TextHelpCommand,
1810 TextApplyCommand
1811 };
1812
1813 static MagickBooleanType
1814 transparent_box = MagickTrue,
1815 transparent_pen = MagickFalse;
1816
1817 static double
1818 degrees = 0.0;
1819
1820 static unsigned int
1821 box_id = MaxNumberPens-2,
1822 font_id = 0,
1823 pen_id = 0;
1824
1825 char
1826 command[MagickPathExtent],
1827 *p,
1828 text[MagickPathExtent];
1829
1830 const char
1831 *ColorMenu[MaxNumberPens+1];
1832
1833 Cursor
1834 cursor;
1835
1836 GC
1837 annotate_context;
1838
1839 int
1840 id,
1841 pen_number,
1842 status,
1843 x,
1844 y;
1845
1846 KeySym
1847 key_symbol;
1848
1849 size_t
1850 state;
1851
1852 ssize_t
1853 i;
1854
1855 unsigned int
1856 height,
1857 width;
1858
1859 XAnnotateInfo
1860 *annotate_info,
1861 *previous_info;
1862
1863 XColor
1864 color;
1865
1866 XFontStruct
1867 *font_info;
1868
1869 XEvent
1870 event,
1871 text_event;
1872
1873 /*
1874 Map Command widget.
1875 */
1876 (void) CloneString(&windows->command.name,"Annotate");
1877 windows->command.data=4;
1878 (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1879 (void) XMapRaised(display,windows->command.id);
1880 XClientMessage(display,windows->image.id,windows->im_protocols,
1881 windows->im_update_widget,CurrentTime);
1882 /*
1883 Track pointer until button 1 is pressed.
1884 */
1885 XQueryPosition(display,windows->image.id,&x,&y);
1886 (void) XSelectInput(display,windows->image.id,
1887 windows->image.attributes.event_mask | PointerMotionMask);
1888 cursor=XCreateFontCursor(display,XC_left_side);
1889 (void) XCheckDefineCursor(display,windows->image.id,cursor);
1890 state=DefaultState;
1891 do
1892 {
1893 if (windows->info.mapped != MagickFalse)
1894 {
1895 /*
1896 Display pointer position.
1897 */
1898 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
1899 x+windows->image.x,y+windows->image.y);
1900 XInfoWidget(display,windows,text);
1901 }
1902 /*
1903 Wait for next event.
1904 */
1905 XScreenEvent(display,windows,&event,exception);
1906 if (event.xany.window == windows->command.id)
1907 {
1908 /*
1909 Select a command from the Command widget.
1910 */
1911 id=XCommandWidget(display,windows,AnnotateMenu,&event);
1912 (void) XCheckDefineCursor(display,windows->image.id,cursor);
1913 if (id < 0)
1914 continue;
1915 switch (AnnotateCommands[id])
1916 {
1917 case AnnotateNameCommand:
1918 {
1919 const char
1920 *FontMenu[MaxNumberFonts];
1921
1922 int
1923 font_number;
1924
1925 /*
1926 Initialize menu selections.
1927 */
1928 for (i=0; i < MaxNumberFonts; i++)
1929 FontMenu[i]=resource_info->font_name[i];
1930 FontMenu[MaxNumberFonts-2]="Browser...";
1931 FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1932 /*
1933 Select a font name from the pop-up menu.
1934 */
1935 font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1936 (const char **) FontMenu,command);
1937 if (font_number < 0)
1938 break;
1939 if (font_number == (MaxNumberFonts-2))
1940 {
1941 static char
1942 font_name[MagickPathExtent] = "fixed";
1943
1944 /*
1945 Select a font name from a browser.
1946 */
1947 resource_info->font_name[font_number]=font_name;
1948 XFontBrowserWidget(display,windows,"Select",font_name);
1949 if (*font_name == '\0')
1950 break;
1951 }
1952 /*
1953 Initialize font info.
1954 */
1955 font_info=XLoadQueryFont(display,resource_info->font_name[
1956 font_number]);
1957 if (font_info == (XFontStruct *) NULL)
1958 {
1959 XNoticeWidget(display,windows,"Unable to load font:",
1960 resource_info->font_name[font_number]);
1961 break;
1962 }
1963 font_id=(unsigned int) font_number;
1964 (void) XFreeFont(display,font_info);
1965 break;
1966 }
1967 case AnnotateFontColorCommand:
1968 {
1969 /*
1970 Initialize menu selections.
1971 */
1972 for (i=0; i < (int) (MaxNumberPens-2); i++)
1973 ColorMenu[i]=resource_info->pen_colors[i];
1974 ColorMenu[MaxNumberPens-2]="transparent";
1975 ColorMenu[MaxNumberPens-1]="Browser...";
1976 ColorMenu[MaxNumberPens]=(const char *) NULL;
1977 /*
1978 Select a pen color from the pop-up menu.
1979 */
1980 pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
1981 (const char **) ColorMenu,command);
1982 if (pen_number < 0)
1983 break;
1984 transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
1985 MagickFalse;
1986 if (transparent_pen != MagickFalse)
1987 break;
1988 if (pen_number == (MaxNumberPens-1))
1989 {
1990 static char
1991 color_name[MagickPathExtent] = "gray";
1992
1993 /*
1994 Select a pen color from a dialog.
1995 */
1996 resource_info->pen_colors[pen_number]=color_name;
1997 XColorBrowserWidget(display,windows,"Select",color_name);
1998 if (*color_name == '\0')
1999 break;
2000 }
2001 /*
2002 Set pen color.
2003 */
2004 (void) XParseColor(display,windows->map_info->colormap,
2005 resource_info->pen_colors[pen_number],&color);
2006 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2007 (unsigned int) MaxColors,&color);
2008 windows->pixel_info->pen_colors[pen_number]=color;
2009 pen_id=(unsigned int) pen_number;
2010 break;
2011 }
2012 case AnnotateBackgroundColorCommand:
2013 {
2014 /*
2015 Initialize menu selections.
2016 */
2017 for (i=0; i < (int) (MaxNumberPens-2); i++)
2018 ColorMenu[i]=resource_info->pen_colors[i];
2019 ColorMenu[MaxNumberPens-2]="transparent";
2020 ColorMenu[MaxNumberPens-1]="Browser...";
2021 ColorMenu[MaxNumberPens]=(const char *) NULL;
2022 /*
2023 Select a pen color from the pop-up menu.
2024 */
2025 pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2026 (const char **) ColorMenu,command);
2027 if (pen_number < 0)
2028 break;
2029 transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2030 MagickFalse;
2031 if (transparent_box != MagickFalse)
2032 break;
2033 if (pen_number == (MaxNumberPens-1))
2034 {
2035 static char
2036 color_name[MagickPathExtent] = "gray";
2037
2038 /*
2039 Select a pen color from a dialog.
2040 */
2041 resource_info->pen_colors[pen_number]=color_name;
2042 XColorBrowserWidget(display,windows,"Select",color_name);
2043 if (*color_name == '\0')
2044 break;
2045 }
2046 /*
2047 Set pen color.
2048 */
2049 (void) XParseColor(display,windows->map_info->colormap,
2050 resource_info->pen_colors[pen_number],&color);
2051 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2052 (unsigned int) MaxColors,&color);
2053 windows->pixel_info->pen_colors[pen_number]=color;
2054 box_id=(unsigned int) pen_number;
2055 break;
2056 }
2057 case AnnotateRotateCommand:
2058 {
2059 int
2060 entry;
2061
2062 const char
2063 *const RotateMenu[] =
2064 {
2065 "-90",
2066 "-45",
2067 "-30",
2068 "0",
2069 "30",
2070 "45",
2071 "90",
2072 "180",
2073 "Dialog...",
2074 (char *) NULL,
2075 };
2076
2077 static char
2078 angle[MagickPathExtent] = "30.0";
2079
2080 /*
2081 Select a command from the pop-up menu.
2082 */
2083 entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2084 command);
2085 if (entry < 0)
2086 break;
2087 if (entry != 8)
2088 {
2089 degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2090 break;
2091 }
2092 (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2093 angle);
2094 if (*angle == '\0')
2095 break;
2096 degrees=StringToDouble(angle,(char **) NULL);
2097 break;
2098 }
2099 case AnnotateHelpCommand:
2100 {
2101 XTextViewHelp(display,resource_info,windows,MagickFalse,
2102 "Help Viewer - Image Annotation",ImageAnnotateHelp);
2103 break;
2104 }
2105 case AnnotateDismissCommand:
2106 {
2107 /*
2108 Prematurely exit.
2109 */
2110 state|=EscapeState;
2111 state|=ExitState;
2112 break;
2113 }
2114 default:
2115 break;
2116 }
2117 continue;
2118 }
2119 switch (event.type)
2120 {
2121 case ButtonPress:
2122 {
2123 if (event.xbutton.button != Button1)
2124 break;
2125 if (event.xbutton.window != windows->image.id)
2126 break;
2127 /*
2128 Change to text entering mode.
2129 */
2130 x=event.xbutton.x;
2131 y=event.xbutton.y;
2132 state|=ExitState;
2133 break;
2134 }
2135 case ButtonRelease:
2136 break;
2137 case Expose:
2138 break;
2139 case KeyPress:
2140 {
2141 if (event.xkey.window != windows->image.id)
2142 break;
2143 /*
2144 Respond to a user key press.
2145 */
2146 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2147 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2148 switch ((int) key_symbol)
2149 {
2150 case XK_Escape:
2151 case XK_F20:
2152 {
2153 /*
2154 Prematurely exit.
2155 */
2156 state|=EscapeState;
2157 state|=ExitState;
2158 break;
2159 }
2160 case XK_F1:
2161 case XK_Help:
2162 {
2163 XTextViewHelp(display,resource_info,windows,MagickFalse,
2164 "Help Viewer - Image Annotation",ImageAnnotateHelp);
2165 break;
2166 }
2167 default:
2168 {
2169 (void) XBell(display,0);
2170 break;
2171 }
2172 }
2173 break;
2174 }
2175 case MotionNotify:
2176 {
2177 /*
2178 Map and unmap Info widget as cursor crosses its boundaries.
2179 */
2180 x=event.xmotion.x;
2181 y=event.xmotion.y;
2182 if (windows->info.mapped != MagickFalse)
2183 {
2184 if ((x < (windows->info.x+(int) windows->info.width)) &&
2185 (y < (windows->info.y+(int) windows->info.height)))
2186 (void) XWithdrawWindow(display,windows->info.id,
2187 windows->info.screen);
2188 }
2189 else
2190 if ((x > (windows->info.x+(int) windows->info.width)) ||
2191 (y > (windows->info.y+(int) windows->info.height)))
2192 (void) XMapWindow(display,windows->info.id);
2193 break;
2194 }
2195 default:
2196 break;
2197 }
2198 } while ((state & ExitState) == 0);
2199 (void) XSelectInput(display,windows->image.id,
2200 windows->image.attributes.event_mask);
2201 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2202 if ((state & EscapeState) != 0)
2203 return(MagickTrue);
2204 /*
2205 Set font info and check boundary conditions.
2206 */
2207 font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2208 if (font_info == (XFontStruct *) NULL)
2209 {
2210 XNoticeWidget(display,windows,"Unable to load font:",
2211 resource_info->font_name[font_id]);
2212 font_info=windows->font_info;
2213 }
2214 if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2215 x=(int) windows->image.width-font_info->max_bounds.width;
2216 if (y < (int) (font_info->ascent+font_info->descent))
2217 y=(int) font_info->ascent+font_info->descent;
2218 if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2219 ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2220 return(MagickFalse);
2221 /*
2222 Initialize annotate structure.
2223 */
2224 annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2225 if (annotate_info == (XAnnotateInfo *) NULL)
2226 return(MagickFalse);
2227 XGetAnnotateInfo(annotate_info);
2228 annotate_info->x=x;
2229 annotate_info->y=y;
2230 if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2231 annotate_info->stencil=OpaqueStencil;
2232 else
2233 if (transparent_box == MagickFalse)
2234 annotate_info->stencil=BackgroundStencil;
2235 else
2236 annotate_info->stencil=ForegroundStencil;
2237 annotate_info->height=(unsigned int) (font_info->ascent+font_info->descent);
2238 annotate_info->degrees=degrees;
2239 annotate_info->font_info=font_info;
2240 annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2241 windows->image.width/(size_t) MagickMax(font_info->min_bounds.width,1)+2UL,
2242 sizeof(*annotate_info->text));
2243 if (annotate_info->text == (char *) NULL)
2244 return(MagickFalse);
2245 /*
2246 Create cursor and set graphic context.
2247 */
2248 cursor=XCreateFontCursor(display,XC_pencil);
2249 (void) XCheckDefineCursor(display,windows->image.id,cursor);
2250 annotate_context=windows->image.annotate_context;
2251 (void) XSetFont(display,annotate_context,font_info->fid);
2252 (void) XSetBackground(display,annotate_context,
2253 windows->pixel_info->pen_colors[box_id].pixel);
2254 (void) XSetForeground(display,annotate_context,
2255 windows->pixel_info->pen_colors[pen_id].pixel);
2256 /*
2257 Begin annotating the image with text.
2258 */
2259 (void) CloneString(&windows->command.name,"Text");
2260 windows->command.data=0;
2261 (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2262 state=DefaultState;
2263 (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2264 text_event.xexpose.width=(int) font_info->max_bounds.width;
2265 text_event.xexpose.height=font_info->max_bounds.ascent+
2266 font_info->max_bounds.descent;
2267 p=annotate_info->text;
2268 do
2269 {
2270 /*
2271 Display text cursor.
2272 */
2273 *p='\0';
2274 (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2275 /*
2276 Wait for next event.
2277 */
2278 XScreenEvent(display,windows,&event,exception);
2279 if (event.xany.window == windows->command.id)
2280 {
2281 /*
2282 Select a command from the Command widget.
2283 */
2284 (void) XSetBackground(display,annotate_context,
2285 windows->pixel_info->background_color.pixel);
2286 (void) XSetForeground(display,annotate_context,
2287 windows->pixel_info->foreground_color.pixel);
2288 id=XCommandWidget(display,windows,AnnotateMenu,&event);
2289 (void) XSetBackground(display,annotate_context,
2290 windows->pixel_info->pen_colors[box_id].pixel);
2291 (void) XSetForeground(display,annotate_context,
2292 windows->pixel_info->pen_colors[pen_id].pixel);
2293 if (id < 0)
2294 continue;
2295 switch (TextCommands[id])
2296 {
2297 case TextHelpCommand:
2298 {
2299 XTextViewHelp(display,resource_info,windows,MagickFalse,
2300 "Help Viewer - Image Annotation",ImageAnnotateHelp);
2301 (void) XCheckDefineCursor(display,windows->image.id,cursor);
2302 break;
2303 }
2304 case TextApplyCommand:
2305 {
2306 /*
2307 Finished annotating.
2308 */
2309 annotate_info->width=(unsigned int) XTextWidth(font_info,
2310 annotate_info->text,(int) strlen(annotate_info->text));
2311 XRefreshWindow(display,&windows->image,&text_event);
2312 state|=ExitState;
2313 break;
2314 }
2315 default:
2316 break;
2317 }
2318 continue;
2319 }
2320 /*
2321 Erase text cursor.
2322 */
2323 text_event.xexpose.x=x;
2324 text_event.xexpose.y=y-font_info->max_bounds.ascent;
2325 (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2326 (unsigned int) text_event.xexpose.width,(unsigned int)
2327 text_event.xexpose.height,MagickFalse);
2328 XRefreshWindow(display,&windows->image,&text_event);
2329 switch (event.type)
2330 {
2331 case ButtonPress:
2332 {
2333 if (event.xbutton.window != windows->image.id)
2334 break;
2335 if (event.xbutton.button == Button2)
2336 {
2337 /*
2338 Request primary selection.
2339 */
2340 (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2341 windows->image.id,CurrentTime);
2342 break;
2343 }
2344 break;
2345 }
2346 case Expose:
2347 {
2348 if (event.xexpose.count == 0)
2349 {
2350 XAnnotateInfo
2351 *text_info;
2352
2353 /*
2354 Refresh Image window.
2355 */
2356 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2357 text_info=annotate_info;
2358 while (text_info != (XAnnotateInfo *) NULL)
2359 {
2360 if (annotate_info->stencil == ForegroundStencil)
2361 (void) XDrawString(display,windows->image.id,annotate_context,
2362 text_info->x,text_info->y,text_info->text,
2363 (int) strlen(text_info->text));
2364 else
2365 (void) XDrawImageString(display,windows->image.id,
2366 annotate_context,text_info->x,text_info->y,text_info->text,
2367 (int) strlen(text_info->text));
2368 text_info=text_info->previous;
2369 }
2370 (void) XDrawString(display,windows->image.id,annotate_context,
2371 x,y,"_",1);
2372 }
2373 break;
2374 }
2375 case KeyPress:
2376 {
2377 int
2378 length;
2379
2380 if (event.xkey.window != windows->image.id)
2381 break;
2382 /*
2383 Respond to a user key press.
2384 */
2385 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2386 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2387 *(command+length)='\0';
2388 if (((event.xkey.state & ControlMask) != 0) ||
2389 ((event.xkey.state & Mod1Mask) != 0))
2390 state|=ModifierState;
2391 if ((state & ModifierState) != 0)
2392 switch ((int) key_symbol)
2393 {
2394 case XK_u:
2395 case XK_U:
2396 {
2397 key_symbol=DeleteCommand;
2398 break;
2399 }
2400 default:
2401 break;
2402 }
2403 switch ((int) key_symbol)
2404 {
2405 case XK_BackSpace:
2406 {
2407 /*
2408 Erase one character.
2409 */
2410 if (p == annotate_info->text)
2411 {
2412 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2413 break;
2414 else
2415 {
2416 /*
2417 Go to end of the previous line of text.
2418 */
2419 annotate_info=annotate_info->previous;
2420 p=annotate_info->text;
2421 x=annotate_info->x+(int) annotate_info->width;
2422 y=annotate_info->y;
2423 if (annotate_info->width != 0)
2424 p+=strlen(annotate_info->text);
2425 break;
2426 }
2427 }
2428 p--;
2429 x-=XTextWidth(font_info,p,1);
2430 text_event.xexpose.x=x;
2431 text_event.xexpose.y=y-font_info->max_bounds.ascent;
2432 XRefreshWindow(display,&windows->image,&text_event);
2433 break;
2434 }
2435 case XK_bracketleft:
2436 {
2437 key_symbol=XK_Escape;
2438 break;
2439 }
2440 case DeleteCommand:
2441 {
2442 /*
2443 Erase the entire line of text.
2444 */
2445 while (p != annotate_info->text)
2446 {
2447 p--;
2448 x-=XTextWidth(font_info,p,1);
2449 text_event.xexpose.x=x;
2450 XRefreshWindow(display,&windows->image,&text_event);
2451 }
2452 break;
2453 }
2454 case XK_Escape:
2455 case XK_F20:
2456 {
2457 /*
2458 Finished annotating.
2459 */
2460 annotate_info->width=(unsigned int) XTextWidth(font_info,
2461 annotate_info->text,(int) strlen(annotate_info->text));
2462 XRefreshWindow(display,&windows->image,&text_event);
2463 state|=ExitState;
2464 break;
2465 }
2466 default:
2467 {
2468 /*
2469 Draw a single character on the Image window.
2470 */
2471 if ((state & ModifierState) != 0)
2472 break;
2473 if (*command == '\0')
2474 break;
2475 *p=(*command);
2476 if (annotate_info->stencil == ForegroundStencil)
2477 (void) XDrawString(display,windows->image.id,annotate_context,
2478 x,y,p,1);
2479 else
2480 (void) XDrawImageString(display,windows->image.id,
2481 annotate_context,x,y,p,1);
2482 x+=XTextWidth(font_info,p,1);
2483 p++;
2484 if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2485 break;
2486 magick_fallthrough;
2487 }
2488 case XK_Return:
2489 case XK_KP_Enter:
2490 {
2491 /*
2492 Advance to the next line of text.
2493 */
2494 *p='\0';
2495 annotate_info->width=(unsigned int) XTextWidth(font_info,
2496 annotate_info->text,(int) strlen(annotate_info->text));
2497 if (annotate_info->next != (XAnnotateInfo *) NULL)
2498 {
2499 /*
2500 Line of text already exists.
2501 */
2502 annotate_info=annotate_info->next;
2503 x=annotate_info->x;
2504 y=annotate_info->y;
2505 p=annotate_info->text;
2506 break;
2507 }
2508 annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2509 sizeof(*annotate_info->next));
2510 if (annotate_info->next == (XAnnotateInfo *) NULL)
2511 return(MagickFalse);
2512 *annotate_info->next=(*annotate_info);
2513 annotate_info->next->previous=annotate_info;
2514 annotate_info=annotate_info->next;
2515 annotate_info->text=(char *) AcquireQuantumMemory((size_t) (
2516 (ssize_t) windows->image.width/MagickMax((ssize_t)
2517 font_info->min_bounds.width,1)+2L),sizeof(*annotate_info->text));
2518 if (annotate_info->text == (char *) NULL)
2519 return(MagickFalse);
2520 annotate_info->y+=(ssize_t) annotate_info->height;
2521 if (annotate_info->y > (int) windows->image.height)
2522 annotate_info->y=(int) annotate_info->height;
2523 annotate_info->next=(XAnnotateInfo *) NULL;
2524 x=annotate_info->x;
2525 y=annotate_info->y;
2526 p=annotate_info->text;
2527 break;
2528 }
2529 }
2530 break;
2531 }
2532 case KeyRelease:
2533 {
2534 /*
2535 Respond to a user key release.
2536 */
2537 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2538 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2539 state&=(size_t) (~ModifierState);
2540 break;
2541 }
2542 case SelectionNotify:
2543 {
2544 Atom
2545 type;
2546
2547 int
2548 format;
2549
2550 unsigned char
2551 *data;
2552
2553 unsigned long
2554 after,
2555 length;
2556
2557 /*
2558 Obtain response from primary selection.
2559 */
2560 if (event.xselection.property == (Atom) None)
2561 break;
2562 status=XGetWindowProperty(display,event.xselection.requestor,
2563 event.xselection.property,0L,(long) MagickPathExtent,True,XA_STRING,
2564 &type,&format,&length,&after,&data);
2565 if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2566 (length == 0))
2567 break;
2568 /*
2569 Annotate Image window with primary selection.
2570 */
2571 for (i=0; i < (ssize_t) length; i++)
2572 {
2573 if ((char) data[i] != '\n')
2574 {
2575 /*
2576 Draw a single character on the Image window.
2577 */
2578 *p=(char) data[i];
2579 (void) XDrawString(display,windows->image.id,annotate_context,
2580 x,y,p,1);
2581 x+=XTextWidth(font_info,p,1);
2582 p++;
2583 if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2584 continue;
2585 }
2586 /*
2587 Advance to the next line of text.
2588 */
2589 *p='\0';
2590 annotate_info->width=(unsigned int) XTextWidth(font_info,
2591 annotate_info->text,(int) strlen(annotate_info->text));
2592 if (annotate_info->next != (XAnnotateInfo *) NULL)
2593 {
2594 /*
2595 Line of text already exists.
2596 */
2597 annotate_info=annotate_info->next;
2598 x=annotate_info->x;
2599 y=annotate_info->y;
2600 p=annotate_info->text;
2601 continue;
2602 }
2603 annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2604 sizeof(*annotate_info->next));
2605 if (annotate_info->next == (XAnnotateInfo *) NULL)
2606 return(MagickFalse);
2607 *annotate_info->next=(*annotate_info);
2608 annotate_info->next->previous=annotate_info;
2609 annotate_info=annotate_info->next;
2610 annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2611 (windows->image.width/MagickMax((ssize_t)
2612 font_info->min_bounds.width,1)+2L),sizeof(*annotate_info->text));
2613 if (annotate_info->text == (char *) NULL)
2614 return(MagickFalse);
2615 annotate_info->y+=(ssize_t) annotate_info->height;
2616 if (annotate_info->y > (int) windows->image.height)
2617 annotate_info->y=(int) annotate_info->height;
2618 annotate_info->next=(XAnnotateInfo *) NULL;
2619 x=annotate_info->x;
2620 y=annotate_info->y;
2621 p=annotate_info->text;
2622 }
2623 (void) XFree((void *) data);
2624 break;
2625 }
2626 default:
2627 break;
2628 }
2629 } while ((state & ExitState) == 0);
2630 (void) XFreeCursor(display,cursor);
2631 /*
2632 Annotation is relative to image configuration.
2633 */
2634 width=(unsigned int) image->columns;
2635 height=(unsigned int) image->rows;
2636 x=0;
2637 y=0;
2638 if (windows->image.crop_geometry != (char *) NULL)
2639 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2640 /*
2641 Initialize annotated image.
2642 */
2643 XSetCursorState(display,windows,MagickTrue);
2644 XCheckRefreshWindows(display,windows);
2645 while (annotate_info != (XAnnotateInfo *) NULL)
2646 {
2647 if (annotate_info->width == 0)
2648 {
2649 /*
2650 No text on this line-- go to the next line of text.
2651 */
2652 previous_info=annotate_info->previous;
2653 annotate_info->text=(char *)
2654 RelinquishMagickMemory(annotate_info->text);
2655 annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2656 annotate_info=previous_info;
2657 continue;
2658 }
2659 /*
2660 Determine pixel index for box and pen color.
2661 */
2662 windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2663 if (windows->pixel_info->colors != 0)
2664 for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2665 if (windows->pixel_info->pixels[i] ==
2666 windows->pixel_info->pen_colors[box_id].pixel)
2667 {
2668 windows->pixel_info->box_index=(unsigned short) i;
2669 break;
2670 }
2671 windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2672 if (windows->pixel_info->colors != 0)
2673 for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2674 if (windows->pixel_info->pixels[i] ==
2675 windows->pixel_info->pen_colors[pen_id].pixel)
2676 {
2677 windows->pixel_info->pen_index=(unsigned short) i;
2678 break;
2679 }
2680 /*
2681 Define the annotate geometry string.
2682 */
2683 annotate_info->x=(int)
2684 width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2685 annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2686 windows->image.y)/windows->image.ximage->height;
2687 (void) FormatLocaleString(annotate_info->geometry,MagickPathExtent,
2688 "%gx%g%+g%+g",(double) width*annotate_info->width/
2689 windows->image.ximage->width,(double) height*annotate_info->height/
2690 windows->image.ximage->height,(double) annotate_info->x+x,(double)
2691 annotate_info->y+y);
2692 /*
2693 Annotate image with text.
2694 */
2695 status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2696 exception) == MagickFalse ? 0 : 1;
2697 if (status == 0)
2698 return(MagickFalse);
2699 /*
2700 Free up memory.
2701 */
2702 previous_info=annotate_info->previous;
2703 annotate_info->text=DestroyString(annotate_info->text);
2704 annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2705 annotate_info=previous_info;
2706 }
2707 (void) XSetForeground(display,annotate_context,
2708 windows->pixel_info->foreground_color.pixel);
2709 (void) XSetBackground(display,annotate_context,
2710 windows->pixel_info->background_color.pixel);
2711 (void) XSetFont(display,annotate_context,windows->font_info->fid);
2712 XSetCursorState(display,windows,MagickFalse);
2713 (void) XFreeFont(display,font_info);
2714 /*
2715 Update image configuration.
2716 */
2717 XConfigureImageColormap(display,resource_info,windows,image,exception);
2718 (void) XConfigureImage(display,resource_info,windows,image,exception);
2719 return(MagickTrue);
2720}
2721
2722/*
2723%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2724% %
2725% %
2726% %
2727+ X B a c k g r o u n d I m a g e %
2728% %
2729% %
2730% %
2731%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2732%
2733% XBackgroundImage() displays the image in the background of a window.
2734%
2735% The format of the XBackgroundImage method is:
2736%
2737% MagickBooleanType XBackgroundImage(Display *display,
2738% XResourceInfo *resource_info,XWindows *windows,Image **image,
2739% ExceptionInfo *exception)
2740%
2741% A description of each parameter follows:
2742%
2743% o display: Specifies a connection to an X server; returned from
2744% XOpenDisplay.
2745%
2746% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2747%
2748% o windows: Specifies a pointer to a XWindows structure.
2749%
2750% o image: the image.
2751%
2752% o exception: return any errors or warnings in this structure.
2753%
2754*/
2755static MagickBooleanType XBackgroundImage(Display *display,
2756 XResourceInfo *resource_info,XWindows *windows,Image **image,
2757 ExceptionInfo *exception)
2758{
2759#define BackgroundImageTag "Background/Image"
2760
2761 int
2762 status;
2763
2764 static char
2765 window_id[MagickPathExtent] = "root";
2766
2767 XResourceInfo
2768 background_resources;
2769
2770 /*
2771 Put image in background.
2772 */
2773 status=XDialogWidget(display,windows,"Background",
2774 "Enter window id (id 0x00 selects window with pointer):",window_id);
2775 if (*window_id == '\0')
2776 return(MagickFalse);
2777 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2778 exception);
2779 XInfoWidget(display,windows,BackgroundImageTag);
2780 XSetCursorState(display,windows,MagickTrue);
2781 XCheckRefreshWindows(display,windows);
2782 background_resources=(*resource_info);
2783 background_resources.window_id=window_id;
2784 background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2785 status=XDisplayBackgroundImage(display,&background_resources,*image,
2786 exception) == MagickFalse ? 0 : 1;
2787 if (status != MagickFalse)
2788 XClientMessage(display,windows->image.id,windows->im_protocols,
2789 windows->im_retain_colors,CurrentTime);
2790 XSetCursorState(display,windows,MagickFalse);
2791 (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2792 exception);
2793 return(MagickTrue);
2794}
2795
2796/*
2797%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2798% %
2799% %
2800% %
2801+ X C h o p I m a g e %
2802% %
2803% %
2804% %
2805%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2806%
2807% XChopImage() chops the X image.
2808%
2809% The format of the XChopImage method is:
2810%
2811% MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2812% XWindows *windows,Image **image,ExceptionInfo *exception)
2813%
2814% A description of each parameter follows:
2815%
2816% o display: Specifies a connection to an X server; returned from
2817% XOpenDisplay.
2818%
2819% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2820%
2821% o windows: Specifies a pointer to a XWindows structure.
2822%
2823% o image: the image.
2824%
2825% o exception: return any errors or warnings in this structure.
2826%
2827*/
2828static MagickBooleanType XChopImage(Display *display,
2829 XResourceInfo *resource_info,XWindows *windows,Image **image,
2830 ExceptionInfo *exception)
2831{
2832 const char
2833 *const ChopMenu[] =
2834 {
2835 "Direction",
2836 "Help",
2837 "Dismiss",
2838 (char *) NULL
2839 };
2840
2841 static ModeType
2842 direction = HorizontalChopCommand;
2843
2844 static const ModeType
2845 ChopCommands[] =
2846 {
2847 ChopDirectionCommand,
2848 ChopHelpCommand,
2849 ChopDismissCommand
2850 },
2851 DirectionCommands[] =
2852 {
2853 HorizontalChopCommand,
2854 VerticalChopCommand
2855 };
2856
2857 char
2858 text[MagickPathExtent];
2859
2860 double
2861 scale_factor;
2862
2863 Image
2864 *chop_image;
2865
2866 int
2867 id,
2868 x,
2869 y;
2870
2872 chop_info;
2873
2874 size_t
2875 state;
2876
2877 unsigned int
2878 distance,
2879 height,
2880 width;
2881
2882 XEvent
2883 event;
2884
2885 XSegment
2886 segment_info;
2887
2888 /*
2889 Map Command widget.
2890 */
2891 (void) CloneString(&windows->command.name,"Chop");
2892 windows->command.data=1;
2893 (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2894 (void) XMapRaised(display,windows->command.id);
2895 XClientMessage(display,windows->image.id,windows->im_protocols,
2896 windows->im_update_widget,CurrentTime);
2897 /*
2898 Track pointer until button 1 is pressed.
2899 */
2900 XQueryPosition(display,windows->image.id,&x,&y);
2901 (void) XSelectInput(display,windows->image.id,
2902 windows->image.attributes.event_mask | PointerMotionMask);
2903 state=DefaultState;
2904 (void) memset(&segment_info,0,sizeof(segment_info));
2905 do
2906 {
2907 if (windows->info.mapped != MagickFalse)
2908 {
2909 /*
2910 Display pointer position.
2911 */
2912 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
2913 x+windows->image.x,y+windows->image.y);
2914 XInfoWidget(display,windows,text);
2915 }
2916 /*
2917 Wait for next event.
2918 */
2919 XScreenEvent(display,windows,&event,exception);
2920 if (event.xany.window == windows->command.id)
2921 {
2922 /*
2923 Select a command from the Command widget.
2924 */
2925 id=XCommandWidget(display,windows,ChopMenu,&event);
2926 if (id < 0)
2927 continue;
2928 switch (ChopCommands[id])
2929 {
2930 case ChopDirectionCommand:
2931 {
2932 char
2933 command[MagickPathExtent];
2934
2935 const char
2936 *const Directions[] =
2937 {
2938 "horizontal",
2939 "vertical",
2940 (char *) NULL,
2941 };
2942
2943 /*
2944 Select a command from the pop-up menu.
2945 */
2946 id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2947 if (id >= 0)
2948 direction=DirectionCommands[id];
2949 break;
2950 }
2951 case ChopHelpCommand:
2952 {
2953 XTextViewHelp(display,resource_info,windows,MagickFalse,
2954 "Help Viewer - Image Chop",ImageChopHelp);
2955 break;
2956 }
2957 case ChopDismissCommand:
2958 {
2959 /*
2960 Prematurely exit.
2961 */
2962 state|=EscapeState;
2963 state|=ExitState;
2964 break;
2965 }
2966 default:
2967 break;
2968 }
2969 continue;
2970 }
2971 switch (event.type)
2972 {
2973 case ButtonPress:
2974 {
2975 if (event.xbutton.button != Button1)
2976 break;
2977 if (event.xbutton.window != windows->image.id)
2978 break;
2979 /*
2980 User has committed to start point of chopping line.
2981 */
2982 segment_info.x1=(short int) event.xbutton.x;
2983 segment_info.x2=(short int) event.xbutton.x;
2984 segment_info.y1=(short int) event.xbutton.y;
2985 segment_info.y2=(short int) event.xbutton.y;
2986 state|=ExitState;
2987 break;
2988 }
2989 case ButtonRelease:
2990 break;
2991 case Expose:
2992 break;
2993 case KeyPress:
2994 {
2995 char
2996 command[MagickPathExtent];
2997
2998 KeySym
2999 key_symbol;
3000
3001 if (event.xkey.window != windows->image.id)
3002 break;
3003 /*
3004 Respond to a user key press.
3005 */
3006 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3007 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3008 switch ((int) key_symbol)
3009 {
3010 case XK_Escape:
3011 case XK_F20:
3012 {
3013 /*
3014 Prematurely exit.
3015 */
3016 state|=EscapeState;
3017 state|=ExitState;
3018 break;
3019 }
3020 case XK_F1:
3021 case XK_Help:
3022 {
3023 (void) XSetFunction(display,windows->image.highlight_context,
3024 GXcopy);
3025 XTextViewHelp(display,resource_info,windows,MagickFalse,
3026 "Help Viewer - Image Chop",ImageChopHelp);
3027 (void) XSetFunction(display,windows->image.highlight_context,
3028 GXinvert);
3029 break;
3030 }
3031 default:
3032 {
3033 (void) XBell(display,0);
3034 break;
3035 }
3036 }
3037 break;
3038 }
3039 case MotionNotify:
3040 {
3041 /*
3042 Map and unmap Info widget as text cursor crosses its boundaries.
3043 */
3044 x=event.xmotion.x;
3045 y=event.xmotion.y;
3046 if (windows->info.mapped != MagickFalse)
3047 {
3048 if ((x < (windows->info.x+(int) windows->info.width)) &&
3049 (y < (windows->info.y+(int) windows->info.height)))
3050 (void) XWithdrawWindow(display,windows->info.id,
3051 windows->info.screen);
3052 }
3053 else
3054 if ((x > (windows->info.x+(int) windows->info.width)) ||
3055 (y > (windows->info.y+(int) windows->info.height)))
3056 (void) XMapWindow(display,windows->info.id);
3057 }
3058 }
3059 } while ((state & ExitState) == 0);
3060 (void) XSelectInput(display,windows->image.id,
3061 windows->image.attributes.event_mask);
3062 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3063 if ((state & EscapeState) != 0)
3064 return(MagickTrue);
3065 /*
3066 Draw line as pointer moves until the mouse button is released.
3067 */
3068 chop_info.width=0;
3069 chop_info.height=0;
3070 chop_info.x=0;
3071 chop_info.y=0;
3072 distance=0;
3073 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3074 state=DefaultState;
3075 do
3076 {
3077 if (distance > 9)
3078 {
3079 /*
3080 Display info and draw chopping line.
3081 */
3082 if (windows->info.mapped == MagickFalse)
3083 (void) XMapWindow(display,windows->info.id);
3084 (void) FormatLocaleString(text,MagickPathExtent,
3085 " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3086 chop_info.height,(double) chop_info.x,(double) chop_info.y);
3087 XInfoWidget(display,windows,text);
3088 XHighlightLine(display,windows->image.id,
3089 windows->image.highlight_context,&segment_info);
3090 }
3091 else
3092 if (windows->info.mapped != MagickFalse)
3093 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3094 /*
3095 Wait for next event.
3096 */
3097 XScreenEvent(display,windows,&event,exception);
3098 if (distance > 9)
3099 XHighlightLine(display,windows->image.id,
3100 windows->image.highlight_context,&segment_info);
3101 switch (event.type)
3102 {
3103 case ButtonPress:
3104 {
3105 segment_info.x2=(short int) event.xmotion.x;
3106 segment_info.y2=(short int) event.xmotion.y;
3107 break;
3108 }
3109 case ButtonRelease:
3110 {
3111 /*
3112 User has committed to chopping line.
3113 */
3114 segment_info.x2=(short int) event.xbutton.x;
3115 segment_info.y2=(short int) event.xbutton.y;
3116 state|=ExitState;
3117 break;
3118 }
3119 case Expose:
3120 break;
3121 case MotionNotify:
3122 {
3123 segment_info.x2=(short int) event.xmotion.x;
3124 segment_info.y2=(short int) event.xmotion.y;
3125 }
3126 default:
3127 break;
3128 }
3129 /*
3130 Check boundary conditions.
3131 */
3132 if (segment_info.x2 < 0)
3133 segment_info.x2=0;
3134 else
3135 if (segment_info.x2 > windows->image.ximage->width)
3136 segment_info.x2=windows->image.ximage->width;
3137 if (segment_info.y2 < 0)
3138 segment_info.y2=0;
3139 else
3140 if (segment_info.y2 > windows->image.ximage->height)
3141 segment_info.y2=windows->image.ximage->height;
3142 distance=(unsigned int)
3143 (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3144 ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3145 /*
3146 Compute chopping geometry.
3147 */
3148 if (direction == HorizontalChopCommand)
3149 {
3150 chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3151 chop_info.height=0;
3152 chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3153 chop_info.y=0;
3154 if (segment_info.x1 > segment_info.x2)
3155 {
3156 chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3157 chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3158 }
3159 }
3160 else
3161 {
3162 chop_info.width=0;
3163 chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3164 chop_info.x=0;
3165 chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3166 if (segment_info.y1 > segment_info.y2)
3167 {
3168 chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3169 chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3170 }
3171 }
3172 } while ((state & ExitState) == 0);
3173 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3174 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3175 if (distance <= 9)
3176 return(MagickTrue);
3177 /*
3178 Image chopping is relative to image configuration.
3179 */
3180 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3181 exception);
3182 XSetCursorState(display,windows,MagickTrue);
3183 XCheckRefreshWindows(display,windows);
3184 windows->image.window_changes.width=windows->image.ximage->width-
3185 (int) chop_info.width;
3186 windows->image.window_changes.height=windows->image.ximage->height-
3187 (int) chop_info.height;
3188 width=(unsigned int) (*image)->columns;
3189 height=(unsigned int) (*image)->rows;
3190 x=0;
3191 y=0;
3192 if (windows->image.crop_geometry != (char *) NULL)
3193 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3194 scale_factor=(double) width/windows->image.ximage->width;
3195 chop_info.x+=x;
3196 chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3197 chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3198 scale_factor=(double) height/windows->image.ximage->height;
3199 chop_info.y+=y;
3200 chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3201 chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3202 /*
3203 Chop image.
3204 */
3205 chop_image=ChopImage(*image,&chop_info,exception);
3206 XSetCursorState(display,windows,MagickFalse);
3207 if (chop_image == (Image *) NULL)
3208 return(MagickFalse);
3209 *image=DestroyImage(*image);
3210 *image=chop_image;
3211 /*
3212 Update image configuration.
3213 */
3214 XConfigureImageColormap(display,resource_info,windows,*image,exception);
3215 (void) XConfigureImage(display,resource_info,windows,*image,exception);
3216 return(MagickTrue);
3217}
3218
3219/*
3220%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3221% %
3222% %
3223% %
3224+ X C o l o r E d i t I m a g e %
3225% %
3226% %
3227% %
3228%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3229%
3230% XColorEditImage() allows the user to interactively change the color of one
3231% pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3232%
3233% The format of the XColorEditImage method is:
3234%
3235% MagickBooleanType XColorEditImage(Display *display,
3236% XResourceInfo *resource_info,XWindows *windows,Image **image,
3237% ExceptionInfo *exception)
3238%
3239% A description of each parameter follows:
3240%
3241% o display: Specifies a connection to an X server; returned from
3242% XOpenDisplay.
3243%
3244% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3245%
3246% o windows: Specifies a pointer to a XWindows structure.
3247%
3248% o image: the image; returned from ReadImage.
3249%
3250% o exception: return any errors or warnings in this structure.
3251%
3252*/
3253static MagickBooleanType XColorEditImage(Display *display,
3254 XResourceInfo *resource_info,XWindows *windows,Image **image,
3255 ExceptionInfo *exception)
3256{
3257 const char
3258 *const ColorEditMenu[] =
3259 {
3260 "Method",
3261 "Pixel Color",
3262 "Border Color",
3263 "Fuzz",
3264 "Undo",
3265 "Help",
3266 "Dismiss",
3267 (char *) NULL
3268 };
3269
3270 static const ModeType
3271 ColorEditCommands[] =
3272 {
3273 ColorEditMethodCommand,
3274 ColorEditColorCommand,
3275 ColorEditBorderCommand,
3276 ColorEditFuzzCommand,
3277 ColorEditUndoCommand,
3278 ColorEditHelpCommand,
3279 ColorEditDismissCommand
3280 };
3281
3282 static PaintMethod
3283 method = PointMethod;
3284
3285 static unsigned int
3286 pen_id = 0;
3287
3288 static XColor
3289 border_color = { 0, 0, 0, 0, 0, 0 };
3290
3291 char
3292 command[MagickPathExtent],
3293 text[MagickPathExtent];
3294
3295 Cursor
3296 cursor;
3297
3298 int
3299 entry,
3300 id,
3301 x,
3302 x_offset,
3303 y,
3304 y_offset;
3305
3306 Quantum
3307 *q;
3308
3309 size_t
3310 state;
3311
3312 ssize_t
3313 i;
3314
3315 unsigned int
3316 height,
3317 width;
3318
3319 XColor
3320 color;
3321
3322 XEvent
3323 event;
3324
3325 /*
3326 Map Command widget.
3327 */
3328 (void) CloneString(&windows->command.name,"Color Edit");
3329 windows->command.data=4;
3330 (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3331 (void) XMapRaised(display,windows->command.id);
3332 XClientMessage(display,windows->image.id,windows->im_protocols,
3333 windows->im_update_widget,CurrentTime);
3334 /*
3335 Make cursor.
3336 */
3337 cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3338 resource_info->background_color,resource_info->foreground_color);
3339 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3340 /*
3341 Track pointer until button 1 is pressed.
3342 */
3343 XQueryPosition(display,windows->image.id,&x,&y);
3344 (void) XSelectInput(display,windows->image.id,
3345 windows->image.attributes.event_mask | PointerMotionMask);
3346 state=DefaultState;
3347 do
3348 {
3349 if (windows->info.mapped != MagickFalse)
3350 {
3351 /*
3352 Display pointer position.
3353 */
3354 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
3355 x+windows->image.x,y+windows->image.y);
3356 XInfoWidget(display,windows,text);
3357 }
3358 /*
3359 Wait for next event.
3360 */
3361 XScreenEvent(display,windows,&event,exception);
3362 if (event.xany.window == windows->command.id)
3363 {
3364 /*
3365 Select a command from the Command widget.
3366 */
3367 id=XCommandWidget(display,windows,ColorEditMenu,&event);
3368 if (id < 0)
3369 {
3370 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3371 continue;
3372 }
3373 switch (ColorEditCommands[id])
3374 {
3375 case ColorEditMethodCommand:
3376 {
3377 char
3378 **methods;
3379
3380 /*
3381 Select a method from the pop-up menu.
3382 */
3383 methods=(char **) GetCommandOptions(MagickMethodOptions);
3384 if (methods == (char **) NULL)
3385 break;
3386 entry=XMenuWidget(display,windows,ColorEditMenu[id],
3387 (const char **) methods,command);
3388 if (entry >= 0)
3389 method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3390 MagickFalse,methods[entry]);
3391 methods=DestroyStringList(methods);
3392 break;
3393 }
3394 case ColorEditColorCommand:
3395 {
3396 const char
3397 *ColorMenu[MaxNumberPens];
3398
3399 int
3400 pen_number;
3401
3402 /*
3403 Initialize menu selections.
3404 */
3405 for (i=0; i < (int) (MaxNumberPens-2); i++)
3406 ColorMenu[i]=resource_info->pen_colors[i];
3407 ColorMenu[MaxNumberPens-2]="Browser...";
3408 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3409 /*
3410 Select a pen color from the pop-up menu.
3411 */
3412 pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3413 (const char **) ColorMenu,command);
3414 if (pen_number < 0)
3415 break;
3416 if (pen_number == (MaxNumberPens-2))
3417 {
3418 static char
3419 color_name[MagickPathExtent] = "gray";
3420
3421 /*
3422 Select a pen color from a dialog.
3423 */
3424 resource_info->pen_colors[pen_number]=color_name;
3425 XColorBrowserWidget(display,windows,"Select",color_name);
3426 if (*color_name == '\0')
3427 break;
3428 }
3429 /*
3430 Set pen color.
3431 */
3432 (void) XParseColor(display,windows->map_info->colormap,
3433 resource_info->pen_colors[pen_number],&color);
3434 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3435 (unsigned int) MaxColors,&color);
3436 windows->pixel_info->pen_colors[pen_number]=color;
3437 pen_id=(unsigned int) pen_number;
3438 break;
3439 }
3440 case ColorEditBorderCommand:
3441 {
3442 const char
3443 *ColorMenu[MaxNumberPens];
3444
3445 int
3446 pen_number;
3447
3448 /*
3449 Initialize menu selections.
3450 */
3451 for (i=0; i < (int) (MaxNumberPens-2); i++)
3452 ColorMenu[i]=resource_info->pen_colors[i];
3453 ColorMenu[MaxNumberPens-2]="Browser...";
3454 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3455 /*
3456 Select a pen color from the pop-up menu.
3457 */
3458 pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3459 (const char **) ColorMenu,command);
3460 if (pen_number < 0)
3461 break;
3462 if (pen_number == (MaxNumberPens-2))
3463 {
3464 static char
3465 color_name[MagickPathExtent] = "gray";
3466
3467 /*
3468 Select a pen color from a dialog.
3469 */
3470 resource_info->pen_colors[pen_number]=color_name;
3471 XColorBrowserWidget(display,windows,"Select",color_name);
3472 if (*color_name == '\0')
3473 break;
3474 }
3475 /*
3476 Set border color.
3477 */
3478 (void) XParseColor(display,windows->map_info->colormap,
3479 resource_info->pen_colors[pen_number],&border_color);
3480 break;
3481 }
3482 case ColorEditFuzzCommand:
3483 {
3484 const char
3485 *const FuzzMenu[] =
3486 {
3487 "0%",
3488 "2%",
3489 "5%",
3490 "10%",
3491 "15%",
3492 "Dialog...",
3493 (char *) NULL,
3494 };
3495
3496 static char
3497 fuzz[MagickPathExtent];
3498
3499 /*
3500 Select a command from the pop-up menu.
3501 */
3502 entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3503 command);
3504 if (entry < 0)
3505 break;
3506 if (entry != 5)
3507 {
3508 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3509 QuantumRange+1.0);
3510 break;
3511 }
3512 (void) (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
3513 (void) XDialogWidget(display,windows,"Ok",
3514 "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3515 if (*fuzz == '\0')
3516 break;
3517 (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
3518 (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3519 1.0);
3520 break;
3521 }
3522 case ColorEditUndoCommand:
3523 {
3524 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3525 image,exception);
3526 break;
3527 }
3528 case ColorEditHelpCommand:
3529 default:
3530 {
3531 XTextViewHelp(display,resource_info,windows,MagickFalse,
3532 "Help Viewer - Image Annotation",ImageColorEditHelp);
3533 break;
3534 }
3535 case ColorEditDismissCommand:
3536 {
3537 /*
3538 Prematurely exit.
3539 */
3540 state|=EscapeState;
3541 state|=ExitState;
3542 break;
3543 }
3544 }
3545 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3546 continue;
3547 }
3548 switch (event.type)
3549 {
3550 case ButtonPress:
3551 {
3552 if (event.xbutton.button != Button1)
3553 break;
3554 if ((event.xbutton.window != windows->image.id) &&
3555 (event.xbutton.window != windows->magnify.id))
3556 break;
3557 /*
3558 exit loop.
3559 */
3560 x=event.xbutton.x;
3561 y=event.xbutton.y;
3562 (void) XMagickCommand(display,resource_info,windows,
3563 SaveToUndoBufferCommand,image,exception);
3564 state|=UpdateConfigurationState;
3565 break;
3566 }
3567 case ButtonRelease:
3568 {
3569 if (event.xbutton.button != Button1)
3570 break;
3571 if ((event.xbutton.window != windows->image.id) &&
3572 (event.xbutton.window != windows->magnify.id))
3573 break;
3574 /*
3575 Update colormap information.
3576 */
3577 x=event.xbutton.x;
3578 y=event.xbutton.y;
3579 XConfigureImageColormap(display,resource_info,windows,*image,exception);
3580 (void) XConfigureImage(display,resource_info,windows,*image,exception);
3581 XInfoWidget(display,windows,text);
3582 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3583 state&=(size_t) (~UpdateConfigurationState);
3584 break;
3585 }
3586 case Expose:
3587 break;
3588 case KeyPress:
3589 {
3590 KeySym
3591 key_symbol;
3592
3593 if (event.xkey.window == windows->magnify.id)
3594 {
3595 Window
3596 window;
3597
3598 window=windows->magnify.id;
3599 while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3600 }
3601 if (event.xkey.window != windows->image.id)
3602 break;
3603 /*
3604 Respond to a user key press.
3605 */
3606 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3607 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3608 switch ((int) key_symbol)
3609 {
3610 case XK_Escape:
3611 case XK_F20:
3612 {
3613 /*
3614 Prematurely exit.
3615 */
3616 state|=ExitState;
3617 break;
3618 }
3619 case XK_F1:
3620 case XK_Help:
3621 {
3622 XTextViewHelp(display,resource_info,windows,MagickFalse,
3623 "Help Viewer - Image Annotation",ImageColorEditHelp);
3624 break;
3625 }
3626 default:
3627 {
3628 (void) XBell(display,0);
3629 break;
3630 }
3631 }
3632 break;
3633 }
3634 case MotionNotify:
3635 {
3636 /*
3637 Map and unmap Info widget as cursor crosses its boundaries.
3638 */
3639 x=event.xmotion.x;
3640 y=event.xmotion.y;
3641 if (windows->info.mapped != MagickFalse)
3642 {
3643 if ((x < (windows->info.x+(int) windows->info.width)) &&
3644 (y < (windows->info.y+(int) windows->info.height)))
3645 (void) XWithdrawWindow(display,windows->info.id,
3646 windows->info.screen);
3647 }
3648 else
3649 if ((x > (windows->info.x+(int) windows->info.width)) ||
3650 (y > (windows->info.y+(int) windows->info.height)))
3651 (void) XMapWindow(display,windows->info.id);
3652 break;
3653 }
3654 default:
3655 break;
3656 }
3657 if (event.xany.window == windows->magnify.id)
3658 {
3659 x=windows->magnify.x-windows->image.x;
3660 y=windows->magnify.y-windows->image.y;
3661 }
3662 x_offset=x;
3663 y_offset=y;
3664 if ((state & UpdateConfigurationState) != 0)
3665 {
3666 CacheView
3667 *image_view;
3668
3669 int
3670 x,
3671 y;
3672
3673 /*
3674 Pixel edit is relative to image configuration.
3675 */
3676 (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3677 MagickTrue);
3678 color=windows->pixel_info->pen_colors[pen_id];
3679 XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3680 width=(unsigned int) (*image)->columns;
3681 height=(unsigned int) (*image)->rows;
3682 x=0;
3683 y=0;
3684 if (windows->image.crop_geometry != (char *) NULL)
3685 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3686 &width,&height);
3687 x_offset=((int) width*(windows->image.x+x_offset)/(int)
3688 windows->image.ximage->width+x);
3689 y_offset=((int) height*(windows->image.y+y_offset)/(int)
3690 windows->image.ximage->height+y);
3691 if ((x_offset < 0) || (y_offset < 0))
3692 continue;
3693 if ((x_offset >= (int) (*image)->columns) ||
3694 (y_offset >= (int) (*image)->rows))
3695 continue;
3696 image_view=AcquireAuthenticCacheView(*image,exception);
3697 switch (method)
3698 {
3699 case PointMethod:
3700 default:
3701 {
3702 /*
3703 Update color information using point algorithm.
3704 */
3705 if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3706 return(MagickFalse);
3707 q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3708 (ssize_t) y_offset,1,1,exception);
3709 if (q == (Quantum *) NULL)
3710 break;
3711 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3712 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3713 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3714 (void) SyncCacheViewAuthenticPixels(image_view,exception);
3715 break;
3716 }
3717 case ReplaceMethod:
3718 {
3719 PixelInfo
3720 pixel,
3721 target;
3722
3723 /*
3724 Update color information using replace algorithm.
3725 */
3726 (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3727 x_offset,(ssize_t) y_offset,&target,exception);
3728 if ((*image)->storage_class == DirectClass)
3729 {
3730 for (y=0; y < (int) (*image)->rows; y++)
3731 {
3732 q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3733 (*image)->columns,1,exception);
3734 if (q == (Quantum *) NULL)
3735 break;
3736 for (x=0; x < (int) (*image)->columns; x++)
3737 {
3738 GetPixelInfoPixel(*image,q,&pixel);
3739 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3740 {
3741 SetPixelRed(*image,ScaleShortToQuantum(
3742 color.red),q);
3743 SetPixelGreen(*image,ScaleShortToQuantum(
3744 color.green),q);
3745 SetPixelBlue(*image,ScaleShortToQuantum(
3746 color.blue),q);
3747 }
3748 q+=GetPixelChannels(*image);
3749 }
3750 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3751 break;
3752 }
3753 }
3754 else
3755 {
3756 for (i=0; i < (ssize_t) (*image)->colors; i++)
3757 if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3758 {
3759 (*image)->colormap[i].red=(double) ScaleShortToQuantum(
3760 color.red);
3761 (*image)->colormap[i].green=(double) ScaleShortToQuantum(
3762 color.green);
3763 (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
3764 color.blue);
3765 }
3766 (void) SyncImage(*image,exception);
3767 }
3768 break;
3769 }
3770 case FloodfillMethod:
3771 case FillToBorderMethod:
3772 {
3773 DrawInfo
3774 *draw_info;
3775
3776 PixelInfo
3777 target;
3778
3779 /*
3780 Update color information using floodfill algorithm.
3781 */
3782 (void) GetOneVirtualPixelInfo(*image,
3783 GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3784 y_offset,&target,exception);
3785 if (method == FillToBorderMethod)
3786 {
3787 target.red=(double)
3788 ScaleShortToQuantum(border_color.red);
3789 target.green=(double)
3790 ScaleShortToQuantum(border_color.green);
3791 target.blue=(double)
3792 ScaleShortToQuantum(border_color.blue);
3793 }
3794 draw_info=CloneDrawInfo(resource_info->image_info,
3795 (DrawInfo *) NULL);
3796 (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3797 AllCompliance,&draw_info->fill,exception);
3798 (void) FloodfillPaintImage(*image,draw_info,&target,
3799 (ssize_t)x_offset,(ssize_t)y_offset,
3800 method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
3801 draw_info=DestroyDrawInfo(draw_info);
3802 break;
3803 }
3804 case ResetMethod:
3805 {
3806 /*
3807 Update color information using reset algorithm.
3808 */
3809 if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3810 return(MagickFalse);
3811 for (y=0; y < (int) (*image)->rows; y++)
3812 {
3813 q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3814 (*image)->columns,1,exception);
3815 if (q == (Quantum *) NULL)
3816 break;
3817 for (x=0; x < (int) (*image)->columns; x++)
3818 {
3819 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3820 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3821 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3822 q+=GetPixelChannels(*image);
3823 }
3824 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3825 break;
3826 }
3827 break;
3828 }
3829 }
3830 image_view=DestroyCacheView(image_view);
3831 state&=(~0x0080U);
3832 }
3833 } while ((state & ExitState) == 0);
3834 (void) XSelectInput(display,windows->image.id,
3835 windows->image.attributes.event_mask);
3836 XSetCursorState(display,windows,MagickFalse);
3837 (void) XFreeCursor(display,cursor);
3838 return(MagickTrue);
3839}
3840
3841/*
3842%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3843% %
3844% %
3845% %
3846+ X C o m p o s i t e I m a g e %
3847% %
3848% %
3849% %
3850%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3851%
3852% XCompositeImage() requests an image name from the user, reads the image and
3853% composites it with the X window image at a location the user chooses with
3854% the pointer.
3855%
3856% The format of the XCompositeImage method is:
3857%
3858% MagickBooleanType XCompositeImage(Display *display,
3859% XResourceInfo *resource_info,XWindows *windows,Image *image,
3860% ExceptionInfo *exception)
3861%
3862% A description of each parameter follows:
3863%
3864% o display: Specifies a connection to an X server; returned from
3865% XOpenDisplay.
3866%
3867% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3868%
3869% o windows: Specifies a pointer to a XWindows structure.
3870%
3871% o image: the image; returned from ReadImage.
3872%
3873% o exception: return any errors or warnings in this structure.
3874%
3875*/
3876static MagickBooleanType XCompositeImage(Display *display,
3877 XResourceInfo *resource_info,XWindows *windows,Image *image,
3878 ExceptionInfo *exception)
3879{
3880 const char
3881 *const CompositeMenu[] =
3882 {
3883 "Operators",
3884 "Dissolve",
3885 "Displace",
3886 "Help",
3887 "Dismiss",
3888 (char *) NULL
3889 };
3890
3891 static char
3892 displacement_geometry[MagickPathExtent] = "30x30",
3893 filename[MagickPathExtent] = "\0";
3894
3895 static CompositeOperator
3896 compose = CopyCompositeOp;
3897
3898 static const ModeType
3899 CompositeCommands[] =
3900 {
3901 CompositeOperatorsCommand,
3902 CompositeDissolveCommand,
3903 CompositeDisplaceCommand,
3904 CompositeHelpCommand,
3905 CompositeDismissCommand
3906 };
3907
3908 char
3909 text[MagickPathExtent];
3910
3911 Cursor
3912 cursor;
3913
3914 Image
3915 *composite_image;
3916
3917 int
3918 entry,
3919 id,
3920 x,
3921 y;
3922
3923 double
3924 blend,
3925 scale_factor;
3926
3928 highlight_info,
3929 composite_info;
3930
3931 unsigned int
3932 height,
3933 width;
3934
3935 size_t
3936 state;
3937
3938 XEvent
3939 event;
3940
3941 /*
3942 Request image file name from user.
3943 */
3944 XFileBrowserWidget(display,windows,"Composite",filename);
3945 if (*filename == '\0')
3946 return(MagickTrue);
3947 /*
3948 Read image.
3949 */
3950 XSetCursorState(display,windows,MagickTrue);
3951 XCheckRefreshWindows(display,windows);
3952 (void) CopyMagickString(resource_info->image_info->filename,filename,
3953 MagickPathExtent);
3954 composite_image=ReadImage(resource_info->image_info,exception);
3955 CatchException(exception);
3956 XSetCursorState(display,windows,MagickFalse);
3957 if (composite_image == (Image *) NULL)
3958 return(MagickFalse);
3959 /*
3960 Map Command widget.
3961 */
3962 (void) CloneString(&windows->command.name,"Composite");
3963 windows->command.data=1;
3964 (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3965 (void) XMapRaised(display,windows->command.id);
3966 XClientMessage(display,windows->image.id,windows->im_protocols,
3967 windows->im_update_widget,CurrentTime);
3968 /*
3969 Track pointer until button 1 is pressed.
3970 */
3971 XQueryPosition(display,windows->image.id,&x,&y);
3972 (void) XSelectInput(display,windows->image.id,
3973 windows->image.attributes.event_mask | PointerMotionMask);
3974 composite_info.x=(ssize_t) windows->image.x+x;
3975 composite_info.y=(ssize_t) windows->image.y+y;
3976 composite_info.width=0;
3977 composite_info.height=0;
3978 cursor=XCreateFontCursor(display,XC_ul_angle);
3979 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3980 blend=0.0;
3981 state=DefaultState;
3982 do
3983 {
3984 if (windows->info.mapped != MagickFalse)
3985 {
3986 /*
3987 Display pointer position.
3988 */
3989 (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
3990 (long) composite_info.x,(long) composite_info.y);
3991 XInfoWidget(display,windows,text);
3992 }
3993 highlight_info=composite_info;
3994 highlight_info.x=composite_info.x-windows->image.x;
3995 highlight_info.y=composite_info.y-windows->image.y;
3996 XHighlightRectangle(display,windows->image.id,
3997 windows->image.highlight_context,&highlight_info);
3998 /*
3999 Wait for next event.
4000 */
4001 XScreenEvent(display,windows,&event,exception);
4002 XHighlightRectangle(display,windows->image.id,
4003 windows->image.highlight_context,&highlight_info);
4004 if (event.xany.window == windows->command.id)
4005 {
4006 /*
4007 Select a command from the Command widget.
4008 */
4009 id=XCommandWidget(display,windows,CompositeMenu,&event);
4010 if (id < 0)
4011 continue;
4012 switch (CompositeCommands[id])
4013 {
4014 case CompositeOperatorsCommand:
4015 {
4016 char
4017 command[MagickPathExtent],
4018 **operators;
4019
4020 /*
4021 Select a command from the pop-up menu.
4022 */
4023 operators=GetCommandOptions(MagickComposeOptions);
4024 if (operators == (char **) NULL)
4025 break;
4026 entry=XMenuWidget(display,windows,CompositeMenu[id],
4027 (const char **) operators,command);
4028 if (entry >= 0)
4029 compose=(CompositeOperator) ParseCommandOption(
4030 MagickComposeOptions,MagickFalse,operators[entry]);
4031 operators=DestroyStringList(operators);
4032 break;
4033 }
4034 case CompositeDissolveCommand:
4035 {
4036 static char
4037 factor[MagickPathExtent] = "20.0";
4038
4039 /*
4040 Dissolve the two images a given percent.
4041 */
4042 (void) XSetFunction(display,windows->image.highlight_context,
4043 GXcopy);
4044 (void) XDialogWidget(display,windows,"Dissolve",
4045 "Enter the blend factor (0.0 - 99.9%):",factor);
4046 (void) XSetFunction(display,windows->image.highlight_context,
4047 GXinvert);
4048 if (*factor == '\0')
4049 break;
4050 blend=StringToDouble(factor,(char **) NULL);
4051 compose=DissolveCompositeOp;
4052 break;
4053 }
4054 case CompositeDisplaceCommand:
4055 {
4056 /*
4057 Get horizontal and vertical scale displacement geometry.
4058 */
4059 (void) XSetFunction(display,windows->image.highlight_context,
4060 GXcopy);
4061 (void) XDialogWidget(display,windows,"Displace",
4062 "Enter the horizontal and vertical scale:",displacement_geometry);
4063 (void) XSetFunction(display,windows->image.highlight_context,
4064 GXinvert);
4065 if (*displacement_geometry == '\0')
4066 break;
4067 compose=DisplaceCompositeOp;
4068 break;
4069 }
4070 case CompositeHelpCommand:
4071 {
4072 (void) XSetFunction(display,windows->image.highlight_context,
4073 GXcopy);
4074 XTextViewHelp(display,resource_info,windows,MagickFalse,
4075 "Help Viewer - Image Composite",ImageCompositeHelp);
4076 (void) XSetFunction(display,windows->image.highlight_context,
4077 GXinvert);
4078 break;
4079 }
4080 case CompositeDismissCommand:
4081 {
4082 /*
4083 Prematurely exit.
4084 */
4085 state|=EscapeState;
4086 state|=ExitState;
4087 break;
4088 }
4089 default:
4090 break;
4091 }
4092 continue;
4093 }
4094 switch (event.type)
4095 {
4096 case ButtonPress:
4097 {
4098 if (resource_info->debug != MagickFalse)
4099 (void) LogMagickEvent(X11Event,GetMagickModule(),
4100 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4101 event.xbutton.button,event.xbutton.x,event.xbutton.y);
4102 if (event.xbutton.button != Button1)
4103 break;
4104 if (event.xbutton.window != windows->image.id)
4105 break;
4106 /*
4107 Change cursor.
4108 */
4109 composite_info.width=composite_image->columns;
4110 composite_info.height=composite_image->rows;
4111 (void) XCheckDefineCursor(display,windows->image.id,cursor);
4112 composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4113 composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4114 break;
4115 }
4116 case ButtonRelease:
4117 {
4118 if (resource_info->debug != MagickFalse)
4119 (void) LogMagickEvent(X11Event,GetMagickModule(),
4120 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4121 event.xbutton.button,event.xbutton.x,event.xbutton.y);
4122 if (event.xbutton.button != Button1)
4123 break;
4124 if (event.xbutton.window != windows->image.id)
4125 break;
4126 if ((composite_info.width != 0) && (composite_info.height != 0))
4127 {
4128 /*
4129 User has selected the location of the composite image.
4130 */
4131 composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4132 composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4133 state|=ExitState;
4134 }
4135 break;
4136 }
4137 case Expose:
4138 break;
4139 case KeyPress:
4140 {
4141 char
4142 command[MagickPathExtent];
4143
4144 KeySym
4145 key_symbol;
4146
4147 int
4148 length;
4149
4150 if (event.xkey.window != windows->image.id)
4151 break;
4152 /*
4153 Respond to a user key press.
4154 */
4155 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4156 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4157 *(command+length)='\0';
4158 if (resource_info->debug != MagickFalse)
4159 (void) LogMagickEvent(X11Event,GetMagickModule(),
4160 "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4161 switch ((int) key_symbol)
4162 {
4163 case XK_Escape:
4164 case XK_F20:
4165 {
4166 /*
4167 Prematurely exit.
4168 */
4169 composite_image=DestroyImage(composite_image);
4170 state|=EscapeState;
4171 state|=ExitState;
4172 break;
4173 }
4174 case XK_F1:
4175 case XK_Help:
4176 {
4177 (void) XSetFunction(display,windows->image.highlight_context,
4178 GXcopy);
4179 XTextViewHelp(display,resource_info,windows,MagickFalse,
4180 "Help Viewer - Image Composite",ImageCompositeHelp);
4181 (void) XSetFunction(display,windows->image.highlight_context,
4182 GXinvert);
4183 break;
4184 }
4185 default:
4186 {
4187 (void) XBell(display,0);
4188 break;
4189 }
4190 }
4191 break;
4192 }
4193 case MotionNotify:
4194 {
4195 /*
4196 Map and unmap Info widget as text cursor crosses its boundaries.
4197 */
4198 x=event.xmotion.x;
4199 y=event.xmotion.y;
4200 if (windows->info.mapped != MagickFalse)
4201 {
4202 if ((x < (windows->info.x+(int) windows->info.width)) &&
4203 (y < (windows->info.y+(int) windows->info.height)))
4204 (void) XWithdrawWindow(display,windows->info.id,
4205 windows->info.screen);
4206 }
4207 else
4208 if ((x > (windows->info.x+(int) windows->info.width)) ||
4209 (y > (windows->info.y+(int) windows->info.height)))
4210 (void) XMapWindow(display,windows->info.id);
4211 composite_info.x=(ssize_t) windows->image.x+x;
4212 composite_info.y=(ssize_t) windows->image.y+y;
4213 break;
4214 }
4215 default:
4216 {
4217 if (resource_info->debug != MagickFalse)
4218 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4219 event.type);
4220 break;
4221 }
4222 }
4223 } while ((state & ExitState) == 0);
4224 (void) XSelectInput(display,windows->image.id,
4225 windows->image.attributes.event_mask);
4226 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4227 XSetCursorState(display,windows,MagickFalse);
4228 (void) XFreeCursor(display,cursor);
4229 if ((state & EscapeState) != 0)
4230 return(MagickTrue);
4231 /*
4232 Image compositing is relative to image configuration.
4233 */
4234 XSetCursorState(display,windows,MagickTrue);
4235 XCheckRefreshWindows(display,windows);
4236 width=(unsigned int) image->columns;
4237 height=(unsigned int) image->rows;
4238 x=0;
4239 y=0;
4240 if (windows->image.crop_geometry != (char *) NULL)
4241 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4242 scale_factor=(double) width/windows->image.ximage->width;
4243 composite_info.x+=x;
4244 composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4245 composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4246 scale_factor=(double) height/windows->image.ximage->height;
4247 composite_info.y+=y;
4248 composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4249 composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4250 if ((composite_info.width != composite_image->columns) ||
4251 (composite_info.height != composite_image->rows))
4252 {
4253 Image
4254 *resize_image;
4255
4256 /*
4257 Scale composite image.
4258 */
4259 resize_image=ResizeImage(composite_image,composite_info.width,
4260 composite_info.height,composite_image->filter,exception);
4261 composite_image=DestroyImage(composite_image);
4262 if (resize_image == (Image *) NULL)
4263 {
4264 XSetCursorState(display,windows,MagickFalse);
4265 return(MagickFalse);
4266 }
4267 composite_image=resize_image;
4268 }
4269 if (compose == DisplaceCompositeOp)
4270 (void) SetImageArtifact(composite_image,"compose:args",
4271 displacement_geometry);
4272 if (blend != 0.0)
4273 {
4274 CacheView
4275 *image_view;
4276
4277 int
4278 y;
4279
4280 Quantum
4281 opacity;
4282
4283 int
4284 x;
4285
4286 Quantum
4287 *q;
4288
4289 /*
4290 Create mattes for blending.
4291 */
4292 (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4293 opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4294 ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4295 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4296 return(MagickFalse);
4297 image->alpha_trait=BlendPixelTrait;
4298 image_view=AcquireAuthenticCacheView(image,exception);
4299 for (y=0; y < (int) image->rows; y++)
4300 {
4301 q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4302 exception);
4303 if (q == (Quantum *) NULL)
4304 break;
4305 for (x=0; x < (int) image->columns; x++)
4306 {
4307 SetPixelAlpha(image,opacity,q);
4308 q+=GetPixelChannels(image);
4309 }
4310 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4311 break;
4312 }
4313 image_view=DestroyCacheView(image_view);
4314 }
4315 /*
4316 Composite image with X Image window.
4317 */
4318 (void) CompositeImage(image,composite_image,compose,MagickTrue,
4319 composite_info.x,composite_info.y,exception);
4320 composite_image=DestroyImage(composite_image);
4321 XSetCursorState(display,windows,MagickFalse);
4322 /*
4323 Update image configuration.
4324 */
4325 XConfigureImageColormap(display,resource_info,windows,image,exception);
4326 (void) XConfigureImage(display,resource_info,windows,image,exception);
4327 return(MagickTrue);
4328}
4329
4330/*
4331%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4332% %
4333% %
4334% %
4335+ X C o n f i g u r e I m a g e %
4336% %
4337% %
4338% %
4339%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4340%
4341% XConfigureImage() creates a new X image. It also notifies the window
4342% manager of the new image size and configures the transient widows.
4343%
4344% The format of the XConfigureImage method is:
4345%
4346% MagickBooleanType XConfigureImage(Display *display,
4347% XResourceInfo *resource_info,XWindows *windows,Image *image,
4348% ExceptionInfo *exception)
4349%
4350% A description of each parameter follows:
4351%
4352% o display: Specifies a connection to an X server; returned from
4353% XOpenDisplay.
4354%
4355% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4356%
4357% o windows: Specifies a pointer to a XWindows structure.
4358%
4359% o image: the image.
4360%
4361% o exception: return any errors or warnings in this structure.
4362%
4363% o exception: return any errors or warnings in this structure.
4364%
4365*/
4366static MagickBooleanType XConfigureImage(Display *display,
4367 XResourceInfo *resource_info,XWindows *windows,Image *image,
4368 ExceptionInfo *exception)
4369{
4370 char
4371 geometry[MagickPathExtent];
4372
4373 MagickStatusType
4374 status;
4375
4376 size_t
4377 mask,
4378 height,
4379 width;
4380
4381 ssize_t
4382 x,
4383 y;
4384
4385 XSizeHints
4386 *size_hints;
4387
4388 XWindowChanges
4389 window_changes;
4390
4391 /*
4392 Dismiss if window dimensions are zero.
4393 */
4394 width=(unsigned int) windows->image.window_changes.width;
4395 height=(unsigned int) windows->image.window_changes.height;
4396 if (resource_info->debug != MagickFalse)
4397 (void) LogMagickEvent(X11Event,GetMagickModule(),
4398 "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4399 windows->image.ximage->height,(double) width,(double) height);
4400 if ((width*height) == 0)
4401 return(MagickTrue);
4402 x=0;
4403 y=0;
4404 /*
4405 Resize image to fit Image window dimensions.
4406 */
4407 XSetCursorState(display,windows,MagickTrue);
4408 (void) XFlush(display);
4409 if (((int) width != windows->image.ximage->width) ||
4410 ((int) height != windows->image.ximage->height))
4411 image->taint=MagickTrue;
4412 windows->magnify.x=(int)
4413 width*windows->magnify.x/windows->image.ximage->width;
4414 windows->magnify.y=(int)
4415 height*windows->magnify.y/windows->image.ximage->height;
4416 windows->image.x=((int) width*windows->image.x/windows->image.ximage->width);
4417 windows->image.y=((int) height*windows->image.y/
4418 windows->image.ximage->height);
4419 status=XMakeImage(display,resource_info,&windows->image,image,
4420 (unsigned int) width,(unsigned int) height,exception);
4421 if (status == MagickFalse)
4422 XNoticeWidget(display,windows,"Unable to configure X image:",
4423 windows->image.name);
4424 /*
4425 Notify window manager of the new configuration.
4426 */
4427 if (resource_info->image_geometry != (char *) NULL)
4428 (void) FormatLocaleString(geometry,MagickPathExtent,"%s>!",
4429 resource_info->image_geometry);
4430 else
4431 (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
4432 XDisplayWidth(display,windows->image.screen),
4433 XDisplayHeight(display,windows->image.screen));
4434 (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4435 window_changes.width=(int) width;
4436 if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4437 window_changes.width=XDisplayWidth(display,windows->image.screen);
4438 window_changes.height=(int) height;
4439 if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4440 window_changes.height=XDisplayHeight(display,windows->image.screen);
4441 mask=(size_t) (CWWidth | CWHeight);
4442 if (resource_info->backdrop)
4443 {
4444 mask|=CWX | CWY;
4445 window_changes.x=((XDisplayWidth(display,windows->image.screen)/2)-
4446 ((int) width/2));
4447 window_changes.y=((XDisplayHeight(display,windows->image.screen)/2)-
4448 ((int) height/2));
4449 }
4450 (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4451 (unsigned int) mask,&window_changes);
4452 (void) XClearWindow(display,windows->image.id);
4453 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4454 /*
4455 Update Magnify window configuration.
4456 */
4457 if (windows->magnify.mapped != MagickFalse)
4458 XMakeMagnifyImage(display,windows,exception);
4459 windows->pan.crop_geometry=windows->image.crop_geometry;
4460 XBestIconSize(display,&windows->pan,image);
4461 while (((windows->pan.width << 1) < MaxIconSize) &&
4462 ((windows->pan.height << 1) < MaxIconSize))
4463 {
4464 windows->pan.width<<=1;
4465 windows->pan.height<<=1;
4466 }
4467 if (windows->pan.geometry != (char *) NULL)
4468 (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4469 &windows->pan.width,&windows->pan.height);
4470 window_changes.width=(int) windows->pan.width;
4471 window_changes.height=(int) windows->pan.height;
4472 size_hints=XAllocSizeHints();
4473 if (size_hints != (XSizeHints *) NULL)
4474 {
4475 /*
4476 Set new size hints.
4477 */
4478 size_hints->flags=PSize | PMinSize | PMaxSize;
4479 size_hints->width=window_changes.width;
4480 size_hints->height=window_changes.height;
4481 size_hints->min_width=size_hints->width;
4482 size_hints->min_height=size_hints->height;
4483 size_hints->max_width=size_hints->width;
4484 size_hints->max_height=size_hints->height;
4485 (void) XSetNormalHints(display,windows->pan.id,size_hints);
4486 (void) XFree((void *) size_hints);
4487 }
4488 (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4489 (unsigned int) (CWWidth | CWHeight),&window_changes);
4490 /*
4491 Update icon window configuration.
4492 */
4493 windows->icon.crop_geometry=windows->image.crop_geometry;
4494 XBestIconSize(display,&windows->icon,image);
4495 window_changes.width=(int) windows->icon.width;
4496 window_changes.height=(int) windows->icon.height;
4497 (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4498 (unsigned int) (CWWidth | CWHeight),&window_changes);
4499 XSetCursorState(display,windows,MagickFalse);
4500 return(status != 0 ? MagickTrue : MagickFalse);
4501}
4502
4503/*
4504%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4505% %
4506% %
4507% %
4508+ X C r o p I m a g e %
4509% %
4510% %
4511% %
4512%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4513%
4514% XCropImage() allows the user to select a region of the image and crop, copy,
4515% or cut it. For copy or cut, the image can subsequently be composited onto
4516% the image with XPasteImage.
4517%
4518% The format of the XCropImage method is:
4519%
4520% MagickBooleanType XCropImage(Display *display,
4521% XResourceInfo *resource_info,XWindows *windows,Image *image,
4522% const ClipboardMode mode,ExceptionInfo *exception)
4523%
4524% A description of each parameter follows:
4525%
4526% o display: Specifies a connection to an X server; returned from
4527% XOpenDisplay.
4528%
4529% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4530%
4531% o windows: Specifies a pointer to a XWindows structure.
4532%
4533% o image: the image; returned from ReadImage.
4534%
4535% o mode: This unsigned value specified whether the image should be
4536% cropped, copied, or cut.
4537%
4538% o exception: return any errors or warnings in this structure.
4539%
4540*/
4541static MagickBooleanType XCropImage(Display *display,
4542 XResourceInfo *resource_info,XWindows *windows,Image *image,
4543 const ClipboardMode mode,ExceptionInfo *exception)
4544{
4545 const char
4546 *const CropModeMenu[] =
4547 {
4548 "Help",
4549 "Dismiss",
4550 (char *) NULL
4551 },
4552 *RectifyModeMenu[] =
4553 {
4554 "Crop",
4555 "Help",
4556 "Dismiss",
4557 (char *) NULL
4558 };
4559
4560 static const ModeType
4561 CropCommands[] =
4562 {
4563 CropHelpCommand,
4564 CropDismissCommand
4565 },
4566 RectifyCommands[] =
4567 {
4568 RectifyCopyCommand,
4569 RectifyHelpCommand,
4570 RectifyDismissCommand
4571 };
4572
4573 CacheView
4574 *image_view;
4575
4576 char
4577 command[MagickPathExtent],
4578 text[MagickPathExtent];
4579
4580 Cursor
4581 cursor;
4582
4583 int
4584 id,
4585 x,
4586 y;
4587
4588 KeySym
4589 key_symbol;
4590
4591 Image
4592 *crop_image;
4593
4594 double
4595 scale_factor;
4596
4598 crop_info,
4599 highlight_info;
4600
4601 Quantum
4602 *q;
4603
4604 unsigned int
4605 height,
4606 width;
4607
4608 size_t
4609 state;
4610
4611 XEvent
4612 event;
4613
4614 /*
4615 Map Command widget.
4616 */
4617 switch (mode)
4618 {
4619 case CopyMode:
4620 {
4621 (void) CloneString(&windows->command.name,"Copy");
4622 break;
4623 }
4624 case CropMode:
4625 {
4626 (void) CloneString(&windows->command.name,"Crop");
4627 break;
4628 }
4629 case CutMode:
4630 {
4631 (void) CloneString(&windows->command.name,"Cut");
4632 break;
4633 }
4634 }
4635 RectifyModeMenu[0]=windows->command.name;
4636 windows->command.data=0;
4637 (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4638 (void) XMapRaised(display,windows->command.id);
4639 XClientMessage(display,windows->image.id,windows->im_protocols,
4640 windows->im_update_widget,CurrentTime);
4641 /*
4642 Track pointer until button 1 is pressed.
4643 */
4644 XQueryPosition(display,windows->image.id,&x,&y);
4645 (void) XSelectInput(display,windows->image.id,
4646 windows->image.attributes.event_mask | PointerMotionMask);
4647 crop_info.x=(ssize_t) windows->image.x+x;
4648 crop_info.y=(ssize_t) windows->image.y+y;
4649 crop_info.width=0;
4650 crop_info.height=0;
4651 cursor=XCreateFontCursor(display,XC_fleur);
4652 state=DefaultState;
4653 do
4654 {
4655 if (windows->info.mapped != MagickFalse)
4656 {
4657 /*
4658 Display pointer position.
4659 */
4660 (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
4661 (long) crop_info.x,(long) crop_info.y);
4662 XInfoWidget(display,windows,text);
4663 }
4664 /*
4665 Wait for next event.
4666 */
4667 XScreenEvent(display,windows,&event,exception);
4668 if (event.xany.window == windows->command.id)
4669 {
4670 /*
4671 Select a command from the Command widget.
4672 */
4673 id=XCommandWidget(display,windows,CropModeMenu,&event);
4674 if (id < 0)
4675 continue;
4676 switch (CropCommands[id])
4677 {
4678 case CropHelpCommand:
4679 {
4680 switch (mode)
4681 {
4682 case CopyMode:
4683 {
4684 XTextViewHelp(display,resource_info,windows,MagickFalse,
4685 "Help Viewer - Image Copy",ImageCopyHelp);
4686 break;
4687 }
4688 case CropMode:
4689 {
4690 XTextViewHelp(display,resource_info,windows,MagickFalse,
4691 "Help Viewer - Image Crop",ImageCropHelp);
4692 break;
4693 }
4694 case CutMode:
4695 {
4696 XTextViewHelp(display,resource_info,windows,MagickFalse,
4697 "Help Viewer - Image Cut",ImageCutHelp);
4698 break;
4699 }
4700 }
4701 break;
4702 }
4703 case CropDismissCommand:
4704 {
4705 /*
4706 Prematurely exit.
4707 */
4708 state|=EscapeState;
4709 state|=ExitState;
4710 break;
4711 }
4712 default:
4713 break;
4714 }
4715 continue;
4716 }
4717 switch (event.type)
4718 {
4719 case ButtonPress:
4720 {
4721 if (event.xbutton.button != Button1)
4722 break;
4723 if (event.xbutton.window != windows->image.id)
4724 break;
4725 /*
4726 Note first corner of cropping rectangle-- exit loop.
4727 */
4728 (void) XCheckDefineCursor(display,windows->image.id,cursor);
4729 crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4730 crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4731 state|=ExitState;
4732 break;
4733 }
4734 case ButtonRelease:
4735 break;
4736 case Expose:
4737 break;
4738 case KeyPress:
4739 {
4740 if (event.xkey.window != windows->image.id)
4741 break;
4742 /*
4743 Respond to a user key press.
4744 */
4745 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4746 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4747 switch ((int) key_symbol)
4748 {
4749 case XK_Escape:
4750 case XK_F20:
4751 {
4752 /*
4753 Prematurely exit.
4754 */
4755 state|=EscapeState;
4756 state|=ExitState;
4757 break;
4758 }
4759 case XK_F1:
4760 case XK_Help:
4761 {
4762 switch (mode)
4763 {
4764 case CopyMode:
4765 {
4766 XTextViewHelp(display,resource_info,windows,MagickFalse,
4767 "Help Viewer - Image Copy",ImageCopyHelp);
4768 break;
4769 }
4770 case CropMode:
4771 {
4772 XTextViewHelp(display,resource_info,windows,MagickFalse,
4773 "Help Viewer - Image Crop",ImageCropHelp);
4774 break;
4775 }
4776 case CutMode:
4777 {
4778 XTextViewHelp(display,resource_info,windows,MagickFalse,
4779 "Help Viewer - Image Cut",ImageCutHelp);
4780 break;
4781 }
4782 }
4783 break;
4784 }
4785 default:
4786 {
4787 (void) XBell(display,0);
4788 break;
4789 }
4790 }
4791 break;
4792 }
4793 case MotionNotify:
4794 {
4795 if (event.xmotion.window != windows->image.id)
4796 break;
4797 /*
4798 Map and unmap Info widget as text cursor crosses its boundaries.
4799 */
4800 x=event.xmotion.x;
4801 y=event.xmotion.y;
4802 if (windows->info.mapped != MagickFalse)
4803 {
4804 if ((x < (windows->info.x+(int) windows->info.width)) &&
4805 (y < (windows->info.y+(int) windows->info.height)))
4806 (void) XWithdrawWindow(display,windows->info.id,
4807 windows->info.screen);
4808 }
4809 else
4810 if ((x > (windows->info.x+(int) windows->info.width)) ||
4811 (y > (windows->info.y+(int) windows->info.height)))
4812 (void) XMapWindow(display,windows->info.id);
4813 crop_info.x=(ssize_t) windows->image.x+x;
4814 crop_info.y=(ssize_t) windows->image.y+y;
4815 break;
4816 }
4817 default:
4818 break;
4819 }
4820 } while ((state & ExitState) == 0);
4821 (void) XSelectInput(display,windows->image.id,
4822 windows->image.attributes.event_mask);
4823 if ((state & EscapeState) != 0)
4824 {
4825 /*
4826 User want to exit without cropping.
4827 */
4828 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4829 (void) XFreeCursor(display,cursor);
4830 return(MagickTrue);
4831 }
4832 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4833 do
4834 {
4835 /*
4836 Size rectangle as pointer moves until the mouse button is released.
4837 */
4838 x=(int) crop_info.x;
4839 y=(int) crop_info.y;
4840 crop_info.width=0;
4841 crop_info.height=0;
4842 state=DefaultState;
4843 do
4844 {
4845 highlight_info=crop_info;
4846 highlight_info.x=crop_info.x-windows->image.x;
4847 highlight_info.y=crop_info.y-windows->image.y;
4848 if ((highlight_info.width > 3) && (highlight_info.height > 3))
4849 {
4850 /*
4851 Display info and draw cropping rectangle.
4852 */
4853 if (windows->info.mapped == MagickFalse)
4854 (void) XMapWindow(display,windows->info.id);
4855 (void) FormatLocaleString(text,MagickPathExtent,
4856 " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4857 crop_info.height,(double) crop_info.x,(double) crop_info.y);
4858 XInfoWidget(display,windows,text);
4859 XHighlightRectangle(display,windows->image.id,
4860 windows->image.highlight_context,&highlight_info);
4861 }
4862 else
4863 if (windows->info.mapped != MagickFalse)
4864 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4865 /*
4866 Wait for next event.
4867 */
4868 XScreenEvent(display,windows,&event,exception);
4869 if ((highlight_info.width > 3) && (highlight_info.height > 3))
4870 XHighlightRectangle(display,windows->image.id,
4871 windows->image.highlight_context,&highlight_info);
4872 switch (event.type)
4873 {
4874 case ButtonPress:
4875 {
4876 crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4877 crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4878 break;
4879 }
4880 case ButtonRelease:
4881 {
4882 /*
4883 User has committed to cropping rectangle.
4884 */
4885 crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4886 crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4887 XSetCursorState(display,windows,MagickFalse);
4888 state|=ExitState;
4889 windows->command.data=0;
4890 (void) XCommandWidget(display,windows,RectifyModeMenu,
4891 (XEvent *) NULL);
4892 break;
4893 }
4894 case Expose:
4895 break;
4896 case MotionNotify:
4897 {
4898 crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4899 crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4900 }
4901 default:
4902 break;
4903 }
4904 if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4905 ((state & ExitState) != 0))
4906 {
4907 /*
4908 Check boundary conditions.
4909 */
4910 if (crop_info.x < 0)
4911 crop_info.x=0;
4912 else
4913 if (crop_info.x > (ssize_t) windows->image.ximage->width)
4914 crop_info.x=(ssize_t) windows->image.ximage->width;
4915 if ((int) crop_info.x < x)
4916 crop_info.width=(unsigned int) (x-crop_info.x);
4917 else
4918 {
4919 crop_info.width=(unsigned int) (crop_info.x-x);
4920 crop_info.x=(ssize_t) x;
4921 }
4922 if (crop_info.y < 0)
4923 crop_info.y=0;
4924 else
4925 if (crop_info.y > (ssize_t) windows->image.ximage->height)
4926 crop_info.y=(ssize_t) windows->image.ximage->height;
4927 if ((int) crop_info.y < y)
4928 crop_info.height=(unsigned int) (y-crop_info.y);
4929 else
4930 {
4931 crop_info.height=(unsigned int) (crop_info.y-y);
4932 crop_info.y=(ssize_t) y;
4933 }
4934 }
4935 } while ((state & ExitState) == 0);
4936 /*
4937 Wait for user to grab a corner of the rectangle or press return.
4938 */
4939 state=DefaultState;
4940 (void) XMapWindow(display,windows->info.id);
4941 do
4942 {
4943 if (windows->info.mapped != MagickFalse)
4944 {
4945 /*
4946 Display pointer position.
4947 */
4948 (void) FormatLocaleString(text,MagickPathExtent,
4949 " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4950 crop_info.height,(double) crop_info.x,(double) crop_info.y);
4951 XInfoWidget(display,windows,text);
4952 }
4953 highlight_info=crop_info;
4954 highlight_info.x=crop_info.x-windows->image.x;
4955 highlight_info.y=crop_info.y-windows->image.y;
4956 if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4957 {
4958 state|=EscapeState;
4959 state|=ExitState;
4960 break;
4961 }
4962 XHighlightRectangle(display,windows->image.id,
4963 windows->image.highlight_context,&highlight_info);
4964 XScreenEvent(display,windows,&event,exception);
4965 if (event.xany.window == windows->command.id)
4966 {
4967 /*
4968 Select a command from the Command widget.
4969 */
4970 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4971 id=XCommandWidget(display,windows,RectifyModeMenu,&event);
4972 (void) XSetFunction(display,windows->image.highlight_context,
4973 GXinvert);
4974 XHighlightRectangle(display,windows->image.id,
4975 windows->image.highlight_context,&highlight_info);
4976 if (id >= 0)
4977 switch (RectifyCommands[id])
4978 {
4979 case RectifyCopyCommand:
4980 {
4981 state|=ExitState;
4982 break;
4983 }
4984 case RectifyHelpCommand:
4985 {
4986 (void) XSetFunction(display,windows->image.highlight_context,
4987 GXcopy);
4988 switch (mode)
4989 {
4990 case CopyMode:
4991 {
4992 XTextViewHelp(display,resource_info,windows,MagickFalse,
4993 "Help Viewer - Image Copy",ImageCopyHelp);
4994 break;
4995 }
4996 case CropMode:
4997 {
4998 XTextViewHelp(display,resource_info,windows,MagickFalse,
4999 "Help Viewer - Image Crop",ImageCropHelp);
5000 break;
5001 }
5002 case CutMode:
5003 {
5004 XTextViewHelp(display,resource_info,windows,MagickFalse,
5005 "Help Viewer - Image Cut",ImageCutHelp);
5006 break;
5007 }
5008 }
5009 (void) XSetFunction(display,windows->image.highlight_context,
5010 GXinvert);
5011 break;
5012 }
5013 case RectifyDismissCommand:
5014 {
5015 /*
5016 Prematurely exit.
5017 */
5018 state|=EscapeState;
5019 state|=ExitState;
5020 break;
5021 }
5022 default:
5023 break;
5024 }
5025 continue;
5026 }
5027 XHighlightRectangle(display,windows->image.id,
5028 windows->image.highlight_context,&highlight_info);
5029 switch (event.type)
5030 {
5031 case ButtonPress:
5032 {
5033 if (event.xbutton.button != Button1)
5034 break;
5035 if (event.xbutton.window != windows->image.id)
5036 break;
5037 x=windows->image.x+event.xbutton.x;
5038 y=windows->image.y+event.xbutton.y;
5039 if ((x < (int) (crop_info.x+RoiDelta)) &&
5040 (x > (int) (crop_info.x-RoiDelta)) &&
5041 (y < (int) (crop_info.y+RoiDelta)) &&
5042 (y > (int) (crop_info.y-RoiDelta)))
5043 {
5044 crop_info.x=crop_info.x+(ssize_t) crop_info.width;
5045 crop_info.y=crop_info.y+(ssize_t) crop_info.height;
5046 state|=UpdateConfigurationState;
5047 break;
5048 }
5049 if ((x < (int) (crop_info.x+RoiDelta)) &&
5050 (x > (int) (crop_info.x-RoiDelta)) &&
5051 (y < (crop_info.y+(int) crop_info.height+RoiDelta)) &&
5052 (y > (crop_info.y+(int) crop_info.height-RoiDelta)))
5053 {
5054 crop_info.x=(crop_info.x+(int) crop_info.width);
5055 state|=UpdateConfigurationState;
5056 break;
5057 }
5058 if ((x < (crop_info.x+(int) crop_info.width+RoiDelta)) &&
5059 (x > (crop_info.x+(int) crop_info.width-RoiDelta)) &&
5060 (y < (int) (crop_info.y+RoiDelta)) &&
5061 (y > (int) (crop_info.y-RoiDelta)))
5062 {
5063 crop_info.y=(crop_info.y+(ssize_t) crop_info.height);
5064 state|=UpdateConfigurationState;
5065 break;
5066 }
5067 if ((x < (crop_info.x+(int) crop_info.width+RoiDelta)) &&
5068 (x > (crop_info.x+(int) crop_info.width-RoiDelta)) &&
5069 (y < (crop_info.y+(int) crop_info.height+RoiDelta)) &&
5070 (y > (crop_info.y+(int) crop_info.height-RoiDelta)))
5071 {
5072 state|=UpdateConfigurationState;
5073 break;
5074 }
5075 magick_fallthrough;
5076 }
5077 case ButtonRelease:
5078 {
5079 if (event.xbutton.window == windows->pan.id)
5080 if ((highlight_info.x != crop_info.x-windows->image.x) ||
5081 (highlight_info.y != crop_info.y-windows->image.y))
5082 XHighlightRectangle(display,windows->image.id,
5083 windows->image.highlight_context,&highlight_info);
5084 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5085 event.xbutton.time);
5086 break;
5087 }
5088 case Expose:
5089 {
5090 if (event.xexpose.window == windows->image.id)
5091 if (event.xexpose.count == 0)
5092 {
5093 event.xexpose.x=(int) highlight_info.x;
5094 event.xexpose.y=(int) highlight_info.y;
5095 event.xexpose.width=(int) highlight_info.width;
5096 event.xexpose.height=(int) highlight_info.height;
5097 XRefreshWindow(display,&windows->image,&event);
5098 }
5099 if (event.xexpose.window == windows->info.id)
5100 if (event.xexpose.count == 0)
5101 XInfoWidget(display,windows,text);
5102 break;
5103 }
5104 case KeyPress:
5105 {
5106 if (event.xkey.window != windows->image.id)
5107 break;
5108 /*
5109 Respond to a user key press.
5110 */
5111 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5112 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5113 switch ((int) key_symbol)
5114 {
5115 case XK_Escape:
5116 case XK_F20:
5117 {
5118 state|=EscapeState;
5119 magick_fallthrough;
5120 }
5121 case XK_Return:
5122 {
5123 state|=ExitState;
5124 break;
5125 }
5126 case XK_Home:
5127 case XK_KP_Home:
5128 {
5129 crop_info.x=(ssize_t) (windows->image.width/2L-crop_info.width/
5130 2L);
5131 crop_info.y=(ssize_t) (windows->image.height/2L-crop_info.height/
5132 2L);
5133 break;
5134 }
5135 case XK_Left:
5136 case XK_KP_Left:
5137 {
5138 crop_info.x--;
5139 break;
5140 }
5141 case XK_Up:
5142 case XK_KP_Up:
5143 case XK_Next:
5144 {
5145 crop_info.y--;
5146 break;
5147 }
5148 case XK_Right:
5149 case XK_KP_Right:
5150 {
5151 crop_info.x++;
5152 break;
5153 }
5154 case XK_Prior:
5155 case XK_Down:
5156 case XK_KP_Down:
5157 {
5158 crop_info.y++;
5159 break;
5160 }
5161 case XK_F1:
5162 case XK_Help:
5163 {
5164 (void) XSetFunction(display,windows->image.highlight_context,
5165 GXcopy);
5166 switch (mode)
5167 {
5168 case CopyMode:
5169 {
5170 XTextViewHelp(display,resource_info,windows,MagickFalse,
5171 "Help Viewer - Image Copy",ImageCopyHelp);
5172 break;
5173 }
5174 case CropMode:
5175 {
5176 XTextViewHelp(display,resource_info,windows,MagickFalse,
5177 "Help Viewer - Image Cropg",ImageCropHelp);
5178 break;
5179 }
5180 case CutMode:
5181 {
5182 XTextViewHelp(display,resource_info,windows,MagickFalse,
5183 "Help Viewer - Image Cutg",ImageCutHelp);
5184 break;
5185 }
5186 }
5187 (void) XSetFunction(display,windows->image.highlight_context,
5188 GXinvert);
5189 break;
5190 }
5191 default:
5192 {
5193 (void) XBell(display,0);
5194 break;
5195 }
5196 }
5197 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5198 event.xkey.time);
5199 break;
5200 }
5201 case KeyRelease:
5202 break;
5203 case MotionNotify:
5204 {
5205 if (event.xmotion.window != windows->image.id)
5206 break;
5207 /*
5208 Map and unmap Info widget as text cursor crosses its boundaries.
5209 */
5210 x=event.xmotion.x;
5211 y=event.xmotion.y;
5212 if (windows->info.mapped != MagickFalse)
5213 {
5214 if ((x < (windows->info.x+(int) windows->info.width)) &&
5215 (y < (windows->info.y+(int) windows->info.height)))
5216 (void) XWithdrawWindow(display,windows->info.id,
5217 windows->info.screen);
5218 }
5219 else
5220 if ((x > (windows->info.x+(int) windows->info.width)) ||
5221 (y > (windows->info.y+(int) windows->info.height)))
5222 (void) XMapWindow(display,windows->info.id);
5223 crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5224 crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5225 break;
5226 }
5227 case SelectionRequest:
5228 {
5229 XSelectionEvent
5230 notify;
5231
5232 XSelectionRequestEvent
5233 *request;
5234
5235 /*
5236 Set primary selection.
5237 */
5238 (void) FormatLocaleString(text,MagickPathExtent,
5239 "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5240 crop_info.height,(double) crop_info.x,(double) crop_info.y);
5241 request=(&(event.xselectionrequest));
5242 (void) XChangeProperty(request->display,request->requestor,
5243 request->property,request->target,8,PropModeReplace,
5244 (unsigned char *) text,(int) strlen(text));
5245 notify.type=SelectionNotify;
5246 notify.display=request->display;
5247 notify.requestor=request->requestor;
5248 notify.selection=request->selection;
5249 notify.target=request->target;
5250 notify.time=request->time;
5251 if (request->property == None)
5252 notify.property=request->target;
5253 else
5254 notify.property=request->property;
5255 (void) XSendEvent(request->display,request->requestor,False,0,
5256 (XEvent *) &notify);
5257 }
5258 default:
5259 break;
5260 }
5261 if ((state & UpdateConfigurationState) != 0)
5262 {
5263 (void) XPutBackEvent(display,&event);
5264 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5265 break;
5266 }
5267 } while ((state & ExitState) == 0);
5268 } while ((state & ExitState) == 0);
5269 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5270 XSetCursorState(display,windows,MagickFalse);
5271 if ((state & EscapeState) != 0)
5272 return(MagickTrue);
5273 if (mode == CropMode)
5274 if (((int) crop_info.width != windows->image.ximage->width) ||
5275 ((int) crop_info.height != windows->image.ximage->height))
5276 {
5277 /*
5278 Reconfigure Image window as defined by cropping rectangle.
5279 */
5280 XSetCropGeometry(display,windows,&crop_info,image);
5281 windows->image.window_changes.width=(int) crop_info.width;
5282 windows->image.window_changes.height=(int) crop_info.height;
5283 (void) XConfigureImage(display,resource_info,windows,image,exception);
5284 return(MagickTrue);
5285 }
5286 /*
5287 Copy image before applying image transforms.
5288 */
5289 XSetCursorState(display,windows,MagickTrue);
5290 XCheckRefreshWindows(display,windows);
5291 width=(unsigned int) image->columns;
5292 height=(unsigned int) image->rows;
5293 x=0;
5294 y=0;
5295 if (windows->image.crop_geometry != (char *) NULL)
5296 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5297 scale_factor=(double) width/windows->image.ximage->width;
5298 crop_info.x+=x;
5299 crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5300 crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5301 scale_factor=(double) height/windows->image.ximage->height;
5302 crop_info.y+=y;
5303 crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5304 crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5305 crop_info.x+=image->page.x;
5306 crop_info.y+=image->page.y;
5307 crop_image=CropImage(image,&crop_info,exception);
5308 XSetCursorState(display,windows,MagickFalse);
5309 if (crop_image == (Image *) NULL)
5310 return(MagickFalse);
5311 if (resource_info->copy_image != (Image *) NULL)
5312 resource_info->copy_image=DestroyImage(resource_info->copy_image);
5313 resource_info->copy_image=crop_image;
5314 if (mode == CopyMode)
5315 {
5316 (void) XConfigureImage(display,resource_info,windows,image,exception);
5317 return(MagickTrue);
5318 }
5319 /*
5320 Cut image.
5321 */
5322 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5323 return(MagickFalse);
5324 image->alpha_trait=BlendPixelTrait;
5325 image_view=AcquireAuthenticCacheView(image,exception);
5326 for (y=0; y < (int) crop_info.height; y++)
5327 {
5328 q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5329 crop_info.width,1,exception);
5330 if (q == (Quantum *) NULL)
5331 break;
5332 for (x=0; x < (int) crop_info.width; x++)
5333 {
5334 SetPixelAlpha(image,TransparentAlpha,q);
5335 q+=GetPixelChannels(image);
5336 }
5337 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5338 break;
5339 }
5340 image_view=DestroyCacheView(image_view);
5341 /*
5342 Update image configuration.
5343 */
5344 XConfigureImageColormap(display,resource_info,windows,image,exception);
5345 (void) XConfigureImage(display,resource_info,windows,image,exception);
5346 return(MagickTrue);
5347}
5348
5349/*
5350%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5351% %
5352% %
5353% %
5354+ X D r a w I m a g e %
5355% %
5356% %
5357% %
5358%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5359%
5360% XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5361% the image.
5362%
5363% The format of the XDrawEditImage method is:
5364%
5365% MagickBooleanType XDrawEditImage(Display *display,
5366% XResourceInfo *resource_info,XWindows *windows,Image **image,
5367% ExceptionInfo *exception)
5368%
5369% A description of each parameter follows:
5370%
5371% o display: Specifies a connection to an X server; returned from
5372% XOpenDisplay.
5373%
5374% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5375%
5376% o windows: Specifies a pointer to a XWindows structure.
5377%
5378% o image: the image.
5379%
5380% o exception: return any errors or warnings in this structure.
5381%
5382*/
5383static MagickBooleanType XDrawEditImage(Display *display,
5384 XResourceInfo *resource_info,XWindows *windows,Image **image,
5385 ExceptionInfo *exception)
5386{
5387 const char
5388 *const DrawMenu[] =
5389 {
5390 "Element",
5391 "Color",
5392 "Stipple",
5393 "Width",
5394 "Undo",
5395 "Help",
5396 "Dismiss",
5397 (char *) NULL
5398 };
5399
5400 static ElementType
5401 element = PointElement;
5402
5403 static const ModeType
5404 DrawCommands[] =
5405 {
5406 DrawElementCommand,
5407 DrawColorCommand,
5408 DrawStippleCommand,
5409 DrawWidthCommand,
5410 DrawUndoCommand,
5411 DrawHelpCommand,
5412 DrawDismissCommand
5413 };
5414
5415 static Pixmap
5416 stipple = (Pixmap) NULL;
5417
5418 static unsigned int
5419 pen_id = 0,
5420 line_width = 1;
5421
5422 char
5423 command[MagickPathExtent],
5424 text[MagickPathExtent];
5425
5426 Cursor
5427 cursor;
5428
5429 int
5430 entry,
5431 id,
5432 number_coordinates,
5433 x,
5434 y;
5435
5436 double
5437 degrees;
5438
5439 MagickStatusType
5440 status;
5441
5443 rectangle_info;
5444
5445 int
5446 i;
5447
5448 unsigned int
5449 distance,
5450 height,
5451 max_coordinates,
5452 width;
5453
5454 size_t
5455 state;
5456
5457 Window
5458 root_window;
5459
5460 XDrawInfo
5461 draw_info;
5462
5463 XEvent
5464 event;
5465
5466 XPoint
5467 *coordinate_info;
5468
5469 XSegment
5470 line_info;
5471
5472 /*
5473 Allocate polygon info.
5474 */
5475 max_coordinates=2048;
5476 coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5477 sizeof(*coordinate_info));
5478 if (coordinate_info == (XPoint *) NULL)
5479 {
5480 (void) ThrowMagickException(exception,GetMagickModule(),
5481 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5482 return(MagickFalse);
5483 }
5484 /*
5485 Map Command widget.
5486 */
5487 (void) CloneString(&windows->command.name,"Draw");
5488 windows->command.data=4;
5489 (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5490 (void) XMapRaised(display,windows->command.id);
5491 XClientMessage(display,windows->image.id,windows->im_protocols,
5492 windows->im_update_widget,CurrentTime);
5493 /*
5494 Wait for first button press.
5495 */
5496 root_window=XRootWindow(display,XDefaultScreen(display));
5497 draw_info.stencil=OpaqueStencil;
5498 status=MagickTrue;
5499 cursor=XCreateFontCursor(display,XC_tcross);
5500 for ( ; ; )
5501 {
5502 XQueryPosition(display,windows->image.id,&x,&y);
5503 (void) XSelectInput(display,windows->image.id,
5504 windows->image.attributes.event_mask | PointerMotionMask);
5505 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5506 state=DefaultState;
5507 do
5508 {
5509 if (windows->info.mapped != MagickFalse)
5510 {
5511 /*
5512 Display pointer position.
5513 */
5514 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
5515 x+windows->image.x,y+windows->image.y);
5516 XInfoWidget(display,windows,text);
5517 }
5518 /*
5519 Wait for next event.
5520 */
5521 XScreenEvent(display,windows,&event,exception);
5522 if (event.xany.window == windows->command.id)
5523 {
5524 /*
5525 Select a command from the Command widget.
5526 */
5527 id=XCommandWidget(display,windows,DrawMenu,&event);
5528 if (id < 0)
5529 continue;
5530 switch (DrawCommands[id])
5531 {
5532 case DrawElementCommand:
5533 {
5534 const char
5535 *const Elements[] =
5536 {
5537 "point",
5538 "line",
5539 "rectangle",
5540 "fill rectangle",
5541 "circle",
5542 "fill circle",
5543 "ellipse",
5544 "fill ellipse",
5545 "polygon",
5546 "fill polygon",
5547 (char *) NULL,
5548 };
5549
5550 /*
5551 Select a command from the pop-up menu.
5552 */
5553 element=(ElementType) (XMenuWidget(display,windows,
5554 DrawMenu[id],Elements,command)+1);
5555 break;
5556 }
5557 case DrawColorCommand:
5558 {
5559 const char
5560 *ColorMenu[MaxNumberPens+1];
5561
5562 int
5563 pen_number;
5564
5565 MagickBooleanType
5566 transparent;
5567
5568 XColor
5569 color;
5570
5571 /*
5572 Initialize menu selections.
5573 */
5574 for (i=0; i < (int) (MaxNumberPens-2); i++)
5575 ColorMenu[i]=resource_info->pen_colors[i];
5576 ColorMenu[MaxNumberPens-2]="transparent";
5577 ColorMenu[MaxNumberPens-1]="Browser...";
5578 ColorMenu[MaxNumberPens]=(char *) NULL;
5579 /*
5580 Select a pen color from the pop-up menu.
5581 */
5582 pen_number=XMenuWidget(display,windows,DrawMenu[id],
5583 (const char **) ColorMenu,command);
5584 if (pen_number < 0)
5585 break;
5586 transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5587 MagickFalse;
5588 if (transparent != MagickFalse)
5589 {
5590 draw_info.stencil=TransparentStencil;
5591 break;
5592 }
5593 if (pen_number == (MaxNumberPens-1))
5594 {
5595 static char
5596 color_name[MagickPathExtent] = "gray";
5597
5598 /*
5599 Select a pen color from a dialog.
5600 */
5601 resource_info->pen_colors[pen_number]=color_name;
5602 XColorBrowserWidget(display,windows,"Select",color_name);
5603 if (*color_name == '\0')
5604 break;
5605 }
5606 /*
5607 Set pen color.
5608 */
5609 (void) XParseColor(display,windows->map_info->colormap,
5610 resource_info->pen_colors[pen_number],&color);
5611 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5612 (unsigned int) MaxColors,&color);
5613 windows->pixel_info->pen_colors[pen_number]=color;
5614 pen_id=(unsigned int) pen_number;
5615 draw_info.stencil=OpaqueStencil;
5616 break;
5617 }
5618 case DrawStippleCommand:
5619 {
5620 const char
5621 *StipplesMenu[] =
5622 {
5623 "Brick",
5624 "Diagonal",
5625 "Scales",
5626 "Vertical",
5627 "Wavy",
5628 "Translucent",
5629 "Opaque",
5630 (char *) NULL,
5631 (char *) NULL,
5632 };
5633
5634 Image
5635 *stipple_image;
5636
5637 ImageInfo
5638 *image_info;
5639
5640 int
5641 status;
5642
5643 static char
5644 filename[MagickPathExtent] = "\0";
5645
5646 /*
5647 Select a command from the pop-up menu.
5648 */
5649 StipplesMenu[7]="Open...";
5650 entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5651 command);
5652 if (entry < 0)
5653 break;
5654 if (stipple != (Pixmap) NULL)
5655 (void) XFreePixmap(display,stipple);
5656 stipple=(Pixmap) NULL;
5657 if (entry != 7)
5658 {
5659 switch (entry)
5660 {
5661 case 0:
5662 {
5663 stipple=XCreateBitmapFromData(display,root_window,
5664 (char *) BricksBitmap,BricksWidth,BricksHeight);
5665 break;
5666 }
5667 case 1:
5668 {
5669 stipple=XCreateBitmapFromData(display,root_window,
5670 (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5671 break;
5672 }
5673 case 2:
5674 {
5675 stipple=XCreateBitmapFromData(display,root_window,
5676 (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5677 break;
5678 }
5679 case 3:
5680 {
5681 stipple=XCreateBitmapFromData(display,root_window,
5682 (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5683 break;
5684 }
5685 case 4:
5686 {
5687 stipple=XCreateBitmapFromData(display,root_window,
5688 (char *) WavyBitmap,WavyWidth,WavyHeight);
5689 break;
5690 }
5691 case 5:
5692 {
5693 stipple=XCreateBitmapFromData(display,root_window,
5694 (char *) HighlightBitmap,HighlightWidth,
5695 HighlightHeight);
5696 break;
5697 }
5698 case 6:
5699 default:
5700 {
5701 stipple=XCreateBitmapFromData(display,root_window,
5702 (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5703 break;
5704 }
5705 }
5706 break;
5707 }
5708 XFileBrowserWidget(display,windows,"Stipple",filename);
5709 if (*filename == '\0')
5710 break;
5711 /*
5712 Read image.
5713 */
5714 XSetCursorState(display,windows,MagickTrue);
5715 XCheckRefreshWindows(display,windows);
5716 image_info=AcquireImageInfo();
5717 (void) CopyMagickString(image_info->filename,filename,
5718 MagickPathExtent);
5719 stipple_image=ReadImage(image_info,exception);
5720 CatchException(exception);
5721 XSetCursorState(display,windows,MagickFalse);
5722 if (stipple_image == (Image *) NULL)
5723 break;
5724 (void) AcquireUniqueFileResource(filename);
5725 (void) FormatLocaleString(stipple_image->filename,
5726 MagickPathExtent,"xbm:%s",filename);
5727 (void) WriteImage(image_info,stipple_image,exception);
5728 stipple_image=DestroyImage(stipple_image);
5729 image_info=DestroyImageInfo(image_info);
5730 status=XReadBitmapFile(display,root_window,filename,&width,
5731 &height,&stipple,&x,&y);
5732 (void) RelinquishUniqueFileResource(filename);
5733 if ((status != BitmapSuccess) != 0)
5734 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5735 filename);
5736 break;
5737 }
5738 case DrawWidthCommand:
5739 {
5740 const char
5741 *const WidthsMenu[] =
5742 {
5743 "1",
5744 "2",
5745 "4",
5746 "8",
5747 "16",
5748 "Dialog...",
5749 (char *) NULL,
5750 };
5751
5752 static char
5753 width[MagickPathExtent] = "0";
5754
5755 /*
5756 Select a command from the pop-up menu.
5757 */
5758 entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5759 command);
5760 if (entry < 0)
5761 break;
5762 if (entry != 5)
5763 {
5764 line_width=(unsigned int) StringToUnsignedLong(
5765 WidthsMenu[entry]);
5766 break;
5767 }
5768 (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5769 width);
5770 if (*width == '\0')
5771 break;
5772 line_width=(unsigned int) StringToUnsignedLong(width);
5773 break;
5774 }
5775 case DrawUndoCommand:
5776 {
5777 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5778 image,exception);
5779 break;
5780 }
5781 case DrawHelpCommand:
5782 {
5783 XTextViewHelp(display,resource_info,windows,MagickFalse,
5784 "Help Viewer - Image Rotation",ImageDrawHelp);
5785 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5786 break;
5787 }
5788 case DrawDismissCommand:
5789 {
5790 /*
5791 Prematurely exit.
5792 */
5793 state|=EscapeState;
5794 state|=ExitState;
5795 break;
5796 }
5797 default:
5798 break;
5799 }
5800 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5801 continue;
5802 }
5803 switch (event.type)
5804 {
5805 case ButtonPress:
5806 {
5807 if (event.xbutton.button != Button1)
5808 break;
5809 if (event.xbutton.window != windows->image.id)
5810 break;
5811 /*
5812 exit loop.
5813 */
5814 x=event.xbutton.x;
5815 y=event.xbutton.y;
5816 state|=ExitState;
5817 break;
5818 }
5819 case ButtonRelease:
5820 break;
5821 case Expose:
5822 break;
5823 case KeyPress:
5824 {
5825 KeySym
5826 key_symbol;
5827
5828 if (event.xkey.window != windows->image.id)
5829 break;
5830 /*
5831 Respond to a user key press.
5832 */
5833 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5834 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5835 switch ((int) key_symbol)
5836 {
5837 case XK_Escape:
5838 case XK_F20:
5839 {
5840 /*
5841 Prematurely exit.
5842 */
5843 state|=EscapeState;
5844 state|=ExitState;
5845 break;
5846 }
5847 case XK_F1:
5848 case XK_Help:
5849 {
5850 XTextViewHelp(display,resource_info,windows,MagickFalse,
5851 "Help Viewer - Image Rotation",ImageDrawHelp);
5852 break;
5853 }
5854 default:
5855 {
5856 (void) XBell(display,0);
5857 break;
5858 }
5859 }
5860 break;
5861 }
5862 case MotionNotify:
5863 {
5864 /*
5865 Map and unmap Info widget as text cursor crosses its boundaries.
5866 */
5867 x=event.xmotion.x;
5868 y=event.xmotion.y;
5869 if (windows->info.mapped != MagickFalse)
5870 {
5871 if ((x < (windows->info.x+(int) windows->info.width)) &&
5872 (y < (windows->info.y+(int) windows->info.height)))
5873 (void) XWithdrawWindow(display,windows->info.id,
5874 windows->info.screen);
5875 }
5876 else
5877 if ((x > (windows->info.x+(int) windows->info.width)) ||
5878 (y > (windows->info.y+(int) windows->info.height)))
5879 (void) XMapWindow(display,windows->info.id);
5880 break;
5881 }
5882 }
5883 } while ((state & ExitState) == 0);
5884 (void) XSelectInput(display,windows->image.id,
5885 windows->image.attributes.event_mask);
5886 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5887 if ((state & EscapeState) != 0)
5888 break;
5889 /*
5890 Draw element as pointer moves until the button is released.
5891 */
5892 distance=0;
5893 degrees=0.0;
5894 line_info.x1=x;
5895 line_info.y1=y;
5896 line_info.x2=x;
5897 line_info.y2=y;
5898 rectangle_info.x=(ssize_t) x;
5899 rectangle_info.y=(ssize_t) y;
5900 rectangle_info.width=0;
5901 rectangle_info.height=0;
5902 number_coordinates=1;
5903 coordinate_info->x=x;
5904 coordinate_info->y=y;
5905 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5906 state=DefaultState;
5907 do
5908 {
5909 switch (element)
5910 {
5911 case PointElement:
5912 default:
5913 {
5914 if (number_coordinates > 1)
5915 {
5916 (void) XDrawLines(display,windows->image.id,
5917 windows->image.highlight_context,coordinate_info,
5918 number_coordinates,CoordModeOrigin);
5919 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d",
5920 coordinate_info[number_coordinates-1].x,
5921 coordinate_info[number_coordinates-1].y);
5922 XInfoWidget(display,windows,text);
5923 }
5924 break;
5925 }
5926 case LineElement:
5927 {
5928 if (distance > 9)
5929 {
5930 /*
5931 Display angle of the line.
5932 */
5933 degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5934 line_info.y1),(double) (line_info.x2-line_info.x1)));
5935 (void) FormatLocaleString(text,MagickPathExtent," %g",
5936 (double) degrees);
5937 XInfoWidget(display,windows,text);
5938 XHighlightLine(display,windows->image.id,
5939 windows->image.highlight_context,&line_info);
5940 }
5941 else
5942 if (windows->info.mapped != MagickFalse)
5943 (void) XWithdrawWindow(display,windows->info.id,
5944 windows->info.screen);
5945 break;
5946 }
5947 case RectangleElement:
5948 case FillRectangleElement:
5949 {
5950 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5951 {
5952 /*
5953 Display info and draw drawing rectangle.
5954 */
5955 (void) FormatLocaleString(text,MagickPathExtent,
5956 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5957 (double) rectangle_info.height,(double) rectangle_info.x,
5958 (double) rectangle_info.y);
5959 XInfoWidget(display,windows,text);
5960 XHighlightRectangle(display,windows->image.id,
5961 windows->image.highlight_context,&rectangle_info);
5962 }
5963 else
5964 if (windows->info.mapped != MagickFalse)
5965 (void) XWithdrawWindow(display,windows->info.id,
5966 windows->info.screen);
5967 break;
5968 }
5969 case CircleElement:
5970 case FillCircleElement:
5971 case EllipseElement:
5972 case FillEllipseElement:
5973 {
5974 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5975 {
5976 /*
5977 Display info and draw drawing rectangle.
5978 */
5979 (void) FormatLocaleString(text,MagickPathExtent,
5980 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5981 (double) rectangle_info.height,(double) rectangle_info.x,
5982 (double) rectangle_info.y);
5983 XInfoWidget(display,windows,text);
5984 XHighlightEllipse(display,windows->image.id,
5985 windows->image.highlight_context,&rectangle_info);
5986 }
5987 else
5988 if (windows->info.mapped != MagickFalse)
5989 (void) XWithdrawWindow(display,windows->info.id,
5990 windows->info.screen);
5991 break;
5992 }
5993 case PolygonElement:
5994 case FillPolygonElement:
5995 {
5996 if (number_coordinates > 1)
5997 (void) XDrawLines(display,windows->image.id,
5998 windows->image.highlight_context,coordinate_info,
5999 number_coordinates,CoordModeOrigin);
6000 if (distance > 9)
6001 {
6002 /*
6003 Display angle of the line.
6004 */
6005 degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6006 line_info.y1),(double) (line_info.x2-line_info.x1)));
6007 (void) FormatLocaleString(text,MagickPathExtent," %g",
6008 (double) degrees);
6009 XInfoWidget(display,windows,text);
6010 XHighlightLine(display,windows->image.id,
6011 windows->image.highlight_context,&line_info);
6012 }
6013 else
6014 if (windows->info.mapped != MagickFalse)
6015 (void) XWithdrawWindow(display,windows->info.id,
6016 windows->info.screen);
6017 break;
6018 }
6019 }
6020 /*
6021 Wait for next event.
6022 */
6023 XScreenEvent(display,windows,&event,exception);
6024 switch (element)
6025 {
6026 case PointElement:
6027 default:
6028 {
6029 if (number_coordinates > 1)
6030 (void) XDrawLines(display,windows->image.id,
6031 windows->image.highlight_context,coordinate_info,
6032 number_coordinates,CoordModeOrigin);
6033 break;
6034 }
6035 case LineElement:
6036 {
6037 if (distance > 9)
6038 XHighlightLine(display,windows->image.id,
6039 windows->image.highlight_context,&line_info);
6040 break;
6041 }
6042 case RectangleElement:
6043 case FillRectangleElement:
6044 {
6045 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6046 XHighlightRectangle(display,windows->image.id,
6047 windows->image.highlight_context,&rectangle_info);
6048 break;
6049 }
6050 case CircleElement:
6051 case FillCircleElement:
6052 case EllipseElement:
6053 case FillEllipseElement:
6054 {
6055 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6056 XHighlightEllipse(display,windows->image.id,
6057 windows->image.highlight_context,&rectangle_info);
6058 break;
6059 }
6060 case PolygonElement:
6061 case FillPolygonElement:
6062 {
6063 if (number_coordinates > 1)
6064 (void) XDrawLines(display,windows->image.id,
6065 windows->image.highlight_context,coordinate_info,
6066 number_coordinates,CoordModeOrigin);
6067 if (distance > 9)
6068 XHighlightLine(display,windows->image.id,
6069 windows->image.highlight_context,&line_info);
6070 break;
6071 }
6072 }
6073 switch (event.type)
6074 {
6075 case ButtonPress:
6076 break;
6077 case ButtonRelease:
6078 {
6079 /*
6080 User has committed to element.
6081 */
6082 line_info.x2=event.xbutton.x;
6083 line_info.y2=event.xbutton.y;
6084 rectangle_info.x=(ssize_t) event.xbutton.x;
6085 rectangle_info.y=(ssize_t) event.xbutton.y;
6086 coordinate_info[number_coordinates].x=event.xbutton.x;
6087 coordinate_info[number_coordinates].y=event.xbutton.y;
6088 if (((element != PolygonElement) &&
6089 (element != FillPolygonElement)) || (distance <= 9))
6090 {
6091 state|=ExitState;
6092 break;
6093 }
6094 number_coordinates++;
6095 if (number_coordinates < (int) max_coordinates)
6096 {
6097 line_info.x1=event.xbutton.x;
6098 line_info.y1=event.xbutton.y;
6099 break;
6100 }
6101 max_coordinates<<=1;
6102 coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6103 max_coordinates,sizeof(*coordinate_info));
6104 if (coordinate_info == (XPoint *) NULL)
6105 (void) ThrowMagickException(exception,GetMagickModule(),
6106 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6107 break;
6108 }
6109 case Expose:
6110 break;
6111 case MotionNotify:
6112 {
6113 if (event.xmotion.window != windows->image.id)
6114 break;
6115 if (element != PointElement)
6116 {
6117 line_info.x2=event.xmotion.x;
6118 line_info.y2=event.xmotion.y;
6119 rectangle_info.x=(ssize_t) event.xmotion.x;
6120 rectangle_info.y=(ssize_t) event.xmotion.y;
6121 break;
6122 }
6123 coordinate_info[number_coordinates].x=event.xbutton.x;
6124 coordinate_info[number_coordinates].y=event.xbutton.y;
6125 number_coordinates++;
6126 if (number_coordinates < (int) max_coordinates)
6127 break;
6128 max_coordinates<<=1;
6129 coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6130 max_coordinates,sizeof(*coordinate_info));
6131 if (coordinate_info == (XPoint *) NULL)
6132 (void) ThrowMagickException(exception,GetMagickModule(),
6133 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6134 break;
6135 }
6136 default:
6137 break;
6138 }
6139 /*
6140 Check boundary conditions.
6141 */
6142 if (line_info.x2 < 0)
6143 line_info.x2=0;
6144 else
6145 if (line_info.x2 > (int) windows->image.width)
6146 line_info.x2=(short) windows->image.width;
6147 if (line_info.y2 < 0)
6148 line_info.y2=0;
6149 else
6150 if (line_info.y2 > (int) windows->image.height)
6151 line_info.y2=(short) windows->image.height;
6152 distance=(unsigned int)
6153 (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6154 ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6155 if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6156 ((state & ExitState) != 0))
6157 {
6158 if (rectangle_info.x < 0)
6159 rectangle_info.x=0;
6160 else
6161 if (rectangle_info.x > (ssize_t) windows->image.width)
6162 rectangle_info.x=(ssize_t) windows->image.width;
6163 if ((int) rectangle_info.x < x)
6164 rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6165 else
6166 {
6167 rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6168 rectangle_info.x=(ssize_t) x;
6169 }
6170 if (rectangle_info.y < 0)
6171 rectangle_info.y=0;
6172 else
6173 if (rectangle_info.y > (ssize_t) windows->image.height)
6174 rectangle_info.y=(ssize_t) windows->image.height;
6175 if ((int) rectangle_info.y < y)
6176 rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6177 else
6178 {
6179 rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6180 rectangle_info.y=(ssize_t) y;
6181 }
6182 }
6183 } while ((state & ExitState) == 0);
6184 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6185 if ((element == PointElement) || (element == PolygonElement) ||
6186 (element == FillPolygonElement))
6187 {
6188 /*
6189 Determine polygon bounding box.
6190 */
6191 rectangle_info.x=(ssize_t) coordinate_info->x;
6192 rectangle_info.y=(ssize_t) coordinate_info->y;
6193 x=coordinate_info->x;
6194 y=coordinate_info->y;
6195 for (i=1; i < number_coordinates; i++)
6196 {
6197 if (coordinate_info[i].x > x)
6198 x=coordinate_info[i].x;
6199 if (coordinate_info[i].y > y)
6200 y=coordinate_info[i].y;
6201 if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6202 rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6203 if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6204 rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6205 }
6206 rectangle_info.width=(size_t) (x-rectangle_info.x);
6207 rectangle_info.height=(size_t) (y-rectangle_info.y);
6208 for (i=0; i < number_coordinates; i++)
6209 {
6210 coordinate_info[i].x-=rectangle_info.x;
6211 coordinate_info[i].y-=rectangle_info.y;
6212 }
6213 }
6214 else
6215 if (distance <= 9)
6216 continue;
6217 else
6218 if ((element == RectangleElement) ||
6219 (element == CircleElement) || (element == EllipseElement))
6220 {
6221 rectangle_info.width--;
6222 rectangle_info.height--;
6223 }
6224 /*
6225 Drawing is relative to image configuration.
6226 */
6227 draw_info.x=(int) rectangle_info.x;
6228 draw_info.y=(int) rectangle_info.y;
6229 (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6230 image,exception);
6231 width=(unsigned int) (*image)->columns;
6232 height=(unsigned int) (*image)->rows;
6233 x=0;
6234 y=0;
6235 if (windows->image.crop_geometry != (char *) NULL)
6236 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6237 draw_info.x+=windows->image.x-((int) line_width/2);
6238 if (draw_info.x < 0)
6239 draw_info.x=0;
6240 draw_info.x=(int) width*draw_info.x/windows->image.ximage->width;
6241 draw_info.y+=windows->image.y-((int) line_width/2);
6242 if (draw_info.y < 0)
6243 draw_info.y=0;
6244 draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6245 draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6246 if (draw_info.width > (unsigned int) (*image)->columns)
6247 draw_info.width=(unsigned int) (*image)->columns;
6248 draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6249 if (draw_info.height > (unsigned int) (*image)->rows)
6250 draw_info.height=(unsigned int) (*image)->rows;
6251 (void) FormatLocaleString(draw_info.geometry,MagickPathExtent,"%ux%u%+d%+d",
6252 width*draw_info.width/(unsigned int) windows->image.ximage->width,
6253 height*draw_info.height/(unsigned int) windows->image.ximage->height,
6254 draw_info.x+x,draw_info.y+y);
6255 /*
6256 Initialize drawing attributes.
6257 */
6258 draw_info.degrees=0.0;
6259 draw_info.element=element;
6260 draw_info.stipple=stipple;
6261 draw_info.line_width=line_width;
6262 draw_info.line_info=line_info;
6263 if (line_info.x1 > (int) (line_width/2))
6264 draw_info.line_info.x1=(short) line_width/2;
6265 if (line_info.y1 > (int) (line_width/2))
6266 draw_info.line_info.y1=(short) line_width/2;
6267 draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+
6268 ((int) line_width/2));
6269 draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+
6270 ((int) line_width/2));
6271 if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6272 {
6273 draw_info.line_info.x2=(-draw_info.line_info.x2);
6274 draw_info.line_info.y2=(-draw_info.line_info.y2);
6275 }
6276 if (draw_info.line_info.x2 < 0)
6277 {
6278 draw_info.line_info.x2=(-draw_info.line_info.x2);
6279 Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6280 }
6281 if (draw_info.line_info.y2 < 0)
6282 {
6283 draw_info.line_info.y2=(-draw_info.line_info.y2);
6284 Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6285 }
6286 draw_info.rectangle_info=rectangle_info;
6287 if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6288 draw_info.rectangle_info.x=(ssize_t) line_width/2;
6289 if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6290 draw_info.rectangle_info.y=(ssize_t) line_width/2;
6291 draw_info.number_coordinates=(unsigned int) number_coordinates;
6292 draw_info.coordinate_info=coordinate_info;
6293 windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6294 /*
6295 Draw element on image.
6296 */
6297 XSetCursorState(display,windows,MagickTrue);
6298 XCheckRefreshWindows(display,windows);
6299 status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6300 XSetCursorState(display,windows,MagickFalse);
6301 /*
6302 Update image colormap and return to image drawing.
6303 */
6304 XConfigureImageColormap(display,resource_info,windows,*image,exception);
6305 (void) XConfigureImage(display,resource_info,windows,*image,exception);
6306 }
6307 XSetCursorState(display,windows,MagickFalse);
6308 coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6309 return(status != 0 ? MagickTrue : MagickFalse);
6310}
6311
6312/*
6313%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6314% %
6315% %
6316% %
6317+ X D r a w P a n R e c t a n g l e %
6318% %
6319% %
6320% %
6321%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6322%
6323% XDrawPanRectangle() draws a rectangle in the pan window. The pan window
6324% displays a zoom image and the rectangle shows which portion of the image is
6325% displayed in the Image window.
6326%
6327% The format of the XDrawPanRectangle method is:
6328%
6329% XDrawPanRectangle(Display *display,XWindows *windows)
6330%
6331% A description of each parameter follows:
6332%
6333% o display: Specifies a connection to an X server; returned from
6334% XOpenDisplay.
6335%
6336% o windows: Specifies a pointer to a XWindows structure.
6337%
6338*/
6339static void XDrawPanRectangle(Display *display,XWindows *windows)
6340{
6341 double
6342 scale_factor;
6343
6345 highlight_info;
6346
6347 /*
6348 Determine dimensions of the panning rectangle.
6349 */
6350 scale_factor=(double) windows->pan.width/windows->image.ximage->width;
6351 highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6352 highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6353 scale_factor=(double)
6354 windows->pan.height/windows->image.ximage->height;
6355 highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6356 highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6357 /*
6358 Display the panning rectangle.
6359 */
6360 (void) XClearWindow(display,windows->pan.id);
6361 XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6362 &highlight_info);
6363}
6364
6365/*
6366%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6367% %
6368% %
6369% %
6370+ X I m a g e C a c h e %
6371% %
6372% %
6373% %
6374%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6375%
6376% XImageCache() handles the creation, manipulation, and destruction of the
6377% image cache (undo and redo buffers).
6378%
6379% The format of the XImageCache method is:
6380%
6381% void XImageCache(Display *display,XResourceInfo *resource_info,
6382% XWindows *windows,const DisplayCommand command,Image **image,
6383% ExceptionInfo *exception)
6384%
6385% A description of each parameter follows:
6386%
6387% o display: Specifies a connection to an X server; returned from
6388% XOpenDisplay.
6389%
6390% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6391%
6392% o windows: Specifies a pointer to a XWindows structure.
6393%
6394% o command: Specifies a command to perform.
6395%
6396% o image: the image; XImageCache may transform the image and return a new
6397% image pointer.
6398%
6399% o exception: return any errors or warnings in this structure.
6400%
6401*/
6402static void XImageCache(Display *display,XResourceInfo *resource_info,
6403 XWindows *windows,const DisplayCommand command,Image **image,
6404 ExceptionInfo *exception)
6405{
6406 Image
6407 *cache_image;
6408
6409 static Image
6410 *redo_image = (Image *) NULL,
6411 *undo_image = (Image *) NULL;
6412
6413 switch (command)
6414 {
6415 case FreeBuffersCommand:
6416 {
6417 /*
6418 Free memory from the undo and redo cache.
6419 */
6420 while (undo_image != (Image *) NULL)
6421 {
6422 cache_image=undo_image;
6423 undo_image=GetPreviousImageInList(undo_image);
6424 cache_image->list=DestroyImage(cache_image->list);
6425 cache_image=DestroyImage(cache_image);
6426 }
6427 undo_image=NewImageList();
6428 if (redo_image != (Image *) NULL)
6429 redo_image=DestroyImage(redo_image);
6430 redo_image=NewImageList();
6431 return;
6432 }
6433 case UndoCommand:
6434 {
6435 char
6436 image_geometry[MagickPathExtent];
6437
6438 /*
6439 Undo the last image transformation.
6440 */
6441 if (undo_image == (Image *) NULL)
6442 {
6443 (void) XBell(display,0);
6444 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 EdgeDetectCommand,
11228 SpreadCommand,
11229 ShadeCommand,
11230 RaiseCommand,
11231 SegmentCommand
11232 },
11233 FXCommands[] =
11234 {
11235 SolarizeCommand,
11236 SepiaToneCommand,
11237 SwirlCommand,
11238 ImplodeCommand,
11239 VignetteCommand,
11240 WaveCommand,
11241 OilPaintCommand,
11242 CharcoalDrawCommand
11243 },
11244 MiscellanyCommands[] =
11245 {
11246 InfoCommand,
11247 ZoomCommand,
11248 ShowPreviewCommand,
11249 ShowHistogramCommand,
11250 ShowMatteCommand
11251 },
11252 ROICommands[] =
11253 {
11254 ROIHelpCommand,
11255 ROIDismissCommand
11256 };
11257
11258 static const DisplayCommand
11259 *Commands[ApplyMenus] =
11260 {
11261 FileCommands,
11262 EditCommands,
11263 TransformCommands,
11264 EnhanceCommands,
11265 EffectsCommands,
11266 FXCommands,
11267 MiscellanyCommands
11268 };
11269
11270 char
11271 command[MagickPathExtent],
11272 text[MagickPathExtent];
11273
11274 DisplayCommand
11275 display_command;
11276
11277 Cursor
11278 cursor;
11279
11280 Image
11281 *roi_image;
11282
11283 int
11284 entry,
11285 id,
11286 x,
11287 y;
11288
11289 double
11290 scale_factor;
11291
11292 MagickProgressMonitor
11293 progress_monitor;
11294
11296 crop_info,
11297 highlight_info,
11298 roi_info;
11299
11300 unsigned int
11301 height,
11302 width;
11303
11304 size_t
11305 state;
11306
11307 XEvent
11308 event;
11309
11310 /*
11311 Map Command widget.
11312 */
11313 (void) CloneString(&windows->command.name,"ROI");
11314 windows->command.data=0;
11315 (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11316 (void) XMapRaised(display,windows->command.id);
11317 XClientMessage(display,windows->image.id,windows->im_protocols,
11318 windows->im_update_widget,CurrentTime);
11319 /*
11320 Track pointer until button 1 is pressed.
11321 */
11322 XQueryPosition(display,windows->image.id,&x,&y);
11323 (void) XSelectInput(display,windows->image.id,
11324 windows->image.attributes.event_mask | PointerMotionMask);
11325 roi_info.x=(ssize_t) windows->image.x+x;
11326 roi_info.y=(ssize_t) windows->image.y+y;
11327 roi_info.width=0;
11328 roi_info.height=0;
11329 cursor=XCreateFontCursor(display,XC_fleur);
11330 state=DefaultState;
11331 do
11332 {
11333 if (windows->info.mapped != MagickFalse)
11334 {
11335 /*
11336 Display pointer position.
11337 */
11338 (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
11339 (long) roi_info.x,(long) roi_info.y);
11340 XInfoWidget(display,windows,text);
11341 }
11342 /*
11343 Wait for next event.
11344 */
11345 XScreenEvent(display,windows,&event,exception);
11346 if (event.xany.window == windows->command.id)
11347 {
11348 /*
11349 Select a command from the Command widget.
11350 */
11351 id=XCommandWidget(display,windows,ROIMenu,&event);
11352 if (id < 0)
11353 continue;
11354 switch (ROICommands[id])
11355 {
11356 case ROIHelpCommand:
11357 {
11358 XTextViewHelp(display,resource_info,windows,MagickFalse,
11359 "Help Viewer - Region of Interest",ImageROIHelp);
11360 break;
11361 }
11362 case ROIDismissCommand:
11363 {
11364 /*
11365 Prematurely exit.
11366 */
11367 state|=EscapeState;
11368 state|=ExitState;
11369 break;
11370 }
11371 default:
11372 break;
11373 }
11374 continue;
11375 }
11376 switch (event.type)
11377 {
11378 case ButtonPress:
11379 {
11380 if (event.xbutton.button != Button1)
11381 break;
11382 if (event.xbutton.window != windows->image.id)
11383 break;
11384 /*
11385 Note first corner of region of interest rectangle-- exit loop.
11386 */
11387 (void) XCheckDefineCursor(display,windows->image.id,cursor);
11388 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11389 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11390 state|=ExitState;
11391 break;
11392 }
11393 case ButtonRelease:
11394 break;
11395 case Expose:
11396 break;
11397 case KeyPress:
11398 {
11399 KeySym
11400 key_symbol;
11401
11402 if (event.xkey.window != windows->image.id)
11403 break;
11404 /*
11405 Respond to a user key press.
11406 */
11407 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11408 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11409 switch ((int) key_symbol)
11410 {
11411 case XK_Escape:
11412 case XK_F20:
11413 {
11414 /*
11415 Prematurely exit.
11416 */
11417 state|=EscapeState;
11418 state|=ExitState;
11419 break;
11420 }
11421 case XK_F1:
11422 case XK_Help:
11423 {
11424 XTextViewHelp(display,resource_info,windows,MagickFalse,
11425 "Help Viewer - Region of Interest",ImageROIHelp);
11426 break;
11427 }
11428 default:
11429 {
11430 (void) XBell(display,0);
11431 break;
11432 }
11433 }
11434 break;
11435 }
11436 case MotionNotify:
11437 {
11438 /*
11439 Map and unmap Info widget as text cursor crosses its boundaries.
11440 */
11441 x=event.xmotion.x;
11442 y=event.xmotion.y;
11443 if (windows->info.mapped != MagickFalse)
11444 {
11445 if ((x < (windows->info.x+(int) windows->info.width)) &&
11446 (y < (windows->info.y+(int) windows->info.height)))
11447 (void) XWithdrawWindow(display,windows->info.id,
11448 windows->info.screen);
11449 }
11450 else
11451 if ((x > (windows->info.x+(int) windows->info.width)) ||
11452 (y > (windows->info.y+(int) windows->info.height)))
11453 (void) XMapWindow(display,windows->info.id);
11454 roi_info.x=(ssize_t) windows->image.x+x;
11455 roi_info.y=(ssize_t) windows->image.y+y;
11456 break;
11457 }
11458 default:
11459 break;
11460 }
11461 } while ((state & ExitState) == 0);
11462 (void) XSelectInput(display,windows->image.id,
11463 windows->image.attributes.event_mask);
11464 if ((state & EscapeState) != 0)
11465 {
11466 /*
11467 User want to exit without region of interest.
11468 */
11469 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11470 (void) XFreeCursor(display,cursor);
11471 return(MagickTrue);
11472 }
11473 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11474 do
11475 {
11476 /*
11477 Size rectangle as pointer moves until the mouse button is released.
11478 */
11479 x=(int) roi_info.x;
11480 y=(int) roi_info.y;
11481 roi_info.width=0;
11482 roi_info.height=0;
11483 state=DefaultState;
11484 do
11485 {
11486 highlight_info=roi_info;
11487 highlight_info.x=roi_info.x-windows->image.x;
11488 highlight_info.y=roi_info.y-windows->image.y;
11489 if ((highlight_info.width > 3) && (highlight_info.height > 3))
11490 {
11491 /*
11492 Display info and draw region of interest rectangle.
11493 */
11494 if (windows->info.mapped == MagickFalse)
11495 (void) XMapWindow(display,windows->info.id);
11496 (void) FormatLocaleString(text,MagickPathExtent,
11497 " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11498 roi_info.height,(double) roi_info.x,(double) roi_info.y);
11499 XInfoWidget(display,windows,text);
11500 XHighlightRectangle(display,windows->image.id,
11501 windows->image.highlight_context,&highlight_info);
11502 }
11503 else
11504 if (windows->info.mapped != MagickFalse)
11505 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11506 /*
11507 Wait for next event.
11508 */
11509 XScreenEvent(display,windows,&event,exception);
11510 if ((highlight_info.width > 3) && (highlight_info.height > 3))
11511 XHighlightRectangle(display,windows->image.id,
11512 windows->image.highlight_context,&highlight_info);
11513 switch (event.type)
11514 {
11515 case ButtonPress:
11516 {
11517 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11518 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11519 break;
11520 }
11521 case ButtonRelease:
11522 {
11523 /*
11524 User has committed to region of interest rectangle.
11525 */
11526 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11527 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11528 XSetCursorState(display,windows,MagickFalse);
11529 state|=ExitState;
11530 if (LocaleCompare(windows->command.name,"Apply") == 0)
11531 break;
11532 (void) CloneString(&windows->command.name,"Apply");
11533 windows->command.data=ApplyMenus;
11534 (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11535 break;
11536 }
11537 case Expose:
11538 break;
11539 case MotionNotify:
11540 {
11541 roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11542 roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11543 }
11544 default:
11545 break;
11546 }
11547 if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11548 ((state & ExitState) != 0))
11549 {
11550 /*
11551 Check boundary conditions.
11552 */
11553 if (roi_info.x < 0)
11554 roi_info.x=0;
11555 else
11556 if (roi_info.x > (ssize_t) windows->image.ximage->width)
11557 roi_info.x=(ssize_t) windows->image.ximage->width;
11558 if ((int) roi_info.x < x)
11559 roi_info.width=(unsigned int) (x-roi_info.x);
11560 else
11561 {
11562 roi_info.width=(unsigned int) (roi_info.x-x);
11563 roi_info.x=(ssize_t) x;
11564 }
11565 if (roi_info.y < 0)
11566 roi_info.y=0;
11567 else
11568 if (roi_info.y > (ssize_t) windows->image.ximage->height)
11569 roi_info.y=(ssize_t) windows->image.ximage->height;
11570 if ((int) roi_info.y < y)
11571 roi_info.height=(unsigned int) (y-roi_info.y);
11572 else
11573 {
11574 roi_info.height=(unsigned int) (roi_info.y-y);
11575 roi_info.y=(ssize_t) y;
11576 }
11577 }
11578 } while ((state & ExitState) == 0);
11579 /*
11580 Wait for user to grab a corner of the rectangle or press return.
11581 */
11582 state=DefaultState;
11583 display_command=NullCommand;
11584 crop_info.x=0;
11585 crop_info.y=0;
11586 (void) XMapWindow(display,windows->info.id);
11587 do
11588 {
11589 if (windows->info.mapped != MagickFalse)
11590 {
11591 /*
11592 Display pointer position.
11593 */
11594 (void) FormatLocaleString(text,MagickPathExtent,
11595 " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11596 roi_info.height,(double) roi_info.x,(double) roi_info.y);
11597 XInfoWidget(display,windows,text);
11598 }
11599 highlight_info=roi_info;
11600 highlight_info.x=roi_info.x-windows->image.x;
11601 highlight_info.y=roi_info.y-windows->image.y;
11602 if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11603 {
11604 state|=EscapeState;
11605 state|=ExitState;
11606 break;
11607 }
11608 if ((state & UpdateRegionState) != 0)
11609 {
11610 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11611 switch (display_command)
11612 {
11613 case UndoCommand:
11614 case RedoCommand:
11615 {
11616 (void) XMagickCommand(display,resource_info,windows,
11617 display_command,image,exception);
11618 break;
11619 }
11620 default:
11621 {
11622 /*
11623 Region of interest is relative to image configuration.
11624 */
11625 progress_monitor=SetImageProgressMonitor(*image,
11626 (MagickProgressMonitor) NULL,(*image)->client_data);
11627 crop_info=roi_info;
11628 width=(unsigned int) (*image)->columns;
11629 height=(unsigned int) (*image)->rows;
11630 x=0;
11631 y=0;
11632 if (windows->image.crop_geometry != (char *) NULL)
11633 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11634 &width,&height);
11635 scale_factor=(double) width/windows->image.ximage->width;
11636 crop_info.x+=x;
11637 crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11638 crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11639 scale_factor=(double)
11640 height/windows->image.ximage->height;
11641 crop_info.y+=y;
11642 crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11643 crop_info.height=(unsigned int)
11644 (scale_factor*crop_info.height+0.5);
11645 roi_image=CropImage(*image,&crop_info,exception);
11646 (void) SetImageProgressMonitor(*image,progress_monitor,
11647 (*image)->client_data);
11648 if (roi_image == (Image *) NULL)
11649 continue;
11650 /*
11651 Apply image processing technique to the region of interest.
11652 */
11653 windows->image.orphan=MagickTrue;
11654 (void) XMagickCommand(display,resource_info,windows,
11655 display_command,&roi_image,exception);
11656 progress_monitor=SetImageProgressMonitor(*image,
11657 (MagickProgressMonitor) NULL,(*image)->client_data);
11658 (void) XMagickCommand(display,resource_info,windows,
11659 SaveToUndoBufferCommand,image,exception);
11660 windows->image.orphan=MagickFalse;
11661 (void) CompositeImage(*image,roi_image,CopyCompositeOp,
11662 MagickTrue,crop_info.x,crop_info.y,exception);
11663 roi_image=DestroyImage(roi_image);
11664 (void) SetImageProgressMonitor(*image,progress_monitor,
11665 (*image)->client_data);
11666 break;
11667 }
11668 }
11669 if (display_command != InfoCommand)
11670 {
11671 XConfigureImageColormap(display,resource_info,windows,*image,
11672 exception);
11673 (void) XConfigureImage(display,resource_info,windows,*image,
11674 exception);
11675 }
11676 XCheckRefreshWindows(display,windows);
11677 XInfoWidget(display,windows,text);
11678 (void) XSetFunction(display,windows->image.highlight_context,
11679 GXinvert);
11680 state&=(unsigned int) (~UpdateRegionState);
11681 }
11682 XHighlightRectangle(display,windows->image.id,
11683 windows->image.highlight_context,&highlight_info);
11684 XScreenEvent(display,windows,&event,exception);
11685 if (event.xany.window == windows->command.id)
11686 {
11687 /*
11688 Select a command from the Command widget.
11689 */
11690 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11691 display_command=NullCommand;
11692 id=XCommandWidget(display,windows,ApplyMenu,&event);
11693 if (id >= 0)
11694 {
11695 (void) CopyMagickString(command,ApplyMenu[id],MagickPathExtent);
11696 display_command=ApplyCommands[id];
11697 if (id < ApplyMenus)
11698 {
11699 /*
11700 Select a command from a pop-up menu.
11701 */
11702 entry=XMenuWidget(display,windows,ApplyMenu[id],
11703 (const char **) Menus[id],command);
11704 if (entry >= 0)
11705 {
11706 (void) CopyMagickString(command,Menus[id][entry],
11707 MagickPathExtent);
11708 display_command=Commands[id][entry];
11709 }
11710 }
11711 }
11712 (void) XSetFunction(display,windows->image.highlight_context,
11713 GXinvert);
11714 XHighlightRectangle(display,windows->image.id,
11715 windows->image.highlight_context,&highlight_info);
11716 if (display_command == HelpCommand)
11717 {
11718 (void) XSetFunction(display,windows->image.highlight_context,
11719 GXcopy);
11720 XTextViewHelp(display,resource_info,windows,MagickFalse,
11721 "Help Viewer - Region of Interest",ImageROIHelp);
11722 (void) XSetFunction(display,windows->image.highlight_context,
11723 GXinvert);
11724 continue;
11725 }
11726 if (display_command == QuitCommand)
11727 {
11728 /*
11729 exit.
11730 */
11731 state|=EscapeState;
11732 state|=ExitState;
11733 continue;
11734 }
11735 if (display_command != NullCommand)
11736 state|=UpdateRegionState;
11737 continue;
11738 }
11739 XHighlightRectangle(display,windows->image.id,
11740 windows->image.highlight_context,&highlight_info);
11741 switch (event.type)
11742 {
11743 case ButtonPress:
11744 {
11745 x=windows->image.x;
11746 y=windows->image.y;
11747 if (event.xbutton.button != Button1)
11748 break;
11749 if (event.xbutton.window != windows->image.id)
11750 break;
11751 x=windows->image.x+event.xbutton.x;
11752 y=windows->image.y+event.xbutton.y;
11753 if ((x < (int) (roi_info.x+RoiDelta)) &&
11754 (x > (int) (roi_info.x-RoiDelta)) &&
11755 (y < (int) (roi_info.y+RoiDelta)) &&
11756 (y > (int) (roi_info.y-RoiDelta)))
11757 {
11758 roi_info.x=roi_info.x+(int) roi_info.width;
11759 roi_info.y=roi_info.y+(int) roi_info.height;
11760 state|=UpdateConfigurationState;
11761 break;
11762 }
11763 if ((x < (int) (roi_info.x+RoiDelta)) &&
11764 (x > (int) (roi_info.x-RoiDelta)) &&
11765 (y < (roi_info.y+(int) roi_info.height+RoiDelta)) &&
11766 (y > (roi_info.y+(int) roi_info.height-RoiDelta)))
11767 {
11768 roi_info.x=roi_info.x+(int) roi_info.width;
11769 state|=UpdateConfigurationState;
11770 break;
11771 }
11772 if ((x < (roi_info.x+(int) roi_info.width+RoiDelta)) &&
11773 (x > (roi_info.x+(int) roi_info.width-RoiDelta)) &&
11774 (y < (int) (roi_info.y+RoiDelta)) &&
11775 (y > (int) (roi_info.y-RoiDelta)))
11776 {
11777 roi_info.y=roi_info.y+(int) roi_info.height;
11778 state|=UpdateConfigurationState;
11779 break;
11780 }
11781 if ((x < (roi_info.x+(int) roi_info.width+RoiDelta)) &&
11782 (x > (roi_info.x+(int) roi_info.width-RoiDelta)) &&
11783 (y < (roi_info.y+(int) roi_info.height+RoiDelta)) &&
11784 (y > (roi_info.y+(int) roi_info.height-RoiDelta)))
11785 {
11786 state|=UpdateConfigurationState;
11787 break;
11788 }
11789 magick_fallthrough;
11790 }
11791 case ButtonRelease:
11792 {
11793 if (event.xbutton.window == windows->pan.id)
11794 if ((highlight_info.x != crop_info.x-windows->image.x) ||
11795 (highlight_info.y != crop_info.y-windows->image.y))
11796 XHighlightRectangle(display,windows->image.id,
11797 windows->image.highlight_context,&highlight_info);
11798 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11799 event.xbutton.time);
11800 break;
11801 }
11802 case Expose:
11803 {
11804 if (event.xexpose.window == windows->image.id)
11805 if (event.xexpose.count == 0)
11806 {
11807 event.xexpose.x=(int) highlight_info.x;
11808 event.xexpose.y=(int) highlight_info.y;
11809 event.xexpose.width=(int) highlight_info.width;
11810 event.xexpose.height=(int) highlight_info.height;
11811 XRefreshWindow(display,&windows->image,&event);
11812 }
11813 if (event.xexpose.window == windows->info.id)
11814 if (event.xexpose.count == 0)
11815 XInfoWidget(display,windows,text);
11816 break;
11817 }
11818 case KeyPress:
11819 {
11820 KeySym
11821 key_symbol;
11822
11823 if (event.xkey.window != windows->image.id)
11824 break;
11825 /*
11826 Respond to a user key press.
11827 */
11828 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11829 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11830 switch ((int) key_symbol)
11831 {
11832 case XK_Shift_L:
11833 case XK_Shift_R:
11834 break;
11835 case XK_Escape:
11836 case XK_F20:
11837 {
11838 state|=EscapeState;
11839 magick_fallthrough;
11840 }
11841 case XK_Return:
11842 {
11843 state|=ExitState;
11844 break;
11845 }
11846 case XK_Home:
11847 case XK_KP_Home:
11848 {
11849 roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11850 roi_info.y=(ssize_t) (windows->image.height/2L-
11851 roi_info.height/2L);
11852 break;
11853 }
11854 case XK_Left:
11855 case XK_KP_Left:
11856 {
11857 roi_info.x--;
11858 break;
11859 }
11860 case XK_Up:
11861 case XK_KP_Up:
11862 case XK_Next:
11863 {
11864 roi_info.y--;
11865 break;
11866 }
11867 case XK_Right:
11868 case XK_KP_Right:
11869 {
11870 roi_info.x++;
11871 break;
11872 }
11873 case XK_Prior:
11874 case XK_Down:
11875 case XK_KP_Down:
11876 {
11877 roi_info.y++;
11878 break;
11879 }
11880 case XK_F1:
11881 case XK_Help:
11882 {
11883 (void) XSetFunction(display,windows->image.highlight_context,
11884 GXcopy);
11885 XTextViewHelp(display,resource_info,windows,MagickFalse,
11886 "Help Viewer - Region of Interest",ImageROIHelp);
11887 (void) XSetFunction(display,windows->image.highlight_context,
11888 GXinvert);
11889 break;
11890 }
11891 default:
11892 {
11893 display_command=XImageWindowCommand(display,resource_info,windows,
11894 event.xkey.state,key_symbol,image,exception);
11895 if (display_command != NullCommand)
11896 state|=UpdateRegionState;
11897 break;
11898 }
11899 }
11900 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11901 event.xkey.time);
11902 break;
11903 }
11904 case KeyRelease:
11905 break;
11906 case MotionNotify:
11907 {
11908 if (event.xbutton.window != windows->image.id)
11909 break;
11910 /*
11911 Map and unmap Info widget as text cursor crosses its boundaries.
11912 */
11913 x=event.xmotion.x;
11914 y=event.xmotion.y;
11915 if (windows->info.mapped != MagickFalse)
11916 {
11917 if ((x < (windows->info.x+(int) windows->info.width)) &&
11918 (y < (windows->info.y+(int) windows->info.height)))
11919 (void) XWithdrawWindow(display,windows->info.id,
11920 windows->info.screen);
11921 }
11922 else
11923 if ((x > (windows->info.x+(int) windows->info.width)) ||
11924 (y > (windows->info.y+(int) windows->info.height)))
11925 (void) XMapWindow(display,windows->info.id);
11926 roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11927 roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11928 break;
11929 }
11930 case SelectionRequest:
11931 {
11932 XSelectionEvent
11933 notify;
11934
11935 XSelectionRequestEvent
11936 *request;
11937
11938 /*
11939 Set primary selection.
11940 */
11941 (void) FormatLocaleString(text,MagickPathExtent,
11942 "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11943 roi_info.height,(double) roi_info.x,(double) roi_info.y);
11944 request=(&(event.xselectionrequest));
11945 (void) XChangeProperty(request->display,request->requestor,
11946 request->property,request->target,8,PropModeReplace,
11947 (unsigned char *) text,(int) strlen(text));
11948 notify.type=SelectionNotify;
11949 notify.display=request->display;
11950 notify.requestor=request->requestor;
11951 notify.selection=request->selection;
11952 notify.target=request->target;
11953 notify.time=request->time;
11954 if (request->property == None)
11955 notify.property=request->target;
11956 else
11957 notify.property=request->property;
11958 (void) XSendEvent(request->display,request->requestor,False,0,
11959 (XEvent *) &notify);
11960 }
11961 default:
11962 break;
11963 }
11964 if ((state & UpdateConfigurationState) != 0)
11965 {
11966 (void) XPutBackEvent(display,&event);
11967 (void) XCheckDefineCursor(display,windows->image.id,cursor);
11968 break;
11969 }
11970 } while ((state & ExitState) == 0);
11971 } while ((state & ExitState) == 0);
11972 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11973 XSetCursorState(display,windows,MagickFalse);
11974 if ((state & EscapeState) != 0)
11975 return(MagickTrue);
11976 return(MagickTrue);
11977}
11978
11979/*
11980%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11981% %
11982% %
11983% %
11984+ X R o t a t e I m a g e %
11985% %
11986% %
11987% %
11988%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11989%
11990% XRotateImage() rotates the X image. If the degrees parameter if zero, the
11991% rotation angle is computed from the slope of a line drawn by the user.
11992%
11993% The format of the XRotateImage method is:
11994%
11995% MagickBooleanType XRotateImage(Display *display,
11996% XResourceInfo *resource_info,XWindows *windows,double degrees,
11997% Image **image,ExceptionInfo *exception)
11998%
11999% A description of each parameter follows:
12000%
12001% o display: Specifies a connection to an X server; returned from
12002% XOpenDisplay.
12003%
12004% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12005%
12006% o windows: Specifies a pointer to a XWindows structure.
12007%
12008% o degrees: Specifies the number of degrees to rotate the image.
12009%
12010% o image: the image.
12011%
12012% o exception: return any errors or warnings in this structure.
12013%
12014*/
12015static MagickBooleanType XRotateImage(Display *display,
12016 XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12017 ExceptionInfo *exception)
12018{
12019 const char
12020 *const RotateMenu[] =
12021 {
12022 "Pixel Color",
12023 "Direction",
12024 "Help",
12025 "Dismiss",
12026 (char *) NULL
12027 };
12028
12029 static ModeType
12030 direction = HorizontalRotateCommand;
12031
12032 static const ModeType
12033 DirectionCommands[] =
12034 {
12035 HorizontalRotateCommand,
12036 VerticalRotateCommand
12037 },
12038 RotateCommands[] =
12039 {
12040 RotateColorCommand,
12041 RotateDirectionCommand,
12042 RotateHelpCommand,
12043 RotateDismissCommand
12044 };
12045
12046 static unsigned int
12047 pen_id = 0;
12048
12049 char
12050 command[MagickPathExtent],
12051 text[MagickPathExtent];
12052
12053 Image
12054 *rotate_image;
12055
12056 int
12057 id,
12058 x,
12059 y;
12060
12061 double
12062 normalized_degrees;
12063
12064 int
12065 i;
12066
12067 unsigned int
12068 height,
12069 rotations,
12070 width;
12071
12072 if (degrees == 0.0)
12073 {
12074 unsigned int
12075 distance;
12076
12077 size_t
12078 state;
12079
12080 XEvent
12081 event;
12082
12083 XSegment
12084 rotate_info;
12085
12086 /*
12087 Map Command widget.
12088 */
12089 (void) CloneString(&windows->command.name,"Rotate");
12090 windows->command.data=2;
12091 (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12092 (void) XMapRaised(display,windows->command.id);
12093 XClientMessage(display,windows->image.id,windows->im_protocols,
12094 windows->im_update_widget,CurrentTime);
12095 /*
12096 Wait for first button press.
12097 */
12098 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12099 XQueryPosition(display,windows->image.id,&x,&y);
12100 rotate_info.x1=x;
12101 rotate_info.y1=y;
12102 rotate_info.x2=x;
12103 rotate_info.y2=y;
12104 state=DefaultState;
12105 do
12106 {
12107 XHighlightLine(display,windows->image.id,
12108 windows->image.highlight_context,&rotate_info);
12109 /*
12110 Wait for next event.
12111 */
12112 XScreenEvent(display,windows,&event,exception);
12113 XHighlightLine(display,windows->image.id,
12114 windows->image.highlight_context,&rotate_info);
12115 if (event.xany.window == windows->command.id)
12116 {
12117 /*
12118 Select a command from the Command widget.
12119 */
12120 id=XCommandWidget(display,windows,RotateMenu,&event);
12121 if (id < 0)
12122 continue;
12123 (void) XSetFunction(display,windows->image.highlight_context,
12124 GXcopy);
12125 switch (RotateCommands[id])
12126 {
12127 case RotateColorCommand:
12128 {
12129 const char
12130 *ColorMenu[MaxNumberPens];
12131
12132 int
12133 pen_number;
12134
12135 XColor
12136 color;
12137
12138 /*
12139 Initialize menu selections.
12140 */
12141 for (i=0; i < (int) (MaxNumberPens-2); i++)
12142 ColorMenu[i]=resource_info->pen_colors[i];
12143 ColorMenu[MaxNumberPens-2]="Browser...";
12144 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12145 /*
12146 Select a pen color from the pop-up menu.
12147 */
12148 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12149 (const char **) ColorMenu,command);
12150 if (pen_number < 0)
12151 break;
12152 if (pen_number == (MaxNumberPens-2))
12153 {
12154 static char
12155 color_name[MagickPathExtent] = "gray";
12156
12157 /*
12158 Select a pen color from a dialog.
12159 */
12160 resource_info->pen_colors[pen_number]=color_name;
12161 XColorBrowserWidget(display,windows,"Select",color_name);
12162 if (*color_name == '\0')
12163 break;
12164 }
12165 /*
12166 Set pen color.
12167 */
12168 (void) XParseColor(display,windows->map_info->colormap,
12169 resource_info->pen_colors[pen_number],&color);
12170 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12171 (unsigned int) MaxColors,&color);
12172 windows->pixel_info->pen_colors[pen_number]=color;
12173 pen_id=(unsigned int) pen_number;
12174 break;
12175 }
12176 case RotateDirectionCommand:
12177 {
12178 const char
12179 *Directions[] =
12180 {
12181 "horizontal",
12182 "vertical",
12183 (char *) NULL,
12184 };
12185
12186 /*
12187 Select a command from the pop-up menu.
12188 */
12189 id=XMenuWidget(display,windows,RotateMenu[id],
12190 Directions,command);
12191 if (id >= 0)
12192 direction=DirectionCommands[id];
12193 break;
12194 }
12195 case RotateHelpCommand:
12196 {
12197 XTextViewHelp(display,resource_info,windows,MagickFalse,
12198 "Help Viewer - Image Rotation",ImageRotateHelp);
12199 break;
12200 }
12201 case RotateDismissCommand:
12202 {
12203 /*
12204 Prematurely exit.
12205 */
12206 state|=EscapeState;
12207 state|=ExitState;
12208 break;
12209 }
12210 default:
12211 break;
12212 }
12213 (void) XSetFunction(display,windows->image.highlight_context,
12214 GXinvert);
12215 continue;
12216 }
12217 switch (event.type)
12218 {
12219 case ButtonPress:
12220 {
12221 if (event.xbutton.button != Button1)
12222 break;
12223 if (event.xbutton.window != windows->image.id)
12224 break;
12225 /*
12226 exit loop.
12227 */
12228 (void) XSetFunction(display,windows->image.highlight_context,
12229 GXcopy);
12230 rotate_info.x1=event.xbutton.x;
12231 rotate_info.y1=event.xbutton.y;
12232 state|=ExitState;
12233 break;
12234 }
12235 case ButtonRelease:
12236 break;
12237 case Expose:
12238 break;
12239 case KeyPress:
12240 {
12241 char
12242 command[MagickPathExtent];
12243
12244 KeySym
12245 key_symbol;
12246
12247 if (event.xkey.window != windows->image.id)
12248 break;
12249 /*
12250 Respond to a user key press.
12251 */
12252 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12253 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12254 switch ((int) key_symbol)
12255 {
12256 case XK_Escape:
12257 case XK_F20:
12258 {
12259 /*
12260 Prematurely exit.
12261 */
12262 state|=EscapeState;
12263 state|=ExitState;
12264 break;
12265 }
12266 case XK_F1:
12267 case XK_Help:
12268 {
12269 (void) XSetFunction(display,windows->image.highlight_context,
12270 GXcopy);
12271 XTextViewHelp(display,resource_info,windows,MagickFalse,
12272 "Help Viewer - Image Rotation",ImageRotateHelp);
12273 (void) XSetFunction(display,windows->image.highlight_context,
12274 GXinvert);
12275 break;
12276 }
12277 default:
12278 {
12279 (void) XBell(display,0);
12280 break;
12281 }
12282 }
12283 break;
12284 }
12285 case MotionNotify:
12286 {
12287 rotate_info.x1=event.xmotion.x;
12288 rotate_info.y1=event.xmotion.y;
12289 }
12290 }
12291 rotate_info.x2=rotate_info.x1;
12292 rotate_info.y2=rotate_info.y1;
12293 if (direction == HorizontalRotateCommand)
12294 rotate_info.x2+=32;
12295 else
12296 rotate_info.y2-=32;
12297 } while ((state & ExitState) == 0);
12298 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12299 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12300 if ((state & EscapeState) != 0)
12301 return(MagickTrue);
12302 /*
12303 Draw line as pointer moves until the mouse button is released.
12304 */
12305 distance=0;
12306 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12307 state=DefaultState;
12308 do
12309 {
12310 if (distance > 9)
12311 {
12312 /*
12313 Display info and draw rotation line.
12314 */
12315 if (windows->info.mapped == MagickFalse)
12316 (void) XMapWindow(display,windows->info.id);
12317 (void) FormatLocaleString(text,MagickPathExtent," %g",
12318 direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12319 XInfoWidget(display,windows,text);
12320 XHighlightLine(display,windows->image.id,
12321 windows->image.highlight_context,&rotate_info);
12322 }
12323 else
12324 if (windows->info.mapped != MagickFalse)
12325 (void) XWithdrawWindow(display,windows->info.id,
12326 windows->info.screen);
12327 /*
12328 Wait for next event.
12329 */
12330 XScreenEvent(display,windows,&event,exception);
12331 if (distance > 9)
12332 XHighlightLine(display,windows->image.id,
12333 windows->image.highlight_context,&rotate_info);
12334 switch (event.type)
12335 {
12336 case ButtonPress:
12337 break;
12338 case ButtonRelease:
12339 {
12340 /*
12341 User has committed to rotation line.
12342 */
12343 rotate_info.x2=event.xbutton.x;
12344 rotate_info.y2=event.xbutton.y;
12345 state|=ExitState;
12346 break;
12347 }
12348 case Expose:
12349 break;
12350 case MotionNotify:
12351 {
12352 rotate_info.x2=event.xmotion.x;
12353 rotate_info.y2=event.xmotion.y;
12354 }
12355 default:
12356 break;
12357 }
12358 /*
12359 Check boundary conditions.
12360 */
12361 if (rotate_info.x2 < 0)
12362 rotate_info.x2=0;
12363 else
12364 if (rotate_info.x2 > (int) windows->image.width)
12365 rotate_info.x2=(short) windows->image.width;
12366 if (rotate_info.y2 < 0)
12367 rotate_info.y2=0;
12368 else
12369 if (rotate_info.y2 > (int) windows->image.height)
12370 rotate_info.y2=(short) windows->image.height;
12371 /*
12372 Compute rotation angle from the slope of the line.
12373 */
12374 degrees=0.0;
12375 distance=(unsigned int)
12376 (((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12377 ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1)));
12378 if (distance > 9)
12379 degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12380 rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12381 } while ((state & ExitState) == 0);
12382 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12383 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12384 if (distance <= 9)
12385 return(MagickTrue);
12386 }
12387 if (direction == VerticalRotateCommand)
12388 degrees-=90.0;
12389 if (degrees == 0.0)
12390 return(MagickTrue);
12391 /*
12392 Rotate image.
12393 */
12394 normalized_degrees=degrees;
12395 while (normalized_degrees < -45.0)
12396 normalized_degrees+=360.0;
12397 for (rotations=0; normalized_degrees > 45.0; rotations++)
12398 normalized_degrees-=90.0;
12399 if (normalized_degrees != 0.0)
12400 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12401 exception);
12402 XSetCursorState(display,windows,MagickTrue);
12403 XCheckRefreshWindows(display,windows);
12404 (*image)->background_color.red=(double) ScaleShortToQuantum(
12405 windows->pixel_info->pen_colors[pen_id].red);
12406 (*image)->background_color.green=(double) ScaleShortToQuantum(
12407 windows->pixel_info->pen_colors[pen_id].green);
12408 (*image)->background_color.blue=(double) ScaleShortToQuantum(
12409 windows->pixel_info->pen_colors[pen_id].blue);
12410 rotate_image=RotateImage(*image,degrees,exception);
12411 XSetCursorState(display,windows,MagickFalse);
12412 if (rotate_image == (Image *) NULL)
12413 return(MagickFalse);
12414 *image=DestroyImage(*image);
12415 *image=rotate_image;
12416 if (windows->image.crop_geometry != (char *) NULL)
12417 {
12418 /*
12419 Rotate crop geometry.
12420 */
12421 width=(unsigned int) (*image)->columns;
12422 height=(unsigned int) (*image)->rows;
12423 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12424 switch (rotations % 4)
12425 {
12426 default:
12427 case 0:
12428 break;
12429 case 1:
12430 {
12431 /*
12432 Rotate 90 degrees.
12433 */
12434 (void) FormatLocaleString(windows->image.crop_geometry,
12435 MagickPathExtent,"%ux%u%+d%+d",height,width,(int) (*image)->columns-
12436 (int) height-y,x);
12437 break;
12438 }
12439 case 2:
12440 {
12441 /*
12442 Rotate 180 degrees.
12443 */
12444 (void) FormatLocaleString(windows->image.crop_geometry,
12445 MagickPathExtent,"%ux%u%+d%+d",width,height,(int) width-x,(int)
12446 height-y);
12447 break;
12448 }
12449 case 3:
12450 {
12451 /*
12452 Rotate 270 degrees.
12453 */
12454 (void) FormatLocaleString(windows->image.crop_geometry,
12455 MagickPathExtent,"%ux%u%+d%+d",height,width,y,(int) (*image)->rows-
12456 (int) width-x);
12457 break;
12458 }
12459 }
12460 }
12461 if (windows->image.orphan != MagickFalse)
12462 return(MagickTrue);
12463 if (normalized_degrees != 0.0)
12464 {
12465 /*
12466 Update image colormap.
12467 */
12468 windows->image.window_changes.width=(int) (*image)->columns;
12469 windows->image.window_changes.height=(int) (*image)->rows;
12470 if (windows->image.crop_geometry != (char *) NULL)
12471 {
12472 /*
12473 Obtain dimensions of image from crop geometry.
12474 */
12475 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12476 &width,&height);
12477 windows->image.window_changes.width=(int) width;
12478 windows->image.window_changes.height=(int) height;
12479 }
12480 XConfigureImageColormap(display,resource_info,windows,*image,exception);
12481 }
12482 else
12483 if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12484 {
12485 windows->image.window_changes.width=windows->image.ximage->height;
12486 windows->image.window_changes.height=windows->image.ximage->width;
12487 }
12488 /*
12489 Update image configuration.
12490 */
12491 (void) XConfigureImage(display,resource_info,windows,*image,exception);
12492 return(MagickTrue);
12493}
12494
12495/*
12496%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12497% %
12498% %
12499% %
12500+ X S a v e I m a g e %
12501% %
12502% %
12503% %
12504%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12505%
12506% XSaveImage() saves an image to a file.
12507%
12508% The format of the XSaveImage method is:
12509%
12510% MagickBooleanType XSaveImage(Display *display,
12511% XResourceInfo *resource_info,XWindows *windows,Image *image,
12512% ExceptionInfo *exception)
12513%
12514% A description of each parameter follows:
12515%
12516% o display: Specifies a connection to an X server; returned from
12517% XOpenDisplay.
12518%
12519% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12520%
12521% o windows: Specifies a pointer to a XWindows structure.
12522%
12523% o image: the image.
12524%
12525% o exception: return any errors or warnings in this structure.
12526%
12527*/
12528static MagickBooleanType XSaveImage(Display *display,
12529 XResourceInfo *resource_info,XWindows *windows,Image *image,
12530 ExceptionInfo *exception)
12531{
12532 char
12533 filename[MagickPathExtent],
12534 geometry[MagickPathExtent];
12535
12536 Image
12537 *save_image;
12538
12539 ImageInfo
12540 *image_info;
12541
12542 MagickStatusType
12543 status;
12544
12545 /*
12546 Request file name from user.
12547 */
12548 if (resource_info->write_filename != (char *) NULL)
12549 (void) CopyMagickString(filename,resource_info->write_filename,
12550 MagickPathExtent);
12551 else
12552 {
12553 char
12554 path[MagickPathExtent];
12555
12556 int
12557 status;
12558
12559 GetPathComponent(image->filename,HeadPath,path);
12560 GetPathComponent(image->filename,TailPath,filename);
12561 if (*path != '\0')
12562 {
12563 status=chdir(path);
12564 if (status == -1)
12565 (void) ThrowMagickException(exception,GetMagickModule(),
12566 FileOpenError,"UnableToOpenFile","%s",path);
12567 }
12568 }
12569 XFileBrowserWidget(display,windows,"Save",filename);
12570 if (*filename == '\0')
12571 return(MagickTrue);
12572 if (IsPathAccessible(filename) != MagickFalse)
12573 {
12574 int
12575 status;
12576
12577 /*
12578 File exists-- seek user's permission before overwriting.
12579 */
12580 status=XConfirmWidget(display,windows,"Overwrite",filename);
12581 if (status <= 0)
12582 return(MagickTrue);
12583 }
12584 image_info=CloneImageInfo(resource_info->image_info);
12585 (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
12586 (void) SetImageInfo(image_info,1,exception);
12587 if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12588 (LocaleCompare(image_info->magick,"JPG") == 0))
12589 {
12590 char
12591 quality[MagickPathExtent];
12592
12593 int
12594 status;
12595
12596 /*
12597 Request JPEG quality from user.
12598 */
12599 (void) FormatLocaleString(quality,MagickPathExtent,"%.20g",(double)
12600 image->quality);
12601 status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12602 quality);
12603 if (*quality == '\0')
12604 return(MagickTrue);
12605 image->quality=StringToUnsignedLong(quality);
12606 image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12607 }
12608 if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12609 (LocaleCompare(image_info->magick,"PDF") == 0) ||
12610 (LocaleCompare(image_info->magick,"PS") == 0) ||
12611 (LocaleCompare(image_info->magick,"PS2") == 0))
12612 {
12613 char
12614 geometry[MagickPathExtent];
12615
12616 const char
12617 *const PageSizes[] =
12618 {
12619 "Letter",
12620 "Tabloid",
12621 "Ledger",
12622 "Legal",
12623 "Statement",
12624 "Executive",
12625 "A3",
12626 "A4",
12627 "A5",
12628 "B4",
12629 "B5",
12630 "Folio",
12631 "Quarto",
12632 "10x14",
12633 (char *) NULL
12634 };
12635
12636 /*
12637 Request page geometry from user.
12638 */
12639 (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12640 if (LocaleCompare(image_info->magick,"PDF") == 0)
12641 (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12642 if (image_info->page != (char *) NULL)
12643 (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
12644 XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12645 "Select page geometry:",geometry);
12646 if (*geometry != '\0')
12647 image_info->page=GetPageGeometry(geometry);
12648 }
12649 /*
12650 Apply image transforms.
12651 */
12652 XSetCursorState(display,windows,MagickTrue);
12653 XCheckRefreshWindows(display,windows);
12654 save_image=CloneImage(image,0,0,MagickTrue,exception);
12655 if (save_image == (Image *) NULL)
12656 return(MagickFalse);
12657 (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
12658 windows->image.ximage->width,windows->image.ximage->height);
12659 (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12660 exception);
12661 /*
12662 Write image.
12663 */
12664 (void) CopyMagickString(save_image->filename,filename,MagickPathExtent);
12665 status=WriteImage(image_info,save_image,exception);
12666 if (status != MagickFalse)
12667 image->taint=MagickFalse;
12668 save_image=DestroyImage(save_image);
12669 image_info=DestroyImageInfo(image_info);
12670 XSetCursorState(display,windows,MagickFalse);
12671 return(status != 0 ? MagickTrue : MagickFalse);
12672}
12673
12674/*
12675%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12676% %
12677% %
12678% %
12679+ X S c r e e n E v e n t %
12680% %
12681% %
12682% %
12683%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12684%
12685% XScreenEvent() handles global events associated with the Pan and Magnify
12686% windows.
12687%
12688% The format of the XScreenEvent function is:
12689%
12690% void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12691% ExceptionInfo *exception)
12692%
12693% A description of each parameter follows:
12694%
12695% o display: Specifies a pointer to the Display structure; returned from
12696% XOpenDisplay.
12697%
12698% o windows: Specifies a pointer to a XWindows structure.
12699%
12700% o event: Specifies a pointer to a X11 XEvent structure.
12701%
12702% o exception: return any errors or warnings in this structure.
12703%
12704*/
12705
12706#if defined(__cplusplus) || defined(c_plusplus)
12707extern "C" {
12708#endif
12709
12710static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12711{
12712 XWindows
12713 *windows;
12714
12715 windows=(XWindows *) data;
12716 if ((event->type == ClientMessage) &&
12717 (event->xclient.window == windows->image.id))
12718 return(MagickFalse);
12719 return(MagickTrue);
12720}
12721
12722#if defined(__cplusplus) || defined(c_plusplus)
12723}
12724#endif
12725
12726static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12727 ExceptionInfo *exception)
12728{
12729 int
12730 x,
12731 y;
12732
12733 (void) XIfEvent(display,event,XPredicate,(char *) windows);
12734 if (event->xany.window == windows->command.id)
12735 return;
12736 switch (event->type)
12737 {
12738 case ButtonPress:
12739 case ButtonRelease:
12740 {
12741 if ((event->xbutton.button == Button3) &&
12742 (event->xbutton.state & Mod1Mask))
12743 {
12744 /*
12745 Convert Alt-Button3 to Button2.
12746 */
12747 event->xbutton.button=Button2;
12748 event->xbutton.state&=(unsigned int) (~Mod1Mask);
12749 }
12750 if (event->xbutton.window == windows->backdrop.id)
12751 {
12752 (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12753 event->xbutton.time);
12754 break;
12755 }
12756 if (event->xbutton.window == windows->pan.id)
12757 {
12758 XPanImage(display,windows,event,exception);
12759 break;
12760 }
12761 if (event->xbutton.window == windows->image.id)
12762 if (event->xbutton.button == Button2)
12763 {
12764 /*
12765 Update magnified image.
12766 */
12767 x=event->xbutton.x;
12768 y=event->xbutton.y;
12769 if (x < 0)
12770 x=0;
12771 else
12772 if (x >= (int) windows->image.width)
12773 x=(int) (windows->image.width-1);
12774 windows->magnify.x=(int) windows->image.x+x;
12775 if (y < 0)
12776 y=0;
12777 else
12778 if (y >= (int) windows->image.height)
12779 y=(int) (windows->image.height-1);
12780 windows->magnify.y=windows->image.y+y;
12781 if (windows->magnify.mapped == MagickFalse)
12782 (void) XMapRaised(display,windows->magnify.id);
12783 XMakeMagnifyImage(display,windows,exception);
12784 if (event->type == ButtonRelease)
12785 (void) XWithdrawWindow(display,windows->info.id,
12786 windows->info.screen);
12787 break;
12788 }
12789 break;
12790 }
12791 case ClientMessage:
12792 {
12793 /*
12794 If client window delete message, exit.
12795 */
12796 if (event->xclient.message_type != windows->wm_protocols)
12797 break;
12798 if (*event->xclient.data.l != (long) windows->wm_delete_window)
12799 break;
12800 if (event->xclient.window == windows->magnify.id)
12801 {
12802 (void) XWithdrawWindow(display,windows->magnify.id,
12803 windows->magnify.screen);
12804 break;
12805 }
12806 break;
12807 }
12808 case ConfigureNotify:
12809 {
12810 if (event->xconfigure.window == windows->magnify.id)
12811 {
12812 unsigned int
12813 magnify;
12814
12815 /*
12816 Magnify window has a new configuration.
12817 */
12818 windows->magnify.width=(unsigned int) event->xconfigure.width;
12819 windows->magnify.height=(unsigned int) event->xconfigure.height;
12820 if (windows->magnify.mapped == MagickFalse)
12821 break;
12822 magnify=1;
12823 while ((int) magnify <= event->xconfigure.width)
12824 magnify<<=1;
12825 while ((int) magnify <= event->xconfigure.height)
12826 magnify<<=1;
12827 magnify>>=1;
12828 if (((int) magnify != event->xconfigure.width) ||
12829 ((int) magnify != event->xconfigure.height))
12830 {
12831 XWindowChanges
12832 window_changes;
12833
12834 window_changes.width=(int) magnify;
12835 window_changes.height=(int) magnify;
12836 (void) XReconfigureWMWindow(display,windows->magnify.id,
12837 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12838 &window_changes);
12839 break;
12840 }
12841 XMakeMagnifyImage(display,windows,exception);
12842 break;
12843 }
12844 break;
12845 }
12846 case Expose:
12847 {
12848 if (event->xexpose.window == windows->image.id)
12849 {
12850 XRefreshWindow(display,&windows->image,event);
12851 break;
12852 }
12853 if (event->xexpose.window == windows->pan.id)
12854 if (event->xexpose.count == 0)
12855 {
12856 XDrawPanRectangle(display,windows);
12857 break;
12858 }
12859 if (event->xexpose.window == windows->magnify.id)
12860 if (event->xexpose.count == 0)
12861 {
12862 XMakeMagnifyImage(display,windows,exception);
12863 break;
12864 }
12865 break;
12866 }
12867 case KeyPress:
12868 {
12869 char
12870 command[MagickPathExtent];
12871
12872 KeySym
12873 key_symbol;
12874
12875 if (event->xkey.window != windows->magnify.id)
12876 break;
12877 /*
12878 Respond to a user key press.
12879 */
12880 (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12881 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12882 XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12883 exception);
12884 break;
12885 }
12886 case MapNotify:
12887 {
12888 if (event->xmap.window == windows->magnify.id)
12889 {
12890 windows->magnify.mapped=MagickTrue;
12891 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12892 break;
12893 }
12894 if (event->xmap.window == windows->info.id)
12895 {
12896 windows->info.mapped=MagickTrue;
12897 break;
12898 }
12899 break;
12900 }
12901 case MotionNotify:
12902 {
12903 while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12904 if (event->xmotion.window == windows->image.id)
12905 if (windows->magnify.mapped != MagickFalse)
12906 {
12907 /*
12908 Update magnified image.
12909 */
12910 x=event->xmotion.x;
12911 y=event->xmotion.y;
12912 if (x < 0)
12913 x=0;
12914 else
12915 if (x >= (int) windows->image.width)
12916 x=(int) (windows->image.width-1);
12917 windows->magnify.x=(int) windows->image.x+x;
12918 if (y < 0)
12919 y=0;
12920 else
12921 if (y >= (int) windows->image.height)
12922 y=(int) (windows->image.height-1);
12923 windows->magnify.y=windows->image.y+y;
12924 XMakeMagnifyImage(display,windows,exception);
12925 }
12926 break;
12927 }
12928 case UnmapNotify:
12929 {
12930 if (event->xunmap.window == windows->magnify.id)
12931 {
12932 windows->magnify.mapped=MagickFalse;
12933 break;
12934 }
12935 if (event->xunmap.window == windows->info.id)
12936 {
12937 windows->info.mapped=MagickFalse;
12938 break;
12939 }
12940 break;
12941 }
12942 default:
12943 break;
12944 }
12945}
12946
12947/*
12948%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12949% %
12950% %
12951% %
12952+ X S e t C r o p G e o m e t r y %
12953% %
12954% %
12955% %
12956%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12957%
12958% XSetCropGeometry() accepts a cropping geometry relative to the Image window
12959% and translates it to a cropping geometry relative to the image.
12960%
12961% The format of the XSetCropGeometry method is:
12962%
12963% void XSetCropGeometry(Display *display,XWindows *windows,
12964% RectangleInfo *crop_info,Image *image)
12965%
12966% A description of each parameter follows:
12967%
12968% o display: Specifies a connection to an X server; returned from
12969% XOpenDisplay.
12970%
12971% o windows: Specifies a pointer to a XWindows structure.
12972%
12973% o crop_info: A pointer to a RectangleInfo that defines a region of the
12974% Image window to crop.
12975%
12976% o image: the image.
12977%
12978*/
12979static void XSetCropGeometry(Display *display,XWindows *windows,
12980 RectangleInfo *crop_info,Image *image)
12981{
12982 char
12983 text[MagickPathExtent];
12984
12985 int
12986 x,
12987 y;
12988
12989 double
12990 scale_factor;
12991
12992 unsigned int
12993 height,
12994 width;
12995
12996 if (windows->info.mapped != MagickFalse)
12997 {
12998 /*
12999 Display info on cropping rectangle.
13000 */
13001 (void) FormatLocaleString(text,MagickPathExtent," %.20gx%.20g%+.20g%+.20g",
13002 (double) crop_info->width,(double) crop_info->height,(double)
13003 crop_info->x,(double) crop_info->y);
13004 XInfoWidget(display,windows,text);
13005 }
13006 /*
13007 Cropping geometry is relative to any previous crop geometry.
13008 */
13009 x=0;
13010 y=0;
13011 width=(unsigned int) image->columns;
13012 height=(unsigned int) image->rows;
13013 if (windows->image.crop_geometry != (char *) NULL)
13014 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13015 else
13016 windows->image.crop_geometry=AcquireString((char *) NULL);
13017 /*
13018 Define the crop geometry string from the cropping rectangle.
13019 */
13020 scale_factor=(double) width/windows->image.ximage->width;
13021 if (crop_info->x > 0)
13022 x+=(int) (scale_factor*crop_info->x+0.5);
13023 width=(unsigned int) (scale_factor*crop_info->width+0.5);
13024 if (width == 0)
13025 width=1;
13026 scale_factor=(double) height/windows->image.ximage->height;
13027 if (crop_info->y > 0)
13028 y+=(int) (scale_factor*crop_info->y+0.5);
13029 height=(unsigned int) (scale_factor*crop_info->height+0.5);
13030 if (height == 0)
13031 height=1;
13032 (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
13033 "%ux%u%+d%+d",width,height,x,y);
13034}
13035
13036/*
13037%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13038% %
13039% %
13040% %
13041+ X T i l e I m a g e %
13042% %
13043% %
13044% %
13045%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13046%
13047% XTileImage() loads or deletes a selected tile from a visual image directory.
13048% The load or delete command is chosen from a menu.
13049%
13050% The format of the XTileImage method is:
13051%
13052% Image *XTileImage(Display *display,XResourceInfo *resource_info,
13053% XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13054%
13055% A description of each parameter follows:
13056%
13057% o tile_image: XTileImage reads or deletes the tile image
13058% and returns it. A null image is returned if an error occurs.
13059%
13060% o display: Specifies a connection to an X server; returned from
13061% XOpenDisplay.
13062%
13063% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13064%
13065% o windows: Specifies a pointer to a XWindows structure.
13066%
13067% o image: the image; returned from ReadImage.
13068%
13069% o event: Specifies a pointer to a XEvent structure. If it is NULL,
13070% the entire image is refreshed.
13071%
13072% o exception: return any errors or warnings in this structure.
13073%
13074*/
13075static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13076 XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13077{
13078 const char
13079 *const VerbMenu[] =
13080 {
13081 "Load",
13082 "Next",
13083 "Former",
13084 "Delete",
13085 "Update",
13086 (char *) NULL,
13087 };
13088
13089 static const ModeType
13090 TileCommands[] =
13091 {
13092 TileLoadCommand,
13093 TileNextCommand,
13094 TileFormerCommand,
13095 TileDeleteCommand,
13096 TileUpdateCommand
13097 };
13098
13099 char
13100 command[MagickPathExtent],
13101 filename[MagickPathExtent];
13102
13103 Image
13104 *tile_image;
13105
13106 int
13107 id,
13108 status,
13109 tile,
13110 x,
13111 y;
13112
13113 double
13114 scale_factor;
13115
13116 char
13117 *p,
13118 *q;
13119
13120 int
13121 i;
13122
13123 unsigned int
13124 height,
13125 width;
13126
13127 /*
13128 Tile image is relative to montage image configuration.
13129 */
13130 x=0;
13131 y=0;
13132 width=(unsigned int) image->columns;
13133 height=(unsigned int) image->rows;
13134 if (windows->image.crop_geometry != (char *) NULL)
13135 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13136 scale_factor=(double) width/windows->image.ximage->width;
13137 event->xbutton.x+=windows->image.x;
13138 event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13139 scale_factor=(double) height/windows->image.ximage->height;
13140 event->xbutton.y+=windows->image.y;
13141 event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13142 /*
13143 Determine size and location of each tile in the visual image directory.
13144 */
13145 width=(unsigned int) image->columns;
13146 height=(unsigned int) image->rows;
13147 x=0;
13148 y=0;
13149 (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13150 tile=((event->xbutton.y-y)/(int) height)*(((int) image->columns-x)/(int)
13151 width)+(event->xbutton.x-x)/(int) width;
13152 if (tile < 0)
13153 {
13154 /*
13155 Button press is outside any tile.
13156 */
13157 (void) XBell(display,0);
13158 return((Image *) NULL);
13159 }
13160 /*
13161 Determine file name from the tile directory.
13162 */
13163 p=image->directory;
13164 for (i=tile; (i != 0) && (*p != '\0'); )
13165 {
13166 if (*p == '\xff')
13167 i--;
13168 p++;
13169 }
13170 if (*p == '\0')
13171 {
13172 /*
13173 Button press is outside any tile.
13174 */
13175 (void) XBell(display,0);
13176 return((Image *) NULL);
13177 }
13178 /*
13179 Select a command from the pop-up menu.
13180 */
13181 id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13182 if (id < 0)
13183 return((Image *) NULL);
13184 q=p;
13185 while ((*q != '\xff') && (*q != '\0'))
13186 q++;
13187 (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13188 /*
13189 Perform command for the selected tile.
13190 */
13191 XSetCursorState(display,windows,MagickTrue);
13192 XCheckRefreshWindows(display,windows);
13193 tile_image=NewImageList();
13194 switch (TileCommands[id])
13195 {
13196 case TileLoadCommand:
13197 {
13198 /*
13199 Load tile image.
13200 */
13201 XCheckRefreshWindows(display,windows);
13202 (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13203 MagickPathExtent);
13204 (void) CopyMagickString(resource_info->image_info->filename,filename,
13205 MagickPathExtent);
13206 tile_image=ReadImage(resource_info->image_info,exception);
13207 CatchException(exception);
13208 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13209 break;
13210 }
13211 case TileNextCommand:
13212 {
13213 /*
13214 Display next image.
13215 */
13216 XClientMessage(display,windows->image.id,windows->im_protocols,
13217 windows->im_next_image,CurrentTime);
13218 break;
13219 }
13220 case TileFormerCommand:
13221 {
13222 /*
13223 Display former image.
13224 */
13225 XClientMessage(display,windows->image.id,windows->im_protocols,
13226 windows->im_former_image,CurrentTime);
13227 break;
13228 }
13229 case TileDeleteCommand:
13230 {
13231 /*
13232 Delete tile image.
13233 */
13234 if (IsPathAccessible(filename) == MagickFalse)
13235 {
13236 XNoticeWidget(display,windows,"Image file does not exist:",filename);
13237 break;
13238 }
13239 status=XConfirmWidget(display,windows,"Really delete tile",filename);
13240 if (status <= 0)
13241 break;
13242 status=ShredFile(filename) == MagickFalse ? 0 : 1;
13243 status|=remove_utf8(filename);
13244 if (status != MagickFalse)
13245 {
13246 XNoticeWidget(display,windows,"Unable to delete image file:",
13247 filename);
13248 break;
13249 }
13250 magick_fallthrough;
13251 }
13252 case TileUpdateCommand:
13253 {
13254 int
13255 x_offset,
13256 y_offset;
13257
13258 PixelInfo
13259 pixel;
13260
13261 int
13262 j;
13263
13264 Quantum
13265 *s;
13266
13267 /*
13268 Ensure all the images exist.
13269 */
13270 tile=0;
13271 GetPixelInfo(image,&pixel);
13272 for (p=image->directory; *p != '\0'; p++)
13273 {
13274 CacheView
13275 *image_view;
13276
13277 q=p;
13278 while ((*q != '\xff') && (*q != '\0'))
13279 q++;
13280 (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13281 p=q;
13282 if (IsPathAccessible(filename) != MagickFalse)
13283 {
13284 tile++;
13285 continue;
13286 }
13287 /*
13288 Overwrite tile with background color.
13289 */
13290 x_offset=((int) width*(tile % (((int) image->columns-x)/(int) width))+
13291 x);
13292 y_offset=((int) height*(tile/(((int) image->columns-x)/(int) width))+
13293 y);
13294 image_view=AcquireAuthenticCacheView(image,exception);
13295 (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
13296 for (i=0; i < (int) height; i++)
13297 {
13298 s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13299 y_offset+i,width,1,exception);
13300 if (s == (Quantum *) NULL)
13301 break;
13302 for (j=0; j < (int) width; j++)
13303 {
13304 SetPixelViaPixelInfo(image,&pixel,s);
13305 s+=GetPixelChannels(image);
13306 }
13307 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13308 break;
13309 }
13310 image_view=DestroyCacheView(image_view);
13311 tile++;
13312 }
13313 windows->image.window_changes.width=(int) image->columns;
13314 windows->image.window_changes.height=(int) image->rows;
13315 XConfigureImageColormap(display,resource_info,windows,image,exception);
13316 (void) XConfigureImage(display,resource_info,windows,image,exception);
13317 break;
13318 }
13319 default:
13320 break;
13321 }
13322 XSetCursorState(display,windows,MagickFalse);
13323 return(tile_image);
13324}
13325
13326/*
13327%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13328% %
13329% %
13330% %
13331+ X T r a n s l a t e I m a g e %
13332% %
13333% %
13334% %
13335%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13336%
13337% XTranslateImage() translates the image within an Image window by one pixel
13338% as specified by the key symbol. If the image has a montage string the
13339% translation is respect to the width and height contained within the string.
13340%
13341% The format of the XTranslateImage method is:
13342%
13343% void XTranslateImage(Display *display,XWindows *windows,
13344% Image *image,const KeySym key_symbol)
13345%
13346% A description of each parameter follows:
13347%
13348% o display: Specifies a connection to an X server; returned from
13349% XOpenDisplay.
13350%
13351% o windows: Specifies a pointer to a XWindows structure.
13352%
13353% o image: the image.
13354%
13355% o key_symbol: Specifies a KeySym which indicates which side of the image
13356% to trim.
13357%
13358*/
13359static void XTranslateImage(Display *display,XWindows *windows,
13360 Image *image,const KeySym key_symbol)
13361{
13362 char
13363 text[MagickPathExtent];
13364
13365 int
13366 x,
13367 y;
13368
13369 unsigned int
13370 x_offset,
13371 y_offset;
13372
13373 /*
13374 User specified a pan position offset.
13375 */
13376 x_offset=windows->image.width;
13377 y_offset=windows->image.height;
13378 if (image->montage != (char *) NULL)
13379 (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13380 switch ((int) key_symbol)
13381 {
13382 case XK_Home:
13383 case XK_KP_Home:
13384 {
13385 windows->image.x=(int) windows->image.width/2;
13386 windows->image.y=(int) windows->image.height/2;
13387 break;
13388 }
13389 case XK_Left:
13390 case XK_KP_Left:
13391 {
13392 windows->image.x-=(int) x_offset;
13393 break;
13394 }
13395 case XK_Next:
13396 case XK_Up:
13397 case XK_KP_Up:
13398 {
13399 windows->image.y-=(int) y_offset;
13400 break;
13401 }
13402 case XK_Right:
13403 case XK_KP_Right:
13404 {
13405 windows->image.x+=(int) x_offset;
13406 break;
13407 }
13408 case XK_Prior:
13409 case XK_Down:
13410 case XK_KP_Down:
13411 {
13412 windows->image.y+=(int) y_offset;
13413 break;
13414 }
13415 default:
13416 return;
13417 }
13418 /*
13419 Check boundary conditions.
13420 */
13421 if (windows->image.x < 0)
13422 windows->image.x=0;
13423 else
13424 if ((windows->image.x+(int) windows->image.width) > windows->image.ximage->width)
13425 windows->image.x=windows->image.ximage->width-(int) windows->image.width;
13426 if (windows->image.y < 0)
13427 windows->image.y=0;
13428 else
13429 if ((windows->image.y+(int) windows->image.height) > windows->image.ximage->height)
13430 windows->image.y=windows->image.ximage->height-(int)
13431 windows->image.height;
13432 /*
13433 Refresh Image window.
13434 */
13435 (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
13436 windows->image.width,windows->image.height,windows->image.x,
13437 windows->image.y);
13438 XInfoWidget(display,windows,text);
13439 XCheckRefreshWindows(display,windows);
13440 XDrawPanRectangle(display,windows);
13441 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13442 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13443}
13444
13445/*
13446%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13447% %
13448% %
13449% %
13450+ X T r i m I m a g e %
13451% %
13452% %
13453% %
13454%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13455%
13456% XTrimImage() trims the edges from the Image window.
13457%
13458% The format of the XTrimImage method is:
13459%
13460% MagickBooleanType XTrimImage(Display *display,
13461% XResourceInfo *resource_info,XWindows *windows,Image *image,
13462% ExceptionInfo *exception)
13463%
13464% A description of each parameter follows:
13465%
13466% o display: Specifies a connection to an X server; returned from
13467% XOpenDisplay.
13468%
13469% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13470%
13471% o windows: Specifies a pointer to a XWindows structure.
13472%
13473% o image: the image.
13474%
13475% o exception: return any errors or warnings in this structure.
13476%
13477*/
13478static MagickBooleanType XTrimImage(Display *display,
13479 XResourceInfo *resource_info,XWindows *windows,Image *image,
13480 ExceptionInfo *exception)
13481{
13483 trim_info;
13484
13485 int
13486 x,
13487 y;
13488
13489 size_t
13490 background,
13491 pixel;
13492
13493 /*
13494 Trim edges from image.
13495 */
13496 XSetCursorState(display,windows,MagickTrue);
13497 XCheckRefreshWindows(display,windows);
13498 /*
13499 Crop the left edge.
13500 */
13501 background=XGetPixel(windows->image.ximage,0,0);
13502 trim_info.width=(size_t) windows->image.ximage->width;
13503 for (x=0; x < windows->image.ximage->width; x++)
13504 {
13505 for (y=0; y < windows->image.ximage->height; y++)
13506 {
13507 pixel=XGetPixel(windows->image.ximage,x,y);
13508 if (pixel != background)
13509 break;
13510 }
13511 if (y < windows->image.ximage->height)
13512 break;
13513 }
13514 trim_info.x=(ssize_t) x;
13515 if (trim_info.x == (ssize_t) windows->image.ximage->width)
13516 {
13517 XSetCursorState(display,windows,MagickFalse);
13518 return(MagickFalse);
13519 }
13520 /*
13521 Crop the right edge.
13522 */
13523 background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13524 for (x=windows->image.ximage->width-1; x != 0; x--)
13525 {
13526 for (y=0; y < windows->image.ximage->height; y++)
13527 {
13528 pixel=XGetPixel(windows->image.ximage,x,y);
13529 if (pixel != background)
13530 break;
13531 }
13532 if (y < windows->image.ximage->height)
13533 break;
13534 }
13535 trim_info.width=(size_t) (x-trim_info.x+1);
13536 /*
13537 Crop the top edge.
13538 */
13539 background=XGetPixel(windows->image.ximage,0,0);
13540 trim_info.height=(size_t) windows->image.ximage->height;
13541 for (y=0; y < windows->image.ximage->height; y++)
13542 {
13543 for (x=0; x < windows->image.ximage->width; x++)
13544 {
13545 pixel=XGetPixel(windows->image.ximage,x,y);
13546 if (pixel != background)
13547 break;
13548 }
13549 if (x < windows->image.ximage->width)
13550 break;
13551 }
13552 trim_info.y=(ssize_t) y;
13553 /*
13554 Crop the bottom edge.
13555 */
13556 background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13557 for (y=windows->image.ximage->height-1; y != 0; y--)
13558 {
13559 for (x=0; x < windows->image.ximage->width; x++)
13560 {
13561 pixel=XGetPixel(windows->image.ximage,x,y);
13562 if (pixel != background)
13563 break;
13564 }
13565 if (x < windows->image.ximage->width)
13566 break;
13567 }
13568 trim_info.height=(size_t) (y-trim_info.y+1);
13569 if (((unsigned int) trim_info.width != windows->image.width) ||
13570 ((unsigned int) trim_info.height != windows->image.height))
13571 {
13572 /*
13573 Reconfigure Image window as defined by the trimming rectangle.
13574 */
13575 XSetCropGeometry(display,windows,&trim_info,image);
13576 windows->image.window_changes.width=(int) trim_info.width;
13577 windows->image.window_changes.height=(int) trim_info.height;
13578 (void) XConfigureImage(display,resource_info,windows,image,exception);
13579 }
13580 XSetCursorState(display,windows,MagickFalse);
13581 return(MagickTrue);
13582}
13583
13584/*
13585%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13586% %
13587% %
13588% %
13589+ X V i s u a l D i r e c t o r y I m a g e %
13590% %
13591% %
13592% %
13593%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13594%
13595% XVisualDirectoryImage() creates a Visual Image Directory.
13596%
13597% The format of the XVisualDirectoryImage method is:
13598%
13599% Image *XVisualDirectoryImage(Display *display,
13600% XResourceInfo *resource_info,XWindows *windows,
13601% ExceptionInfo *exception)
13602%
13603% A description of each parameter follows:
13604%
13605% o display: Specifies a connection to an X server; returned from
13606% XOpenDisplay.
13607%
13608% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13609%
13610% o windows: Specifies a pointer to a XWindows structure.
13611%
13612% o exception: return any errors or warnings in this structure.
13613%
13614*/
13615static Image *XVisualDirectoryImage(Display *display,
13616 XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13617{
13618#define TileImageTag "Scale/Image"
13619#define XClientName "montage"
13620
13621 char
13622 **filelist;
13623
13624 Image
13625 *images,
13626 *montage_image,
13627 *next_image,
13628 *thumbnail_image;
13629
13630 ImageInfo
13631 *read_info;
13632
13633 int
13634 number_files;
13635
13636 MagickBooleanType
13637 backdrop;
13638
13639 MagickStatusType
13640 status;
13641
13643 *montage_info;
13644
13646 geometry;
13647
13648 int
13649 i;
13650
13651 static char
13652 filename[MagickPathExtent] = "\0",
13653 filenames[MagickPathExtent] = "*";
13654
13655 XResourceInfo
13656 background_resources;
13657
13658 /*
13659 Request file name from user.
13660 */
13661 XFileBrowserWidget(display,windows,"Directory",filenames);
13662 if (*filenames == '\0')
13663 return((Image *) NULL);
13664 /*
13665 Expand the filenames.
13666 */
13667 filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13668 if (filelist == (char **) NULL)
13669 {
13670 ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13671 filenames);
13672 return((Image *) NULL);
13673 }
13674 number_files=1;
13675 filelist[0]=filenames;
13676 status=ExpandFilenames(&number_files,&filelist);
13677 if ((status == MagickFalse) || (number_files == 0))
13678 {
13679 if (number_files == 0)
13680 ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13681 else
13682 ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13683 filenames);
13684 return((Image *) NULL);
13685 }
13686 /*
13687 Set image background resources.
13688 */
13689 background_resources=(*resource_info);
13690 background_resources.window_id=AcquireString("");
13691 (void) FormatLocaleString(background_resources.window_id,MagickPathExtent,
13692 "0x%lx",windows->image.id);
13693 background_resources.backdrop=MagickTrue;
13694 /*
13695 Read each image and convert them to a tile.
13696 */
13697 backdrop=((windows->visual_info->klass == TrueColor) ||
13698 (windows->visual_info->klass == DirectColor)) ? MagickTrue : MagickFalse;
13699 read_info=CloneImageInfo(resource_info->image_info);
13700 (void) SetImageOption(read_info,"jpeg:size","120x120");
13701 (void) CloneString(&read_info->size,DefaultTileGeometry);
13702 (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13703 (void *) NULL);
13704 images=NewImageList();
13705 XSetCursorState(display,windows,MagickTrue);
13706 XCheckRefreshWindows(display,windows);
13707 for (i=0; i < (int) number_files; i++)
13708 {
13709 (void) CopyMagickString(read_info->filename,filelist[i],MagickPathExtent);
13710 filelist[i]=DestroyString(filelist[i]);
13711 *read_info->magick='\0';
13712 next_image=ReadImage(read_info,exception);
13713 CatchException(exception);
13714 if (next_image != (Image *) NULL)
13715 {
13716 (void) DeleteImageProperty(next_image,"label");
13717 (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13718 read_info,next_image,DefaultTileLabel,exception),exception);
13719 (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13720 exception);
13721 thumbnail_image=ThumbnailImage(next_image,geometry.width,
13722 geometry.height,exception);
13723 if (thumbnail_image != (Image *) NULL)
13724 {
13725 next_image=DestroyImage(next_image);
13726 next_image=thumbnail_image;
13727 }
13728 if (backdrop)
13729 {
13730 (void) XDisplayBackgroundImage(display,&background_resources,
13731 next_image,exception);
13732 XSetCursorState(display,windows,MagickTrue);
13733 }
13734 AppendImageToList(&images,next_image);
13735 if (images->progress_monitor != (MagickProgressMonitor) NULL)
13736 {
13737 MagickBooleanType
13738 proceed;
13739
13740 proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13741 (MagickSizeType) number_files);
13742 if (proceed == MagickFalse)
13743 break;
13744 }
13745 }
13746 }
13747 filelist=(char **) RelinquishMagickMemory(filelist);
13748 if (images == (Image *) NULL)
13749 {
13750 read_info=DestroyImageInfo(read_info);
13751 XSetCursorState(display,windows,MagickFalse);
13752 ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13753 return((Image *) NULL);
13754 }
13755 /*
13756 Create the Visual Image Directory.
13757 */
13758 montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13759 montage_info->pointsize=10;
13760 if (resource_info->font != (char *) NULL)
13761 (void) CloneString(&montage_info->font,resource_info->font);
13762 (void) CopyMagickString(montage_info->filename,filename,MagickPathExtent);
13763 montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13764 images),exception);
13765 images=DestroyImageList(images);
13766 montage_info=DestroyMontageInfo(montage_info);
13767 read_info=DestroyImageInfo(read_info);
13768 XSetCursorState(display,windows,MagickFalse);
13769 if (montage_image == (Image *) NULL)
13770 return(montage_image);
13771 XClientMessage(display,windows->image.id,windows->im_protocols,
13772 windows->im_next_image,CurrentTime);
13773 return(montage_image);
13774}
13775
13776/*
13777%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13778% %
13779% %
13780% %
13781% X D i s p l a y B a c k g r o u n d I m a g e %
13782% %
13783% %
13784% %
13785%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13786%
13787% XDisplayBackgroundImage() displays an image in the background of a window.
13788%
13789% The format of the XDisplayBackgroundImage method is:
13790%
13791% MagickBooleanType XDisplayBackgroundImage(Display *display,
13792% XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13793%
13794% A description of each parameter follows:
13795%
13796% o display: Specifies a connection to an X server; returned from
13797% XOpenDisplay.
13798%
13799% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13800%
13801% o image: the image.
13802%
13803% o exception: return any errors or warnings in this structure.
13804%
13805*/
13806MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13807 XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13808{
13809 char
13810 geometry[MagickPathExtent],
13811 visual_type[MagickPathExtent];
13812
13813 int
13814 height,
13815 status,
13816 width;
13817
13819 geometry_info;
13820
13821 static XPixelInfo
13822 pixel;
13823
13824 static XStandardColormap
13825 *map_info;
13826
13827 static XVisualInfo
13828 *visual_info = (XVisualInfo *) NULL;
13829
13830 static XWindowInfo
13831 window_info;
13832
13833 size_t
13834 delay;
13835
13836 Window
13837 root_window;
13838
13839 XGCValues
13840 context_values;
13841
13842 XResourceInfo
13843 resources;
13844
13845 XWindowAttributes
13846 window_attributes;
13847
13848 /*
13849 Determine target window.
13850 */
13851 assert(image != (Image *) NULL);
13852 assert(image->signature == MagickCoreSignature);
13853 if (IsEventLogging() != MagickFalse)
13854 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13855 resources=(*resource_info);
13856 window_info.id=(Window) NULL;
13857 root_window=XRootWindow(display,XDefaultScreen(display));
13858 if (LocaleCompare(resources.window_id,"root") == 0)
13859 window_info.id=root_window;
13860 else
13861 {
13862 if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13863 window_info.id=XWindowByID(display,root_window,
13864 (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13865 if (window_info.id == (Window) NULL)
13866 window_info.id=XWindowByName(display,root_window,resources.window_id);
13867 }
13868 if (window_info.id == (Window) NULL)
13869 {
13870 ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13871 resources.window_id);
13872 return(MagickFalse);
13873 }
13874 /*
13875 Determine window visual id.
13876 */
13877 window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13878 window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13879 (void) CopyMagickString(visual_type,"default",MagickPathExtent);
13880 status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13881 if (status != 0)
13882 (void) FormatLocaleString(visual_type,MagickPathExtent,"0x%lx",
13883 XVisualIDFromVisual(window_attributes.visual));
13884 if (visual_info == (XVisualInfo *) NULL)
13885 {
13886 /*
13887 Allocate standard colormap.
13888 */
13889 map_info=XAllocStandardColormap();
13890 if (map_info == (XStandardColormap *) NULL)
13891 ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13892 image->filename);
13893 map_info->colormap=(Colormap) NULL;
13894 pixel.pixels=(unsigned long *) NULL;
13895 /*
13896 Initialize visual info.
13897 */
13898 resources.map_type=(char *) NULL;
13899 resources.visual_type=visual_type;
13900 visual_info=XBestVisualInfo(display,map_info,&resources);
13901 if (visual_info == (XVisualInfo *) NULL)
13902 ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13903 resources.visual_type);
13904 /*
13905 Initialize window info.
13906 */
13907 window_info.ximage=(XImage *) NULL;
13908 window_info.matte_image=(XImage *) NULL;
13909 window_info.pixmap=(Pixmap) NULL;
13910 window_info.matte_pixmap=(Pixmap) NULL;
13911 }
13912 /*
13913 Free previous root colors.
13914 */
13915 if (window_info.id == root_window)
13916 (void) XDestroyWindowColors(display,root_window);
13917 /*
13918 Initialize Standard Colormap.
13919 */
13920 resources.colormap=SharedColormap;
13921 XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13922 exception);
13923 /*
13924 Graphic context superclass.
13925 */
13926 context_values.background=pixel.foreground_color.pixel;
13927 context_values.foreground=pixel.background_color.pixel;
13928 pixel.annotate_context=XCreateGC(display,window_info.id,
13929 (size_t) (GCBackground | GCForeground),&context_values);
13930 if (pixel.annotate_context == (GC) NULL)
13931 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13932 image->filename);
13933 /*
13934 Initialize Image window attributes.
13935 */
13936 window_info.name=AcquireString("\0");
13937 window_info.icon_name=AcquireString("\0");
13938 XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13939 &resources,&window_info);
13940 /*
13941 Create the X image.
13942 */
13943 window_info.width=(unsigned int) image->columns;
13944 window_info.height=(unsigned int) image->rows;
13945 if ((image->columns != window_info.width) ||
13946 (image->rows != window_info.height))
13947 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13948 image->filename);
13949 (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>",
13950 window_attributes.width,window_attributes.height);
13951 geometry_info.width=window_info.width;
13952 geometry_info.height=window_info.height;
13953 geometry_info.x=(ssize_t) window_info.x;
13954 geometry_info.y=(ssize_t) window_info.y;
13955 (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13956 &geometry_info.width,&geometry_info.height);
13957 window_info.width=(unsigned int) geometry_info.width;
13958 window_info.height=(unsigned int) geometry_info.height;
13959 window_info.x=(int) geometry_info.x;
13960 window_info.y=(int) geometry_info.y;
13961 status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13962 window_info.height,exception) == MagickFalse ? 0 : 1;
13963 if (status == MagickFalse)
13964 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13965 image->filename);
13966 window_info.x=0;
13967 window_info.y=0;
13968 if (resource_info->debug != MagickFalse)
13969 {
13970 (void) LogMagickEvent(X11Event,GetMagickModule(),
13971 "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13972 (double) image->columns,(double) image->rows);
13973 if (image->colors != 0)
13974 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13975 image->colors);
13976 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13977 }
13978 /*
13979 Adjust image dimensions as specified by backdrop or geometry options.
13980 */
13981 width=(int) window_info.width;
13982 height=(int) window_info.height;
13983 if (resources.backdrop != MagickFalse)
13984 {
13985 /*
13986 Center image on window.
13987 */
13988 window_info.x=(window_attributes.width/2)-(window_info.ximage->width/2);
13989 window_info.y=(window_attributes.height/2)-(window_info.ximage->height/2);
13990 width=window_attributes.width;
13991 height=window_attributes.height;
13992 }
13993 if ((resources.image_geometry != (char *) NULL) &&
13994 (*resources.image_geometry != '\0'))
13995 {
13996 char
13997 default_geometry[MagickPathExtent];
13998
13999 int
14000 flags,
14001 gravity;
14002
14003 XSizeHints
14004 *size_hints;
14005
14006 /*
14007 User specified geometry.
14008 */
14009 size_hints=XAllocSizeHints();
14010 if (size_hints == (XSizeHints *) NULL)
14011 ThrowXWindowFatalException(ResourceLimitFatalError,
14012 "MemoryAllocationFailed",image->filename);
14013 size_hints->flags=0L;
14014 (void) FormatLocaleString(default_geometry,MagickPathExtent,"%dx%d",
14015 width,height);
14016 flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
14017 default_geometry,window_info.border_width,size_hints,&window_info.x,
14018 &window_info.y,&width,&height,&gravity);
14019 if (flags & (XValue | YValue))
14020 {
14021 width=window_attributes.width;
14022 height=window_attributes.height;
14023 }
14024 (void) XFree((void *) size_hints);
14025 }
14026 /*
14027 Create the X pixmap.
14028 */
14029 window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14030 (unsigned int) height,window_info.depth);
14031 if (window_info.pixmap == (Pixmap) NULL)
14032 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14033 image->filename);
14034 /*
14035 Display pixmap on the window.
14036 */
14037 if (((unsigned int) width > window_info.width) ||
14038 ((unsigned int) height > window_info.height))
14039 (void) XFillRectangle(display,window_info.pixmap,
14040 window_info.annotate_context,0,0,(unsigned int) width,
14041 (unsigned int) height);
14042 (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14043 window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14044 window_info.width,(unsigned int) window_info.height);
14045 (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14046 (void) XClearWindow(display,window_info.id);
14047 delay=1000*image->delay/(size_t) MagickMax(image->ticks_per_second,1L);
14048 XDelay(display,delay == 0UL ? 10UL : delay);
14049 (void) XSync(display,MagickFalse);
14050 return(window_info.id == root_window ? MagickTrue : MagickFalse);
14051}
14052
14053/*
14054%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14055% %
14056% %
14057% %
14058+ X D i s p l a y I m a g e %
14059% %
14060% %
14061% %
14062%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14063%
14064% XDisplayImage() displays an image via X11. A new image is created and
14065% returned if the user interactively transforms the displayed image.
14066%
14067% The format of the XDisplayImage method is:
14068%
14069% Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14070% char **argv,int argc,Image **image,size_t *state,
14071% ExceptionInfo *exception)
14072%
14073% A description of each parameter follows:
14074%
14075% o nexus: Method XDisplayImage returns an image when the
14076% user chooses 'Open Image' from the command menu or picks a tile
14077% from the image directory. Otherwise a null image is returned.
14078%
14079% o display: Specifies a connection to an X server; returned from
14080% XOpenDisplay.
14081%
14082% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14083%
14084% o argv: Specifies the application's argument list.
14085%
14086% o argc: Specifies the number of arguments.
14087%
14088% o image: Specifies an address to an address of an Image structure;
14089%
14090% o exception: return any errors or warnings in this structure.
14091%
14092*/
14093MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14094 char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14095{
14096#define MagnifySize 256 /* must be a power of 2 */
14097#define MagickMenus 10
14098#define MagickTitle "Commands"
14099
14100 const char
14101 *const CommandMenu[] =
14102 {
14103 "File",
14104 "Edit",
14105 "View",
14106 "Transform",
14107 "Enhance",
14108 "Effects",
14109 "F/X",
14110 "Image Edit",
14111 "Miscellany",
14112 "Help",
14113 (char *) NULL
14114 },
14115 *const FileMenu[] =
14116 {
14117 "Open...",
14118 "Next",
14119 "Former",
14120 "Select...",
14121 "Save...",
14122 "Print...",
14123 "Delete...",
14124 "New...",
14125 "Visual Directory...",
14126 "Quit",
14127 (char *) NULL
14128 },
14129 *const EditMenu[] =
14130 {
14131 "Undo",
14132 "Redo",
14133 "Cut",
14134 "Copy",
14135 "Paste",
14136 (char *) NULL
14137 },
14138 *const ViewMenu[] =
14139 {
14140 "Half Size",
14141 "Original Size",
14142 "Double Size",
14143 "Resize...",
14144 "Apply",
14145 "Refresh",
14146 "Restore",
14147 (char *) NULL
14148 },
14149 *const TransformMenu[] =
14150 {
14151 "Crop",
14152 "Chop",
14153 "Flop",
14154 "Flip",
14155 "Rotate Right",
14156 "Rotate Left",
14157 "Rotate...",
14158 "Shear...",
14159 "Roll...",
14160 "Trim Edges",
14161 (char *) NULL
14162 },
14163 *const EnhanceMenu[] =
14164 {
14165 "Hue...",
14166 "Saturation...",
14167 "Brightness...",
14168 "Gamma...",
14169 "Spiff",
14170 "Dull",
14171 "Contrast Stretch...",
14172 "Sigmoidal Contrast...",
14173 "Normalize",
14174 "Equalize",
14175 "Negate",
14176 "Grayscale",
14177 "Map...",
14178 "Quantize...",
14179 (char *) NULL
14180 },
14181 *const EffectsMenu[] =
14182 {
14183 "Despeckle",
14184 "Emboss",
14185 "Reduce Noise",
14186 "Add Noise...",
14187 "Sharpen...",
14188 "Blur...",
14189 "Threshold...",
14190 "Edge Detect...",
14191 "Spread...",
14192 "Shade...",
14193 "Raise...",
14194 "Segment...",
14195 (char *) NULL
14196 },
14197 *const FXMenu[] =
14198 {
14199 "Solarize...",
14200 "Sepia Tone...",
14201 "Swirl...",
14202 "Implode...",
14203 "Vignette...",
14204 "Wave...",
14205 "Oil Paint...",
14206 "Charcoal Draw...",
14207 (char *) NULL
14208 },
14209 *const ImageEditMenu[] =
14210 {
14211 "Annotate...",
14212 "Draw...",
14213 "Color...",
14214 "Matte...",
14215 "Composite...",
14216 "Add Border...",
14217 "Add Frame...",
14218 "Comment...",
14219 "Launch...",
14220 "Region of Interest...",
14221 (char *) NULL
14222 },
14223 *const MiscellanyMenu[] =
14224 {
14225 "Image Info",
14226 "Zoom Image",
14227 "Show Preview...",
14228 "Show Histogram",
14229 "Show Matte",
14230 "Background...",
14231 "Slide Show...",
14232 "Preferences...",
14233 (char *) NULL
14234 },
14235 *const HelpMenu[] =
14236 {
14237 "Overview",
14238 "Browse Documentation",
14239 "About Display",
14240 (char *) NULL
14241 },
14242 *const ShortCutsMenu[] =
14243 {
14244 "Next",
14245 "Former",
14246 "Open...",
14247 "Save...",
14248 "Print...",
14249 "Undo",
14250 "Restore",
14251 "Image Info",
14252 "Quit",
14253 (char *) NULL
14254 },
14255 *const VirtualMenu[] =
14256 {
14257 "Image Info",
14258 "Print",
14259 "Next",
14260 "Quit",
14261 (char *) NULL
14262 };
14263
14264 const char
14265 *const *Menus[MagickMenus] =
14266 {
14267 FileMenu,
14268 EditMenu,
14269 ViewMenu,
14270 TransformMenu,
14271 EnhanceMenu,
14272 EffectsMenu,
14273 FXMenu,
14274 ImageEditMenu,
14275 MiscellanyMenu,
14276 HelpMenu
14277 };
14278
14279 static DisplayCommand
14280 CommandMenus[] =
14281 {
14282 NullCommand,
14283 NullCommand,
14284 NullCommand,
14285 NullCommand,
14286 NullCommand,
14287 NullCommand,
14288 NullCommand,
14289 NullCommand,
14290 NullCommand,
14291 NullCommand,
14292 },
14293 FileCommands[] =
14294 {
14295 OpenCommand,
14296 NextCommand,
14297 FormerCommand,
14298 SelectCommand,
14299 SaveCommand,
14300 PrintCommand,
14301 DeleteCommand,
14302 NewCommand,
14303 VisualDirectoryCommand,
14304 QuitCommand
14305 },
14306 EditCommands[] =
14307 {
14308 UndoCommand,
14309 RedoCommand,
14310 CutCommand,
14311 CopyCommand,
14312 PasteCommand
14313 },
14314 ViewCommands[] =
14315 {
14316 HalfSizeCommand,
14317 OriginalSizeCommand,
14318 DoubleSizeCommand,
14319 ResizeCommand,
14320 ApplyCommand,
14321 RefreshCommand,
14322 RestoreCommand
14323 },
14324 TransformCommands[] =
14325 {
14326 CropCommand,
14327 ChopCommand,
14328 FlopCommand,
14329 FlipCommand,
14330 RotateRightCommand,
14331 RotateLeftCommand,
14332 RotateCommand,
14333 ShearCommand,
14334 RollCommand,
14335 TrimCommand
14336 },
14337 EnhanceCommands[] =
14338 {
14339 HueCommand,
14340 SaturationCommand,
14341 BrightnessCommand,
14342 GammaCommand,
14343 SpiffCommand,
14344 DullCommand,
14345 ContrastStretchCommand,
14346 SigmoidalContrastCommand,
14347 NormalizeCommand,
14348 EqualizeCommand,
14349 NegateCommand,
14350 GrayscaleCommand,
14351 MapCommand,
14352 QuantizeCommand
14353 },
14354 EffectsCommands[] =
14355 {
14356 DespeckleCommand,
14357 EmbossCommand,
14358 ReduceNoiseCommand,
14359 AddNoiseCommand,
14360 SharpenCommand,
14361 BlurCommand,
14362 ThresholdCommand,
14363 EdgeDetectCommand,
14364 SpreadCommand,
14365 ShadeCommand,
14366 RaiseCommand,
14367 SegmentCommand
14368 },
14369 FXCommands[] =
14370 {
14371 SolarizeCommand,
14372 SepiaToneCommand,
14373 SwirlCommand,
14374 ImplodeCommand,
14375 VignetteCommand,
14376 WaveCommand,
14377 OilPaintCommand,
14378 CharcoalDrawCommand
14379 },
14380 ImageEditCommands[] =
14381 {
14382 AnnotateCommand,
14383 DrawCommand,
14384 ColorCommand,
14385 MatteCommand,
14386 CompositeCommand,
14387 AddBorderCommand,
14388 AddFrameCommand,
14389 CommentCommand,
14390 LaunchCommand,
14391 RegionOfInterestCommand
14392 },
14393 MiscellanyCommands[] =
14394 {
14395 InfoCommand,
14396 ZoomCommand,
14397 ShowPreviewCommand,
14398 ShowHistogramCommand,
14399 ShowMatteCommand,
14400 BackgroundCommand,
14401 SlideShowCommand,
14402 PreferencesCommand
14403 },
14404 HelpCommands[] =
14405 {
14406 HelpCommand,
14407 BrowseDocumentationCommand,
14408 VersionCommand
14409 },
14410 ShortCutsCommands[] =
14411 {
14412 NextCommand,
14413 FormerCommand,
14414 OpenCommand,
14415 SaveCommand,
14416 PrintCommand,
14417 UndoCommand,
14418 RestoreCommand,
14419 InfoCommand,
14420 QuitCommand
14421 },
14422 VirtualCommands[] =
14423 {
14424 InfoCommand,
14425 PrintCommand,
14426 NextCommand,
14427 QuitCommand
14428 };
14429
14430 static DisplayCommand
14431 *Commands[MagickMenus] =
14432 {
14433 FileCommands,
14434 EditCommands,
14435 ViewCommands,
14436 TransformCommands,
14437 EnhanceCommands,
14438 EffectsCommands,
14439 FXCommands,
14440 ImageEditCommands,
14441 MiscellanyCommands,
14442 HelpCommands
14443 };
14444
14445 char
14446 command[MagickPathExtent],
14447 *directory,
14448 geometry[MagickPathExtent],
14449 resource_name[MagickPathExtent];
14450
14451 DisplayCommand
14452 display_command;
14453
14454 Image
14455 *display_image,
14456 *nexus;
14457
14458 int
14459 entry,
14460 id;
14461
14462 KeySym
14463 key_symbol;
14464
14465 MagickStatusType
14466 context_mask,
14467 status;
14468
14470 geometry_info;
14471
14472 int
14473 i;
14474
14475 static char
14476 working_directory[MagickPathExtent];
14477
14478 static XPoint
14479 vid_info;
14480
14481 static XWindowInfo
14482 *magick_windows[MaxXWindows];
14483
14484 static unsigned int
14485 number_windows;
14486
14487 struct stat
14488 attributes;
14489
14490 time_t
14491 timer,
14492 timestamp,
14493 update_time;
14494
14495 unsigned int
14496 height,
14497 width;
14498
14499 size_t
14500 delay;
14501
14502 WarningHandler
14503 warning_handler;
14504
14505 Window
14506 root_window;
14507
14508 XClassHint
14509 *class_hints;
14510
14511 XEvent
14512 event;
14513
14514 XFontStruct
14515 *font_info;
14516
14517 XGCValues
14518 context_values;
14519
14520 XPixelInfo
14521 *icon_pixel,
14522 *pixel;
14523
14524 XResourceInfo
14525 *icon_resources;
14526
14527 XStandardColormap
14528 *icon_map,
14529 *map_info;
14530
14531 XVisualInfo
14532 *icon_visual,
14533 *visual_info;
14534
14535 XWindowChanges
14536 window_changes;
14537
14538 XWindows
14539 *windows;
14540
14541 XWMHints
14542 *manager_hints;
14543
14544 assert(image != (Image **) NULL);
14545 assert((*image)->signature == MagickCoreSignature);
14546 if (IsEventLogging() != MagickFalse)
14547 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14548 display_image=(*image);
14549 warning_handler=(WarningHandler) NULL;
14550 windows=XSetWindows((XWindows *) ~0);
14551 if (windows != (XWindows *) NULL)
14552 {
14553 int
14554 status;
14555
14556 if (*working_directory == '\0')
14557 (void) CopyMagickString(working_directory,".",MagickPathExtent);
14558 status=chdir(working_directory);
14559 if (status == -1)
14560 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14561 "UnableToOpenFile","%s",working_directory);
14562 warning_handler=resource_info->display_warnings ?
14563 SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14564 warning_handler=resource_info->display_warnings ?
14565 SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14566 }
14567 else
14568 {
14569 /*
14570 Allocate windows structure.
14571 */
14572 resource_info->colors=display_image->colors;
14573 windows=XSetWindows(XInitializeWindows(display,resource_info));
14574 if (windows == (XWindows *) NULL)
14575 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14576 (*image)->filename);
14577 /*
14578 Initialize window id's.
14579 */
14580 number_windows=0;
14581 magick_windows[number_windows++]=(&windows->icon);
14582 magick_windows[number_windows++]=(&windows->backdrop);
14583 magick_windows[number_windows++]=(&windows->image);
14584 magick_windows[number_windows++]=(&windows->info);
14585 magick_windows[number_windows++]=(&windows->command);
14586 magick_windows[number_windows++]=(&windows->widget);
14587 magick_windows[number_windows++]=(&windows->popup);
14588 magick_windows[number_windows++]=(&windows->magnify);
14589 magick_windows[number_windows++]=(&windows->pan);
14590 for (i=0; i < (int) number_windows; i++)
14591 magick_windows[i]->id=(Window) NULL;
14592 vid_info.x=0;
14593 vid_info.y=0;
14594 }
14595 /*
14596 Initialize font info.
14597 */
14598 if (windows->font_info != (XFontStruct *) NULL)
14599 (void) XFreeFont(display,windows->font_info);
14600 windows->font_info=XBestFont(display,resource_info,MagickFalse);
14601 if (windows->font_info == (XFontStruct *) NULL)
14602 ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14603 resource_info->font);
14604 /*
14605 Initialize Standard Colormap.
14606 */
14607 map_info=windows->map_info;
14608 icon_map=windows->icon_map;
14609 visual_info=windows->visual_info;
14610 icon_visual=windows->icon_visual;
14611 pixel=windows->pixel_info;
14612 icon_pixel=windows->icon_pixel;
14613 font_info=windows->font_info;
14614 icon_resources=windows->icon_resources;
14615 class_hints=windows->class_hints;
14616 manager_hints=windows->manager_hints;
14617 root_window=XRootWindow(display,visual_info->screen);
14618 nexus=NewImageList();
14619 if (resource_info->debug != MagickFalse)
14620 {
14621 (void) LogMagickEvent(X11Event,GetMagickModule(),
14622 "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14623 (double) display_image->scene,(double) display_image->columns,
14624 (double) display_image->rows);
14625 if (display_image->colors != 0)
14626 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14627 display_image->colors);
14628 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14629 display_image->magick);
14630 }
14631 XMakeStandardColormap(display,visual_info,resource_info,display_image,
14632 map_info,pixel,exception);
14633 display_image->taint=MagickFalse;
14634 /*
14635 Initialize graphic context.
14636 */
14637 windows->context.id=(Window) NULL;
14638 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14639 resource_info,&windows->context);
14640 (void) CloneString(&class_hints->res_name,resource_info->client_name);
14641 (void) CloneString(&class_hints->res_class,resource_info->client_name);
14642 class_hints->res_class[0]=(char) LocaleToUppercase((int)
14643 class_hints->res_class[0]);
14644 manager_hints->flags=InputHint | StateHint;
14645 manager_hints->input=MagickFalse;
14646 manager_hints->initial_state=WithdrawnState;
14647 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14648 &windows->context);
14649 if (resource_info->debug != MagickFalse)
14650 (void) LogMagickEvent(X11Event,GetMagickModule(),
14651 "Window id: 0x%lx (context)",windows->context.id);
14652 context_values.background=pixel->background_color.pixel;
14653 context_values.font=font_info->fid;
14654 context_values.foreground=pixel->foreground_color.pixel;
14655 context_values.graphics_exposures=MagickFalse;
14656 context_mask=(MagickStatusType)
14657 (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14658 if (pixel->annotate_context != (GC) NULL)
14659 (void) XFreeGC(display,pixel->annotate_context);
14660 pixel->annotate_context=XCreateGC(display,windows->context.id,
14661 context_mask,&context_values);
14662 if (pixel->annotate_context == (GC) NULL)
14663 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14664 display_image->filename);
14665 context_values.background=pixel->depth_color.pixel;
14666 if (pixel->widget_context != (GC) NULL)
14667 (void) XFreeGC(display,pixel->widget_context);
14668 pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14669 &context_values);
14670 if (pixel->widget_context == (GC) NULL)
14671 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14672 display_image->filename);
14673 context_values.background=pixel->foreground_color.pixel;
14674 context_values.foreground=pixel->background_color.pixel;
14675 context_values.plane_mask=context_values.background ^
14676 context_values.foreground;
14677 if (pixel->highlight_context != (GC) NULL)
14678 (void) XFreeGC(display,pixel->highlight_context);
14679 pixel->highlight_context=XCreateGC(display,windows->context.id,
14680 (size_t) (context_mask | GCPlaneMask),&context_values);
14681 if (pixel->highlight_context == (GC) NULL)
14682 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14683 display_image->filename);
14684 (void) XDestroyWindow(display,windows->context.id);
14685 /*
14686 Initialize icon window.
14687 */
14688 XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14689 icon_resources,&windows->icon);
14690 windows->icon.geometry=resource_info->icon_geometry;
14691 XBestIconSize(display,&windows->icon,display_image);
14692 windows->icon.attributes.colormap=XDefaultColormap(display,
14693 icon_visual->screen);
14694 windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14695 manager_hints->flags=InputHint | StateHint;
14696 manager_hints->input=MagickFalse;
14697 manager_hints->initial_state=IconicState;
14698 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14699 &windows->icon);
14700 if (resource_info->debug != MagickFalse)
14701 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14702 windows->icon.id);
14703 /*
14704 Initialize graphic context for icon window.
14705 */
14706 if (icon_pixel->annotate_context != (GC) NULL)
14707 (void) XFreeGC(display,icon_pixel->annotate_context);
14708 context_values.background=icon_pixel->background_color.pixel;
14709 context_values.foreground=icon_pixel->foreground_color.pixel;
14710 icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14711 (size_t) (GCBackground | GCForeground),&context_values);
14712 if (icon_pixel->annotate_context == (GC) NULL)
14713 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14714 display_image->filename);
14715 windows->icon.annotate_context=icon_pixel->annotate_context;
14716 /*
14717 Initialize Image window.
14718 */
14719 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14720 &windows->image);
14721 windows->image.shape=MagickTrue; /* non-rectangular shape hint */
14722 if (resource_info->use_shared_memory == MagickFalse)
14723 windows->image.shared_memory=MagickFalse;
14724 if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14725 {
14726 char
14727 *title;
14728
14729 title=InterpretImageProperties(resource_info->image_info,display_image,
14730 resource_info->title,exception);
14731 (void) CloneString(&windows->image.name,title);
14732 (void) CloneString(&windows->image.icon_name,title);
14733 title=DestroyString(title);
14734 }
14735 else
14736 {
14737 char
14738 filename[MagickPathExtent],
14739 window_name[MagickPathExtent];
14740
14741 /*
14742 Window name is the base of the filename.
14743 */
14744 GetPathComponent(display_image->magick_filename,TailPath,filename);
14745 if (display_image->scene == 0)
14746 (void) FormatLocaleString(window_name,MagickPathExtent,"%s: %s",
14747 MagickPackageName,filename);
14748 else
14749 (void) FormatLocaleString(window_name,MagickPathExtent,
14750 "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14751 (double) display_image->scene,(double) GetImageListLength(
14752 display_image));
14753 (void) CloneString(&windows->image.name,window_name);
14754 (void) CloneString(&windows->image.icon_name,filename);
14755 }
14756 if (resource_info->immutable)
14757 windows->image.immutable=MagickTrue;
14758 windows->image.use_pixmap=resource_info->use_pixmap;
14759 windows->image.geometry=resource_info->image_geometry;
14760 (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
14761 XDisplayWidth(display,visual_info->screen),
14762 XDisplayHeight(display,visual_info->screen));
14763 geometry_info.width=display_image->columns;
14764 geometry_info.height=display_image->rows;
14765 geometry_info.x=0;
14766 geometry_info.y=0;
14767 (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14768 &geometry_info.width,&geometry_info.height);
14769 windows->image.width=(unsigned int) geometry_info.width;
14770 windows->image.height=(unsigned int) geometry_info.height;
14771 windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14772 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14773 KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14774 PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14775 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14776 resource_info,&windows->backdrop);
14777 if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14778 {
14779 /*
14780 Initialize backdrop window.
14781 */
14782 windows->backdrop.x=0;
14783 windows->backdrop.y=0;
14784 (void) CloneString(&windows->backdrop.name,"Backdrop");
14785 windows->backdrop.flags=(size_t) (USSize | USPosition);
14786 windows->backdrop.width=(unsigned int)
14787 XDisplayWidth(display,visual_info->screen);
14788 windows->backdrop.height=(unsigned int)
14789 XDisplayHeight(display,visual_info->screen);
14790 windows->backdrop.border_width=0;
14791 windows->backdrop.immutable=MagickTrue;
14792 windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14793 ButtonReleaseMask;
14794 windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14795 StructureNotifyMask;
14796 manager_hints->flags=IconWindowHint | InputHint | StateHint;
14797 manager_hints->icon_window=windows->icon.id;
14798 manager_hints->input=MagickTrue;
14799 manager_hints->initial_state=resource_info->iconic ? IconicState :
14800 NormalState;
14801 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14802 &windows->backdrop);
14803 if (resource_info->debug != MagickFalse)
14804 (void) LogMagickEvent(X11Event,GetMagickModule(),
14805 "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14806 (void) XMapWindow(display,windows->backdrop.id);
14807 (void) XClearWindow(display,windows->backdrop.id);
14808 if (windows->image.id != (Window) NULL)
14809 {
14810 (void) XDestroyWindow(display,windows->image.id);
14811 windows->image.id=(Window) NULL;
14812 }
14813 /*
14814 Position image in the center the backdrop.
14815 */
14816 windows->image.flags|=USPosition;
14817 windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14818 ((int) windows->image.width/2);
14819 windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14820 ((int) windows->image.height/2);
14821 }
14822 manager_hints->flags=IconWindowHint | InputHint | StateHint;
14823 manager_hints->icon_window=windows->icon.id;
14824 manager_hints->input=MagickTrue;
14825 manager_hints->initial_state=resource_info->iconic ? IconicState :
14826 NormalState;
14827 if (windows->group_leader.id != (Window) NULL)
14828 {
14829 /*
14830 Follow the leader.
14831 */
14832 manager_hints->flags|=WindowGroupHint;
14833 manager_hints->window_group=windows->group_leader.id;
14834 (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14835 if (resource_info->debug != MagickFalse)
14836 (void) LogMagickEvent(X11Event,GetMagickModule(),
14837 "Window id: 0x%lx (group leader)",windows->group_leader.id);
14838 }
14839 XMakeWindow(display,
14840 (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14841 argv,argc,class_hints,manager_hints,&windows->image);
14842 (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14843 XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14844 if (windows->group_leader.id != (Window) NULL)
14845 (void) XSetTransientForHint(display,windows->image.id,
14846 windows->group_leader.id);
14847 if (resource_info->debug != MagickFalse)
14848 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14849 windows->image.id);
14850 /*
14851 Initialize Info widget.
14852 */
14853 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14854 &windows->info);
14855 (void) CloneString(&windows->info.name,"Info");
14856 (void) CloneString(&windows->info.icon_name,"Info");
14857 windows->info.border_width=1;
14858 windows->info.x=2;
14859 windows->info.y=2;
14860 windows->info.flags|=PPosition;
14861 windows->info.attributes.win_gravity=UnmapGravity;
14862 windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14863 StructureNotifyMask;
14864 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14865 manager_hints->input=MagickFalse;
14866 manager_hints->initial_state=NormalState;
14867 manager_hints->window_group=windows->image.id;
14868 XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14869 &windows->info);
14870 windows->info.highlight_stipple=XCreateBitmapFromData(display,
14871 windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14872 windows->info.shadow_stipple=XCreateBitmapFromData(display,
14873 windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14874 (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14875 if (windows->image.mapped != MagickFalse)
14876 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14877 if (resource_info->debug != MagickFalse)
14878 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14879 windows->info.id);
14880 /*
14881 Initialize Command widget.
14882 */
14883 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14884 resource_info,&windows->command);
14885 windows->command.data=MagickMenus;
14886 (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14887 (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.command",
14888 resource_info->client_name);
14889 windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14890 resource_name,"geometry",(char *) NULL);
14891 (void) CloneString(&windows->command.name,MagickTitle);
14892 windows->command.border_width=0;
14893 windows->command.flags|=PPosition;
14894 windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14895 ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14896 OwnerGrabButtonMask | StructureNotifyMask;
14897 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14898 manager_hints->input=MagickTrue;
14899 manager_hints->initial_state=NormalState;
14900 manager_hints->window_group=windows->image.id;
14901 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14902 &windows->command);
14903 windows->command.highlight_stipple=XCreateBitmapFromData(display,
14904 windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14905 HighlightHeight);
14906 windows->command.shadow_stipple=XCreateBitmapFromData(display,
14907 windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14908 (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14909 if (windows->command.mapped != MagickFalse)
14910 (void) XMapRaised(display,windows->command.id);
14911 if (resource_info->debug != MagickFalse)
14912 (void) LogMagickEvent(X11Event,GetMagickModule(),
14913 "Window id: 0x%lx (command)",windows->command.id);
14914 /*
14915 Initialize Widget window.
14916 */
14917 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14918 resource_info,&windows->widget);
14919 (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.widget",
14920 resource_info->client_name);
14921 windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14922 resource_name,"geometry",(char *) NULL);
14923 windows->widget.border_width=0;
14924 windows->widget.flags|=PPosition;
14925 windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14926 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14927 KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14928 StructureNotifyMask;
14929 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14930 manager_hints->input=MagickTrue;
14931 manager_hints->initial_state=NormalState;
14932 manager_hints->window_group=windows->image.id;
14933 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14934 &windows->widget);
14935 windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14936 windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14937 windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14938 windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14939 (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14940 if (resource_info->debug != MagickFalse)
14941 (void) LogMagickEvent(X11Event,GetMagickModule(),
14942 "Window id: 0x%lx (widget)",windows->widget.id);
14943 /*
14944 Initialize popup window.
14945 */
14946 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14947 resource_info,&windows->popup);
14948 windows->popup.border_width=0;
14949 windows->popup.flags|=PPosition;
14950 windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14951 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14952 KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14953 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14954 manager_hints->input=MagickTrue;
14955 manager_hints->initial_state=NormalState;
14956 manager_hints->window_group=windows->image.id;
14957 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14958 &windows->popup);
14959 windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14960 windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14961 windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14962 windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14963 (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14964 if (resource_info->debug != MagickFalse)
14965 (void) LogMagickEvent(X11Event,GetMagickModule(),
14966 "Window id: 0x%lx (pop up)",windows->popup.id);
14967 /*
14968 Initialize Magnify window and cursor.
14969 */
14970 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14971 resource_info,&windows->magnify);
14972 if (resource_info->use_shared_memory == MagickFalse)
14973 windows->magnify.shared_memory=MagickFalse;
14974 (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.magnify",
14975 resource_info->client_name);
14976 windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14977 resource_name,"geometry",(char *) NULL);
14978 (void) FormatLocaleString(windows->magnify.name,MagickPathExtent,
14979 "Magnify %uX",resource_info->magnify);
14980 if (windows->magnify.cursor != (Cursor) NULL)
14981 (void) XFreeCursor(display,windows->magnify.cursor);
14982 windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14983 map_info->colormap,resource_info->background_color,
14984 resource_info->foreground_color);
14985 if (windows->magnify.cursor == (Cursor) NULL)
14986 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14987 display_image->filename);
14988 windows->magnify.width=MagnifySize;
14989 windows->magnify.height=MagnifySize;
14990 windows->magnify.flags|=PPosition;
14991 windows->magnify.min_width=MagnifySize;
14992 windows->magnify.min_height=MagnifySize;
14993 windows->magnify.width_inc=MagnifySize;
14994 windows->magnify.height_inc=MagnifySize;
14995 windows->magnify.data=resource_info->magnify;
14996 windows->magnify.attributes.cursor=windows->magnify.cursor;
14997 windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14998 ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14999 StructureNotifyMask;
15000 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15001 manager_hints->input=MagickTrue;
15002 manager_hints->initial_state=NormalState;
15003 manager_hints->window_group=windows->image.id;
15004 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15005 &windows->magnify);
15006 if (resource_info->debug != MagickFalse)
15007 (void) LogMagickEvent(X11Event,GetMagickModule(),
15008 "Window id: 0x%lx (magnify)",windows->magnify.id);
15009 (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
15010 /*
15011 Initialize panning window.
15012 */
15013 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
15014 resource_info,&windows->pan);
15015 (void) CloneString(&windows->pan.name,"Pan Icon");
15016 windows->pan.width=windows->icon.width;
15017 windows->pan.height=windows->icon.height;
15018 (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.pan",
15019 resource_info->client_name);
15020 windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
15021 resource_name,"geometry",(char *) NULL);
15022 (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15023 &windows->pan.width,&windows->pan.height);
15024 windows->pan.flags|=PPosition;
15025 windows->pan.immutable=MagickTrue;
15026 windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15027 ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15028 StructureNotifyMask;
15029 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15030 manager_hints->input=MagickFalse;
15031 manager_hints->initial_state=NormalState;
15032 manager_hints->window_group=windows->image.id;
15033 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15034 &windows->pan);
15035 if (resource_info->debug != MagickFalse)
15036 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15037 windows->pan.id);
15038 (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15039 if (windows->info.mapped != MagickFalse)
15040 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15041 if ((windows->image.mapped == MagickFalse) ||
15042 (windows->backdrop.id != (Window) NULL))
15043 (void) XMapWindow(display,windows->image.id);
15044 /*
15045 Set our progress monitor and warning handlers.
15046 */
15047 if (warning_handler == (WarningHandler) NULL)
15048 {
15049 warning_handler=resource_info->display_warnings ?
15050 SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15051 warning_handler=resource_info->display_warnings ?
15052 SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15053 }
15054 /*
15055 Initialize Image and Magnify X images.
15056 */
15057 windows->image.x=0;
15058 windows->image.y=0;
15059 windows->magnify.shape=MagickFalse;
15060 width=(unsigned int) display_image->columns;
15061 height=(unsigned int) display_image->rows;
15062 if ((display_image->columns != width) || (display_image->rows != height))
15063 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15064 display_image->filename);
15065 status=XMakeImage(display,resource_info,&windows->image,display_image,
15066 width,height,exception);
15067 if (status == MagickFalse)
15068 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15069 display_image->filename);
15070 status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15071 windows->magnify.width,windows->magnify.height,exception);
15072 if (status == MagickFalse)
15073 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15074 display_image->filename);
15075 if (windows->magnify.mapped != MagickFalse)
15076 (void) XMapRaised(display,windows->magnify.id);
15077 if (windows->pan.mapped != MagickFalse)
15078 (void) XMapRaised(display,windows->pan.id);
15079 windows->image.window_changes.width=(int) display_image->columns;
15080 windows->image.window_changes.height=(int) display_image->rows;
15081 (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15082 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15083 (void) XSync(display,MagickFalse);
15084 /*
15085 Respond to events.
15086 */
15087 delay=display_image->delay/(size_t)
15088 MagickMax(display_image->ticks_per_second,1L);
15089 timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15090 update_time=0;
15091 if (resource_info->update != MagickFalse)
15092 {
15093 MagickBooleanType
15094 status;
15095
15096 /*
15097 Determine when file data was last modified.
15098 */
15099 status=GetPathAttributes(display_image->filename,&attributes);
15100 if (status != MagickFalse)
15101 update_time=attributes.st_mtime;
15102 }
15103 *state&=(unsigned int) (~FormerImageState);
15104 *state&=(unsigned int) (~MontageImageState);
15105 *state&=(unsigned int) (~NextImageState);
15106 do
15107 {
15108 /*
15109 Handle a window event.
15110 */
15111 if (windows->image.mapped != MagickFalse)
15112 if ((display_image->delay != 0) || (resource_info->update != 0))
15113 {
15114 if (timer < GetMagickTime())
15115 {
15116 if (resource_info->update == MagickFalse)
15117 *state|=NextImageState | ExitState;
15118 else
15119 {
15120 MagickBooleanType
15121 status;
15122
15123 /*
15124 Determine if image file was modified.
15125 */
15126 status=GetPathAttributes(display_image->filename,&attributes);
15127 if (status != MagickFalse)
15128 if (update_time != attributes.st_mtime)
15129 {
15130 /*
15131 Redisplay image.
15132 */
15133 (void) FormatLocaleString(
15134 resource_info->image_info->filename,MagickPathExtent,
15135 "%s:%s",display_image->magick,
15136 display_image->filename);
15137 nexus=ReadImage(resource_info->image_info,exception);
15138 if (nexus != (Image *) NULL)
15139 *state|=NextImageState | ExitState;
15140 }
15141 delay=display_image->delay/(size_t) MagickMax(
15142 display_image->ticks_per_second,1L);
15143 timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15144 }
15145 }
15146 if (XEventsQueued(display,QueuedAfterFlush) == 0)
15147 {
15148 /*
15149 Do not block if delay > 0.
15150 */
15151 XDelay(display,SuspendTime << 2);
15152 continue;
15153 }
15154 }
15155 timestamp=GetMagickTime();
15156 (void) XNextEvent(display,&event);
15157 if ((windows->image.stasis == MagickFalse) ||
15158 (windows->magnify.stasis == MagickFalse))
15159 {
15160 if ((GetMagickTime()-timestamp) > 0)
15161 {
15162 windows->image.stasis=MagickTrue;
15163 windows->magnify.stasis=MagickTrue;
15164 }
15165 }
15166 if (event.xany.window == windows->command.id)
15167 {
15168 /*
15169 Select a command from the Command widget.
15170 */
15171 id=XCommandWidget(display,windows,CommandMenu,&event);
15172 if (id < 0)
15173 continue;
15174 (void) CopyMagickString(command,CommandMenu[id],MagickPathExtent);
15175 display_command=CommandMenus[id];
15176 if (id < MagickMenus)
15177 {
15178 /*
15179 Select a command from a pop-up menu.
15180 */
15181 entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15182 command);
15183 if (entry < 0)
15184 continue;
15185 (void) CopyMagickString(command,Menus[id][entry],MagickPathExtent);
15186 display_command=Commands[id][entry];
15187 }
15188 if (display_command != NullCommand)
15189 nexus=XMagickCommand(display,resource_info,windows,display_command,
15190 &display_image,exception);
15191 continue;
15192 }
15193 switch (event.type)
15194 {
15195 case ButtonPress:
15196 {
15197 if (resource_info->debug != MagickFalse)
15198 (void) LogMagickEvent(X11Event,GetMagickModule(),
15199 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15200 event.xbutton.button,event.xbutton.x,event.xbutton.y);
15201 if ((event.xbutton.button == Button3) &&
15202 (event.xbutton.state & Mod1Mask))
15203 {
15204 /*
15205 Convert Alt-Button3 to Button2.
15206 */
15207 event.xbutton.button=Button2;
15208 event.xbutton.state&=(unsigned int) (~Mod1Mask);
15209 }
15210 if (event.xbutton.window == windows->backdrop.id)
15211 {
15212 (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15213 event.xbutton.time);
15214 break;
15215 }
15216 if (event.xbutton.window == windows->image.id)
15217 {
15218 switch (event.xbutton.button)
15219 {
15220 case Button1:
15221 {
15222 if (resource_info->immutable)
15223 {
15224 /*
15225 Select a command from the Virtual menu.
15226 */
15227 entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15228 command);
15229 if (entry >= 0)
15230 nexus=XMagickCommand(display,resource_info,windows,
15231 VirtualCommands[entry],&display_image,exception);
15232 break;
15233 }
15234 /*
15235 Map/unmap Command widget.
15236 */
15237 if (windows->command.mapped != MagickFalse)
15238 (void) XWithdrawWindow(display,windows->command.id,
15239 windows->command.screen);
15240 else
15241 {
15242 (void) XCommandWidget(display,windows,CommandMenu,
15243 (XEvent *) NULL);
15244 (void) XMapRaised(display,windows->command.id);
15245 }
15246 break;
15247 }
15248 case Button2:
15249 {
15250 /*
15251 User pressed the image magnify button.
15252 */
15253 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15254 &display_image,exception);
15255 XMagnifyImage(display,windows,&event,exception);
15256 break;
15257 }
15258 case Button3:
15259 {
15260 if (resource_info->immutable)
15261 {
15262 /*
15263 Select a command from the Virtual menu.
15264 */
15265 entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15266 command);
15267 if (entry >= 0)
15268 nexus=XMagickCommand(display,resource_info,windows,
15269 VirtualCommands[entry],&display_image,exception);
15270 break;
15271 }
15272 if (display_image->montage != (char *) NULL)
15273 {
15274 /*
15275 Open or delete a tile from a visual image directory.
15276 */
15277 nexus=XTileImage(display,resource_info,windows,
15278 display_image,&event,exception);
15279 if (nexus != (Image *) NULL)
15280 *state|=MontageImageState | NextImageState | ExitState;
15281 vid_info.x=(short int) windows->image.x;
15282 vid_info.y=(short int) windows->image.y;
15283 break;
15284 }
15285 /*
15286 Select a command from the Short Cuts menu.
15287 */
15288 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15289 command);
15290 if (entry >= 0)
15291 nexus=XMagickCommand(display,resource_info,windows,
15292 ShortCutsCommands[entry],&display_image,exception);
15293 break;
15294 }
15295 case Button4:
15296 {
15297 /*
15298 Wheel up.
15299 */
15300 XTranslateImage(display,windows,*image,XK_Up);
15301 break;
15302 }
15303 case Button5:
15304 {
15305 /*
15306 Wheel down.
15307 */
15308 XTranslateImage(display,windows,*image,XK_Down);
15309 break;
15310 }
15311 default:
15312 break;
15313 }
15314 break;
15315 }
15316 if (event.xbutton.window == windows->magnify.id)
15317 {
15318 const char
15319 *const MagnifyMenu[] =
15320 {
15321 "2",
15322 "4",
15323 "5",
15324 "6",
15325 "7",
15326 "8",
15327 "9",
15328 "3",
15329 (char *) NULL,
15330 };
15331
15332 int
15333 factor;
15334
15335 static KeySym
15336 MagnifyCommands[] =
15337 {
15338 XK_2,
15339 XK_4,
15340 XK_5,
15341 XK_6,
15342 XK_7,
15343 XK_8,
15344 XK_9,
15345 XK_3
15346 };
15347
15348 /*
15349 Select a magnify factor from the pop-up menu.
15350 */
15351 factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15352 if (factor >= 0)
15353 XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15354 exception);
15355 break;
15356 }
15357 if (event.xbutton.window == windows->pan.id)
15358 {
15359 switch (event.xbutton.button)
15360 {
15361 case Button4:
15362 {
15363 /*
15364 Wheel up.
15365 */
15366 XTranslateImage(display,windows,*image,XK_Up);
15367 break;
15368 }
15369 case Button5:
15370 {
15371 /*
15372 Wheel down.
15373 */
15374 XTranslateImage(display,windows,*image,XK_Down);
15375 break;
15376 }
15377 default:
15378 {
15379 XPanImage(display,windows,&event,exception);
15380 break;
15381 }
15382 }
15383 break;
15384 }
15385 delay=display_image->delay/(size_t)
15386 MagickMax(display_image->ticks_per_second,1L);
15387 timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15388 break;
15389 }
15390 case ButtonRelease:
15391 {
15392 if (resource_info->debug != MagickFalse)
15393 (void) LogMagickEvent(X11Event,GetMagickModule(),
15394 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15395 event.xbutton.button,event.xbutton.x,event.xbutton.y);
15396 break;
15397 }
15398 case ClientMessage:
15399 {
15400 if (resource_info->debug != MagickFalse)
15401 (void) LogMagickEvent(X11Event,GetMagickModule(),
15402 "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15403 event.xclient.message_type,event.xclient.format,(unsigned long)
15404 event.xclient.data.l[0]);
15405 if (event.xclient.message_type == windows->im_protocols)
15406 {
15407 if (*event.xclient.data.l == (long) windows->im_update_widget)
15408 {
15409 (void) CloneString(&windows->command.name,MagickTitle);
15410 windows->command.data=MagickMenus;
15411 (void) XCommandWidget(display,windows,CommandMenu,
15412 (XEvent *) NULL);
15413 break;
15414 }
15415 if (*event.xclient.data.l == (long) windows->im_update_colormap)
15416 {
15417 /*
15418 Update graphic context and window colormap.
15419 */
15420 for (i=0; i < (int) number_windows; i++)
15421 {
15422 if (magick_windows[i]->id == windows->icon.id)
15423 continue;
15424 context_values.background=pixel->background_color.pixel;
15425 context_values.foreground=pixel->foreground_color.pixel;
15426 (void) XChangeGC(display,magick_windows[i]->annotate_context,
15427 context_mask,&context_values);
15428 (void) XChangeGC(display,magick_windows[i]->widget_context,
15429 context_mask,&context_values);
15430 context_values.background=pixel->foreground_color.pixel;
15431 context_values.foreground=pixel->background_color.pixel;
15432 context_values.plane_mask=context_values.background ^
15433 context_values.foreground;
15434 (void) XChangeGC(display,magick_windows[i]->highlight_context,
15435 (size_t) (context_mask | GCPlaneMask),
15436 &context_values);
15437 magick_windows[i]->attributes.background_pixel=
15438 pixel->background_color.pixel;
15439 magick_windows[i]->attributes.border_pixel=
15440 pixel->border_color.pixel;
15441 magick_windows[i]->attributes.colormap=map_info->colormap;
15442 (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15443 (unsigned long) magick_windows[i]->mask,
15444 &magick_windows[i]->attributes);
15445 }
15446 if (windows->pan.mapped != MagickFalse)
15447 {
15448 (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15449 windows->pan.pixmap);
15450 (void) XClearWindow(display,windows->pan.id);
15451 XDrawPanRectangle(display,windows);
15452 }
15453 if (windows->backdrop.id != (Window) NULL)
15454 (void) XInstallColormap(display,map_info->colormap);
15455 break;
15456 }
15457 if (*event.xclient.data.l == (long) windows->im_former_image)
15458 {
15459 *state|=FormerImageState | ExitState;
15460 break;
15461 }
15462 if (*event.xclient.data.l == (long) windows->im_next_image)
15463 {
15464 *state|=NextImageState | ExitState;
15465 break;
15466 }
15467 if (*event.xclient.data.l == (long) windows->im_retain_colors)
15468 {
15469 *state|=RetainColorsState;
15470 break;
15471 }
15472 if (*event.xclient.data.l == (long) windows->im_exit)
15473 {
15474 *state|=ExitState;
15475 break;
15476 }
15477 break;
15478 }
15479 if (event.xclient.message_type == windows->dnd_protocols)
15480 {
15481 Atom
15482 selection,
15483 type;
15484
15485 int
15486 format,
15487 status;
15488
15489 unsigned char
15490 *data;
15491
15492 unsigned long
15493 after,
15494 length;
15495
15496 /*
15497 Display image named by the Drag-and-Drop selection.
15498 */
15499 if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15500 break;
15501 selection=XInternAtom(display,"DndSelection",MagickFalse);
15502 status=XGetWindowProperty(display,root_window,selection,0L,(long)
15503 MagickPathExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15504 &length,&after,&data);
15505 if ((status != Success) || (length == 0))
15506 break;
15507 if (*event.xclient.data.l == 2)
15508 {
15509 /*
15510 Offix DND.
15511 */
15512 (void) CopyMagickString(resource_info->image_info->filename,
15513 (char *) data,MagickPathExtent);
15514 }
15515 else
15516 {
15517 /*
15518 XDND.
15519 */
15520 if (strncmp((char *) data, "file:", 5) != 0)
15521 {
15522 (void) XFree((void *) data);
15523 break;
15524 }
15525 (void) CopyMagickString(resource_info->image_info->filename,
15526 ((char *) data)+5,MagickPathExtent);
15527 }
15528 nexus=ReadImage(resource_info->image_info,exception);
15529 CatchException(exception);
15530 if (nexus != (Image *) NULL)
15531 *state|=NextImageState | ExitState;
15532 (void) XFree((void *) data);
15533 break;
15534 }
15535 /*
15536 If client window delete message, exit.
15537 */
15538 if (event.xclient.message_type != windows->wm_protocols)
15539 break;
15540 if (*event.xclient.data.l != (long) windows->wm_delete_window)
15541 break;
15542 (void) XWithdrawWindow(display,event.xclient.window,
15543 visual_info->screen);
15544 if (event.xclient.window == windows->image.id)
15545 {
15546 *state|=ExitState;
15547 break;
15548 }
15549 if (event.xclient.window == windows->pan.id)
15550 {
15551 /*
15552 Restore original image size when pan window is deleted.
15553 */
15554 windows->image.window_changes.width=windows->image.ximage->width;
15555 windows->image.window_changes.height=windows->image.ximage->height;
15556 (void) XConfigureImage(display,resource_info,windows,
15557 display_image,exception);
15558 }
15559 break;
15560 }
15561 case ConfigureNotify:
15562 {
15563 if (resource_info->debug != MagickFalse)
15564 (void) LogMagickEvent(X11Event,GetMagickModule(),
15565 "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15566 event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15567 event.xconfigure.y,event.xconfigure.send_event);
15568 if (event.xconfigure.window == windows->image.id)
15569 {
15570 /*
15571 Image window has a new configuration.
15572 */
15573 if (event.xconfigure.send_event != 0)
15574 {
15575 XWindowChanges
15576 window_changes;
15577
15578 /*
15579 Position the transient windows relative of the Image window.
15580 */
15581 if (windows->command.geometry == (char *) NULL)
15582 if (windows->command.mapped == MagickFalse)
15583 {
15584 windows->command.x=event.xconfigure.x-(int)
15585 windows->command.width-25;
15586 windows->command.y=event.xconfigure.y;
15587 XConstrainWindowPosition(display,&windows->command);
15588 window_changes.x=windows->command.x;
15589 window_changes.y=windows->command.y;
15590 (void) XReconfigureWMWindow(display,windows->command.id,
15591 windows->command.screen,(unsigned int) (CWX | CWY),
15592 &window_changes);
15593 }
15594 if (windows->widget.geometry == (char *) NULL)
15595 if (windows->widget.mapped == MagickFalse)
15596 {
15597 windows->widget.x=event.xconfigure.x+
15598 event.xconfigure.width/10;
15599 windows->widget.y=event.xconfigure.y+
15600 event.xconfigure.height/10;
15601 XConstrainWindowPosition(display,&windows->widget);
15602 window_changes.x=windows->widget.x;
15603 window_changes.y=windows->widget.y;
15604 (void) XReconfigureWMWindow(display,windows->widget.id,
15605 windows->widget.screen,(unsigned int) (CWX | CWY),
15606 &window_changes);
15607 }
15608 if (windows->magnify.geometry == (char *) NULL)
15609 if (windows->magnify.mapped == MagickFalse)
15610 {
15611 windows->magnify.x=event.xconfigure.x+
15612 event.xconfigure.width+25;
15613 windows->magnify.y=event.xconfigure.y;
15614 XConstrainWindowPosition(display,&windows->magnify);
15615 window_changes.x=windows->magnify.x;
15616 window_changes.y=windows->magnify.y;
15617 (void) XReconfigureWMWindow(display,windows->magnify.id,
15618 windows->magnify.screen,(unsigned int) (CWX | CWY),
15619 &window_changes);
15620 }
15621 if (windows->pan.geometry == (char *) NULL)
15622 if (windows->pan.mapped == MagickFalse)
15623 {
15624 windows->pan.x=event.xconfigure.x+(int)
15625 event.xconfigure.width+25;
15626 windows->pan.y=event.xconfigure.y+(int)
15627 windows->magnify.height+50;
15628 XConstrainWindowPosition(display,&windows->pan);
15629 window_changes.x=windows->pan.x;
15630 window_changes.y=windows->pan.y;
15631 (void) XReconfigureWMWindow(display,windows->pan.id,
15632 windows->pan.screen,(unsigned int) (CWX | CWY),
15633 &window_changes);
15634 }
15635 }
15636 if ((event.xconfigure.width == (int) windows->image.width) &&
15637 (event.xconfigure.height == (int) windows->image.height))
15638 break;
15639 windows->image.width=(unsigned int) event.xconfigure.width;
15640 windows->image.height=(unsigned int) event.xconfigure.height;
15641 windows->image.x=0;
15642 windows->image.y=0;
15643 if (display_image->montage != (char *) NULL)
15644 {
15645 windows->image.x=vid_info.x;
15646 windows->image.y=vid_info.y;
15647 }
15648 if (windows->image.mapped != MagickFalse &&
15649 windows->image.stasis != MagickFalse)
15650 {
15651 /*
15652 Update image window configuration.
15653 */
15654 windows->image.window_changes.width=event.xconfigure.width;
15655 windows->image.window_changes.height=event.xconfigure.height;
15656 (void) XConfigureImage(display,resource_info,windows,
15657 display_image,exception);
15658 }
15659 /*
15660 Update pan window configuration.
15661 */
15662 if ((event.xconfigure.width < windows->image.ximage->width) ||
15663 (event.xconfigure.height < windows->image.ximage->height))
15664 {
15665 (void) XMapRaised(display,windows->pan.id);
15666 XDrawPanRectangle(display,windows);
15667 }
15668 else
15669 if (windows->pan.mapped != MagickFalse)
15670 (void) XWithdrawWindow(display,windows->pan.id,
15671 windows->pan.screen);
15672 break;
15673 }
15674 if (event.xconfigure.window == windows->magnify.id)
15675 {
15676 unsigned int
15677 magnify;
15678
15679 /*
15680 Magnify window has a new configuration.
15681 */
15682 windows->magnify.width=(unsigned int) event.xconfigure.width;
15683 windows->magnify.height=(unsigned int) event.xconfigure.height;
15684 if (windows->magnify.mapped == MagickFalse)
15685 break;
15686 magnify=1;
15687 while ((int) magnify <= event.xconfigure.width)
15688 magnify<<=1;
15689 while ((int) magnify <= event.xconfigure.height)
15690 magnify<<=1;
15691 magnify>>=1;
15692 if (((int) magnify != event.xconfigure.width) ||
15693 ((int) magnify != event.xconfigure.height))
15694 {
15695 window_changes.width=(int) magnify;
15696 window_changes.height=(int) magnify;
15697 (void) XReconfigureWMWindow(display,windows->magnify.id,
15698 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15699 &window_changes);
15700 break;
15701 }
15702 if (windows->magnify.mapped != MagickFalse &&
15703 windows->magnify.stasis != MagickFalse)
15704 {
15705 status=XMakeImage(display,resource_info,&windows->magnify,
15706 display_image,windows->magnify.width,windows->magnify.height,
15707 exception);
15708 XMakeMagnifyImage(display,windows,exception);
15709 }
15710 break;
15711 }
15712 if (windows->magnify.mapped != MagickFalse &&
15713 (event.xconfigure.window == windows->pan.id))
15714 {
15715 /*
15716 Pan icon window has a new configuration.
15717 */
15718 if (event.xconfigure.send_event != 0)
15719 {
15720 windows->pan.x=event.xconfigure.x;
15721 windows->pan.y=event.xconfigure.y;
15722 }
15723 windows->pan.width=(unsigned int) event.xconfigure.width;
15724 windows->pan.height=(unsigned int) event.xconfigure.height;
15725 break;
15726 }
15727 if (event.xconfigure.window == windows->icon.id)
15728 {
15729 /*
15730 Icon window has a new configuration.
15731 */
15732 windows->icon.width=(unsigned int) event.xconfigure.width;
15733 windows->icon.height=(unsigned int) event.xconfigure.height;
15734 break;
15735 }
15736 break;
15737 }
15738 case DestroyNotify:
15739 {
15740 /*
15741 Group leader has exited.
15742 */
15743 if (resource_info->debug != MagickFalse)
15744 (void) LogMagickEvent(X11Event,GetMagickModule(),
15745 "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15746 if (event.xdestroywindow.window == windows->group_leader.id)
15747 {
15748 *state|=ExitState;
15749 break;
15750 }
15751 break;
15752 }
15753 case EnterNotify:
15754 {
15755 /*
15756 Selectively install colormap.
15757 */
15758 if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15759 if (event.xcrossing.mode != NotifyUngrab)
15760 XInstallColormap(display,map_info->colormap);
15761 break;
15762 }
15763 case Expose:
15764 {
15765 if (resource_info->debug != MagickFalse)
15766 (void) LogMagickEvent(X11Event,GetMagickModule(),
15767 "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15768 event.xexpose.width,event.xexpose.height,event.xexpose.x,
15769 event.xexpose.y);
15770 /*
15771 Refresh windows that are now exposed.
15772 */
15773 if ((event.xexpose.window == windows->image.id) &&
15774 windows->image.mapped != MagickFalse)
15775 {
15776 XRefreshWindow(display,&windows->image,&event);
15777 delay=display_image->delay/(size_t) MagickMax(
15778 display_image->ticks_per_second,1L);
15779 timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15780 break;
15781 }
15782 if ((event.xexpose.window == windows->magnify.id) &&
15783 windows->magnify.mapped != MagickFalse)
15784 {
15785 XMakeMagnifyImage(display,windows,exception);
15786 break;
15787 }
15788 if (event.xexpose.window == windows->pan.id)
15789 {
15790 XDrawPanRectangle(display,windows);
15791 break;
15792 }
15793 if (event.xexpose.window == windows->icon.id)
15794 {
15795 XRefreshWindow(display,&windows->icon,&event);
15796 break;
15797 }
15798 break;
15799 }
15800 case KeyPress:
15801 {
15802 int
15803 length;
15804
15805 /*
15806 Respond to a user key press.
15807 */
15808 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15809 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15810 *(command+length)='\0';
15811 if (resource_info->debug != MagickFalse)
15812 (void) LogMagickEvent(X11Event,GetMagickModule(),
15813 "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15814 key_symbol,command);
15815 if (event.xkey.window == windows->image.id)
15816 {
15817 display_command=XImageWindowCommand(display,resource_info,windows,
15818 event.xkey.state,key_symbol,&display_image,exception);
15819 if (display_command != NullCommand)
15820 nexus=XMagickCommand(display,resource_info,windows,
15821 display_command,&display_image,exception);
15822 }
15823 if (event.xkey.window == windows->magnify.id)
15824 XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15825 exception);
15826 if (event.xkey.window == windows->pan.id)
15827 {
15828 if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15829 (void) XWithdrawWindow(display,windows->pan.id,
15830 windows->pan.screen);
15831 else
15832 if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15833 XTextViewHelp(display,resource_info,windows,MagickFalse,
15834 "Help Viewer - Image Pan",ImagePanHelp);
15835 else
15836 XTranslateImage(display,windows,*image,key_symbol);
15837 }
15838 delay=display_image->delay/(size_t) MagickMax(
15839 display_image->ticks_per_second,1L);
15840 timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15841 break;
15842 }
15843 case KeyRelease:
15844 {
15845 /*
15846 Respond to a user key release.
15847 */
15848 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15849 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15850 if (resource_info->debug != MagickFalse)
15851 (void) LogMagickEvent(X11Event,GetMagickModule(),
15852 "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15853 break;
15854 }
15855 case LeaveNotify:
15856 {
15857 /*
15858 Selectively uninstall colormap.
15859 */
15860 if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15861 if (event.xcrossing.mode != NotifyUngrab)
15862 XUninstallColormap(display,map_info->colormap);
15863 break;
15864 }
15865 case MapNotify:
15866 {
15867 if (resource_info->debug != MagickFalse)
15868 (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15869 event.xmap.window);
15870 if (event.xmap.window == windows->backdrop.id)
15871 {
15872 (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15873 CurrentTime);
15874 windows->backdrop.mapped=MagickTrue;
15875 break;
15876 }
15877 if (event.xmap.window == windows->image.id)
15878 {
15879 if (windows->backdrop.id != (Window) NULL)
15880 (void) XInstallColormap(display,map_info->colormap);
15881 if (LocaleCompare(display_image->magick,"LOGO") == 0)
15882 {
15883 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15884 nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15885 }
15886 if (((int) windows->image.width < windows->image.ximage->width) ||
15887 ((int) windows->image.height < windows->image.ximage->height))
15888 (void) XMapRaised(display,windows->pan.id);
15889 windows->image.mapped=MagickTrue;
15890 break;
15891 }
15892 if (event.xmap.window == windows->magnify.id)
15893 {
15894 XMakeMagnifyImage(display,windows,exception);
15895 windows->magnify.mapped=MagickTrue;
15896 (void) XWithdrawWindow(display,windows->info.id,
15897 windows->info.screen);
15898 break;
15899 }
15900 if (event.xmap.window == windows->pan.id)
15901 {
15902 XMakePanImage(display,resource_info,windows,display_image,
15903 exception);
15904 windows->pan.mapped=MagickTrue;
15905 break;
15906 }
15907 if (event.xmap.window == windows->info.id)
15908 {
15909 windows->info.mapped=MagickTrue;
15910 break;
15911 }
15912 if (event.xmap.window == windows->icon.id)
15913 {
15914 MagickBooleanType
15915 taint;
15916
15917 /*
15918 Create an icon image.
15919 */
15920 taint=display_image->taint;
15921 XMakeStandardColormap(display,icon_visual,icon_resources,
15922 display_image,icon_map,icon_pixel,exception);
15923 (void) XMakeImage(display,icon_resources,&windows->icon,
15924 display_image,windows->icon.width,windows->icon.height,
15925 exception);
15926 display_image->taint=taint;
15927 (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15928 windows->icon.pixmap);
15929 (void) XClearWindow(display,windows->icon.id);
15930 (void) XWithdrawWindow(display,windows->info.id,
15931 windows->info.screen);
15932 windows->icon.mapped=MagickTrue;
15933 break;
15934 }
15935 if (event.xmap.window == windows->command.id)
15936 {
15937 windows->command.mapped=MagickTrue;
15938 break;
15939 }
15940 if (event.xmap.window == windows->popup.id)
15941 {
15942 windows->popup.mapped=MagickTrue;
15943 break;
15944 }
15945 if (event.xmap.window == windows->widget.id)
15946 {
15947 windows->widget.mapped=MagickTrue;
15948 break;
15949 }
15950 break;
15951 }
15952 case MappingNotify:
15953 {
15954 (void) XRefreshKeyboardMapping(&event.xmapping);
15955 break;
15956 }
15957 case NoExpose:
15958 break;
15959 case PropertyNotify:
15960 {
15961 Atom
15962 type;
15963
15964 int
15965 format,
15966 status;
15967
15968 unsigned char
15969 *data;
15970
15971 unsigned long
15972 after,
15973 length;
15974
15975 if (resource_info->debug != MagickFalse)
15976 (void) LogMagickEvent(X11Event,GetMagickModule(),
15977 "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15978 event.xproperty.atom,event.xproperty.state);
15979 if (event.xproperty.atom != windows->im_remote_command)
15980 break;
15981 /*
15982 Display image named by the remote command protocol.
15983 */
15984 status=XGetWindowProperty(display,event.xproperty.window,
15985 event.xproperty.atom,0L,(long) MagickPathExtent,MagickFalse,(Atom)
15986 AnyPropertyType,&type,&format,&length,&after,&data);
15987 if ((status != Success) || (length == 0))
15988 break;
15989 if (LocaleCompare((char *) data,"-quit") == 0)
15990 {
15991 XClientMessage(display,windows->image.id,windows->im_protocols,
15992 windows->im_exit,CurrentTime);
15993 (void) XFree((void *) data);
15994 break;
15995 }
15996 (void) CopyMagickString(resource_info->image_info->filename,
15997 (char *) data,MagickPathExtent);
15998 (void) XFree((void *) data);
15999 nexus=ReadImage(resource_info->image_info,exception);
16000 CatchException(exception);
16001 if (nexus != (Image *) NULL)
16002 *state|=NextImageState | ExitState;
16003 break;
16004 }
16005 case ReparentNotify:
16006 {
16007 if (resource_info->debug != MagickFalse)
16008 (void) LogMagickEvent(X11Event,GetMagickModule(),
16009 "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
16010 event.xreparent.window);
16011 break;
16012 }
16013 case UnmapNotify:
16014 {
16015 if (resource_info->debug != MagickFalse)
16016 (void) LogMagickEvent(X11Event,GetMagickModule(),
16017 "Unmap Notify: 0x%lx",event.xunmap.window);
16018 if (event.xunmap.window == windows->backdrop.id)
16019 {
16020 windows->backdrop.mapped=MagickFalse;
16021 break;
16022 }
16023 if (event.xunmap.window == windows->image.id)
16024 {
16025 windows->image.mapped=MagickFalse;
16026 break;
16027 }
16028 if (event.xunmap.window == windows->magnify.id)
16029 {
16030 windows->magnify.mapped=MagickFalse;
16031 break;
16032 }
16033 if (event.xunmap.window == windows->pan.id)
16034 {
16035 windows->pan.mapped=MagickFalse;
16036 break;
16037 }
16038 if (event.xunmap.window == windows->info.id)
16039 {
16040 windows->info.mapped=MagickFalse;
16041 break;
16042 }
16043 if (event.xunmap.window == windows->icon.id)
16044 {
16045 if (map_info->colormap == icon_map->colormap)
16046 XConfigureImageColormap(display,resource_info,windows,
16047 display_image,exception);
16048 (void) XFreeStandardColormap(display,icon_visual,icon_map,
16049 icon_pixel);
16050 windows->icon.mapped=MagickFalse;
16051 break;
16052 }
16053 if (event.xunmap.window == windows->command.id)
16054 {
16055 windows->command.mapped=MagickFalse;
16056 break;
16057 }
16058 if (event.xunmap.window == windows->popup.id)
16059 {
16060 if (windows->backdrop.id != (Window) NULL)
16061 (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16062 CurrentTime);
16063 windows->popup.mapped=MagickFalse;
16064 break;
16065 }
16066 if (event.xunmap.window == windows->widget.id)
16067 {
16068 if (windows->backdrop.id != (Window) NULL)
16069 (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16070 CurrentTime);
16071 windows->widget.mapped=MagickFalse;
16072 break;
16073 }
16074 break;
16075 }
16076 default:
16077 {
16078 if (resource_info->debug != MagickFalse)
16079 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16080 event.type);
16081 break;
16082 }
16083 }
16084 } while (!(*state & ExitState));
16085 if ((*state & ExitState) == 0)
16086 (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16087 &display_image,exception);
16088 else
16089 if (resource_info->confirm_edit != MagickFalse)
16090 {
16091 /*
16092 Query user if image has changed.
16093 */
16094 if ((resource_info->immutable == MagickFalse) &&
16095 display_image->taint != MagickFalse)
16096 {
16097 int
16098 status;
16099
16100 status=XConfirmWidget(display,windows,"Your image changed.",
16101 "Do you want to save it");
16102 if (status == 0)
16103 *state&=(unsigned int) (~ExitState);
16104 else
16105 if (status > 0)
16106 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16107 &display_image,exception);
16108 }
16109 }
16110 if ((windows->visual_info->klass == GrayScale) ||
16111 (windows->visual_info->klass == PseudoColor) ||
16112 (windows->visual_info->klass == DirectColor))
16113 {
16114 /*
16115 Withdraw pan and Magnify window.
16116 */
16117 if (windows->info.mapped != MagickFalse)
16118 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16119 if (windows->magnify.mapped != MagickFalse)
16120 (void) XWithdrawWindow(display,windows->magnify.id,
16121 windows->magnify.screen);
16122 if (windows->command.mapped != MagickFalse)
16123 (void) XWithdrawWindow(display,windows->command.id,
16124 windows->command.screen);
16125 }
16126 if (windows->pan.mapped != MagickFalse)
16127 (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16128 if (resource_info->backdrop == MagickFalse)
16129 if (windows->backdrop.mapped)
16130 {
16131 (void) XWithdrawWindow(display,windows->backdrop.id,
16132 windows->backdrop.screen);
16133 (void) XDestroyWindow(display,windows->backdrop.id);
16134 windows->backdrop.id=(Window) NULL;
16135 (void) XWithdrawWindow(display,windows->image.id,
16136 windows->image.screen);
16137 (void) XDestroyWindow(display,windows->image.id);
16138 windows->image.id=(Window) NULL;
16139 }
16140 XSetCursorState(display,windows,MagickTrue);
16141 XCheckRefreshWindows(display,windows);
16142 if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16143 *state&=(unsigned int) (~ExitState);
16144 if (*state & ExitState)
16145 {
16146 /*
16147 Free Standard Colormap.
16148 */
16149 (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16150 if (resource_info->map_type == (char *) NULL)
16151 (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16152 /*
16153 Free X resources.
16154 */
16155 if (resource_info->copy_image != (Image *) NULL)
16156 {
16157 resource_info->copy_image=DestroyImage(resource_info->copy_image);
16158 resource_info->copy_image=NewImageList();
16159 }
16160 DestroyXResources();
16161 }
16162 (void) XSync(display,MagickFalse);
16163 /*
16164 Restore our progress monitor and warning handlers.
16165 */
16166 (void) SetErrorHandler(warning_handler);
16167 (void) SetWarningHandler(warning_handler);
16168 /*
16169 Change to home directory.
16170 */
16171 directory=getcwd(working_directory,MagickPathExtent);
16172 (void) directory;
16173 {
16174 int
16175 status;
16176
16177 if (*resource_info->home_directory == '\0')
16178 (void) CopyMagickString(resource_info->home_directory,".",MagickPathExtent);
16179 status=chdir(resource_info->home_directory);
16180 if (status == -1)
16181 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16182 "UnableToOpenFile","%s",resource_info->home_directory);
16183 }
16184 *image=display_image;
16185 return(nexus);
16186}
16187#else
16188
16189/*
16190%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16191% %
16192% %
16193% %
16194+ D i s p l a y I m a g e s %
16195% %
16196% %
16197% %
16198%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16199%
16200% DisplayImages() displays an image sequence to any X window screen. It
16201% returns a value other than 0 if successful. Check the exception member
16202% of image to determine the reason for any failure.
16203%
16204% The format of the DisplayImages method is:
16205%
16206% MagickBooleanType DisplayImages(const ImageInfo *image_info,
16207% Image *images,ExceptionInfo *exception)
16208%
16209% A description of each parameter follows:
16210%
16211% o image_info: the image info.
16212%
16213% o image: the image.
16214%
16215% o exception: return any errors or warnings in this structure.
16216%
16217*/
16218MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16219 Image *image,ExceptionInfo *exception)
16220{
16221 assert(image_info != (const ImageInfo *) NULL);
16222 assert(image_info->signature == MagickCoreSignature);
16223 assert(image != (Image *) NULL);
16224 assert(image->signature == MagickCoreSignature);
16225 (void) image_info;
16226 if (IsEventLogging() != MagickFalse)
16227 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16228 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16229 "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
16230 return(MagickFalse);
16231}
16232
16233/*
16234%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16235% %
16236% %
16237% %
16238+ R e m o t e D i s p l a y C o m m a n d %
16239% %
16240% %
16241% %
16242%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16243%
16244% RemoteDisplayCommand() encourages a remote display program to display the
16245% specified image filename.
16246%
16247% The format of the RemoteDisplayCommand method is:
16248%
16249% MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16250% const char *window,const char *filename,ExceptionInfo *exception)
16251%
16252% A description of each parameter follows:
16253%
16254% o image_info: the image info.
16255%
16256% o window: Specifies the name or id of an X window.
16257%
16258% o filename: the name of the image filename to display.
16259%
16260% o exception: return any errors or warnings in this structure.
16261%
16262*/
16263MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16264 const char *window,const char *filename,ExceptionInfo *exception)
16265{
16266 assert(image_info != (const ImageInfo *) NULL);
16267 assert(image_info->signature == MagickCoreSignature);
16268 assert(filename != (char *) NULL);
16269 if (IsEventLogging() != MagickFalse)
16270 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16271 (void) window;
16272 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16273 "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image_info->filename);
16274 return(MagickFalse);
16275}
16276#endif