blob: f3e89767878f95409318ea84778cd46addbf2760 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
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 %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000017% July 1992 %
18% %
19% %
Cristyd8420112021-01-01 14:52:00 -050020% Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% 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% %
Cristy9ddfcca2018-09-09 19:46:34 -040026% https://imagemagick.org/script/license.php %
cristy3ed852e2009-09-05 21:47:34 +000027% %
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*/
cristy4c08aed2011-07-01 19:47:50 +000042#include "MagickCore/studio.h"
43#include "MagickCore/artifact.h"
cristy8941c702012-06-21 01:30:15 +000044#include "MagickCore/attribute.h"
cristy4c08aed2011-07-01 19:47:50 +000045#include "MagickCore/blob.h"
46#include "MagickCore/cache.h"
cristy52010022011-10-21 18:07:37 +000047#include "MagickCore/cache-private.h"
cristy6a2180c2013-05-27 10:28:36 +000048#include "MagickCore/channel.h"
cristy4c08aed2011-07-01 19:47:50 +000049#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"
cristyc53413d2011-11-17 13:04:26 +000058#include "MagickCore/distort.h"
cristy4c08aed2011-07-01 19:47:50 +000059#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/log.h"
70#include "MagickCore/magick.h"
71#include "MagickCore/memory_.h"
72#include "MagickCore/monitor.h"
73#include "MagickCore/monitor-private.h"
74#include "MagickCore/montage.h"
cristy1e37e8f2014-02-21 17:05:37 +000075#include "MagickCore/nt-base-private.h"
cristy4c08aed2011-07-01 19:47:50 +000076#include "MagickCore/option.h"
77#include "MagickCore/paint.h"
78#include "MagickCore/pixel.h"
79#include "MagickCore/pixel-accessor.h"
cristy4c08aed2011-07-01 19:47:50 +000080#include "MagickCore/property.h"
81#include "MagickCore/quantum.h"
82#include "MagickCore/quantum-private.h"
83#include "MagickCore/resize.h"
84#include "MagickCore/resource_.h"
85#include "MagickCore/shear.h"
86#include "MagickCore/segment.h"
cristy7497f482011-12-08 01:57:31 +000087#include "MagickCore/statistic.h"
cristy4c08aed2011-07-01 19:47:50 +000088#include "MagickCore/string_.h"
89#include "MagickCore/string-private.h"
Cristyedd02d22019-04-15 09:02:06 -040090#include "MagickCore/timer-private.h"
dirk9d117d02016-03-17 23:34:48 +010091#include "MagickCore/transform.h"
dirk06f59012016-03-13 20:51:16 +010092#include "MagickCore/transform-private.h"
cristy4c08aed2011-07-01 19:47:50 +000093#include "MagickCore/threshold.h"
94#include "MagickCore/utility.h"
cristyd1dd6e42011-09-04 01:46:08 +000095#include "MagickCore/utility-private.h"
cristy4c08aed2011-07-01 19:47:50 +000096#include "MagickCore/version.h"
Cristyd2f22342020-01-05 11:46:47 -050097#include "MagickCore/visual-effects.h"
cristy4c08aed2011-07-01 19:47:50 +000098#include "MagickCore/widget.h"
cristybcbda3f2011-09-03 13:01:22 +000099#include "MagickCore/widget-private.h"
100#include "MagickCore/xwindow.h"
cristy4c08aed2011-07-01 19:47:50 +0000101#include "MagickCore/xwindow-private.h"
cristy3ed852e2009-09-05 21:47:34 +0000102
103#if defined(MAGICKCORE_X11_DELEGATE)
104/*
105 Define declarations.
106*/
cristy49e2d862010-11-12 02:50:30 +0000107#define MaxColors MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
cristy3ed852e2009-09-05 21:47:34 +0000108
109/*
110 Constant declarations.
111*/
112static const unsigned char
113 HighlightBitmap[8] =
114 {
115 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
116 },
cristydd05beb2010-11-21 21:23:39 +0000117 OpaqueBitmap[8] =
118 {
119 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
120 },
cristy3ed852e2009-09-05 21:47:34 +0000121 ShadowBitmap[8] =
122 {
123 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
124 };
cristy3ed852e2009-09-05 21:47:34 +0000125
126/*
127 Help widget declarations.
128*/
129static const char
Cristy6970baa2019-04-20 21:23:54 -0400130 ImageAnnotateHelp[] =
cristy3ed852e2009-09-05 21:47:34 +0000131 {
Cristy6970baa2019-04-20 21:23:54 -0400132 "In annotate mode, the Command widget has these options:\n"
133 "\n"
134 " Font Name\n"
135 " fixed\n"
136 " variable\n"
137 " 5x8\n"
138 " 6x10\n"
139 " 7x13bold\n"
140 " 8x13bold\n"
141 " 9x15bold\n"
142 " 10x20\n"
143 " 12x24\n"
144 " Browser...\n"
145 " Font Color\n"
146 " black\n"
147 " blue\n"
148 " cyan\n"
149 " green\n"
150 " gray\n"
151 " red\n"
152 " magenta\n"
153 " yellow\n"
154 " white\n"
155 " transparent\n"
156 " Browser...\n"
157 " Font Color\n"
158 " black\n"
159 " blue\n"
160 " cyan\n"
161 " green\n"
162 " gray\n"
163 " red\n"
164 " magenta\n"
165 " yellow\n"
166 " white\n"
167 " transparent\n"
168 " Browser...\n"
169 " Rotate Text\n"
170 " -90\n"
171 " -45\n"
172 " -30\n"
173 " 0\n"
174 " 30\n"
175 " 45\n"
176 " 90\n"
177 " 180\n"
178 " Dialog...\n"
179 " Help\n"
180 " Dismiss\n"
181 "\n"
182 "Choose a font name from the Font Name sub-menu. Additional\n"
183 "font names can be specified with the font browser. You can\n"
184 "change the menu names by setting the X resources font1\n"
185 "through font9.\n"
186 "\n"
187 "Choose a font color from the Font Color sub-menu.\n"
188 "Additional font colors can be specified with the color\n"
189 "browser. You can change the menu colors by setting the X\n"
190 "resources pen1 through pen9.\n"
191 "\n"
192 "If you select the color browser and press Grab, you can\n"
193 "choose the font color by moving the pointer to the desired\n"
194 "color on the screen and press any button.\n"
195 "\n"
196 "If you choose to rotate the text, choose Rotate Text from the\n"
197 "menu and select an angle. Typically you will only want to\n"
198 "rotate one line of text at a time. Depending on the angle you\n"
199 "choose, subsequent lines may end up overwriting each other.\n"
200 "\n"
201 "Choosing a font and its color is optional. The default font\n"
202 "is fixed and the default color is black. However, you must\n"
203 "choose a location to begin entering text and press button 1.\n"
204 "An underscore character will appear at the location of the\n"
205 "pointer. The cursor changes to a pencil to indicate you are\n"
206 "in text mode. To exit immediately, press Dismiss.\n"
207 "\n"
208 "In text mode, any key presses will display the character at\n"
209 "the location of the underscore and advance the underscore\n"
210 "cursor. Enter your text and once completed press Apply to\n"
211 "finish your image annotation. To correct errors press BACK\n"
212 "SPACE. To delete an entire line of text, press DELETE. Any\n"
213 "text that exceeds the boundaries of the image window is\n"
214 "automagically continued onto the next line.\n"
215 "\n"
216 "The actual color you request for the font is saved in the\n"
217 "image. However, the color that appears in your image window\n"
218 "may be different. For example, on a monochrome screen the\n"
219 "text will appear black or white even if you choose the color\n"
220 "red as the font color. However, the image saved to a file\n"
221 "with -write is written with red lettering. To assure the\n"
222 "correct color text in the final image, any PseudoClass image\n"
223 "is promoted to DirectClass (see miff(5)). To force a\n"
224 "PseudoClass image to remain PseudoClass, use -colors.\n"
cristy3ed852e2009-09-05 21:47:34 +0000225 },
Cristy6970baa2019-04-20 21:23:54 -0400226 ImageChopHelp[] =
cristy3ed852e2009-09-05 21:47:34 +0000227 {
Cristy6970baa2019-04-20 21:23:54 -0400228 "In chop mode, the Command widget has these options:\n"
229 "\n"
230 " Direction\n"
231 " horizontal\n"
232 " vertical\n"
233 " Help\n"
234 " Dismiss\n"
235 "\n"
236 "If the you choose the horizontal direction (this the\n"
237 "default), the area of the image between the two horizontal\n"
238 "endpoints of the chop line is removed. Otherwise, the area\n"
239 "of the image between the two vertical endpoints of the chop\n"
240 "line is removed.\n"
241 "\n"
242 "Select a location within the image window to begin your chop,\n"
243 "press and hold any button. Next, move the pointer to\n"
244 "another location in the image. As you move a line will\n"
245 "connect the initial location and the pointer. When you\n"
246 "release the button, the area within the image to chop is\n"
247 "determined by which direction you choose from the Command\n"
248 "widget.\n"
249 "\n"
250 "To cancel the image chopping, move the pointer back to the\n"
251 "starting point of the line and release the button.\n"
cristy3ed852e2009-09-05 21:47:34 +0000252 },
Cristy6970baa2019-04-20 21:23:54 -0400253 ImageColorEditHelp[] =
cristy3ed852e2009-09-05 21:47:34 +0000254 {
Cristy6970baa2019-04-20 21:23:54 -0400255 "In color edit mode, the Command widget has these options:\n"
256 "\n"
257 " Method\n"
258 " point\n"
259 " replace\n"
260 " floodfill\n"
261 " filltoborder\n"
262 " reset\n"
263 " Pixel Color\n"
264 " black\n"
265 " blue\n"
266 " cyan\n"
267 " green\n"
268 " gray\n"
269 " red\n"
270 " magenta\n"
271 " yellow\n"
272 " white\n"
273 " Browser...\n"
274 " Border Color\n"
275 " black\n"
276 " blue\n"
277 " cyan\n"
278 " green\n"
279 " gray\n"
280 " red\n"
281 " magenta\n"
282 " yellow\n"
283 " white\n"
284 " Browser...\n"
285 " Fuzz\n"
286 " 0%\n"
287 " 2%\n"
288 " 5%\n"
289 " 10%\n"
290 " 15%\n"
291 " Dialog...\n"
292 " Undo\n"
293 " Help\n"
294 " Dismiss\n"
295 "\n"
296 "Choose a color editing method from the Method sub-menu\n"
297 "of the Command widget. The point method recolors any pixel\n"
298 "selected with the pointer until the button is released. The\n"
299 "replace method recolors any pixel that matches the color of\n"
300 "the pixel you select with a button press. Floodfill recolors\n"
301 "any pixel that matches the color of the pixel you select with\n"
302 "a button press and is a neighbor. Whereas filltoborder recolors\n"
303 "any neighbor pixel that is not the border color. Finally reset\n"
304 "changes the entire image to the designated color.\n"
305 "\n"
306 "Next, choose a pixel color from the Pixel Color sub-menu.\n"
307 "Additional pixel colors can be specified with the color\n"
308 "browser. You can change the menu colors by setting the X\n"
309 "resources pen1 through pen9.\n"
310 "\n"
311 "Now press button 1 to select a pixel within the image window\n"
312 "to change its color. Additional pixels may be recolored as\n"
313 "prescribed by the method you choose.\n"
314 "\n"
315 "If the Magnify widget is mapped, it can be helpful in positioning\n"
316 "your pointer within the image (refer to button 2).\n"
317 "\n"
318 "The actual color you request for the pixels is saved in the\n"
319 "image. However, the color that appears in your image window\n"
320 "may be different. For example, on a monochrome screen the\n"
321 "pixel will appear black or white even if you choose the\n"
322 "color red as the pixel color. However, the image saved to a\n"
323 "file with -write is written with red pixels. To assure the\n"
324 "correct color text in the final image, any PseudoClass image\n"
325 "is promoted to DirectClass (see miff(5)). To force a\n"
326 "PseudoClass image to remain PseudoClass, use -colors.\n"
cristy3ed852e2009-09-05 21:47:34 +0000327 },
Cristy6970baa2019-04-20 21:23:54 -0400328 ImageCompositeHelp[] =
cristy3ed852e2009-09-05 21:47:34 +0000329 {
Cristy6970baa2019-04-20 21:23:54 -0400330 "First a widget window is displayed requesting you to enter an\n"
331 "image name. Press Composite, Grab or type a file name.\n"
332 "Press Cancel if you choose not to create a composite image.\n"
333 "When you choose Grab, move the pointer to the desired window\n"
334 "and press any button.\n"
335 "\n"
336 "If the Composite image does not have any matte information,\n"
337 "you are informed and the file browser is displayed again.\n"
338 "Enter the name of a mask image. The image is typically\n"
339 "grayscale and the same size as the composite image. If the\n"
340 "image is not grayscale, it is converted to grayscale and the\n"
341 "resulting intensities are used as matte information.\n"
342 "\n"
343 "A small window appears showing the location of the cursor in\n"
344 "the image window. You are now in composite mode. To exit\n"
345 "immediately, press Dismiss. In composite mode, the Command\n"
346 "widget has these options:\n"
347 "\n"
348 " Operators\n"
349 " Over\n"
350 " In\n"
351 " Out\n"
352 " Atop\n"
353 " Xor\n"
354 " Plus\n"
355 " Minus\n"
356 " Add\n"
357 " Subtract\n"
358 " Difference\n"
359 " Multiply\n"
360 " Bumpmap\n"
361 " Copy\n"
362 " CopyRed\n"
363 " CopyGreen\n"
364 " CopyBlue\n"
365 " CopyOpacity\n"
366 " Clear\n"
367 " Dissolve\n"
368 " Displace\n"
369 " Help\n"
370 " Dismiss\n"
371 "\n"
372 "Choose a composite operation from the Operators sub-menu of\n"
373 "the Command widget. How each operator behaves is described\n"
374 "below. Image window is the image currently displayed on\n"
375 "your X server and image is the image obtained with the File\n"
376 "Browser widget.\n"
377 "\n"
378 "Over The result is the union of the two image shapes,\n"
379 " with image obscuring image window in the region of\n"
380 " overlap.\n"
381 "\n"
382 "In The result is simply image cut by the shape of\n"
383 " image window. None of the image data of image\n"
384 " window is in the result.\n"
385 "\n"
386 "Out The resulting image is image with the shape of\n"
387 " image window cut out.\n"
388 "\n"
389 "Atop The result is the same shape as image image window,\n"
390 " with image obscuring image window where the image\n"
391 " shapes overlap. Note this differs from over\n"
392 " because the portion of image outside image window's\n"
393 " shape does not appear in the result.\n"
394 "\n"
395 "Xor The result is the image data from both image and\n"
396 " image window that is outside the overlap region.\n"
397 " The overlap region is blank.\n"
398 "\n"
399 "Plus The result is just the sum of the image data.\n"
400 " Output values are cropped to QuantumRange (no overflow).\n"
401 "\n"
402 "Minus The result of image - image window, with underflow\n"
403 " cropped to zero.\n"
404 "\n"
405 "Add The result of image + image window, with overflow\n"
406 " wrapping around (mod 256).\n"
407 "\n"
408 "Subtract The result of image - image window, with underflow\n"
409 " wrapping around (mod 256). The add and subtract\n"
410 " operators can be used to perform reversible\n"
411 " transformations.\n"
412 "\n"
413 "Difference\n"
414 " The result of abs(image - image window). This\n"
415 " useful for comparing two very similar images.\n"
416 "\n"
417 "Multiply\n"
418 " The result of image * image window. This\n"
419 " useful for the creation of drop-shadows.\n"
420 "\n"
421 "Bumpmap The result of surface normals from image * image\n"
422 " window.\n"
423 "\n"
424 "Copy The resulting image is image window replaced with\n"
425 " image. Here the matte information is ignored.\n"
426 "\n"
427 "CopyRed The red layer of the image window is replace with\n"
428 " the red layer of the image. The other layers are\n"
429 " untouched.\n"
430 "\n"
431 "CopyGreen\n"
432 " The green layer of the image window is replace with\n"
433 " the green layer of the image. The other layers are\n"
434 " untouched.\n"
435 "\n"
436 "CopyBlue The blue layer of the image window is replace with\n"
437 " the blue layer of the image. The other layers are\n"
438 " untouched.\n"
439 "\n"
440 "CopyOpacity\n"
441 " The matte layer of the image window is replace with\n"
442 " the matte layer of the image. The other layers are\n"
443 " untouched.\n"
444 "\n"
445 "The image compositor requires a matte, or alpha channel in\n"
446 "the image for some operations. This extra channel usually\n"
447 "defines a mask which represents a sort of a cookie-cutter\n"
448 "for the image. This the case when matte is opaque (full\n"
449 "coverage) for pixels inside the shape, zero outside, and\n"
450 "between 0 and QuantumRange on the boundary. If image does not\n"
451 "have a matte channel, it is initialized with 0 for any pixel\n"
452 "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
453 "\n"
454 "If you choose Dissolve, the composite operator becomes Over. The\n"
455 "image matte channel percent transparency is initialized to factor.\n"
456 "The image window is initialized to (100-factor). Where factor is the\n"
457 "value you specify in the Dialog widget.\n"
458 "\n"
459 "Displace shifts the image pixels as defined by a displacement\n"
460 "map. With this option, image is used as a displacement map.\n"
461 "Black, within the displacement map, is a maximum positive\n"
462 "displacement. White is a maximum negative displacement and\n"
463 "middle gray is neutral. The displacement is scaled to determine\n"
464 "the pixel shift. By default, the displacement applies in both the\n"
465 "horizontal and vertical directions. However, if you specify a mask,\n"
466 "image is the horizontal X displacement and mask the vertical Y\n"
467 "displacement.\n"
468 "\n"
469 "Note that matte information for image window is not retained\n"
470 "for colormapped X server visuals (e.g. StaticColor,\n"
471 "StaticColor, GrayScale, PseudoColor). Correct compositing\n"
472 "behavior may require a TrueColor or DirectColor visual or a\n"
473 "Standard Colormap.\n"
474 "\n"
475 "Choosing a composite operator is optional. The default\n"
476 "operator is replace. However, you must choose a location to\n"
477 "composite your image and press button 1. Press and hold the\n"
478 "button before releasing and an outline of the image will\n"
479 "appear to help you identify your location.\n"
480 "\n"
481 "The actual colors of the composite image is saved. However,\n"
482 "the color that appears in image window may be different.\n"
483 "For example, on a monochrome screen image window will appear\n"
484 "black or white even though your composited image may have\n"
485 "many colors. If the image is saved to a file it is written\n"
486 "with the correct colors. To assure the correct colors are\n"
487 "saved in the final image, any PseudoClass image is promoted\n"
488 "to DirectClass (see miff(5)). To force a PseudoClass image\n"
489 "to remain PseudoClass, use -colors.\n"
cristy3ed852e2009-09-05 21:47:34 +0000490 },
Cristy6970baa2019-04-20 21:23:54 -0400491 ImageCutHelp[] =
cristy3ed852e2009-09-05 21:47:34 +0000492 {
Cristy6970baa2019-04-20 21:23:54 -0400493 "In cut mode, the Command widget has these options:\n"
494 "\n"
495 " Help\n"
496 " Dismiss\n"
497 "\n"
498 "To define a cut region, press button 1 and drag. The\n"
499 "cut region is defined by a highlighted rectangle that\n"
500 "expands or contracts as it follows the pointer. Once you\n"
501 "are satisfied with the cut region, release the button.\n"
502 "You are now in rectify mode. In rectify mode, the Command\n"
503 "widget has these options:\n"
504 "\n"
505 " Cut\n"
506 " Help\n"
507 " Dismiss\n"
508 "\n"
509 "You can make adjustments by moving the pointer to one of the\n"
510 "cut rectangle corners, pressing a button, and dragging.\n"
511 "Finally, press Cut to commit your copy region. To\n"
512 "exit without cutting the image, press Dismiss.\n"
cristy3ed852e2009-09-05 21:47:34 +0000513 },
Cristy6970baa2019-04-20 21:23:54 -0400514 ImageCopyHelp[] =
cristy3ed852e2009-09-05 21:47:34 +0000515 {
Cristy6970baa2019-04-20 21:23:54 -0400516 "In copy mode, the Command widget has these options:\n"
517 "\n"
518 " Help\n"
519 " Dismiss\n"
520 "\n"
521 "To define a copy region, press button 1 and drag. The\n"
522 "copy region is defined by a highlighted rectangle that\n"
523 "expands or contracts as it follows the pointer. Once you\n"
524 "are satisfied with the copy region, release the button.\n"
525 "You are now in rectify mode. In rectify mode, the Command\n"
526 "widget has these options:\n"
527 "\n"
528 " Copy\n"
529 " Help\n"
530 " Dismiss\n"
531 "\n"
532 "You can make adjustments by moving the pointer to one of the\n"
533 "copy rectangle corners, pressing a button, and dragging.\n"
534 "Finally, press Copy to commit your copy region. To\n"
535 "exit without copying the image, press Dismiss.\n"
cristy3ed852e2009-09-05 21:47:34 +0000536 },
Cristy6970baa2019-04-20 21:23:54 -0400537 ImageCropHelp[] =
cristy3ed852e2009-09-05 21:47:34 +0000538 {
Cristy6970baa2019-04-20 21:23:54 -0400539 "In crop mode, the Command widget has these options:\n"
540 "\n"
541 " Help\n"
542 " Dismiss\n"
543 "\n"
544 "To define a cropping region, press button 1 and drag. The\n"
545 "cropping region is defined by a highlighted rectangle that\n"
546 "expands or contracts as it follows the pointer. Once you\n"
547 "are satisfied with the cropping region, release the button.\n"
548 "You are now in rectify mode. In rectify mode, the Command\n"
549 "widget has these options:\n"
550 "\n"
551 " Crop\n"
552 " Help\n"
553 " Dismiss\n"
554 "\n"
555 "You can make adjustments by moving the pointer to one of the\n"
556 "cropping rectangle corners, pressing a button, and dragging.\n"
557 "Finally, press Crop to commit your cropping region. To\n"
558 "exit without cropping the image, press Dismiss.\n"
cristy3ed852e2009-09-05 21:47:34 +0000559 },
Cristy6970baa2019-04-20 21:23:54 -0400560 ImageDrawHelp[] =
cristy3ed852e2009-09-05 21:47:34 +0000561 {
Cristy6970baa2019-04-20 21:23:54 -0400562 "The cursor changes to a crosshair to indicate you are in\n"
563 "draw mode. To exit immediately, press Dismiss. In draw mode,\n"
564 "the Command widget has these options:\n"
565 "\n"
566 " Element\n"
567 " point\n"
568 " line\n"
569 " rectangle\n"
570 " fill rectangle\n"
571 " circle\n"
572 " fill circle\n"
573 " ellipse\n"
574 " fill ellipse\n"
575 " polygon\n"
576 " fill polygon\n"
577 " Color\n"
578 " black\n"
579 " blue\n"
580 " cyan\n"
581 " green\n"
582 " gray\n"
583 " red\n"
584 " magenta\n"
585 " yellow\n"
586 " white\n"
587 " transparent\n"
588 " Browser...\n"
589 " Stipple\n"
590 " Brick\n"
591 " Diagonal\n"
592 " Scales\n"
593 " Vertical\n"
594 " Wavy\n"
595 " Translucent\n"
596 " Opaque\n"
597 " Open...\n"
598 " Width\n"
599 " 1\n"
600 " 2\n"
601 " 4\n"
602 " 8\n"
603 " 16\n"
604 " Dialog...\n"
605 " Undo\n"
606 " Help\n"
607 " Dismiss\n"
608 "\n"
609 "Choose a drawing primitive from the Element sub-menu.\n"
610 "\n"
611 "Choose a color from the Color sub-menu. Additional\n"
612 "colors can be specified with the color browser.\n"
613 "\n"
614 "If you choose the color browser and press Grab, you can\n"
615 "select the color by moving the pointer to the desired\n"
616 "color on the screen and press any button. The transparent\n"
617 "color updates the image matte channel and is useful for\n"
618 "image compositing.\n"
619 "\n"
620 "Choose a stipple, if appropriate, from the Stipple sub-menu.\n"
621 "Additional stipples can be specified with the file browser.\n"
622 "Stipples obtained from the file browser must be on disk in the\n"
623 "X11 bitmap format.\n"
624 "\n"
625 "Choose a width, if appropriate, from the Width sub-menu. To\n"
626 "choose a specific width select the Dialog widget.\n"
627 "\n"
628 "Choose a point in the Image window and press button 1 and\n"
629 "hold. Next, move the pointer to another location in the\n"
630 "image. As you move, a line connects the initial location and\n"
631 "the pointer. When you release the button, the image is\n"
632 "updated with the primitive you just drew. For polygons, the\n"
633 "image is updated when you press and release the button without\n"
634 "moving the pointer.\n"
635 "\n"
636 "To cancel image drawing, move the pointer back to the\n"
637 "starting point of the line and release the button.\n"
cristy3ed852e2009-09-05 21:47:34 +0000638 },
Cristy6970baa2019-04-20 21:23:54 -0400639 DisplayHelp[] =
cristy3ed852e2009-09-05 21:47:34 +0000640 {
Cristy6970baa2019-04-20 21:23:54 -0400641 "BUTTONS\n"
642 " The effects of each button press is described below. Three\n"
643 " buttons are required. If you have a two button mouse,\n"
644 " button 1 and 3 are returned. Press ALT and button 3 to\n"
645 " simulate button 2.\n"
646 "\n"
647 " 1 Press this button to map or unmap the Command widget.\n"
648 "\n"
649 " 2 Press and drag to define a region of the image to\n"
650 " magnify.\n"
651 "\n"
652 " 3 Press and drag to choose from a select set of commands.\n"
653 " This button behaves differently if the image being\n"
654 " displayed is a visual image directory. Here, choose a\n"
655 " particular tile of the directory and press this button and\n"
656 " drag to select a command from a pop-up menu. Choose from\n"
657 " these menu items:\n"
658 "\n"
659 " Open\n"
660 " Next\n"
661 " Former\n"
662 " Delete\n"
663 " Update\n"
664 "\n"
665 " If you choose Open, the image represented by the tile is\n"
666 " displayed. To return to the visual image directory, choose\n"
667 " Next from the Command widget. Next and Former moves to the\n"
668 " next or former image respectively. Choose Delete to delete\n"
669 " a particular image tile. Finally, choose Update to\n"
670 " synchronize all the image tiles with their respective\n"
671 " images.\n"
672 "\n"
673 "COMMAND WIDGET\n"
674 " The Command widget lists a number of sub-menus and commands.\n"
675 " They are\n"
676 "\n"
677 " File\n"
678 " Open...\n"
679 " Next\n"
680 " Former\n"
681 " Select...\n"
682 " Save...\n"
683 " Print...\n"
684 " Delete...\n"
685 " New...\n"
686 " Visual Directory...\n"
687 " Quit\n"
688 " Edit\n"
689 " Undo\n"
690 " Redo\n"
691 " Cut\n"
692 " Copy\n"
693 " Paste\n"
694 " View\n"
695 " Half Size\n"
696 " Original Size\n"
697 " Double Size\n"
698 " Resize...\n"
699 " Apply\n"
700 " Refresh\n"
701 " Restore\n"
702 " Transform\n"
703 " Crop\n"
704 " Chop\n"
705 " Flop\n"
706 " Flip\n"
707 " Rotate Right\n"
708 " Rotate Left\n"
709 " Rotate...\n"
710 " Shear...\n"
711 " Roll...\n"
712 " Trim Edges\n"
713 " Enhance\n"
714 " Brightness...\n"
715 " Saturation...\n"
716 " Hue...\n"
717 " Gamma...\n"
718 " Sharpen...\n"
719 " Dull\n"
720 " Contrast Stretch...\n"
721 " Sigmoidal Contrast...\n"
722 " Normalize\n"
723 " Equalize\n"
724 " Negate\n"
725 " Grayscale\n"
726 " Map...\n"
727 " Quantize...\n"
728 " Effects\n"
729 " Despeckle\n"
730 " Emboss\n"
731 " Reduce Noise\n"
732 " Add Noise\n"
733 " Sharpen...\n"
734 " Blur...\n"
735 " Threshold...\n"
736 " Edge Detect...\n"
737 " Spread...\n"
738 " Shade...\n"
739 " Painting...\n"
740 " Segment...\n"
741 " F/X\n"
742 " Solarize...\n"
743 " Sepia Tone...\n"
744 " Swirl...\n"
745 " Implode...\n"
746 " Vignette...\n"
747 " Wave...\n"
748 " Oil Painting...\n"
749 " Charcoal Drawing...\n"
750 " Image Edit\n"
751 " Annotate...\n"
752 " Draw...\n"
753 " Color...\n"
754 " Matte...\n"
755 " Composite...\n"
756 " Add Border...\n"
757 " Add Frame...\n"
758 " Comment...\n"
759 " Launch...\n"
760 " Region of Interest...\n"
761 " Miscellany\n"
762 " Image Info\n"
763 " Zoom Image\n"
764 " Show Preview...\n"
765 " Show Histogram\n"
766 " Show Matte\n"
767 " Background...\n"
768 " Slide Show\n"
769 " Preferences...\n"
770 " Help\n"
771 " Overview\n"
772 " Browse Documentation\n"
773 " About Display\n"
774 "\n"
775 " Menu items with a indented triangle have a sub-menu. They\n"
776 " are represented above as the indented items. To access a\n"
777 " sub-menu item, move the pointer to the appropriate menu and\n"
778 " press a button and drag. When you find the desired sub-menu\n"
779 " item, release the button and the command is executed. Move\n"
780 " the pointer away from the sub-menu if you decide not to\n"
781 " execute a particular command.\n"
782 "\n"
783 "KEYBOARD ACCELERATORS\n"
784 " Accelerators are one or two key presses that effect a\n"
785 " particular command. The keyboard accelerators that\n"
786 " display(1) understands is:\n"
787 "\n"
788 " Ctl+O Press to open an image from a file.\n"
789 "\n"
790 " space Press to display the next image.\n"
791 "\n"
792 " If the image is a multi-paged document such as a Postscript\n"
793 " document, you can skip ahead several pages by preceding\n"
794 " this command with a number. For example to display the\n"
795 " third page beyond the current page, press 3<space>.\n"
796 "\n"
797 " backspace Press to display the former image.\n"
798 "\n"
799 " If the image is a multi-paged document such as a Postscript\n"
800 " document, you can skip behind several pages by preceding\n"
801 " this command with a number. For example to display the\n"
802 " third page preceding the current page, press 3<backspace>.\n"
803 "\n"
804 " Ctl+S Press to write the image to a file.\n"
805 "\n"
806 " Ctl+P Press to print the image to a Postscript printer.\n"
807 "\n"
808 " Ctl+D Press to delete an image file.\n"
809 "\n"
810 " Ctl+N Press to create a blank canvas.\n"
811 "\n"
812 " Ctl+Q Press to discard all images and exit program.\n"
813 "\n"
814 " Ctl+Z Press to undo last image transformation.\n"
815 "\n"
816 " Ctl+R Press to redo last image transformation.\n"
817 "\n"
818 " Ctl+X Press to cut a region of the image.\n"
819 "\n"
820 " Ctl+C Press to copy a region of the image.\n"
821 "\n"
822 " Ctl+V Press to paste a region to the image.\n"
823 "\n"
824 " < Press to half the image size.\n"
825 "\n"
826 " - Press to return to the original image size.\n"
827 "\n"
828 " > Press to double the image size.\n"
829 "\n"
830 " % Press to resize the image to a width and height you\n"
831 " specify.\n"
832 "\n"
cristy3ed852e2009-09-05 21:47:34 +0000833 "Cmd-A Press to make any image transformations permanent."
Cristy6970baa2019-04-20 21:23:54 -0400834 "\n"
835 " By default, any image size transformations are applied\n"
836 " to the original image to create the image displayed on\n"
837 " the X server. However, the transformations are not\n"
838 " permanent (i.e. the original image does not change\n"
839 " size only the X image does). For example, if you\n"
840 " press > the X image will appear to double in size,\n"
841 " but the original image will in fact remain the same size.\n"
842 " To force the original image to double in size, press >\n"
843 " followed by Cmd-A.\n"
844 "\n"
845 " @ Press to refresh the image window.\n"
846 "\n"
847 " C Press to cut out a rectangular region of the image.\n"
848 "\n"
849 " [ Press to chop the image.\n"
850 "\n"
851 " H Press to flop image in the horizontal direction.\n"
852 "\n"
853 " V Press to flip image in the vertical direction.\n"
854 "\n"
855 " / Press to rotate the image 90 degrees clockwise.\n"
856 "\n"
857 " \\ Press to rotate the image 90 degrees counter-clockwise.\n"
858 "\n"
859 " * Press to rotate the image the number of degrees you\n"
860 " specify.\n"
861 "\n"
862 " S Press to shear the image the number of degrees you\n"
863 " specify.\n"
864 "\n"
865 " R Press to roll the image.\n"
866 "\n"
867 " T Press to trim the image edges.\n"
868 "\n"
869 " Shft-H Press to vary the image hue.\n"
870 "\n"
871 " Shft-S Press to vary the color saturation.\n"
872 "\n"
873 " Shft-L Press to vary the color brightness.\n"
874 "\n"
875 " Shft-G Press to gamma correct the image.\n"
876 "\n"
877 " Shft-C Press to sharpen the image contrast.\n"
878 "\n"
879 " Shft-Z Press to dull the image contrast.\n"
880 "\n"
881 " = Press to perform histogram equalization on the image.\n"
882 "\n"
883 " Shft-N Press to perform histogram normalization on the image.\n"
884 "\n"
885 " Shft-~ Press to negate the colors of the image.\n"
886 "\n"
887 " . Press to convert the image colors to gray.\n"
888 "\n"
889 " Shft-# Press to set the maximum number of unique colors in the\n"
890 " image.\n"
891 "\n"
892 " F2 Press to reduce the speckles in an image.\n"
893 "\n"
894 " F3 Press to eliminate peak noise from an image.\n"
895 "\n"
896 " F4 Press to add noise to an image.\n"
897 "\n"
898 " F5 Press to sharpen an image.\n"
899 "\n"
900 " F6 Press to delete an image file.\n"
901 "\n"
902 " F7 Press to threshold the image.\n"
903 "\n"
904 " F8 Press to detect edges within an image.\n"
905 "\n"
906 " F9 Press to emboss an image.\n"
907 "\n"
908 " F10 Press to displace pixels by a random amount.\n"
909 "\n"
910 " F11 Press to negate all pixels above the threshold level.\n"
911 "\n"
912 " F12 Press to shade the image using a distant light source.\n"
913 "\n"
914 " F13 Press to lighten or darken image edges to create a 3-D effect.\n"
915 "\n"
916 " F14 Press to segment the image by color.\n"
917 "\n"
918 " Meta-S Press to swirl image pixels about the center.\n"
919 "\n"
920 " Meta-I Press to implode image pixels about the center.\n"
921 "\n"
922 " Meta-W Press to alter an image along a sine wave.\n"
923 "\n"
924 " Meta-P Press to simulate an oil painting.\n"
925 "\n"
926 " Meta-C Press to simulate a charcoal drawing.\n"
927 "\n"
928 " Alt-A Press to annotate the image with text.\n"
929 "\n"
930 " Alt-D Press to draw on an image.\n"
931 "\n"
932 " Alt-P Press to edit an image pixel color.\n"
933 "\n"
934 " Alt-M Press to edit the image matte information.\n"
935 "\n"
936 " Alt-V Press to composite the image with another.\n"
937 "\n"
938 " Alt-B Press to add a border to the image.\n"
939 "\n"
940 " Alt-F Press to add an ornamental border to the image.\n"
941 "\n"
942 " Alt-Shft-!\n"
943 " Press to add an image comment.\n"
944 "\n"
945 " Ctl-A Press to apply image processing techniques to a region\n"
946 " of interest.\n"
947 "\n"
948 " Shft-? Press to display information about the image.\n"
949 "\n"
950 " Shft-+ Press to map the zoom image window.\n"
951 "\n"
952 " Shft-P Press to preview an image enhancement, effect, or f/x.\n"
953 "\n"
954 " F1 Press to display helpful information about display(1).\n"
955 "\n"
956 " Find Press to browse documentation about ImageMagick.\n"
957 "\n"
958 " 1-9 Press to change the level of magnification.\n"
959 "\n"
960 " Use the arrow keys to move the image one pixel up, down,\n"
961 " left, or right within the magnify window. Be sure to first\n"
962 " map the magnify window by pressing button 2.\n"
963 "\n"
964 " Press ALT and one of the arrow keys to trim off one pixel\n"
965 " from any side of the image.\n"
cristy3ed852e2009-09-05 21:47:34 +0000966 },
Cristy6970baa2019-04-20 21:23:54 -0400967 ImageMatteEditHelp[] =
cristy3ed852e2009-09-05 21:47:34 +0000968 {
Cristy6970baa2019-04-20 21:23:54 -0400969 "Matte information within an image is useful for some\n"
970 "operations such as image compositing (See IMAGE\n"
971 "COMPOSITING). This extra channel usually defines a mask\n"
972 "which represents a sort of a cookie-cutter for the image.\n"
973 "This the case when matte is opaque (full coverage) for\n"
974 "pixels inside the shape, zero outside, and between 0 and\n"
975 "QuantumRange on the boundary.\n"
976 "\n"
977 "A small window appears showing the location of the cursor in\n"
978 "the image window. You are now in matte edit mode. To exit\n"
979 "immediately, press Dismiss. In matte edit mode, the Command\n"
980 "widget has these options:\n"
981 "\n"
982 " Method\n"
983 " point\n"
984 " replace\n"
985 " floodfill\n"
986 " filltoborder\n"
987 " reset\n"
988 " Border Color\n"
989 " black\n"
990 " blue\n"
991 " cyan\n"
992 " green\n"
993 " gray\n"
994 " red\n"
995 " magenta\n"
996 " yellow\n"
997 " white\n"
998 " Browser...\n"
999 " Fuzz\n"
1000 " 0%\n"
1001 " 2%\n"
1002 " 5%\n"
1003 " 10%\n"
1004 " 15%\n"
1005 " Dialog...\n"
1006 " Matte\n"
1007 " Opaque\n"
1008 " Transparent\n"
1009 " Dialog...\n"
1010 " Undo\n"
1011 " Help\n"
1012 " Dismiss\n"
1013 "\n"
1014 "Choose a matte editing method from the Method sub-menu of\n"
1015 "the Command widget. The point method changes the matte value\n"
1016 "of any pixel selected with the pointer until the button is\n"
1017 "is released. The replace method changes the matte value of\n"
1018 "any pixel that matches the color of the pixel you select with\n"
1019 "a button press. Floodfill changes the matte value of any pixel\n"
1020 "that matches the color of the pixel you select with a button\n"
1021 "press and is a neighbor. Whereas filltoborder changes the matte\n"
1022 "value any neighbor pixel that is not the border color. Finally\n"
1023 "reset changes the entire image to the designated matte value.\n"
1024 "\n"
1025 "Choose Matte Value and pick Opaque or Transarent. For other values\n"
1026 "select the Dialog entry. Here a dialog appears requesting a matte\n"
1027 "value. The value you select is assigned as the opacity value of the\n"
1028 "selected pixel or pixels.\n"
1029 "\n"
1030 "Now, press any button to select a pixel within the image\n"
1031 "window to change its matte value.\n"
1032 "\n"
1033 "If the Magnify widget is mapped, it can be helpful in positioning\n"
1034 "your pointer within the image (refer to button 2).\n"
1035 "\n"
1036 "Matte information is only valid in a DirectClass image.\n"
1037 "Therefore, any PseudoClass image is promoted to DirectClass\n"
1038 "(see miff(5)). Note that matte information for PseudoClass\n"
1039 "is not retained for colormapped X server visuals (e.g.\n"
1040 "StaticColor, StaticColor, GrayScale, PseudoColor) unless you\n"
1041 "immediately save your image to a file (refer to Write).\n"
1042 "Correct matte editing behavior may require a TrueColor or\n"
1043 "DirectColor visual or a Standard Colormap.\n"
cristy3ed852e2009-09-05 21:47:34 +00001044 },
Cristy6970baa2019-04-20 21:23:54 -04001045 ImagePanHelp[] =
cristy3ed852e2009-09-05 21:47:34 +00001046 {
Cristy6970baa2019-04-20 21:23:54 -04001047 "When an image exceeds the width or height of the X server\n"
1048 "screen, display maps a small panning icon. The rectangle\n"
1049 "within the panning icon shows the area that is currently\n"
1050 "displayed in the image window. To pan about the image,\n"
1051 "press any button and drag the pointer within the panning\n"
1052 "icon. The pan rectangle moves with the pointer and the\n"
1053 "image window is updated to reflect the location of the\n"
1054 "rectangle within the panning icon. When you have selected\n"
1055 "the area of the image you wish to view, release the button.\n"
1056 "\n"
1057 "Use the arrow keys to pan the image one pixel up, down,\n"
1058 "left, or right within the image window.\n"
1059 "\n"
1060 "The panning icon is withdrawn if the image becomes smaller\n"
1061 "than the dimensions of the X server screen.\n"
cristy3ed852e2009-09-05 21:47:34 +00001062 },
Cristy6970baa2019-04-20 21:23:54 -04001063 ImagePasteHelp[] =
cristy3ed852e2009-09-05 21:47:34 +00001064 {
Cristy6970baa2019-04-20 21:23:54 -04001065 "A small window appears showing the location of the cursor in\n"
1066 "the image window. You are now in paste mode. To exit\n"
1067 "immediately, press Dismiss. In paste mode, the Command\n"
1068 "widget has these options:\n"
1069 "\n"
1070 " Operators\n"
1071 " over\n"
1072 " in\n"
1073 " out\n"
1074 " atop\n"
1075 " xor\n"
1076 " plus\n"
1077 " minus\n"
1078 " add\n"
1079 " subtract\n"
1080 " difference\n"
1081 " replace\n"
1082 " Help\n"
1083 " Dismiss\n"
1084 "\n"
1085 "Choose a composite operation from the Operators sub-menu of\n"
1086 "the Command widget. How each operator behaves is described\n"
1087 "below. Image window is the image currently displayed on\n"
1088 "your X server and image is the image obtained with the File\n"
1089 "Browser widget.\n"
1090 "\n"
1091 "Over The result is the union of the two image shapes,\n"
1092 " with image obscuring image window in the region of\n"
1093 " overlap.\n"
1094 "\n"
1095 "In The result is simply image cut by the shape of\n"
1096 " image window. None of the image data of image\n"
1097 " window is in the result.\n"
1098 "\n"
1099 "Out The resulting image is image with the shape of\n"
1100 " image window cut out.\n"
1101 "\n"
1102 "Atop The result is the same shape as image image window,\n"
1103 " with image obscuring image window where the image\n"
1104 " shapes overlap. Note this differs from over\n"
1105 " because the portion of image outside image window's\n"
1106 " shape does not appear in the result.\n"
1107 "\n"
1108 "Xor The result is the image data from both image and\n"
1109 " image window that is outside the overlap region.\n"
1110 " The overlap region is blank.\n"
1111 "\n"
1112 "Plus The result is just the sum of the image data.\n"
1113 " Output values are cropped to QuantumRange (no overflow).\n"
1114 " This operation is independent of the matte\n"
1115 " channels.\n"
1116 "\n"
1117 "Minus The result of image - image window, with underflow\n"
1118 " cropped to zero.\n"
1119 "\n"
1120 "Add The result of image + image window, with overflow\n"
1121 " wrapping around (mod 256).\n"
1122 "\n"
1123 "Subtract The result of image - image window, with underflow\n"
1124 " wrapping around (mod 256). The add and subtract\n"
1125 " operators can be used to perform reversible\n"
1126 " transformations.\n"
1127 "\n"
1128 "Difference\n"
1129 " The result of abs(image - image window). This\n"
1130 " useful for comparing two very similar images.\n"
1131 "\n"
1132 "Copy The resulting image is image window replaced with\n"
1133 " image. Here the matte information is ignored.\n"
1134 "\n"
1135 "CopyRed The red layer of the image window is replace with\n"
1136 " the red layer of the image. The other layers are\n"
1137 " untouched.\n"
1138 "\n"
1139 "CopyGreen\n"
1140 " The green layer of the image window is replace with\n"
1141 " the green layer of the image. The other layers are\n"
1142 " untouched.\n"
1143 "\n"
1144 "CopyBlue The blue layer of the image window is replace with\n"
1145 " the blue layer of the image. The other layers are\n"
1146 " untouched.\n"
1147 "\n"
1148 "CopyOpacity\n"
1149 " The matte layer of the image window is replace with\n"
1150 " the matte layer of the image. The other layers are\n"
1151 " untouched.\n"
1152 "\n"
1153 "The image compositor requires a matte, or alpha channel in\n"
1154 "the image for some operations. This extra channel usually\n"
1155 "defines a mask which represents a sort of a cookie-cutter\n"
1156 "for the image. This the case when matte is opaque (full\n"
1157 "coverage) for pixels inside the shape, zero outside, and\n"
1158 "between 0 and QuantumRange on the boundary. If image does not\n"
1159 "have a matte channel, it is initialized with 0 for any pixel\n"
1160 "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
1161 "\n"
1162 "Note that matte information for image window is not retained\n"
1163 "for colormapped X server visuals (e.g. StaticColor,\n"
1164 "StaticColor, GrayScale, PseudoColor). Correct compositing\n"
1165 "behavior may require a TrueColor or DirectColor visual or a\n"
1166 "Standard Colormap.\n"
1167 "\n"
1168 "Choosing a composite operator is optional. The default\n"
1169 "operator is replace. However, you must choose a location to\n"
1170 "paste your image and press button 1. Press and hold the\n"
1171 "button before releasing and an outline of the image will\n"
1172 "appear to help you identify your location.\n"
1173 "\n"
1174 "The actual colors of the pasted image is saved. However,\n"
1175 "the color that appears in image window may be different.\n"
1176 "For example, on a monochrome screen image window will appear\n"
1177 "black or white even though your pasted image may have\n"
1178 "many colors. If the image is saved to a file it is written\n"
1179 "with the correct colors. To assure the correct colors are\n"
1180 "saved in the final image, any PseudoClass image is promoted\n"
1181 "to DirectClass (see miff(5)). To force a PseudoClass image\n"
1182 "to remain PseudoClass, use -colors.\n"
cristy3ed852e2009-09-05 21:47:34 +00001183 },
Cristy6970baa2019-04-20 21:23:54 -04001184 ImageROIHelp[] =
cristy3ed852e2009-09-05 21:47:34 +00001185 {
Cristy6970baa2019-04-20 21:23:54 -04001186 "In region of interest mode, the Command widget has these\n"
1187 "options:\n"
1188 "\n"
1189 " Help\n"
1190 " Dismiss\n"
1191 "\n"
1192 "To define a region of interest, press button 1 and drag.\n"
1193 "The region of interest is defined by a highlighted rectangle\n"
1194 "that expands or contracts as it follows the pointer. Once\n"
1195 "you are satisfied with the region of interest, release the\n"
1196 "button. You are now in apply mode. In apply mode the\n"
1197 "Command widget has these options:\n"
1198 "\n"
1199 " File\n"
1200 " Save...\n"
1201 " Print...\n"
1202 " Edit\n"
1203 " Undo\n"
1204 " Redo\n"
1205 " Transform\n"
1206 " Flop\n"
1207 " Flip\n"
1208 " Rotate Right\n"
1209 " Rotate Left\n"
1210 " Enhance\n"
1211 " Hue...\n"
1212 " Saturation...\n"
1213 " Brightness...\n"
1214 " Gamma...\n"
1215 " Spiff\n"
1216 " Dull\n"
1217 " Contrast Stretch\n"
1218 " Sigmoidal Contrast...\n"
1219 " Normalize\n"
1220 " Equalize\n"
1221 " Negate\n"
1222 " Grayscale\n"
1223 " Map...\n"
1224 " Quantize...\n"
1225 " Effects\n"
1226 " Despeckle\n"
1227 " Emboss\n"
1228 " Reduce Noise\n"
1229 " Sharpen...\n"
1230 " Blur...\n"
1231 " Threshold...\n"
1232 " Edge Detect...\n"
1233 " Spread...\n"
1234 " Shade...\n"
1235 " Raise...\n"
1236 " Segment...\n"
1237 " F/X\n"
1238 " Solarize...\n"
1239 " Sepia Tone...\n"
1240 " Swirl...\n"
1241 " Implode...\n"
1242 " Vignette...\n"
1243 " Wave...\n"
1244 " Oil Painting...\n"
1245 " Charcoal Drawing...\n"
1246 " Miscellany\n"
1247 " Image Info\n"
1248 " Zoom Image\n"
1249 " Show Preview...\n"
1250 " Show Histogram\n"
1251 " Show Matte\n"
1252 " Help\n"
1253 " Dismiss\n"
1254 "\n"
1255 "You can make adjustments to the region of interest by moving\n"
1256 "the pointer to one of the rectangle corners, pressing a\n"
1257 "button, and dragging. Finally, choose an image processing\n"
1258 "technique from the Command widget. You can choose more than\n"
1259 "one image processing technique to apply to an area.\n"
1260 "Alternatively, you can move the region of interest before\n"
1261 "applying another image processing technique. To exit, press\n"
1262 "Dismiss.\n"
cristy3ed852e2009-09-05 21:47:34 +00001263 },
Cristy6970baa2019-04-20 21:23:54 -04001264 ImageRotateHelp[] =
cristy3ed852e2009-09-05 21:47:34 +00001265 {
Cristy6970baa2019-04-20 21:23:54 -04001266 "In rotate mode, the Command widget has these options:\n"
1267 "\n"
1268 " Pixel Color\n"
1269 " black\n"
1270 " blue\n"
1271 " cyan\n"
1272 " green\n"
1273 " gray\n"
1274 " red\n"
1275 " magenta\n"
1276 " yellow\n"
1277 " white\n"
1278 " Browser...\n"
1279 " Direction\n"
1280 " horizontal\n"
1281 " vertical\n"
1282 " Help\n"
1283 " Dismiss\n"
1284 "\n"
1285 "Choose a background color from the Pixel Color sub-menu.\n"
1286 "Additional background colors can be specified with the color\n"
1287 "browser. You can change the menu colors by setting the X\n"
1288 "resources pen1 through pen9.\n"
1289 "\n"
1290 "If you choose the color browser and press Grab, you can\n"
1291 "select the background color by moving the pointer to the\n"
1292 "desired color on the screen and press any button.\n"
1293 "\n"
1294 "Choose a point in the image window and press this button and\n"
1295 "hold. Next, move the pointer to another location in the\n"
1296 "image. As you move a line connects the initial location and\n"
1297 "the pointer. When you release the button, the degree of\n"
1298 "image rotation is determined by the slope of the line you\n"
1299 "just drew. The slope is relative to the direction you\n"
1300 "choose from the Direction sub-menu of the Command widget.\n"
1301 "\n"
1302 "To cancel the image rotation, move the pointer back to the\n"
1303 "starting point of the line and release the button.\n"
cristy3ed852e2009-09-05 21:47:34 +00001304 };
1305
1306/*
1307 Enumeration declarations.
1308*/
1309typedef enum
1310{
1311 CopyMode,
1312 CropMode,
1313 CutMode
1314} ClipboardMode;
1315
1316typedef enum
1317{
1318 OpenCommand,
1319 NextCommand,
1320 FormerCommand,
1321 SelectCommand,
1322 SaveCommand,
1323 PrintCommand,
1324 DeleteCommand,
1325 NewCommand,
1326 VisualDirectoryCommand,
1327 QuitCommand,
1328 UndoCommand,
1329 RedoCommand,
1330 CutCommand,
1331 CopyCommand,
1332 PasteCommand,
1333 HalfSizeCommand,
1334 OriginalSizeCommand,
1335 DoubleSizeCommand,
1336 ResizeCommand,
1337 ApplyCommand,
1338 RefreshCommand,
1339 RestoreCommand,
1340 CropCommand,
1341 ChopCommand,
1342 FlopCommand,
1343 FlipCommand,
1344 RotateRightCommand,
1345 RotateLeftCommand,
1346 RotateCommand,
1347 ShearCommand,
1348 RollCommand,
1349 TrimCommand,
1350 HueCommand,
1351 SaturationCommand,
1352 BrightnessCommand,
1353 GammaCommand,
1354 SpiffCommand,
1355 DullCommand,
1356 ContrastStretchCommand,
1357 SigmoidalContrastCommand,
1358 NormalizeCommand,
1359 EqualizeCommand,
1360 NegateCommand,
1361 GrayscaleCommand,
1362 MapCommand,
1363 QuantizeCommand,
1364 DespeckleCommand,
1365 EmbossCommand,
1366 ReduceNoiseCommand,
1367 AddNoiseCommand,
1368 SharpenCommand,
1369 BlurCommand,
1370 ThresholdCommand,
1371 EdgeDetectCommand,
1372 SpreadCommand,
1373 ShadeCommand,
1374 RaiseCommand,
1375 SegmentCommand,
1376 SolarizeCommand,
1377 SepiaToneCommand,
1378 SwirlCommand,
1379 ImplodeCommand,
1380 VignetteCommand,
1381 WaveCommand,
1382 OilPaintCommand,
1383 CharcoalDrawCommand,
1384 AnnotateCommand,
1385 DrawCommand,
1386 ColorCommand,
1387 MatteCommand,
1388 CompositeCommand,
1389 AddBorderCommand,
1390 AddFrameCommand,
1391 CommentCommand,
1392 LaunchCommand,
1393 RegionofInterestCommand,
1394 ROIHelpCommand,
1395 ROIDismissCommand,
1396 InfoCommand,
1397 ZoomCommand,
1398 ShowPreviewCommand,
1399 ShowHistogramCommand,
1400 ShowMatteCommand,
1401 BackgroundCommand,
1402 SlideShowCommand,
1403 PreferencesCommand,
1404 HelpCommand,
1405 BrowseDocumentationCommand,
1406 VersionCommand,
1407 SaveToUndoBufferCommand,
1408 FreeBuffersCommand,
1409 NullCommand
1410} CommandType;
1411
1412typedef enum
1413{
1414 AnnotateNameCommand,
1415 AnnotateFontColorCommand,
1416 AnnotateBackgroundColorCommand,
1417 AnnotateRotateCommand,
1418 AnnotateHelpCommand,
1419 AnnotateDismissCommand,
1420 TextHelpCommand,
1421 TextApplyCommand,
1422 ChopDirectionCommand,
1423 ChopHelpCommand,
1424 ChopDismissCommand,
1425 HorizontalChopCommand,
1426 VerticalChopCommand,
1427 ColorEditMethodCommand,
1428 ColorEditColorCommand,
1429 ColorEditBorderCommand,
1430 ColorEditFuzzCommand,
1431 ColorEditUndoCommand,
1432 ColorEditHelpCommand,
1433 ColorEditDismissCommand,
1434 CompositeOperatorsCommand,
1435 CompositeDissolveCommand,
1436 CompositeDisplaceCommand,
1437 CompositeHelpCommand,
1438 CompositeDismissCommand,
1439 CropHelpCommand,
1440 CropDismissCommand,
1441 RectifyCopyCommand,
1442 RectifyHelpCommand,
1443 RectifyDismissCommand,
1444 DrawElementCommand,
1445 DrawColorCommand,
1446 DrawStippleCommand,
1447 DrawWidthCommand,
1448 DrawUndoCommand,
1449 DrawHelpCommand,
1450 DrawDismissCommand,
1451 MatteEditMethod,
1452 MatteEditBorderCommand,
1453 MatteEditFuzzCommand,
1454 MatteEditValueCommand,
1455 MatteEditUndoCommand,
1456 MatteEditHelpCommand,
1457 MatteEditDismissCommand,
1458 PasteOperatorsCommand,
1459 PasteHelpCommand,
1460 PasteDismissCommand,
1461 RotateColorCommand,
1462 RotateDirectionCommand,
1463 RotateCropCommand,
1464 RotateSharpenCommand,
1465 RotateHelpCommand,
1466 RotateDismissCommand,
1467 HorizontalRotateCommand,
1468 VerticalRotateCommand,
1469 TileLoadCommand,
1470 TileNextCommand,
1471 TileFormerCommand,
1472 TileDeleteCommand,
1473 TileUpdateCommand
1474} ModeType;
1475
1476/*
1477 Stipples.
1478*/
1479#define BricksWidth 20
1480#define BricksHeight 20
1481#define DiagonalWidth 16
1482#define DiagonalHeight 16
1483#define HighlightWidth 8
1484#define HighlightHeight 8
cristydd05beb2010-11-21 21:23:39 +00001485#define OpaqueWidth 8
1486#define OpaqueHeight 8
cristy3ed852e2009-09-05 21:47:34 +00001487#define ScalesWidth 16
1488#define ScalesHeight 16
1489#define ShadowWidth 8
1490#define ShadowHeight 8
1491#define VerticalWidth 16
1492#define VerticalHeight 16
1493#define WavyWidth 16
1494#define WavyHeight 16
1495
1496/*
1497 Constant declaration.
1498*/
1499static const int
1500 RoiDelta = 8;
1501
1502static const unsigned char
1503 BricksBitmap[] =
1504 {
1505 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1506 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1507 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1508 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1509 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1510 },
1511 DiagonalBitmap[] =
1512 {
1513 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1514 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1515 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1516 },
1517 ScalesBitmap[] =
1518 {
1519 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1520 0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1521 0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1522 },
1523 VerticalBitmap[] =
1524 {
1525 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1526 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1527 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1528 },
1529 WavyBitmap[] =
1530 {
1531 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1532 0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1533 0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1534 };
1535
1536/*
1537 Function prototypes.
1538*/
1539static CommandType
1540 XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
cristy051718b2011-08-28 22:49:25 +00001541 const MagickStatusType,KeySym,Image **,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +00001542
1543static Image
1544 *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
cristy051718b2011-08-28 22:49:25 +00001545 Image **,ExceptionInfo *),
cristy3ed852e2009-09-05 21:47:34 +00001546 *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
cristy051718b2011-08-28 22:49:25 +00001547 *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1548 ExceptionInfo *),
1549 *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1550 ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +00001551
1552static MagickBooleanType
cristy051718b2011-08-28 22:49:25 +00001553 XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1554 ExceptionInfo *),
1555 XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1556 ExceptionInfo *),
1557 XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1558 ExceptionInfo *),
1559 XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1560 ExceptionInfo *),
1561 XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1562 ExceptionInfo *),
1563 XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1564 ExceptionInfo *),
1565 XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1566 XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1567 ExceptionInfo *),
1568 XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1569 ExceptionInfo *),
1570 XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1571 XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1572 XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1573 ExceptionInfo *),
1574 XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1575 XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1576 XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +00001577
1578static void
1579 XDrawPanRectangle(Display *,XWindows *),
cristy051718b2011-08-28 22:49:25 +00001580 XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1581 ExceptionInfo *),
cristy6710d842011-10-20 23:23:00 +00001582 XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
cristy051718b2011-08-28 22:49:25 +00001583 XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
cristy6710d842011-10-20 23:23:00 +00001584 XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
cristy3ed852e2009-09-05 21:47:34 +00001585 XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
cristy6710d842011-10-20 23:23:00 +00001586 const KeySym,ExceptionInfo *),
cristy3ed852e2009-09-05 21:47:34 +00001587 XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
cristy6710d842011-10-20 23:23:00 +00001588 XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
cristy3ed852e2009-09-05 21:47:34 +00001589 XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1590
1591/*
1592%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1593% %
1594% %
1595% %
1596% D i s p l a y I m a g e s %
1597% %
1598% %
1599% %
1600%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1601%
1602% DisplayImages() displays an image sequence to any X window screen. It
1603% returns a value other than 0 if successful. Check the exception member
1604% of image to determine the reason for any failure.
1605%
1606% The format of the DisplayImages method is:
1607%
1608% MagickBooleanType DisplayImages(const ImageInfo *image_info,
cristy051718b2011-08-28 22:49:25 +00001609% Image *images,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001610%
1611% A description of each parameter follows:
1612%
1613% o image_info: the image info.
1614%
1615% o image: the image.
1616%
cristy051718b2011-08-28 22:49:25 +00001617% o exception: return any errors or warnings in this structure.
1618%
cristy3ed852e2009-09-05 21:47:34 +00001619*/
1620MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
cristy051718b2011-08-28 22:49:25 +00001621 Image *images,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001622{
1623 char
1624 *argv[1];
1625
1626 Display
1627 *display;
1628
1629 Image
1630 *image;
1631
Cristyf2dc1dd2020-12-28 13:59:26 -05001632 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001633 i;
1634
cristybb503372010-05-27 20:51:26 +00001635 size_t
cristy3ed852e2009-09-05 21:47:34 +00001636 state;
1637
1638 XrmDatabase
1639 resource_database;
1640
1641 XResourceInfo
1642 resource_info;
1643
1644 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001645 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001646 assert(images != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001647 assert(images->signature == MagickCoreSignature);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07001648 if (images->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001649 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1650 display=XOpenDisplay(image_info->server_name);
1651 if (display == (Display *) NULL)
1652 {
cristy051718b2011-08-28 22:49:25 +00001653 (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
cristyefe601c2013-01-05 17:51:12 +00001654 "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
cristy3ed852e2009-09-05 21:47:34 +00001655 return(MagickFalse);
1656 }
cristy051718b2011-08-28 22:49:25 +00001657 if (exception->severity != UndefinedException)
1658 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00001659 (void) XSetErrorHandler(XError);
1660 resource_database=XGetResourceDatabase(display,GetClientName());
Cristy81bfff22018-03-10 07:58:31 -05001661 (void) memset(&resource_info,0,sizeof(resource_info));
cristy3ed852e2009-09-05 21:47:34 +00001662 XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1663 if (image_info->page != (char *) NULL)
1664 resource_info.image_geometry=AcquireString(image_info->page);
1665 resource_info.immutable=MagickTrue;
1666 argv[0]=AcquireString(GetClientName());
1667 state=DefaultState;
1668 for (i=0; (state & ExitState) == 0; i++)
1669 {
cristybb503372010-05-27 20:51:26 +00001670 if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
cristy3ed852e2009-09-05 21:47:34 +00001671 break;
1672 image=GetImageFromList(images,i % GetImageListLength(images));
cristy051718b2011-08-28 22:49:25 +00001673 (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
cristy3ed852e2009-09-05 21:47:34 +00001674 }
cristye42f6582012-02-11 17:59:50 +00001675 (void) SetErrorHandler((ErrorHandler) NULL);
1676 (void) SetWarningHandler((WarningHandler) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001677 argv[0]=DestroyString(argv[0]);
1678 (void) XCloseDisplay(display);
1679 XDestroyResourceInfo(&resource_info);
cristy051718b2011-08-28 22:49:25 +00001680 if (exception->severity != UndefinedException)
cristy3ed852e2009-09-05 21:47:34 +00001681 return(MagickFalse);
1682 return(MagickTrue);
1683}
1684
1685/*
1686%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1687% %
1688% %
1689% %
1690% R e m o t e D i s p l a y C o m m a n d %
1691% %
1692% %
1693% %
1694%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1695%
1696% RemoteDisplayCommand() encourages a remote display program to display the
1697% specified image filename.
1698%
1699% The format of the RemoteDisplayCommand method is:
1700%
1701% MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1702% const char *window,const char *filename,ExceptionInfo *exception)
1703%
1704% A description of each parameter follows:
1705%
1706% o image_info: the image info.
1707%
1708% o window: Specifies the name or id of an X window.
1709%
1710% o filename: the name of the image filename to display.
1711%
1712% o exception: return any errors or warnings in this structure.
1713%
1714*/
1715MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1716 const char *window,const char *filename,ExceptionInfo *exception)
1717{
1718 Display
1719 *display;
1720
1721 MagickStatusType
1722 status;
1723
1724 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001725 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001726 assert(filename != (char *) NULL);
1727 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1728 display=XOpenDisplay(image_info->server_name);
1729 if (display == (Display *) NULL)
1730 {
1731 (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
cristyefe601c2013-01-05 17:51:12 +00001732 "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
cristy3ed852e2009-09-05 21:47:34 +00001733 return(MagickFalse);
1734 }
1735 (void) XSetErrorHandler(XError);
1736 status=XRemoteCommand(display,window,filename);
1737 (void) XCloseDisplay(display);
dirkb9dbc292015-07-26 09:50:00 +00001738 return(status != 0 ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001739}
1740
1741/*
1742%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1743% %
1744% %
1745% %
1746+ X A n n o t a t e E d i t I m a g e %
1747% %
1748% %
1749% %
1750%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1751%
1752% XAnnotateEditImage() annotates the image with text.
1753%
1754% The format of the XAnnotateEditImage method is:
1755%
1756% MagickBooleanType XAnnotateEditImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00001757% XResourceInfo *resource_info,XWindows *windows,Image *image,
1758% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001759%
1760% A description of each parameter follows:
1761%
1762% o display: Specifies a connection to an X server; returned from
1763% XOpenDisplay.
1764%
1765% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1766%
1767% o windows: Specifies a pointer to a XWindows structure.
1768%
1769% o image: the image; returned from ReadImage.
1770%
1771*/
1772
cristy3ed852e2009-09-05 21:47:34 +00001773static MagickBooleanType XAnnotateEditImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00001774 XResourceInfo *resource_info,XWindows *windows,Image *image,
1775 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001776{
Cristyd93b2e62019-05-22 19:45:01 -04001777 const char
1778 *const AnnotateMenu[] =
cristy3ed852e2009-09-05 21:47:34 +00001779 {
1780 "Font Name",
1781 "Font Color",
1782 "Box Color",
1783 "Rotate Text",
1784 "Help",
1785 "Dismiss",
1786 (char *) NULL
1787 },
Cristyd93b2e62019-05-22 19:45:01 -04001788 *const TextMenu[] =
cristy3ed852e2009-09-05 21:47:34 +00001789 {
1790 "Help",
1791 "Apply",
1792 (char *) NULL
1793 };
1794
1795 static const ModeType
1796 AnnotateCommands[] =
1797 {
1798 AnnotateNameCommand,
1799 AnnotateFontColorCommand,
1800 AnnotateBackgroundColorCommand,
1801 AnnotateRotateCommand,
1802 AnnotateHelpCommand,
1803 AnnotateDismissCommand
1804 },
1805 TextCommands[] =
1806 {
1807 TextHelpCommand,
1808 TextApplyCommand
1809 };
1810
1811 static MagickBooleanType
1812 transparent_box = MagickTrue,
1813 transparent_pen = MagickFalse;
1814
cristya19f1d72012-08-07 18:24:38 +00001815 static double
cristy3ed852e2009-09-05 21:47:34 +00001816 degrees = 0.0;
1817
1818 static unsigned int
1819 box_id = MaxNumberPens-2,
1820 font_id = 0,
1821 pen_id = 0;
1822
1823 char
cristy151b66d2015-04-15 10:50:31 +00001824 command[MagickPathExtent],
1825 text[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00001826
1827 const char
1828 *ColorMenu[MaxNumberPens+1];
1829
1830 Cursor
1831 cursor;
1832
1833 GC
1834 annotate_context;
1835
1836 int
1837 id,
1838 pen_number,
1839 status,
1840 x,
1841 y;
1842
1843 KeySym
1844 key_symbol;
1845
Cristyf2dc1dd2020-12-28 13:59:26 -05001846 char
cristy3ed852e2009-09-05 21:47:34 +00001847 *p;
1848
Cristyf2dc1dd2020-12-28 13:59:26 -05001849 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001850 i;
1851
1852 unsigned int
1853 height,
1854 width;
1855
cristybb503372010-05-27 20:51:26 +00001856 size_t
cristy3ed852e2009-09-05 21:47:34 +00001857 state;
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 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -07001893 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001894 {
1895 /*
1896 Display pointer position.
1897 */
cristy151b66d2015-04-15 10:50:31 +00001898 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
cristy3ed852e2009-09-05 21:47:34 +00001899 x+windows->image.x,y+windows->image.y);
1900 XInfoWidget(display,windows,text);
1901 }
1902 /*
1903 Wait for next event.
1904 */
cristy6710d842011-10-20 23:23:00 +00001905 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +00001906 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
cristy151b66d2015-04-15 10:50:31 +00001942 font_name[MagickPathExtent] = "fixed";
cristy3ed852e2009-09-05 21:47:34 +00001943
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;
Elliott Hughes5d41fba2021-04-12 16:36:42 -07001986 if (transparent_pen != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001987 break;
1988 if (pen_number == (MaxNumberPens-1))
1989 {
1990 static char
cristy151b66d2015-04-15 10:50:31 +00001991 color_name[MagickPathExtent] = "gray";
cristy3ed852e2009-09-05 21:47:34 +00001992
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;
Elliott Hughes5d41fba2021-04-12 16:36:42 -07002031 if (transparent_box != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002032 break;
2033 if (pen_number == (MaxNumberPens-1))
2034 {
2035 static char
cristy151b66d2015-04-15 10:50:31 +00002036 color_name[MagickPathExtent] = "gray";
cristy3ed852e2009-09-05 21:47:34 +00002037
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
Cristyd93b2e62019-05-22 19:45:01 -04002062 const char
2063 *const RotateMenu[] =
cristy3ed852e2009-09-05 21:47:34 +00002064 {
2065 "-90",
2066 "-45",
2067 "-30",
2068 "0",
2069 "30",
2070 "45",
2071 "90",
2072 "180",
2073 "Dialog...",
2074 (char *) NULL,
2075 };
2076
Cristyd93b2e62019-05-22 19:45:01 -04002077 static char
2078 angle[MagickPathExtent] = "30.0";
2079
cristy3ed852e2009-09-05 21:47:34 +00002080 /*
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 {
cristydbdd0e32011-11-04 23:29:40 +00002089 degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002090 break;
2091 }
2092 (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2093 angle);
2094 if (*angle == '\0')
2095 break;
cristydbdd0e32011-11-04 23:29:40 +00002096 degrees=StringToDouble(angle,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002097 break;
2098 }
2099 case AnnotateHelpCommand:
2100 {
Cristy6970baa2019-04-20 21:23:54 -04002101 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00002102 "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 {
Cristy6970baa2019-04-20 21:23:54 -04002163 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00002164 "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;
Elliott Hughes5d41fba2021-04-12 16:36:42 -07002182 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002183 {
2184 if ((x < (int) (windows->info.x+windows->info.width)) &&
2185 (y < (int) (windows->info.y+windows->info.height)))
2186 (void) XWithdrawWindow(display,windows->info.id,
2187 windows->info.screen);
2188 }
2189 else
2190 if ((x > (int) (windows->info.x+windows->info.width)) ||
2191 (y > (int) (windows->info.y+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 */
Cristy8357b5d2020-11-22 12:39:10 +00002224 annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
cristy3ed852e2009-09-05 21:47:34 +00002225 if (annotate_info == (XAnnotateInfo *) NULL)
2226 return(MagickFalse);
2227 XGetAnnotateInfo(annotate_info);
2228 annotate_info->x=x;
2229 annotate_info->y=y;
dirk5d0a4412016-01-05 22:28:51 +01002230 if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00002231 annotate_info->stencil=OpaqueStencil;
2232 else
dirk5d0a4412016-01-05 22:28:51 +01002233 if (transparent_box == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002234 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)
cristy49e2d862010-11-12 02:50:30 +00002241 windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
cristy3ed852e2009-09-05 21:47:34 +00002242 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 */
cristy6710d842011-10-20 23:23:00 +00002278 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +00002279 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 {
Cristy6970baa2019-04-20 21:23:54 -04002299 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00002300 "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+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 }
2487 case XK_Return:
2488 case XK_KP_Enter:
2489 {
2490 /*
2491 Advance to the next line of text.
2492 */
2493 *p='\0';
2494 annotate_info->width=(unsigned int) XTextWidth(font_info,
2495 annotate_info->text,(int) strlen(annotate_info->text));
2496 if (annotate_info->next != (XAnnotateInfo *) NULL)
2497 {
2498 /*
2499 Line of text already exists.
2500 */
2501 annotate_info=annotate_info->next;
2502 x=annotate_info->x;
2503 y=annotate_info->y;
2504 p=annotate_info->text;
2505 break;
2506 }
Cristy566eaf12020-11-15 17:46:43 +00002507 annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
cristy3ed852e2009-09-05 21:47:34 +00002508 sizeof(*annotate_info->next));
2509 if (annotate_info->next == (XAnnotateInfo *) NULL)
2510 return(MagickFalse);
2511 *annotate_info->next=(*annotate_info);
2512 annotate_info->next->previous=annotate_info;
2513 annotate_info=annotate_info->next;
2514 annotate_info->text=(char *) AcquireQuantumMemory((size_t)
cristy49e2d862010-11-12 02:50:30 +00002515 windows->image.width/MagickMax((ssize_t)
2516 font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
cristy3ed852e2009-09-05 21:47:34 +00002517 if (annotate_info->text == (char *) NULL)
2518 return(MagickFalse);
2519 annotate_info->y+=annotate_info->height;
2520 if (annotate_info->y > (int) windows->image.height)
2521 annotate_info->y=(int) annotate_info->height;
2522 annotate_info->next=(XAnnotateInfo *) NULL;
2523 x=annotate_info->x;
2524 y=annotate_info->y;
2525 p=annotate_info->text;
2526 break;
2527 }
2528 }
2529 break;
2530 }
2531 case KeyRelease:
2532 {
2533 /*
2534 Respond to a user key release.
2535 */
2536 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2537 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2538 state&=(~ModifierState);
2539 break;
2540 }
2541 case SelectionNotify:
2542 {
2543 Atom
2544 type;
2545
2546 int
2547 format;
2548
2549 unsigned char
2550 *data;
2551
cristyf2faecf2010-05-28 19:19:36 +00002552 unsigned long
cristy3ed852e2009-09-05 21:47:34 +00002553 after,
2554 length;
2555
2556 /*
2557 Obtain response from primary selection.
2558 */
2559 if (event.xselection.property == (Atom) None)
2560 break;
2561 status=XGetWindowProperty(display,event.xselection.requestor,
cristy151b66d2015-04-15 10:50:31 +00002562 event.xselection.property,0L,(long) MagickPathExtent,True,XA_STRING,
cristy3ed852e2009-09-05 21:47:34 +00002563 &type,&format,&length,&after,&data);
2564 if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2565 (length == 0))
2566 break;
2567 /*
2568 Annotate Image window with primary selection.
2569 */
cristybb503372010-05-27 20:51:26 +00002570 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00002571 {
2572 if ((char) data[i] != '\n')
2573 {
2574 /*
2575 Draw a single character on the Image window.
2576 */
2577 *p=(char) data[i];
2578 (void) XDrawString(display,windows->image.id,annotate_context,
2579 x,y,p,1);
2580 x+=XTextWidth(font_info,p,1);
2581 p++;
2582 if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2583 continue;
2584 }
2585 /*
2586 Advance to the next line of text.
2587 */
2588 *p='\0';
2589 annotate_info->width=(unsigned int) XTextWidth(font_info,
2590 annotate_info->text,(int) strlen(annotate_info->text));
2591 if (annotate_info->next != (XAnnotateInfo *) NULL)
2592 {
2593 /*
2594 Line of text already exists.
2595 */
2596 annotate_info=annotate_info->next;
2597 x=annotate_info->x;
2598 y=annotate_info->y;
2599 p=annotate_info->text;
2600 continue;
2601 }
Cristy566eaf12020-11-15 17:46:43 +00002602 annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
cristy3ed852e2009-09-05 21:47:34 +00002603 sizeof(*annotate_info->next));
2604 if (annotate_info->next == (XAnnotateInfo *) NULL)
2605 return(MagickFalse);
2606 *annotate_info->next=(*annotate_info);
2607 annotate_info->next->previous=annotate_info;
2608 annotate_info=annotate_info->next;
2609 annotate_info->text=(char *) AcquireQuantumMemory((size_t)
cristy49e2d862010-11-12 02:50:30 +00002610 windows->image.width/MagickMax((ssize_t)
2611 font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
cristy3ed852e2009-09-05 21:47:34 +00002612 if (annotate_info->text == (char *) NULL)
2613 return(MagickFalse);
2614 annotate_info->y+=annotate_info->height;
2615 if (annotate_info->y > (int) windows->image.height)
2616 annotate_info->y=(int) annotate_info->height;
2617 annotate_info->next=(XAnnotateInfo *) NULL;
2618 x=annotate_info->x;
2619 y=annotate_info->y;
2620 p=annotate_info->text;
2621 }
2622 (void) XFree((void *) data);
2623 break;
2624 }
2625 default:
2626 break;
2627 }
2628 } while ((state & ExitState) == 0);
2629 (void) XFreeCursor(display,cursor);
2630 /*
2631 Annotation is relative to image configuration.
2632 */
2633 width=(unsigned int) image->columns;
2634 height=(unsigned int) image->rows;
2635 x=0;
2636 y=0;
2637 if (windows->image.crop_geometry != (char *) NULL)
2638 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2639 /*
2640 Initialize annotated image.
2641 */
2642 XSetCursorState(display,windows,MagickTrue);
2643 XCheckRefreshWindows(display,windows);
2644 while (annotate_info != (XAnnotateInfo *) NULL)
2645 {
2646 if (annotate_info->width == 0)
2647 {
2648 /*
2649 No text on this line-- go to the next line of text.
2650 */
2651 previous_info=annotate_info->previous;
2652 annotate_info->text=(char *)
2653 RelinquishMagickMemory(annotate_info->text);
2654 annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2655 annotate_info=previous_info;
2656 continue;
2657 }
2658 /*
2659 Determine pixel index for box and pen color.
2660 */
2661 windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2662 if (windows->pixel_info->colors != 0)
cristybb503372010-05-27 20:51:26 +00002663 for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002664 if (windows->pixel_info->pixels[i] ==
2665 windows->pixel_info->pen_colors[box_id].pixel)
2666 {
2667 windows->pixel_info->box_index=(unsigned short) i;
2668 break;
2669 }
2670 windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2671 if (windows->pixel_info->colors != 0)
cristybb503372010-05-27 20:51:26 +00002672 for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002673 if (windows->pixel_info->pixels[i] ==
2674 windows->pixel_info->pen_colors[pen_id].pixel)
2675 {
2676 windows->pixel_info->pen_index=(unsigned short) i;
2677 break;
2678 }
2679 /*
2680 Define the annotate geometry string.
2681 */
2682 annotate_info->x=(int)
2683 width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2684 annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2685 windows->image.y)/windows->image.ximage->height;
cristy151b66d2015-04-15 10:50:31 +00002686 (void) FormatLocaleString(annotate_info->geometry,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00002687 "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2688 height*annotate_info->height/windows->image.ximage->height,
2689 annotate_info->x+x,annotate_info->y+y);
2690 /*
2691 Annotate image with text.
2692 */
cristy7c3af952011-10-20 16:04:16 +00002693 status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2694 exception);
cristy3ed852e2009-09-05 21:47:34 +00002695 if (status == 0)
2696 return(MagickFalse);
2697 /*
2698 Free up memory.
2699 */
2700 previous_info=annotate_info->previous;
2701 annotate_info->text=DestroyString(annotate_info->text);
2702 annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2703 annotate_info=previous_info;
2704 }
2705 (void) XSetForeground(display,annotate_context,
2706 windows->pixel_info->foreground_color.pixel);
2707 (void) XSetBackground(display,annotate_context,
2708 windows->pixel_info->background_color.pixel);
2709 (void) XSetFont(display,annotate_context,windows->font_info->fid);
2710 XSetCursorState(display,windows,MagickFalse);
2711 (void) XFreeFont(display,font_info);
2712 /*
2713 Update image configuration.
2714 */
cristy6710d842011-10-20 23:23:00 +00002715 XConfigureImageColormap(display,resource_info,windows,image,exception);
cristy051718b2011-08-28 22:49:25 +00002716 (void) XConfigureImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00002717 return(MagickTrue);
2718}
2719
2720/*
2721%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2722% %
2723% %
2724% %
2725+ X B a c k g r o u n d I m a g e %
2726% %
2727% %
2728% %
2729%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2730%
2731% XBackgroundImage() displays the image in the background of a window.
2732%
2733% The format of the XBackgroundImage method is:
2734%
2735% MagickBooleanType XBackgroundImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00002736% XResourceInfo *resource_info,XWindows *windows,Image **image,
2737% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002738%
2739% A description of each parameter follows:
2740%
2741% o display: Specifies a connection to an X server; returned from
2742% XOpenDisplay.
2743%
2744% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2745%
2746% o windows: Specifies a pointer to a XWindows structure.
2747%
2748% o image: the image.
2749%
cristy051718b2011-08-28 22:49:25 +00002750% o exception: return any errors or warnings in this structure.
2751%
cristy3ed852e2009-09-05 21:47:34 +00002752*/
2753static MagickBooleanType XBackgroundImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00002754 XResourceInfo *resource_info,XWindows *windows,Image **image,
2755 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002756{
2757#define BackgroundImageTag "Background/Image"
2758
2759 int
2760 status;
2761
2762 static char
cristy151b66d2015-04-15 10:50:31 +00002763 window_id[MagickPathExtent] = "root";
cristy3ed852e2009-09-05 21:47:34 +00002764
2765 XResourceInfo
2766 background_resources;
2767
2768 /*
2769 Put image in background.
2770 */
2771 status=XDialogWidget(display,windows,"Background",
2772 "Enter window id (id 0x00 selects window with pointer):",window_id);
2773 if (*window_id == '\0')
2774 return(MagickFalse);
cristy051718b2011-08-28 22:49:25 +00002775 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2776 exception);
cristy3ed852e2009-09-05 21:47:34 +00002777 XInfoWidget(display,windows,BackgroundImageTag);
2778 XSetCursorState(display,windows,MagickTrue);
2779 XCheckRefreshWindows(display,windows);
2780 background_resources=(*resource_info);
2781 background_resources.window_id=window_id;
dirkb9dbc292015-07-26 09:50:00 +00002782 background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
cristy051718b2011-08-28 22:49:25 +00002783 status=XDisplayBackgroundImage(display,&background_resources,*image,
2784 exception);
dirk11a7cb72016-01-05 14:00:04 +01002785 if (status != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002786 XClientMessage(display,windows->image.id,windows->im_protocols,
2787 windows->im_retain_colors,CurrentTime);
2788 XSetCursorState(display,windows,MagickFalse);
cristy051718b2011-08-28 22:49:25 +00002789 (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2790 exception);
cristy3ed852e2009-09-05 21:47:34 +00002791 return(MagickTrue);
2792}
2793
2794/*
2795%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2796% %
2797% %
2798% %
2799+ X C h o p I m a g e %
2800% %
2801% %
2802% %
2803%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2804%
2805% XChopImage() chops the X image.
2806%
2807% The format of the XChopImage method is:
2808%
2809% MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +00002810% XWindows *windows,Image **image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002811%
2812% A description of each parameter follows:
2813%
2814% o display: Specifies a connection to an X server; returned from
2815% XOpenDisplay.
2816%
2817% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2818%
2819% o windows: Specifies a pointer to a XWindows structure.
2820%
2821% o image: the image.
2822%
cristy051718b2011-08-28 22:49:25 +00002823% o exception: return any errors or warnings in this structure.
2824%
cristy3ed852e2009-09-05 21:47:34 +00002825*/
2826static MagickBooleanType XChopImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00002827 XResourceInfo *resource_info,XWindows *windows,Image **image,
2828 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002829{
Cristyd93b2e62019-05-22 19:45:01 -04002830 const char
2831 *const ChopMenu[] =
cristy3ed852e2009-09-05 21:47:34 +00002832 {
2833 "Direction",
2834 "Help",
2835 "Dismiss",
2836 (char *) NULL
2837 };
2838
2839 static ModeType
2840 direction = HorizontalChopCommand;
2841
2842 static const ModeType
2843 ChopCommands[] =
2844 {
2845 ChopDirectionCommand,
2846 ChopHelpCommand,
2847 ChopDismissCommand
2848 },
2849 DirectionCommands[] =
2850 {
2851 HorizontalChopCommand,
2852 VerticalChopCommand
2853 };
2854
2855 char
cristy151b66d2015-04-15 10:50:31 +00002856 text[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00002857
2858 Image
2859 *chop_image;
2860
2861 int
2862 id,
2863 x,
2864 y;
2865
cristya19f1d72012-08-07 18:24:38 +00002866 double
cristy3ed852e2009-09-05 21:47:34 +00002867 scale_factor;
2868
2869 RectangleInfo
2870 chop_info;
2871
2872 unsigned int
2873 distance,
2874 height,
2875 width;
2876
cristybb503372010-05-27 20:51:26 +00002877 size_t
cristy3ed852e2009-09-05 21:47:34 +00002878 state;
2879
2880 XEvent
2881 event;
2882
2883 XSegment
2884 segment_info;
2885
2886 /*
2887 Map Command widget.
2888 */
2889 (void) CloneString(&windows->command.name,"Chop");
2890 windows->command.data=1;
2891 (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2892 (void) XMapRaised(display,windows->command.id);
2893 XClientMessage(display,windows->image.id,windows->im_protocols,
2894 windows->im_update_widget,CurrentTime);
2895 /*
2896 Track pointer until button 1 is pressed.
2897 */
2898 XQueryPosition(display,windows->image.id,&x,&y);
2899 (void) XSelectInput(display,windows->image.id,
2900 windows->image.attributes.event_mask | PointerMotionMask);
2901 state=DefaultState;
Cristy81bfff22018-03-10 07:58:31 -05002902 (void) memset(&segment_info,0,sizeof(segment_info));
cristy3ed852e2009-09-05 21:47:34 +00002903 do
2904 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -07002905 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002906 {
2907 /*
2908 Display pointer position.
2909 */
cristy151b66d2015-04-15 10:50:31 +00002910 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
cristy3ed852e2009-09-05 21:47:34 +00002911 x+windows->image.x,y+windows->image.y);
2912 XInfoWidget(display,windows,text);
2913 }
2914 /*
2915 Wait for next event.
2916 */
cristy6710d842011-10-20 23:23:00 +00002917 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +00002918 if (event.xany.window == windows->command.id)
2919 {
2920 /*
2921 Select a command from the Command widget.
2922 */
2923 id=XCommandWidget(display,windows,ChopMenu,&event);
2924 if (id < 0)
2925 continue;
2926 switch (ChopCommands[id])
2927 {
2928 case ChopDirectionCommand:
2929 {
2930 char
cristy151b66d2015-04-15 10:50:31 +00002931 command[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00002932
Cristyd93b2e62019-05-22 19:45:01 -04002933 const char
2934 *const Directions[] =
cristy3ed852e2009-09-05 21:47:34 +00002935 {
2936 "horizontal",
2937 "vertical",
2938 (char *) NULL,
2939 };
2940
2941 /*
2942 Select a command from the pop-up menu.
2943 */
2944 id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2945 if (id >= 0)
2946 direction=DirectionCommands[id];
2947 break;
2948 }
2949 case ChopHelpCommand:
2950 {
Cristy6970baa2019-04-20 21:23:54 -04002951 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00002952 "Help Viewer - Image Chop",ImageChopHelp);
2953 break;
2954 }
2955 case ChopDismissCommand:
2956 {
2957 /*
2958 Prematurely exit.
2959 */
2960 state|=EscapeState;
2961 state|=ExitState;
2962 break;
2963 }
2964 default:
2965 break;
2966 }
2967 continue;
2968 }
2969 switch (event.type)
2970 {
2971 case ButtonPress:
2972 {
2973 if (event.xbutton.button != Button1)
2974 break;
2975 if (event.xbutton.window != windows->image.id)
2976 break;
2977 /*
2978 User has committed to start point of chopping line.
2979 */
2980 segment_info.x1=(short int) event.xbutton.x;
2981 segment_info.x2=(short int) event.xbutton.x;
2982 segment_info.y1=(short int) event.xbutton.y;
2983 segment_info.y2=(short int) event.xbutton.y;
2984 state|=ExitState;
2985 break;
2986 }
2987 case ButtonRelease:
2988 break;
2989 case Expose:
2990 break;
2991 case KeyPress:
2992 {
2993 char
cristy151b66d2015-04-15 10:50:31 +00002994 command[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00002995
2996 KeySym
2997 key_symbol;
2998
2999 if (event.xkey.window != windows->image.id)
3000 break;
3001 /*
3002 Respond to a user key press.
3003 */
3004 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3005 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3006 switch ((int) key_symbol)
3007 {
3008 case XK_Escape:
3009 case XK_F20:
3010 {
3011 /*
3012 Prematurely exit.
3013 */
3014 state|=EscapeState;
3015 state|=ExitState;
3016 break;
3017 }
3018 case XK_F1:
3019 case XK_Help:
3020 {
3021 (void) XSetFunction(display,windows->image.highlight_context,
3022 GXcopy);
Cristy6970baa2019-04-20 21:23:54 -04003023 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003024 "Help Viewer - Image Chop",ImageChopHelp);
3025 (void) XSetFunction(display,windows->image.highlight_context,
3026 GXinvert);
3027 break;
3028 }
3029 default:
3030 {
3031 (void) XBell(display,0);
3032 break;
3033 }
3034 }
3035 break;
3036 }
3037 case MotionNotify:
3038 {
3039 /*
3040 Map and unmap Info widget as text cursor crosses its boundaries.
3041 */
3042 x=event.xmotion.x;
3043 y=event.xmotion.y;
Elliott Hughes5d41fba2021-04-12 16:36:42 -07003044 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003045 {
3046 if ((x < (int) (windows->info.x+windows->info.width)) &&
3047 (y < (int) (windows->info.y+windows->info.height)))
3048 (void) XWithdrawWindow(display,windows->info.id,
3049 windows->info.screen);
3050 }
3051 else
3052 if ((x > (int) (windows->info.x+windows->info.width)) ||
3053 (y > (int) (windows->info.y+windows->info.height)))
3054 (void) XMapWindow(display,windows->info.id);
3055 }
3056 }
3057 } while ((state & ExitState) == 0);
3058 (void) XSelectInput(display,windows->image.id,
3059 windows->image.attributes.event_mask);
3060 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3061 if ((state & EscapeState) != 0)
3062 return(MagickTrue);
3063 /*
3064 Draw line as pointer moves until the mouse button is released.
3065 */
3066 chop_info.width=0;
3067 chop_info.height=0;
3068 chop_info.x=0;
3069 chop_info.y=0;
3070 distance=0;
3071 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3072 state=DefaultState;
3073 do
3074 {
3075 if (distance > 9)
3076 {
3077 /*
3078 Display info and draw chopping line.
3079 */
dirk5d0a4412016-01-05 22:28:51 +01003080 if (windows->info.mapped == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003081 (void) XMapWindow(display,windows->info.id);
cristy151b66d2015-04-15 10:50:31 +00003082 (void) FormatLocaleString(text,MagickPathExtent,
cristy6d8abba2010-06-03 01:10:47 +00003083 " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +00003084 chop_info.height,(double) chop_info.x,(double) chop_info.y);
cristy3ed852e2009-09-05 21:47:34 +00003085 XInfoWidget(display,windows,text);
3086 XHighlightLine(display,windows->image.id,
3087 windows->image.highlight_context,&segment_info);
3088 }
3089 else
Elliott Hughes5d41fba2021-04-12 16:36:42 -07003090 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003091 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3092 /*
3093 Wait for next event.
3094 */
cristy6710d842011-10-20 23:23:00 +00003095 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +00003096 if (distance > 9)
3097 XHighlightLine(display,windows->image.id,
3098 windows->image.highlight_context,&segment_info);
3099 switch (event.type)
3100 {
3101 case ButtonPress:
3102 {
3103 segment_info.x2=(short int) event.xmotion.x;
3104 segment_info.y2=(short int) event.xmotion.y;
3105 break;
3106 }
3107 case ButtonRelease:
3108 {
3109 /*
3110 User has committed to chopping line.
3111 */
3112 segment_info.x2=(short int) event.xbutton.x;
3113 segment_info.y2=(short int) event.xbutton.y;
3114 state|=ExitState;
3115 break;
3116 }
3117 case Expose:
3118 break;
3119 case MotionNotify:
3120 {
3121 segment_info.x2=(short int) event.xmotion.x;
3122 segment_info.y2=(short int) event.xmotion.y;
3123 }
3124 default:
3125 break;
3126 }
3127 /*
3128 Check boundary conditions.
3129 */
3130 if (segment_info.x2 < 0)
3131 segment_info.x2=0;
3132 else
3133 if (segment_info.x2 > windows->image.ximage->width)
3134 segment_info.x2=windows->image.ximage->width;
3135 if (segment_info.y2 < 0)
3136 segment_info.y2=0;
3137 else
3138 if (segment_info.y2 > windows->image.ximage->height)
3139 segment_info.y2=windows->image.ximage->height;
3140 distance=(unsigned int)
3141 (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3142 ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3143 /*
3144 Compute chopping geometry.
3145 */
3146 if (direction == HorizontalChopCommand)
3147 {
cristybb503372010-05-27 20:51:26 +00003148 chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
cristy49e2d862010-11-12 02:50:30 +00003149 chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
cristy3ed852e2009-09-05 21:47:34 +00003150 chop_info.height=0;
3151 chop_info.y=0;
3152 if (segment_info.x1 > (int) segment_info.x2)
3153 {
cristybb503372010-05-27 20:51:26 +00003154 chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
cristy49e2d862010-11-12 02:50:30 +00003155 chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
cristy3ed852e2009-09-05 21:47:34 +00003156 }
3157 }
3158 else
3159 {
3160 chop_info.width=0;
cristybb503372010-05-27 20:51:26 +00003161 chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
cristy3ed852e2009-09-05 21:47:34 +00003162 chop_info.x=0;
cristy49e2d862010-11-12 02:50:30 +00003163 chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
cristy3ed852e2009-09-05 21:47:34 +00003164 if (segment_info.y1 > segment_info.y2)
3165 {
cristy49e2d862010-11-12 02:50:30 +00003166 chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3167 chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
cristy3ed852e2009-09-05 21:47:34 +00003168 }
3169 }
3170 } while ((state & ExitState) == 0);
3171 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3172 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3173 if (distance <= 9)
3174 return(MagickTrue);
3175 /*
3176 Image chopping is relative to image configuration.
3177 */
cristy051718b2011-08-28 22:49:25 +00003178 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3179 exception);
cristy3ed852e2009-09-05 21:47:34 +00003180 XSetCursorState(display,windows,MagickTrue);
3181 XCheckRefreshWindows(display,windows);
3182 windows->image.window_changes.width=windows->image.ximage->width-
3183 (unsigned int) chop_info.width;
3184 windows->image.window_changes.height=windows->image.ximage->height-
3185 (unsigned int) chop_info.height;
3186 width=(unsigned int) (*image)->columns;
3187 height=(unsigned int) (*image)->rows;
3188 x=0;
3189 y=0;
3190 if (windows->image.crop_geometry != (char *) NULL)
3191 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
cristya19f1d72012-08-07 18:24:38 +00003192 scale_factor=(double) width/windows->image.ximage->width;
cristy3ed852e2009-09-05 21:47:34 +00003193 chop_info.x+=x;
cristy49e2d862010-11-12 02:50:30 +00003194 chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003195 chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
cristya19f1d72012-08-07 18:24:38 +00003196 scale_factor=(double) height/windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +00003197 chop_info.y+=y;
cristy49e2d862010-11-12 02:50:30 +00003198 chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003199 chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3200 /*
3201 Chop image.
3202 */
cristy051718b2011-08-28 22:49:25 +00003203 chop_image=ChopImage(*image,&chop_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003204 XSetCursorState(display,windows,MagickFalse);
3205 if (chop_image == (Image *) NULL)
3206 return(MagickFalse);
3207 *image=DestroyImage(*image);
3208 *image=chop_image;
3209 /*
3210 Update image configuration.
3211 */
cristy6710d842011-10-20 23:23:00 +00003212 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00003213 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003214 return(MagickTrue);
3215}
3216
3217/*
3218%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3219% %
3220% %
3221% %
3222+ X C o l o r E d i t I m a g e %
3223% %
3224% %
3225% %
3226%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3227%
3228% XColorEditImage() allows the user to interactively change the color of one
3229% pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3230%
3231% The format of the XColorEditImage method is:
3232%
3233% MagickBooleanType XColorEditImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00003234% XResourceInfo *resource_info,XWindows *windows,Image **image,
3235% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003236%
3237% A description of each parameter follows:
3238%
3239% o display: Specifies a connection to an X server; returned from
3240% XOpenDisplay.
3241%
3242% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3243%
3244% o windows: Specifies a pointer to a XWindows structure.
3245%
3246% o image: the image; returned from ReadImage.
3247%
cristy051718b2011-08-28 22:49:25 +00003248% o exception: return any errors or warnings in this structure.
3249%
cristy3ed852e2009-09-05 21:47:34 +00003250*/
cristy3ed852e2009-09-05 21:47:34 +00003251static MagickBooleanType XColorEditImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00003252 XResourceInfo *resource_info,XWindows *windows,Image **image,
3253 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003254{
Cristyd93b2e62019-05-22 19:45:01 -04003255 const char
3256 *const ColorEditMenu[] =
cristy3ed852e2009-09-05 21:47:34 +00003257 {
3258 "Method",
3259 "Pixel Color",
3260 "Border Color",
3261 "Fuzz",
3262 "Undo",
3263 "Help",
3264 "Dismiss",
3265 (char *) NULL
3266 };
3267
3268 static const ModeType
3269 ColorEditCommands[] =
3270 {
3271 ColorEditMethodCommand,
3272 ColorEditColorCommand,
3273 ColorEditBorderCommand,
3274 ColorEditFuzzCommand,
3275 ColorEditUndoCommand,
3276 ColorEditHelpCommand,
3277 ColorEditDismissCommand
3278 };
3279
3280 static PaintMethod
3281 method = PointMethod;
3282
3283 static unsigned int
3284 pen_id = 0;
3285
3286 static XColor
3287 border_color = { 0, 0, 0, 0, 0, 0 };
3288
3289 char
cristy151b66d2015-04-15 10:50:31 +00003290 command[MagickPathExtent],
3291 text[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00003292
3293 Cursor
3294 cursor;
3295
cristy3ed852e2009-09-05 21:47:34 +00003296 int
3297 entry,
3298 id,
3299 x,
3300 x_offset,
3301 y,
3302 y_offset;
3303
Cristyf2dc1dd2020-12-28 13:59:26 -05003304 Quantum
cristy3ed852e2009-09-05 21:47:34 +00003305 *q;
3306
Cristyf2dc1dd2020-12-28 13:59:26 -05003307 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003308 i;
3309
3310 unsigned int
3311 height,
3312 width;
3313
cristybb503372010-05-27 20:51:26 +00003314 size_t
cristy3ed852e2009-09-05 21:47:34 +00003315 state;
3316
3317 XColor
3318 color;
3319
3320 XEvent
3321 event;
3322
3323 /*
3324 Map Command widget.
3325 */
3326 (void) CloneString(&windows->command.name,"Color Edit");
3327 windows->command.data=4;
3328 (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3329 (void) XMapRaised(display,windows->command.id);
3330 XClientMessage(display,windows->image.id,windows->im_protocols,
3331 windows->im_update_widget,CurrentTime);
3332 /*
3333 Make cursor.
3334 */
3335 cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3336 resource_info->background_color,resource_info->foreground_color);
3337 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3338 /*
3339 Track pointer until button 1 is pressed.
3340 */
3341 XQueryPosition(display,windows->image.id,&x,&y);
3342 (void) XSelectInput(display,windows->image.id,
3343 windows->image.attributes.event_mask | PointerMotionMask);
3344 state=DefaultState;
3345 do
3346 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -07003347 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003348 {
3349 /*
3350 Display pointer position.
3351 */
cristy151b66d2015-04-15 10:50:31 +00003352 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
cristy3ed852e2009-09-05 21:47:34 +00003353 x+windows->image.x,y+windows->image.y);
3354 XInfoWidget(display,windows,text);
3355 }
3356 /*
3357 Wait for next event.
3358 */
cristy6710d842011-10-20 23:23:00 +00003359 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +00003360 if (event.xany.window == windows->command.id)
3361 {
3362 /*
3363 Select a command from the Command widget.
3364 */
3365 id=XCommandWidget(display,windows,ColorEditMenu,&event);
3366 if (id < 0)
3367 {
3368 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3369 continue;
3370 }
3371 switch (ColorEditCommands[id])
3372 {
3373 case ColorEditMethodCommand:
3374 {
3375 char
3376 **methods;
3377
3378 /*
3379 Select a method from the pop-up menu.
3380 */
cristy042ee782011-04-22 18:48:30 +00003381 methods=(char **) GetCommandOptions(MagickMethodOptions);
cristy3ed852e2009-09-05 21:47:34 +00003382 if (methods == (char **) NULL)
3383 break;
3384 entry=XMenuWidget(display,windows,ColorEditMenu[id],
3385 (const char **) methods,command);
3386 if (entry >= 0)
cristy042ee782011-04-22 18:48:30 +00003387 method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
cristy3ed852e2009-09-05 21:47:34 +00003388 MagickFalse,methods[entry]);
3389 methods=DestroyStringList(methods);
3390 break;
3391 }
3392 case ColorEditColorCommand:
3393 {
3394 const char
3395 *ColorMenu[MaxNumberPens];
3396
3397 int
3398 pen_number;
3399
3400 /*
3401 Initialize menu selections.
3402 */
3403 for (i=0; i < (int) (MaxNumberPens-2); i++)
3404 ColorMenu[i]=resource_info->pen_colors[i];
3405 ColorMenu[MaxNumberPens-2]="Browser...";
3406 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3407 /*
3408 Select a pen color from the pop-up menu.
3409 */
3410 pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3411 (const char **) ColorMenu,command);
3412 if (pen_number < 0)
3413 break;
3414 if (pen_number == (MaxNumberPens-2))
3415 {
3416 static char
cristy151b66d2015-04-15 10:50:31 +00003417 color_name[MagickPathExtent] = "gray";
cristy3ed852e2009-09-05 21:47:34 +00003418
3419 /*
3420 Select a pen color from a dialog.
3421 */
3422 resource_info->pen_colors[pen_number]=color_name;
3423 XColorBrowserWidget(display,windows,"Select",color_name);
3424 if (*color_name == '\0')
3425 break;
3426 }
3427 /*
3428 Set pen color.
3429 */
3430 (void) XParseColor(display,windows->map_info->colormap,
3431 resource_info->pen_colors[pen_number],&color);
3432 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3433 (unsigned int) MaxColors,&color);
3434 windows->pixel_info->pen_colors[pen_number]=color;
3435 pen_id=(unsigned int) pen_number;
3436 break;
3437 }
3438 case ColorEditBorderCommand:
3439 {
3440 const char
3441 *ColorMenu[MaxNumberPens];
3442
3443 int
3444 pen_number;
3445
3446 /*
3447 Initialize menu selections.
3448 */
3449 for (i=0; i < (int) (MaxNumberPens-2); i++)
3450 ColorMenu[i]=resource_info->pen_colors[i];
3451 ColorMenu[MaxNumberPens-2]="Browser...";
3452 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3453 /*
3454 Select a pen color from the pop-up menu.
3455 */
3456 pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3457 (const char **) ColorMenu,command);
3458 if (pen_number < 0)
3459 break;
3460 if (pen_number == (MaxNumberPens-2))
3461 {
3462 static char
cristy151b66d2015-04-15 10:50:31 +00003463 color_name[MagickPathExtent] = "gray";
cristy3ed852e2009-09-05 21:47:34 +00003464
3465 /*
3466 Select a pen color from a dialog.
3467 */
3468 resource_info->pen_colors[pen_number]=color_name;
3469 XColorBrowserWidget(display,windows,"Select",color_name);
3470 if (*color_name == '\0')
3471 break;
3472 }
3473 /*
3474 Set border color.
3475 */
3476 (void) XParseColor(display,windows->map_info->colormap,
3477 resource_info->pen_colors[pen_number],&border_color);
3478 break;
3479 }
3480 case ColorEditFuzzCommand:
3481 {
Cristyd93b2e62019-05-22 19:45:01 -04003482 const char
3483 *const FuzzMenu[] =
cristy3ed852e2009-09-05 21:47:34 +00003484 {
3485 "0%",
3486 "2%",
3487 "5%",
3488 "10%",
3489 "15%",
3490 "Dialog...",
3491 (char *) NULL,
3492 };
3493
Cristyd93b2e62019-05-22 19:45:01 -04003494 static char
3495 fuzz[MagickPathExtent];
3496
cristy3ed852e2009-09-05 21:47:34 +00003497 /*
3498 Select a command from the pop-up menu.
3499 */
3500 entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3501 command);
3502 if (entry < 0)
3503 break;
3504 if (entry != 5)
3505 {
cristydbdd0e32011-11-04 23:29:40 +00003506 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
cristy40a08ad2010-02-09 02:27:44 +00003507 QuantumRange+1.0);
cristy3ed852e2009-09-05 21:47:34 +00003508 break;
3509 }
cristy151b66d2015-04-15 10:50:31 +00003510 (void) (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00003511 (void) XDialogWidget(display,windows,"Ok",
3512 "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3513 if (*fuzz == '\0')
3514 break;
cristy151b66d2015-04-15 10:50:31 +00003515 (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
cristy9b34e302011-11-05 02:15:45 +00003516 (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3517 1.0);
cristy3ed852e2009-09-05 21:47:34 +00003518 break;
3519 }
3520 case ColorEditUndoCommand:
3521 {
3522 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
cristy051718b2011-08-28 22:49:25 +00003523 image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003524 break;
3525 }
3526 case ColorEditHelpCommand:
3527 default:
3528 {
Cristy6970baa2019-04-20 21:23:54 -04003529 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003530 "Help Viewer - Image Annotation",ImageColorEditHelp);
3531 break;
3532 }
3533 case ColorEditDismissCommand:
3534 {
3535 /*
3536 Prematurely exit.
3537 */
3538 state|=EscapeState;
3539 state|=ExitState;
3540 break;
3541 }
3542 }
3543 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3544 continue;
3545 }
3546 switch (event.type)
3547 {
3548 case ButtonPress:
3549 {
3550 if (event.xbutton.button != Button1)
3551 break;
3552 if ((event.xbutton.window != windows->image.id) &&
3553 (event.xbutton.window != windows->magnify.id))
3554 break;
3555 /*
3556 exit loop.
3557 */
3558 x=event.xbutton.x;
3559 y=event.xbutton.y;
3560 (void) XMagickCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +00003561 SaveToUndoBufferCommand,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003562 state|=UpdateConfigurationState;
3563 break;
3564 }
3565 case ButtonRelease:
3566 {
3567 if (event.xbutton.button != Button1)
3568 break;
3569 if ((event.xbutton.window != windows->image.id) &&
3570 (event.xbutton.window != windows->magnify.id))
3571 break;
3572 /*
3573 Update colormap information.
3574 */
3575 x=event.xbutton.x;
3576 y=event.xbutton.y;
cristy6710d842011-10-20 23:23:00 +00003577 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00003578 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003579 XInfoWidget(display,windows,text);
3580 (void) XCheckDefineCursor(display,windows->image.id,cursor);
3581 state&=(~UpdateConfigurationState);
3582 break;
3583 }
3584 case Expose:
3585 break;
3586 case KeyPress:
3587 {
3588 KeySym
3589 key_symbol;
3590
3591 if (event.xkey.window == windows->magnify.id)
3592 {
3593 Window
3594 window;
3595
3596 window=windows->magnify.id;
3597 while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3598 }
3599 if (event.xkey.window != windows->image.id)
3600 break;
3601 /*
3602 Respond to a user key press.
3603 */
3604 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3605 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3606 switch ((int) key_symbol)
3607 {
3608 case XK_Escape:
3609 case XK_F20:
3610 {
3611 /*
3612 Prematurely exit.
3613 */
3614 state|=ExitState;
3615 break;
3616 }
3617 case XK_F1:
3618 case XK_Help:
3619 {
Cristy6970baa2019-04-20 21:23:54 -04003620 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003621 "Help Viewer - Image Annotation",ImageColorEditHelp);
3622 break;
3623 }
3624 default:
3625 {
3626 (void) XBell(display,0);
3627 break;
3628 }
3629 }
3630 break;
3631 }
3632 case MotionNotify:
3633 {
3634 /*
3635 Map and unmap Info widget as cursor crosses its boundaries.
3636 */
3637 x=event.xmotion.x;
3638 y=event.xmotion.y;
Elliott Hughes5d41fba2021-04-12 16:36:42 -07003639 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003640 {
3641 if ((x < (int) (windows->info.x+windows->info.width)) &&
3642 (y < (int) (windows->info.y+windows->info.height)))
3643 (void) XWithdrawWindow(display,windows->info.id,
3644 windows->info.screen);
3645 }
3646 else
3647 if ((x > (int) (windows->info.x+windows->info.width)) ||
3648 (y > (int) (windows->info.y+windows->info.height)))
3649 (void) XMapWindow(display,windows->info.id);
3650 break;
3651 }
3652 default:
3653 break;
3654 }
3655 if (event.xany.window == windows->magnify.id)
3656 {
3657 x=windows->magnify.x-windows->image.x;
3658 y=windows->magnify.y-windows->image.y;
3659 }
3660 x_offset=x;
3661 y_offset=y;
3662 if ((state & UpdateConfigurationState) != 0)
3663 {
cristy49e2d862010-11-12 02:50:30 +00003664 CacheView
3665 *image_view;
3666
cristy3ed852e2009-09-05 21:47:34 +00003667 int
3668 x,
3669 y;
3670
3671 /*
3672 Pixel edit is relative to image configuration.
3673 */
3674 (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3675 MagickTrue);
3676 color=windows->pixel_info->pen_colors[pen_id];
3677 XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3678 width=(unsigned int) (*image)->columns;
3679 height=(unsigned int) (*image)->rows;
3680 x=0;
3681 y=0;
3682 if (windows->image.crop_geometry != (char *) NULL)
3683 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3684 &width,&height);
3685 x_offset=(int)
3686 (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3687 y_offset=(int)
3688 (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3689 if ((x_offset < 0) || (y_offset < 0))
3690 continue;
cristy49e2d862010-11-12 02:50:30 +00003691 if ((x_offset >= (int) (*image)->columns) ||
3692 (y_offset >= (int) (*image)->rows))
cristy3ed852e2009-09-05 21:47:34 +00003693 continue;
cristy6f5395d2012-12-14 18:30:30 +00003694 image_view=AcquireAuthenticCacheView(*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003695 switch (method)
3696 {
3697 case PointMethod:
3698 default:
3699 {
3700 /*
3701 Update color information using point algorithm.
3702 */
dirk5d0a4412016-01-05 22:28:51 +01003703 if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003704 return(MagickFalse);
cristy49e2d862010-11-12 02:50:30 +00003705 q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
cristy574cc262011-08-05 01:23:58 +00003706 (ssize_t) y_offset,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003707 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003708 break;
cristy4c08aed2011-07-01 19:47:50 +00003709 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3710 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3711 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
cristy051718b2011-08-28 22:49:25 +00003712 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003713 break;
3714 }
3715 case ReplaceMethod:
3716 {
cristy101ab702011-10-13 13:06:32 +00003717 PixelInfo
cristy4c08aed2011-07-01 19:47:50 +00003718 pixel,
cristy3ed852e2009-09-05 21:47:34 +00003719 target;
3720
3721 /*
3722 Update color information using replace algorithm.
3723 */
cristyf05d4942012-03-17 16:26:09 +00003724 (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3725 x_offset,(ssize_t) y_offset,&target,exception);
cristy3ed852e2009-09-05 21:47:34 +00003726 if ((*image)->storage_class == DirectClass)
3727 {
cristy49e2d862010-11-12 02:50:30 +00003728 for (y=0; y < (int) (*image)->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003729 {
cristy49e2d862010-11-12 02:50:30 +00003730 q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3731 (*image)->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003732 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003733 break;
3734 for (x=0; x < (int) (*image)->columns; x++)
3735 {
cristy101ab702011-10-13 13:06:32 +00003736 GetPixelInfoPixel(*image,q,&pixel);
3737 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
cristy3ed852e2009-09-05 21:47:34 +00003738 {
cristy4c08aed2011-07-01 19:47:50 +00003739 SetPixelRed(*image,ScaleShortToQuantum(
3740 color.red),q);
3741 SetPixelGreen(*image,ScaleShortToQuantum(
3742 color.green),q);
3743 SetPixelBlue(*image,ScaleShortToQuantum(
3744 color.blue),q);
cristy3ed852e2009-09-05 21:47:34 +00003745 }
cristyed231572011-07-14 02:18:59 +00003746 q+=GetPixelChannels(*image);
cristy3ed852e2009-09-05 21:47:34 +00003747 }
dirk5d0a4412016-01-05 22:28:51 +01003748 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003749 break;
3750 }
3751 }
3752 else
3753 {
cristy49e2d862010-11-12 02:50:30 +00003754 for (i=0; i < (ssize_t) (*image)->colors; i++)
cristy101ab702011-10-13 13:06:32 +00003755 if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
cristy3ed852e2009-09-05 21:47:34 +00003756 {
cristye42f6582012-02-11 17:59:50 +00003757 (*image)->colormap[i].red=(double) ScaleShortToQuantum(
cristy4c08aed2011-07-01 19:47:50 +00003758 color.red);
cristye42f6582012-02-11 17:59:50 +00003759 (*image)->colormap[i].green=(double) ScaleShortToQuantum(
cristy3ed852e2009-09-05 21:47:34 +00003760 color.green);
cristye42f6582012-02-11 17:59:50 +00003761 (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
cristy3ed852e2009-09-05 21:47:34 +00003762 color.blue);
3763 }
cristyea1a8aa2011-10-20 13:24:06 +00003764 (void) SyncImage(*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003765 }
3766 break;
3767 }
3768 case FloodfillMethod:
3769 case FillToBorderMethod:
3770 {
3771 DrawInfo
3772 *draw_info;
3773
cristy4c08aed2011-07-01 19:47:50 +00003774 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003775 target;
3776
3777 /*
3778 Update color information using floodfill algorithm.
3779 */
cristy3aa93752011-12-18 15:54:24 +00003780 (void) GetOneVirtualPixelInfo(*image,
cristy52010022011-10-21 18:07:37 +00003781 GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3782 y_offset,&target,exception);
cristy3ed852e2009-09-05 21:47:34 +00003783 if (method == FillToBorderMethod)
3784 {
cristya19f1d72012-08-07 18:24:38 +00003785 target.red=(double)
cristy3ed852e2009-09-05 21:47:34 +00003786 ScaleShortToQuantum(border_color.red);
cristya19f1d72012-08-07 18:24:38 +00003787 target.green=(double)
cristy3ed852e2009-09-05 21:47:34 +00003788 ScaleShortToQuantum(border_color.green);
cristya19f1d72012-08-07 18:24:38 +00003789 target.blue=(double)
cristy3ed852e2009-09-05 21:47:34 +00003790 ScaleShortToQuantum(border_color.blue);
3791 }
3792 draw_info=CloneDrawInfo(resource_info->image_info,
3793 (DrawInfo *) NULL);
cristy9950d572011-10-01 18:22:35 +00003794 (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3795 AllCompliance,&draw_info->fill,exception);
anthony11d32022012-11-17 05:31:33 +00003796 (void) FloodfillPaintImage(*image,draw_info,&target,
3797 (ssize_t)x_offset,(ssize_t)y_offset,
dirkb9dbc292015-07-26 09:50:00 +00003798 method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00003799 draw_info=DestroyDrawInfo(draw_info);
3800 break;
3801 }
3802 case ResetMethod:
3803 {
3804 /*
3805 Update color information using reset algorithm.
3806 */
dirk5d0a4412016-01-05 22:28:51 +01003807 if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003808 return(MagickFalse);
cristy49e2d862010-11-12 02:50:30 +00003809 for (y=0; y < (int) (*image)->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003810 {
cristy49e2d862010-11-12 02:50:30 +00003811 q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3812 (*image)->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003813 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003814 break;
3815 for (x=0; x < (int) (*image)->columns; x++)
3816 {
cristy4c08aed2011-07-01 19:47:50 +00003817 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3818 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3819 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
cristyed231572011-07-14 02:18:59 +00003820 q+=GetPixelChannels(*image);
cristy3ed852e2009-09-05 21:47:34 +00003821 }
dirk5d0a4412016-01-05 22:28:51 +01003822 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003823 break;
3824 }
3825 break;
3826 }
3827 }
cristy49e2d862010-11-12 02:50:30 +00003828 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003829 state&=(~UpdateConfigurationState);
3830 }
3831 } while ((state & ExitState) == 0);
3832 (void) XSelectInput(display,windows->image.id,
3833 windows->image.attributes.event_mask);
3834 XSetCursorState(display,windows,MagickFalse);
3835 (void) XFreeCursor(display,cursor);
3836 return(MagickTrue);
3837}
3838
3839/*
3840%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3841% %
3842% %
3843% %
3844+ X C o m p o s i t e I m a g e %
3845% %
3846% %
3847% %
3848%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3849%
3850% XCompositeImage() requests an image name from the user, reads the image and
3851% composites it with the X window image at a location the user chooses with
3852% the pointer.
3853%
3854% The format of the XCompositeImage method is:
3855%
3856% MagickBooleanType XCompositeImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00003857% XResourceInfo *resource_info,XWindows *windows,Image *image,
3858% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003859%
3860% A description of each parameter follows:
3861%
3862% o display: Specifies a connection to an X server; returned from
3863% XOpenDisplay.
3864%
3865% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3866%
3867% o windows: Specifies a pointer to a XWindows structure.
3868%
3869% o image: the image; returned from ReadImage.
3870%
cristy051718b2011-08-28 22:49:25 +00003871% o exception: return any errors or warnings in this structure.
3872%
cristy3ed852e2009-09-05 21:47:34 +00003873*/
3874static MagickBooleanType XCompositeImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00003875 XResourceInfo *resource_info,XWindows *windows,Image *image,
3876 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003877{
Cristyd93b2e62019-05-22 19:45:01 -04003878 const char
3879 *const CompositeMenu[] =
cristy3ed852e2009-09-05 21:47:34 +00003880 {
3881 "Operators",
3882 "Dissolve",
3883 "Displace",
3884 "Help",
3885 "Dismiss",
3886 (char *) NULL
3887 };
3888
Cristyd93b2e62019-05-22 19:45:01 -04003889 static char
3890 displacement_geometry[MagickPathExtent] = "30x30",
3891 filename[MagickPathExtent] = "\0";
3892
cristy3ed852e2009-09-05 21:47:34 +00003893 static CompositeOperator
3894 compose = CopyCompositeOp;
3895
3896 static const ModeType
3897 CompositeCommands[] =
3898 {
3899 CompositeOperatorsCommand,
3900 CompositeDissolveCommand,
3901 CompositeDisplaceCommand,
3902 CompositeHelpCommand,
3903 CompositeDismissCommand
3904 };
3905
3906 char
cristy151b66d2015-04-15 10:50:31 +00003907 text[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00003908
3909 Cursor
3910 cursor;
3911
3912 Image
3913 *composite_image;
3914
3915 int
3916 entry,
3917 id,
3918 x,
3919 y;
3920
cristya19f1d72012-08-07 18:24:38 +00003921 double
cristy3ed852e2009-09-05 21:47:34 +00003922 blend,
3923 scale_factor;
3924
3925 RectangleInfo
3926 highlight_info,
3927 composite_info;
3928
3929 unsigned int
3930 height,
3931 width;
3932
cristybb503372010-05-27 20:51:26 +00003933 size_t
cristy3ed852e2009-09-05 21:47:34 +00003934 state;
3935
3936 XEvent
3937 event;
3938
3939 /*
3940 Request image file name from user.
3941 */
3942 XFileBrowserWidget(display,windows,"Composite",filename);
3943 if (*filename == '\0')
3944 return(MagickTrue);
3945 /*
3946 Read image.
3947 */
3948 XSetCursorState(display,windows,MagickTrue);
3949 XCheckRefreshWindows(display,windows);
3950 (void) CopyMagickString(resource_info->image_info->filename,filename,
cristy151b66d2015-04-15 10:50:31 +00003951 MagickPathExtent);
cristy051718b2011-08-28 22:49:25 +00003952 composite_image=ReadImage(resource_info->image_info,exception);
3953 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00003954 XSetCursorState(display,windows,MagickFalse);
3955 if (composite_image == (Image *) NULL)
3956 return(MagickFalse);
3957 /*
3958 Map Command widget.
3959 */
3960 (void) CloneString(&windows->command.name,"Composite");
3961 windows->command.data=1;
3962 (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3963 (void) XMapRaised(display,windows->command.id);
3964 XClientMessage(display,windows->image.id,windows->im_protocols,
3965 windows->im_update_widget,CurrentTime);
3966 /*
3967 Track pointer until button 1 is pressed.
3968 */
3969 XQueryPosition(display,windows->image.id,&x,&y);
3970 (void) XSelectInput(display,windows->image.id,
3971 windows->image.attributes.event_mask | PointerMotionMask);
cristy49e2d862010-11-12 02:50:30 +00003972 composite_info.x=(ssize_t) windows->image.x+x;
3973 composite_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +00003974 composite_info.width=0;
3975 composite_info.height=0;
3976 cursor=XCreateFontCursor(display,XC_ul_angle);
3977 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3978 blend=0.0;
3979 state=DefaultState;
3980 do
3981 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -07003982 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003983 {
3984 /*
3985 Display pointer position.
3986 */
cristy151b66d2015-04-15 10:50:31 +00003987 (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
cristyf2faecf2010-05-28 19:19:36 +00003988 (long) composite_info.x,(long) composite_info.y);
cristy3ed852e2009-09-05 21:47:34 +00003989 XInfoWidget(display,windows,text);
3990 }
3991 highlight_info=composite_info;
3992 highlight_info.x=composite_info.x-windows->image.x;
3993 highlight_info.y=composite_info.y-windows->image.y;
3994 XHighlightRectangle(display,windows->image.id,
3995 windows->image.highlight_context,&highlight_info);
3996 /*
3997 Wait for next event.
3998 */
cristy6710d842011-10-20 23:23:00 +00003999 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +00004000 XHighlightRectangle(display,windows->image.id,
4001 windows->image.highlight_context,&highlight_info);
4002 if (event.xany.window == windows->command.id)
4003 {
4004 /*
4005 Select a command from the Command widget.
4006 */
4007 id=XCommandWidget(display,windows,CompositeMenu,&event);
4008 if (id < 0)
4009 continue;
4010 switch (CompositeCommands[id])
4011 {
4012 case CompositeOperatorsCommand:
4013 {
4014 char
cristy151b66d2015-04-15 10:50:31 +00004015 command[MagickPathExtent],
cristy3ed852e2009-09-05 21:47:34 +00004016 **operators;
4017
4018 /*
4019 Select a command from the pop-up menu.
4020 */
cristy042ee782011-04-22 18:48:30 +00004021 operators=GetCommandOptions(MagickComposeOptions);
cristy3ed852e2009-09-05 21:47:34 +00004022 if (operators == (char **) NULL)
4023 break;
4024 entry=XMenuWidget(display,windows,CompositeMenu[id],
4025 (const char **) operators,command);
4026 if (entry >= 0)
cristy042ee782011-04-22 18:48:30 +00004027 compose=(CompositeOperator) ParseCommandOption(
cristy3ed852e2009-09-05 21:47:34 +00004028 MagickComposeOptions,MagickFalse,operators[entry]);
4029 operators=DestroyStringList(operators);
4030 break;
4031 }
4032 case CompositeDissolveCommand:
4033 {
4034 static char
cristy151b66d2015-04-15 10:50:31 +00004035 factor[MagickPathExtent] = "20.0";
cristy3ed852e2009-09-05 21:47:34 +00004036
4037 /*
4038 Dissolve the two images a given percent.
4039 */
4040 (void) XSetFunction(display,windows->image.highlight_context,
4041 GXcopy);
4042 (void) XDialogWidget(display,windows,"Dissolve",
4043 "Enter the blend factor (0.0 - 99.9%):",factor);
4044 (void) XSetFunction(display,windows->image.highlight_context,
4045 GXinvert);
4046 if (*factor == '\0')
4047 break;
cristydbdd0e32011-11-04 23:29:40 +00004048 blend=StringToDouble(factor,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004049 compose=DissolveCompositeOp;
4050 break;
4051 }
4052 case CompositeDisplaceCommand:
4053 {
4054 /*
4055 Get horizontal and vertical scale displacement geometry.
4056 */
4057 (void) XSetFunction(display,windows->image.highlight_context,
4058 GXcopy);
4059 (void) XDialogWidget(display,windows,"Displace",
4060 "Enter the horizontal and vertical scale:",displacement_geometry);
4061 (void) XSetFunction(display,windows->image.highlight_context,
4062 GXinvert);
4063 if (*displacement_geometry == '\0')
4064 break;
4065 compose=DisplaceCompositeOp;
4066 break;
4067 }
4068 case CompositeHelpCommand:
4069 {
4070 (void) XSetFunction(display,windows->image.highlight_context,
4071 GXcopy);
Cristy6970baa2019-04-20 21:23:54 -04004072 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00004073 "Help Viewer - Image Composite",ImageCompositeHelp);
4074 (void) XSetFunction(display,windows->image.highlight_context,
4075 GXinvert);
4076 break;
4077 }
4078 case CompositeDismissCommand:
4079 {
4080 /*
4081 Prematurely exit.
4082 */
4083 state|=EscapeState;
4084 state|=ExitState;
4085 break;
4086 }
4087 default:
4088 break;
4089 }
4090 continue;
4091 }
4092 switch (event.type)
4093 {
4094 case ButtonPress:
4095 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -07004096 if (image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004097 (void) LogMagickEvent(X11Event,GetMagickModule(),
4098 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4099 event.xbutton.button,event.xbutton.x,event.xbutton.y);
4100 if (event.xbutton.button != Button1)
4101 break;
4102 if (event.xbutton.window != windows->image.id)
4103 break;
4104 /*
4105 Change cursor.
4106 */
4107 composite_info.width=composite_image->columns;
4108 composite_info.height=composite_image->rows;
4109 (void) XCheckDefineCursor(display,windows->image.id,cursor);
cristy49e2d862010-11-12 02:50:30 +00004110 composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4111 composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +00004112 break;
4113 }
4114 case ButtonRelease:
4115 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -07004116 if (image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004117 (void) LogMagickEvent(X11Event,GetMagickModule(),
4118 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4119 event.xbutton.button,event.xbutton.x,event.xbutton.y);
4120 if (event.xbutton.button != Button1)
4121 break;
4122 if (event.xbutton.window != windows->image.id)
4123 break;
4124 if ((composite_info.width != 0) && (composite_info.height != 0))
4125 {
4126 /*
4127 User has selected the location of the composite image.
4128 */
cristy49e2d862010-11-12 02:50:30 +00004129 composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4130 composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +00004131 state|=ExitState;
4132 }
4133 break;
4134 }
4135 case Expose:
4136 break;
4137 case KeyPress:
4138 {
4139 char
cristy151b66d2015-04-15 10:50:31 +00004140 command[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00004141
4142 KeySym
4143 key_symbol;
4144
4145 int
4146 length;
4147
4148 if (event.xkey.window != windows->image.id)
4149 break;
4150 /*
4151 Respond to a user key press.
4152 */
4153 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4154 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4155 *(command+length)='\0';
Elliott Hughes5d41fba2021-04-12 16:36:42 -07004156 if (image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004157 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00004158 "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
cristy3ed852e2009-09-05 21:47:34 +00004159 switch ((int) key_symbol)
4160 {
4161 case XK_Escape:
4162 case XK_F20:
4163 {
4164 /*
4165 Prematurely exit.
4166 */
4167 composite_image=DestroyImage(composite_image);
4168 state|=EscapeState;
4169 state|=ExitState;
4170 break;
4171 }
4172 case XK_F1:
4173 case XK_Help:
4174 {
4175 (void) XSetFunction(display,windows->image.highlight_context,
4176 GXcopy);
Cristy6970baa2019-04-20 21:23:54 -04004177 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00004178 "Help Viewer - Image Composite",ImageCompositeHelp);
4179 (void) XSetFunction(display,windows->image.highlight_context,
4180 GXinvert);
4181 break;
4182 }
4183 default:
4184 {
4185 (void) XBell(display,0);
4186 break;
4187 }
4188 }
4189 break;
4190 }
4191 case MotionNotify:
4192 {
4193 /*
4194 Map and unmap Info widget as text cursor crosses its boundaries.
4195 */
4196 x=event.xmotion.x;
4197 y=event.xmotion.y;
Elliott Hughes5d41fba2021-04-12 16:36:42 -07004198 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004199 {
4200 if ((x < (int) (windows->info.x+windows->info.width)) &&
4201 (y < (int) (windows->info.y+windows->info.height)))
4202 (void) XWithdrawWindow(display,windows->info.id,
4203 windows->info.screen);
4204 }
4205 else
4206 if ((x > (int) (windows->info.x+windows->info.width)) ||
4207 (y > (int) (windows->info.y+windows->info.height)))
4208 (void) XMapWindow(display,windows->info.id);
cristy49e2d862010-11-12 02:50:30 +00004209 composite_info.x=(ssize_t) windows->image.x+x;
4210 composite_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +00004211 break;
4212 }
4213 default:
4214 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -07004215 if (image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004216 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4217 event.type);
4218 break;
4219 }
4220 }
4221 } while ((state & ExitState) == 0);
4222 (void) XSelectInput(display,windows->image.id,
4223 windows->image.attributes.event_mask);
4224 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4225 XSetCursorState(display,windows,MagickFalse);
4226 (void) XFreeCursor(display,cursor);
4227 if ((state & EscapeState) != 0)
4228 return(MagickTrue);
4229 /*
4230 Image compositing is relative to image configuration.
4231 */
4232 XSetCursorState(display,windows,MagickTrue);
4233 XCheckRefreshWindows(display,windows);
4234 width=(unsigned int) image->columns;
4235 height=(unsigned int) image->rows;
4236 x=0;
4237 y=0;
4238 if (windows->image.crop_geometry != (char *) NULL)
4239 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
cristya19f1d72012-08-07 18:24:38 +00004240 scale_factor=(double) width/windows->image.ximage->width;
cristy3ed852e2009-09-05 21:47:34 +00004241 composite_info.x+=x;
cristy49e2d862010-11-12 02:50:30 +00004242 composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +00004243 composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
cristya19f1d72012-08-07 18:24:38 +00004244 scale_factor=(double) height/windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +00004245 composite_info.y+=y;
cristy49e2d862010-11-12 02:50:30 +00004246 composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00004247 composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4248 if ((composite_info.width != composite_image->columns) ||
4249 (composite_info.height != composite_image->rows))
4250 {
4251 Image
4252 *resize_image;
4253
4254 /*
4255 Scale composite image.
4256 */
cristy15b98cd2010-09-12 19:42:50 +00004257 resize_image=ResizeImage(composite_image,composite_info.width,
cristyaa2c16c2012-03-25 22:21:35 +00004258 composite_info.height,composite_image->filter,exception);
cristy3ed852e2009-09-05 21:47:34 +00004259 composite_image=DestroyImage(composite_image);
4260 if (resize_image == (Image *) NULL)
4261 {
4262 XSetCursorState(display,windows,MagickFalse);
4263 return(MagickFalse);
4264 }
4265 composite_image=resize_image;
4266 }
4267 if (compose == DisplaceCompositeOp)
4268 (void) SetImageArtifact(composite_image,"compose:args",
4269 displacement_geometry);
4270 if (blend != 0.0)
4271 {
cristy49e2d862010-11-12 02:50:30 +00004272 CacheView
4273 *image_view;
4274
cristy3ed852e2009-09-05 21:47:34 +00004275 int
4276 y;
4277
4278 Quantum
4279 opacity;
4280
Cristyf2dc1dd2020-12-28 13:59:26 -05004281 int
cristy3ed852e2009-09-05 21:47:34 +00004282 x;
4283
Cristyf2dc1dd2020-12-28 13:59:26 -05004284 Quantum
cristy3ed852e2009-09-05 21:47:34 +00004285 *q;
4286
4287 /*
4288 Create mattes for blending.
4289 */
cristy63240882011-08-05 19:05:27 +00004290 (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
cristy6e963d82012-06-19 15:23:24 +00004291 opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4292 ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
dirk5d0a4412016-01-05 22:28:51 +01004293 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004294 return(MagickFalse);
cristy8a46d822012-08-28 23:32:39 +00004295 image->alpha_trait=BlendPixelTrait;
cristy46ff2672012-12-14 15:32:26 +00004296 image_view=AcquireAuthenticCacheView(image,exception);
cristy49e2d862010-11-12 02:50:30 +00004297 for (y=0; y < (int) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004298 {
cristy49e2d862010-11-12 02:50:30 +00004299 q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4300 exception);
cristy4c08aed2011-07-01 19:47:50 +00004301 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004302 break;
4303 for (x=0; x < (int) image->columns; x++)
4304 {
cristy4c08aed2011-07-01 19:47:50 +00004305 SetPixelAlpha(image,opacity,q);
cristyed231572011-07-14 02:18:59 +00004306 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004307 }
dirk5d0a4412016-01-05 22:28:51 +01004308 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004309 break;
4310 }
cristy49e2d862010-11-12 02:50:30 +00004311 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00004312 }
4313 /*
4314 Composite image with X Image window.
4315 */
cristy39172402012-03-30 13:04:39 +00004316 (void) CompositeImage(image,composite_image,compose,MagickTrue,
cristyfeb3e962012-03-29 17:25:55 +00004317 composite_info.x,composite_info.y,exception);
cristy3ed852e2009-09-05 21:47:34 +00004318 composite_image=DestroyImage(composite_image);
4319 XSetCursorState(display,windows,MagickFalse);
4320 /*
4321 Update image configuration.
4322 */
cristy6710d842011-10-20 23:23:00 +00004323 XConfigureImageColormap(display,resource_info,windows,image,exception);
cristy051718b2011-08-28 22:49:25 +00004324 (void) XConfigureImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00004325 return(MagickTrue);
4326}
4327
4328/*
4329%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4330% %
4331% %
4332% %
4333+ X C o n f i g u r e I m a g e %
4334% %
4335% %
4336% %
4337%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4338%
4339% XConfigureImage() creates a new X image. It also notifies the window
4340% manager of the new image size and configures the transient widows.
4341%
4342% The format of the XConfigureImage method is:
4343%
4344% MagickBooleanType XConfigureImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00004345% XResourceInfo *resource_info,XWindows *windows,Image *image,
4346% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004347%
4348% A description of each parameter follows:
4349%
4350% o display: Specifies a connection to an X server; returned from
4351% XOpenDisplay.
4352%
4353% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4354%
4355% o windows: Specifies a pointer to a XWindows structure.
4356%
4357% o image: the image.
4358%
cristy051718b2011-08-28 22:49:25 +00004359% o exception: return any errors or warnings in this structure.
4360%
4361% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00004362%
4363*/
4364static MagickBooleanType XConfigureImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00004365 XResourceInfo *resource_info,XWindows *windows,Image *image,
4366 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004367{
4368 char
cristy151b66d2015-04-15 10:50:31 +00004369 geometry[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00004370
cristy3ed852e2009-09-05 21:47:34 +00004371 MagickStatusType
4372 status;
4373
cristybb503372010-05-27 20:51:26 +00004374 size_t
cristy3ed852e2009-09-05 21:47:34 +00004375 mask,
4376 height,
4377 width;
4378
cristy9d314ff2011-03-09 01:30:28 +00004379 ssize_t
4380 x,
4381 y;
4382
cristy3ed852e2009-09-05 21:47:34 +00004383 XSizeHints
4384 *size_hints;
4385
4386 XWindowChanges
4387 window_changes;
4388
4389 /*
4390 Dismiss if window dimensions are zero.
4391 */
4392 width=(unsigned int) windows->image.window_changes.width;
4393 height=(unsigned int) windows->image.window_changes.height;
Elliott Hughes5d41fba2021-04-12 16:36:42 -07004394 if (image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004395 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004396 "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4397 windows->image.ximage->height,(double) width,(double) height);
cristy3ed852e2009-09-05 21:47:34 +00004398 if ((width*height) == 0)
4399 return(MagickTrue);
4400 x=0;
4401 y=0;
4402 /*
4403 Resize image to fit Image window dimensions.
4404 */
4405 XSetCursorState(display,windows,MagickTrue);
4406 (void) XFlush(display);
4407 if (((int) width != windows->image.ximage->width) ||
4408 ((int) height != windows->image.ximage->height))
4409 image->taint=MagickTrue;
4410 windows->magnify.x=(int)
4411 width*windows->magnify.x/windows->image.ximage->width;
4412 windows->magnify.y=(int)
4413 height*windows->magnify.y/windows->image.ximage->height;
4414 windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4415 windows->image.y=(int)
4416 (height*windows->image.y/windows->image.ximage->height);
4417 status=XMakeImage(display,resource_info,&windows->image,image,
cristy051718b2011-08-28 22:49:25 +00004418 (unsigned int) width,(unsigned int) height,exception);
dirk5d0a4412016-01-05 22:28:51 +01004419 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004420 XNoticeWidget(display,windows,"Unable to configure X image:",
4421 windows->image.name);
4422 /*
4423 Notify window manager of the new configuration.
4424 */
4425 if (resource_info->image_geometry != (char *) NULL)
cristy151b66d2015-04-15 10:50:31 +00004426 (void) FormatLocaleString(geometry,MagickPathExtent,"%s>!",
cristy3ed852e2009-09-05 21:47:34 +00004427 resource_info->image_geometry);
4428 else
cristy151b66d2015-04-15 10:50:31 +00004429 (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
cristy3ed852e2009-09-05 21:47:34 +00004430 XDisplayWidth(display,windows->image.screen),
4431 XDisplayHeight(display,windows->image.screen));
4432 (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4433 window_changes.width=(int) width;
4434 if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4435 window_changes.width=XDisplayWidth(display,windows->image.screen);
4436 window_changes.height=(int) height;
4437 if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4438 window_changes.height=XDisplayHeight(display,windows->image.screen);
cristybb503372010-05-27 20:51:26 +00004439 mask=(size_t) (CWWidth | CWHeight);
cristy3ed852e2009-09-05 21:47:34 +00004440 if (resource_info->backdrop)
4441 {
4442 mask|=CWX | CWY;
4443 window_changes.x=(int)
4444 ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4445 window_changes.y=(int)
4446 ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4447 }
4448 (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4449 (unsigned int) mask,&window_changes);
4450 (void) XClearWindow(display,windows->image.id);
4451 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4452 /*
4453 Update Magnify window configuration.
4454 */
Elliott Hughes5d41fba2021-04-12 16:36:42 -07004455 if (windows->magnify.mapped != MagickFalse)
cristy6710d842011-10-20 23:23:00 +00004456 XMakeMagnifyImage(display,windows,exception);
cristy3ed852e2009-09-05 21:47:34 +00004457 windows->pan.crop_geometry=windows->image.crop_geometry;
4458 XBestIconSize(display,&windows->pan,image);
4459 while (((windows->pan.width << 1) < MaxIconSize) &&
4460 ((windows->pan.height << 1) < MaxIconSize))
4461 {
4462 windows->pan.width<<=1;
4463 windows->pan.height<<=1;
4464 }
4465 if (windows->pan.geometry != (char *) NULL)
4466 (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4467 &windows->pan.width,&windows->pan.height);
4468 window_changes.width=(int) windows->pan.width;
4469 window_changes.height=(int) windows->pan.height;
4470 size_hints=XAllocSizeHints();
4471 if (size_hints != (XSizeHints *) NULL)
4472 {
4473 /*
4474 Set new size hints.
4475 */
4476 size_hints->flags=PSize | PMinSize | PMaxSize;
4477 size_hints->width=window_changes.width;
4478 size_hints->height=window_changes.height;
4479 size_hints->min_width=size_hints->width;
4480 size_hints->min_height=size_hints->height;
4481 size_hints->max_width=size_hints->width;
4482 size_hints->max_height=size_hints->height;
4483 (void) XSetNormalHints(display,windows->pan.id,size_hints);
4484 (void) XFree((void *) size_hints);
4485 }
4486 (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4487 (unsigned int) (CWWidth | CWHeight),&window_changes);
4488 /*
4489 Update icon window configuration.
4490 */
4491 windows->icon.crop_geometry=windows->image.crop_geometry;
4492 XBestIconSize(display,&windows->icon,image);
4493 window_changes.width=(int) windows->icon.width;
4494 window_changes.height=(int) windows->icon.height;
4495 (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4496 (unsigned int) (CWWidth | CWHeight),&window_changes);
4497 XSetCursorState(display,windows,MagickFalse);
dirkb9dbc292015-07-26 09:50:00 +00004498 return(status != 0 ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00004499}
4500
4501/*
4502%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4503% %
4504% %
4505% %
4506+ X C r o p I m a g e %
4507% %
4508% %
4509% %
4510%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4511%
4512% XCropImage() allows the user to select a region of the image and crop, copy,
4513% or cut it. For copy or cut, the image can subsequently be composited onto
4514% the image with XPasteImage.
4515%
4516% The format of the XCropImage method is:
4517%
4518% MagickBooleanType XCropImage(Display *display,
4519% XResourceInfo *resource_info,XWindows *windows,Image *image,
cristy051718b2011-08-28 22:49:25 +00004520% const ClipboardMode mode,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004521%
4522% A description of each parameter follows:
4523%
4524% o display: Specifies a connection to an X server; returned from
4525% XOpenDisplay.
4526%
4527% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4528%
4529% o windows: Specifies a pointer to a XWindows structure.
4530%
4531% o image: the image; returned from ReadImage.
4532%
4533% o mode: This unsigned value specified whether the image should be
4534% cropped, copied, or cut.
4535%
cristy051718b2011-08-28 22:49:25 +00004536% o exception: return any errors or warnings in this structure.
4537%
cristy3ed852e2009-09-05 21:47:34 +00004538*/
4539static MagickBooleanType XCropImage(Display *display,
4540 XResourceInfo *resource_info,XWindows *windows,Image *image,
cristy051718b2011-08-28 22:49:25 +00004541 const ClipboardMode mode,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004542{
Cristyd93b2e62019-05-22 19:45:01 -04004543 const char
4544 *const CropModeMenu[] =
cristy3ed852e2009-09-05 21:47:34 +00004545 {
4546 "Help",
4547 "Dismiss",
4548 (char *) NULL
4549 },
4550 *RectifyModeMenu[] =
4551 {
4552 "Crop",
4553 "Help",
4554 "Dismiss",
4555 (char *) NULL
4556 };
4557
4558 static const ModeType
4559 CropCommands[] =
4560 {
4561 CropHelpCommand,
4562 CropDismissCommand
4563 },
4564 RectifyCommands[] =
4565 {
4566 RectifyCopyCommand,
4567 RectifyHelpCommand,
4568 RectifyDismissCommand
4569 };
4570
cristy49e2d862010-11-12 02:50:30 +00004571 CacheView
4572 *image_view;
4573
cristy3ed852e2009-09-05 21:47:34 +00004574 char
cristy151b66d2015-04-15 10:50:31 +00004575 command[MagickPathExtent],
4576 text[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00004577
4578 Cursor
4579 cursor;
4580
cristy3ed852e2009-09-05 21:47:34 +00004581 int
4582 id,
4583 x,
4584 y;
4585
4586 KeySym
4587 key_symbol;
4588
4589 Image
4590 *crop_image;
4591
cristya19f1d72012-08-07 18:24:38 +00004592 double
cristy3ed852e2009-09-05 21:47:34 +00004593 scale_factor;
4594
4595 RectangleInfo
4596 crop_info,
4597 highlight_info;
4598
Cristyf2dc1dd2020-12-28 13:59:26 -05004599 Quantum
cristy3ed852e2009-09-05 21:47:34 +00004600 *q;
4601
4602 unsigned int
4603 height,
4604 width;
4605
cristybb503372010-05-27 20:51:26 +00004606 size_t
cristy3ed852e2009-09-05 21:47:34 +00004607 state;
4608
4609 XEvent
4610 event;
4611
4612 /*
4613 Map Command widget.
4614 */
4615 switch (mode)
4616 {
4617 case CopyMode:
4618 {
4619 (void) CloneString(&windows->command.name,"Copy");
4620 break;
4621 }
4622 case CropMode:
4623 {
4624 (void) CloneString(&windows->command.name,"Crop");
4625 break;
4626 }
4627 case CutMode:
4628 {
4629 (void) CloneString(&windows->command.name,"Cut");
4630 break;
4631 }
4632 }
4633 RectifyModeMenu[0]=windows->command.name;
4634 windows->command.data=0;
4635 (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4636 (void) XMapRaised(display,windows->command.id);
4637 XClientMessage(display,windows->image.id,windows->im_protocols,
4638 windows->im_update_widget,CurrentTime);
4639 /*
4640 Track pointer until button 1 is pressed.
4641 */
4642 XQueryPosition(display,windows->image.id,&x,&y);
4643 (void) XSelectInput(display,windows->image.id,
4644 windows->image.attributes.event_mask | PointerMotionMask);
cristy49e2d862010-11-12 02:50:30 +00004645 crop_info.x=(ssize_t) windows->image.x+x;
4646 crop_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +00004647 crop_info.width=0;
4648 crop_info.height=0;
4649 cursor=XCreateFontCursor(display,XC_fleur);
4650 state=DefaultState;
4651 do
4652 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -07004653 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004654 {
4655 /*
4656 Display pointer position.
4657 */
cristy151b66d2015-04-15 10:50:31 +00004658 (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
cristyf2faecf2010-05-28 19:19:36 +00004659 (long) crop_info.x,(long) crop_info.y);
cristy3ed852e2009-09-05 21:47:34 +00004660 XInfoWidget(display,windows,text);
4661 }
4662 /*
4663 Wait for next event.
4664 */
cristy6710d842011-10-20 23:23:00 +00004665 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +00004666 if (event.xany.window == windows->command.id)
4667 {
4668 /*
4669 Select a command from the Command widget.
4670 */
4671 id=XCommandWidget(display,windows,CropModeMenu,&event);
4672 if (id < 0)
4673 continue;
4674 switch (CropCommands[id])
4675 {
4676 case CropHelpCommand:
4677 {
4678 switch (mode)
4679 {
4680 case CopyMode:
4681 {
Cristy6970baa2019-04-20 21:23:54 -04004682 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00004683 "Help Viewer - Image Copy",ImageCopyHelp);
4684 break;
4685 }
4686 case CropMode:
4687 {
Cristy6970baa2019-04-20 21:23:54 -04004688 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00004689 "Help Viewer - Image Crop",ImageCropHelp);
4690 break;
4691 }
4692 case CutMode:
4693 {
Cristy6970baa2019-04-20 21:23:54 -04004694 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00004695 "Help Viewer - Image Cut",ImageCutHelp);
4696 break;
4697 }
4698 }
4699 break;
4700 }
4701 case CropDismissCommand:
4702 {
4703 /*
4704 Prematurely exit.
4705 */
4706 state|=EscapeState;
4707 state|=ExitState;
4708 break;
4709 }
4710 default:
4711 break;
4712 }
4713 continue;
4714 }
4715 switch (event.type)
4716 {
4717 case ButtonPress:
4718 {
4719 if (event.xbutton.button != Button1)
4720 break;
4721 if (event.xbutton.window != windows->image.id)
4722 break;
4723 /*
4724 Note first corner of cropping rectangle-- exit loop.
4725 */
4726 (void) XCheckDefineCursor(display,windows->image.id,cursor);
cristy49e2d862010-11-12 02:50:30 +00004727 crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4728 crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +00004729 state|=ExitState;
4730 break;
4731 }
4732 case ButtonRelease:
4733 break;
4734 case Expose:
4735 break;
4736 case KeyPress:
4737 {
4738 if (event.xkey.window != windows->image.id)
4739 break;
4740 /*
4741 Respond to a user key press.
4742 */
4743 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4744 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4745 switch ((int) key_symbol)
4746 {
4747 case XK_Escape:
4748 case XK_F20:
4749 {
4750 /*
4751 Prematurely exit.
4752 */
4753 state|=EscapeState;
4754 state|=ExitState;
4755 break;
4756 }
4757 case XK_F1:
4758 case XK_Help:
4759 {
4760 switch (mode)
4761 {
4762 case CopyMode:
4763 {
Cristy6970baa2019-04-20 21:23:54 -04004764 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00004765 "Help Viewer - Image Copy",ImageCopyHelp);
4766 break;
4767 }
4768 case CropMode:
4769 {
Cristy6970baa2019-04-20 21:23:54 -04004770 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00004771 "Help Viewer - Image Crop",ImageCropHelp);
4772 break;
4773 }
4774 case CutMode:
4775 {
Cristy6970baa2019-04-20 21:23:54 -04004776 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00004777 "Help Viewer - Image Cut",ImageCutHelp);
4778 break;
4779 }
4780 }
4781 break;
4782 }
4783 default:
4784 {
4785 (void) XBell(display,0);
4786 break;
4787 }
4788 }
4789 break;
4790 }
4791 case MotionNotify:
4792 {
4793 if (event.xmotion.window != windows->image.id)
4794 break;
4795 /*
4796 Map and unmap Info widget as text cursor crosses its boundaries.
4797 */
4798 x=event.xmotion.x;
4799 y=event.xmotion.y;
Elliott Hughes5d41fba2021-04-12 16:36:42 -07004800 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004801 {
4802 if ((x < (int) (windows->info.x+windows->info.width)) &&
4803 (y < (int) (windows->info.y+windows->info.height)))
4804 (void) XWithdrawWindow(display,windows->info.id,
4805 windows->info.screen);
4806 }
4807 else
4808 if ((x > (int) (windows->info.x+windows->info.width)) ||
4809 (y > (int) (windows->info.y+windows->info.height)))
4810 (void) XMapWindow(display,windows->info.id);
cristy49e2d862010-11-12 02:50:30 +00004811 crop_info.x=(ssize_t) windows->image.x+x;
4812 crop_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +00004813 break;
4814 }
4815 default:
4816 break;
4817 }
4818 } while ((state & ExitState) == 0);
4819 (void) XSelectInput(display,windows->image.id,
4820 windows->image.attributes.event_mask);
4821 if ((state & EscapeState) != 0)
4822 {
4823 /*
4824 User want to exit without cropping.
4825 */
4826 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4827 (void) XFreeCursor(display,cursor);
4828 return(MagickTrue);
4829 }
4830 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4831 do
4832 {
4833 /*
4834 Size rectangle as pointer moves until the mouse button is released.
4835 */
4836 x=(int) crop_info.x;
4837 y=(int) crop_info.y;
4838 crop_info.width=0;
4839 crop_info.height=0;
4840 state=DefaultState;
4841 do
4842 {
4843 highlight_info=crop_info;
4844 highlight_info.x=crop_info.x-windows->image.x;
4845 highlight_info.y=crop_info.y-windows->image.y;
4846 if ((highlight_info.width > 3) && (highlight_info.height > 3))
4847 {
4848 /*
4849 Display info and draw cropping rectangle.
4850 */
dirk5d0a4412016-01-05 22:28:51 +01004851 if (windows->info.mapped == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004852 (void) XMapWindow(display,windows->info.id);
cristy151b66d2015-04-15 10:50:31 +00004853 (void) FormatLocaleString(text,MagickPathExtent,
cristy6d8abba2010-06-03 01:10:47 +00004854 " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +00004855 crop_info.height,(double) crop_info.x,(double) crop_info.y);
cristy3ed852e2009-09-05 21:47:34 +00004856 XInfoWidget(display,windows,text);
4857 XHighlightRectangle(display,windows->image.id,
4858 windows->image.highlight_context,&highlight_info);
4859 }
4860 else
Elliott Hughes5d41fba2021-04-12 16:36:42 -07004861 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004862 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4863 /*
4864 Wait for next event.
4865 */
cristy6710d842011-10-20 23:23:00 +00004866 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +00004867 if ((highlight_info.width > 3) && (highlight_info.height > 3))
4868 XHighlightRectangle(display,windows->image.id,
4869 windows->image.highlight_context,&highlight_info);
4870 switch (event.type)
4871 {
4872 case ButtonPress:
4873 {
cristy49e2d862010-11-12 02:50:30 +00004874 crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4875 crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +00004876 break;
4877 }
4878 case ButtonRelease:
4879 {
4880 /*
4881 User has committed to cropping rectangle.
4882 */
cristy49e2d862010-11-12 02:50:30 +00004883 crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4884 crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +00004885 XSetCursorState(display,windows,MagickFalse);
4886 state|=ExitState;
4887 windows->command.data=0;
4888 (void) XCommandWidget(display,windows,RectifyModeMenu,
4889 (XEvent *) NULL);
4890 break;
4891 }
4892 case Expose:
4893 break;
4894 case MotionNotify:
4895 {
cristy49e2d862010-11-12 02:50:30 +00004896 crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4897 crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
cristy3ed852e2009-09-05 21:47:34 +00004898 }
4899 default:
4900 break;
4901 }
4902 if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4903 ((state & ExitState) != 0))
4904 {
4905 /*
4906 Check boundary conditions.
4907 */
4908 if (crop_info.x < 0)
4909 crop_info.x=0;
4910 else
cristy49e2d862010-11-12 02:50:30 +00004911 if (crop_info.x > (ssize_t) windows->image.ximage->width)
4912 crop_info.x=(ssize_t) windows->image.ximage->width;
cristy3ed852e2009-09-05 21:47:34 +00004913 if ((int) crop_info.x < x)
4914 crop_info.width=(unsigned int) (x-crop_info.x);
4915 else
4916 {
4917 crop_info.width=(unsigned int) (crop_info.x-x);
cristy49e2d862010-11-12 02:50:30 +00004918 crop_info.x=(ssize_t) x;
cristy3ed852e2009-09-05 21:47:34 +00004919 }
4920 if (crop_info.y < 0)
4921 crop_info.y=0;
4922 else
cristy49e2d862010-11-12 02:50:30 +00004923 if (crop_info.y > (ssize_t) windows->image.ximage->height)
4924 crop_info.y=(ssize_t) windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +00004925 if ((int) crop_info.y < y)
4926 crop_info.height=(unsigned int) (y-crop_info.y);
4927 else
4928 {
4929 crop_info.height=(unsigned int) (crop_info.y-y);
cristy49e2d862010-11-12 02:50:30 +00004930 crop_info.y=(ssize_t) y;
cristy3ed852e2009-09-05 21:47:34 +00004931 }
4932 }
4933 } while ((state & ExitState) == 0);
4934 /*
4935 Wait for user to grab a corner of the rectangle or press return.
4936 */
4937 state=DefaultState;
4938 (void) XMapWindow(display,windows->info.id);
4939 do
4940 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -07004941 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004942 {
4943 /*
4944 Display pointer position.
4945 */
cristy151b66d2015-04-15 10:50:31 +00004946 (void) FormatLocaleString(text,MagickPathExtent,
cristy6d8abba2010-06-03 01:10:47 +00004947 " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +00004948 crop_info.height,(double) crop_info.x,(double) crop_info.y);
cristy3ed852e2009-09-05 21:47:34 +00004949 XInfoWidget(display,windows,text);
4950 }
4951 highlight_info=crop_info;
4952 highlight_info.x=crop_info.x-windows->image.x;
4953 highlight_info.y=crop_info.y-windows->image.y;
4954 if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4955 {
4956 state|=EscapeState;
4957 state|=ExitState;
4958 break;
4959 }
4960 XHighlightRectangle(display,windows->image.id,
4961 windows->image.highlight_context,&highlight_info);
cristy6710d842011-10-20 23:23:00 +00004962 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +00004963 if (event.xany.window == windows->command.id)
4964 {
4965 /*
4966 Select a command from the Command widget.
4967 */
4968 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4969 id=XCommandWidget(display,windows,RectifyModeMenu,&event);
4970 (void) XSetFunction(display,windows->image.highlight_context,
4971 GXinvert);
4972 XHighlightRectangle(display,windows->image.id,
4973 windows->image.highlight_context,&highlight_info);
4974 if (id >= 0)
4975 switch (RectifyCommands[id])
4976 {
4977 case RectifyCopyCommand:
4978 {
4979 state|=ExitState;
4980 break;
4981 }
4982 case RectifyHelpCommand:
4983 {
4984 (void) XSetFunction(display,windows->image.highlight_context,
4985 GXcopy);
4986 switch (mode)
4987 {
4988 case CopyMode:
4989 {
Cristy6970baa2019-04-20 21:23:54 -04004990 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00004991 "Help Viewer - Image Copy",ImageCopyHelp);
4992 break;
4993 }
4994 case CropMode:
4995 {
Cristy6970baa2019-04-20 21:23:54 -04004996 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00004997 "Help Viewer - Image Crop",ImageCropHelp);
4998 break;
4999 }
5000 case CutMode:
5001 {
Cristy6970baa2019-04-20 21:23:54 -04005002 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00005003 "Help Viewer - Image Cut",ImageCutHelp);
5004 break;
5005 }
5006 }
5007 (void) XSetFunction(display,windows->image.highlight_context,
5008 GXinvert);
5009 break;
5010 }
5011 case RectifyDismissCommand:
5012 {
5013 /*
5014 Prematurely exit.
5015 */
5016 state|=EscapeState;
5017 state|=ExitState;
5018 break;
5019 }
5020 default:
5021 break;
5022 }
5023 continue;
5024 }
5025 XHighlightRectangle(display,windows->image.id,
5026 windows->image.highlight_context,&highlight_info);
5027 switch (event.type)
5028 {
5029 case ButtonPress:
5030 {
5031 if (event.xbutton.button != Button1)
5032 break;
5033 if (event.xbutton.window != windows->image.id)
5034 break;
5035 x=windows->image.x+event.xbutton.x;
5036 y=windows->image.y+event.xbutton.y;
5037 if ((x < (int) (crop_info.x+RoiDelta)) &&
5038 (x > (int) (crop_info.x-RoiDelta)) &&
5039 (y < (int) (crop_info.y+RoiDelta)) &&
5040 (y > (int) (crop_info.y-RoiDelta)))
5041 {
cristybb503372010-05-27 20:51:26 +00005042 crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5043 crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
cristy3ed852e2009-09-05 21:47:34 +00005044 state|=UpdateConfigurationState;
5045 break;
5046 }
5047 if ((x < (int) (crop_info.x+RoiDelta)) &&
5048 (x > (int) (crop_info.x-RoiDelta)) &&
5049 (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5050 (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5051 {
cristybb503372010-05-27 20:51:26 +00005052 crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
cristy3ed852e2009-09-05 21:47:34 +00005053 state|=UpdateConfigurationState;
5054 break;
5055 }
5056 if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5057 (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5058 (y < (int) (crop_info.y+RoiDelta)) &&
5059 (y > (int) (crop_info.y-RoiDelta)))
5060 {
cristybb503372010-05-27 20:51:26 +00005061 crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
cristy3ed852e2009-09-05 21:47:34 +00005062 state|=UpdateConfigurationState;
5063 break;
5064 }
5065 if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5066 (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5067 (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5068 (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5069 {
5070 state|=UpdateConfigurationState;
5071 break;
5072 }
5073 }
5074 case ButtonRelease:
5075 {
5076 if (event.xbutton.window == windows->pan.id)
5077 if ((highlight_info.x != crop_info.x-windows->image.x) ||
5078 (highlight_info.y != crop_info.y-windows->image.y))
5079 XHighlightRectangle(display,windows->image.id,
5080 windows->image.highlight_context,&highlight_info);
5081 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5082 event.xbutton.time);
5083 break;
5084 }
5085 case Expose:
5086 {
5087 if (event.xexpose.window == windows->image.id)
5088 if (event.xexpose.count == 0)
5089 {
5090 event.xexpose.x=(int) highlight_info.x;
5091 event.xexpose.y=(int) highlight_info.y;
5092 event.xexpose.width=(int) highlight_info.width;
5093 event.xexpose.height=(int) highlight_info.height;
5094 XRefreshWindow(display,&windows->image,&event);
5095 }
5096 if (event.xexpose.window == windows->info.id)
5097 if (event.xexpose.count == 0)
5098 XInfoWidget(display,windows,text);
5099 break;
5100 }
5101 case KeyPress:
5102 {
5103 if (event.xkey.window != windows->image.id)
5104 break;
5105 /*
5106 Respond to a user key press.
5107 */
5108 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5109 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5110 switch ((int) key_symbol)
5111 {
5112 case XK_Escape:
5113 case XK_F20:
5114 state|=EscapeState;
5115 case XK_Return:
5116 {
5117 state|=ExitState;
5118 break;
5119 }
5120 case XK_Home:
5121 case XK_KP_Home:
5122 {
cristyccdef732015-05-31 14:14:16 +00005123 crop_info.x=(ssize_t) (windows->image.width/2L-crop_info.width/
5124 2L);
5125 crop_info.y=(ssize_t) (windows->image.height/2L-crop_info.height/
5126 2L);
cristy3ed852e2009-09-05 21:47:34 +00005127 break;
5128 }
5129 case XK_Left:
5130 case XK_KP_Left:
5131 {
5132 crop_info.x--;
5133 break;
5134 }
5135 case XK_Up:
5136 case XK_KP_Up:
5137 case XK_Next:
5138 {
5139 crop_info.y--;
5140 break;
5141 }
5142 case XK_Right:
5143 case XK_KP_Right:
5144 {
5145 crop_info.x++;
5146 break;
5147 }
5148 case XK_Prior:
5149 case XK_Down:
5150 case XK_KP_Down:
5151 {
5152 crop_info.y++;
5153 break;
5154 }
5155 case XK_F1:
5156 case XK_Help:
5157 {
5158 (void) XSetFunction(display,windows->image.highlight_context,
5159 GXcopy);
5160 switch (mode)
5161 {
5162 case CopyMode:
5163 {
Cristy6970baa2019-04-20 21:23:54 -04005164 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00005165 "Help Viewer - Image Copy",ImageCopyHelp);
5166 break;
5167 }
5168 case CropMode:
5169 {
Cristy6970baa2019-04-20 21:23:54 -04005170 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00005171 "Help Viewer - Image Cropg",ImageCropHelp);
5172 break;
5173 }
5174 case CutMode:
5175 {
Cristy6970baa2019-04-20 21:23:54 -04005176 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00005177 "Help Viewer - Image Cutg",ImageCutHelp);
5178 break;
5179 }
5180 }
5181 (void) XSetFunction(display,windows->image.highlight_context,
5182 GXinvert);
5183 break;
5184 }
5185 default:
5186 {
5187 (void) XBell(display,0);
5188 break;
5189 }
5190 }
5191 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5192 event.xkey.time);
5193 break;
5194 }
5195 case KeyRelease:
5196 break;
5197 case MotionNotify:
5198 {
5199 if (event.xmotion.window != windows->image.id)
5200 break;
5201 /*
5202 Map and unmap Info widget as text cursor crosses its boundaries.
5203 */
5204 x=event.xmotion.x;
5205 y=event.xmotion.y;
Elliott Hughes5d41fba2021-04-12 16:36:42 -07005206 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005207 {
5208 if ((x < (int) (windows->info.x+windows->info.width)) &&
5209 (y < (int) (windows->info.y+windows->info.height)))
5210 (void) XWithdrawWindow(display,windows->info.id,
5211 windows->info.screen);
5212 }
5213 else
5214 if ((x > (int) (windows->info.x+windows->info.width)) ||
5215 (y > (int) (windows->info.y+windows->info.height)))
5216 (void) XMapWindow(display,windows->info.id);
cristy49e2d862010-11-12 02:50:30 +00005217 crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5218 crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
cristy3ed852e2009-09-05 21:47:34 +00005219 break;
5220 }
5221 case SelectionRequest:
5222 {
5223 XSelectionEvent
5224 notify;
5225
5226 XSelectionRequestEvent
5227 *request;
5228
5229 /*
5230 Set primary selection.
5231 */
cristy151b66d2015-04-15 10:50:31 +00005232 (void) FormatLocaleString(text,MagickPathExtent,
cristy6d8abba2010-06-03 01:10:47 +00005233 "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +00005234 crop_info.height,(double) crop_info.x,(double) crop_info.y);
cristy3ed852e2009-09-05 21:47:34 +00005235 request=(&(event.xselectionrequest));
5236 (void) XChangeProperty(request->display,request->requestor,
5237 request->property,request->target,8,PropModeReplace,
5238 (unsigned char *) text,(int) strlen(text));
5239 notify.type=SelectionNotify;
5240 notify.display=request->display;
5241 notify.requestor=request->requestor;
5242 notify.selection=request->selection;
5243 notify.target=request->target;
5244 notify.time=request->time;
5245 if (request->property == None)
5246 notify.property=request->target;
5247 else
5248 notify.property=request->property;
5249 (void) XSendEvent(request->display,request->requestor,False,0,
5250 (XEvent *) &notify);
5251 }
5252 default:
5253 break;
5254 }
5255 if ((state & UpdateConfigurationState) != 0)
5256 {
5257 (void) XPutBackEvent(display,&event);
5258 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5259 break;
5260 }
5261 } while ((state & ExitState) == 0);
5262 } while ((state & ExitState) == 0);
5263 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5264 XSetCursorState(display,windows,MagickFalse);
5265 if ((state & EscapeState) != 0)
5266 return(MagickTrue);
5267 if (mode == CropMode)
5268 if (((int) crop_info.width != windows->image.ximage->width) ||
5269 ((int) crop_info.height != windows->image.ximage->height))
5270 {
5271 /*
5272 Reconfigure Image window as defined by cropping rectangle.
5273 */
5274 XSetCropGeometry(display,windows,&crop_info,image);
5275 windows->image.window_changes.width=(int) crop_info.width;
5276 windows->image.window_changes.height=(int) crop_info.height;
cristy051718b2011-08-28 22:49:25 +00005277 (void) XConfigureImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005278 return(MagickTrue);
5279 }
5280 /*
5281 Copy image before applying image transforms.
5282 */
5283 XSetCursorState(display,windows,MagickTrue);
5284 XCheckRefreshWindows(display,windows);
5285 width=(unsigned int) image->columns;
5286 height=(unsigned int) image->rows;
5287 x=0;
5288 y=0;
5289 if (windows->image.crop_geometry != (char *) NULL)
5290 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
cristya19f1d72012-08-07 18:24:38 +00005291 scale_factor=(double) width/windows->image.ximage->width;
cristy3ed852e2009-09-05 21:47:34 +00005292 crop_info.x+=x;
cristy49e2d862010-11-12 02:50:30 +00005293 crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +00005294 crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
cristya19f1d72012-08-07 18:24:38 +00005295 scale_factor=(double) height/windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +00005296 crop_info.y+=y;
cristy49e2d862010-11-12 02:50:30 +00005297 crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00005298 crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07005299 crop_info.x+=image->page.x;
5300 crop_info.y+=image->page.y;
cristy051718b2011-08-28 22:49:25 +00005301 crop_image=CropImage(image,&crop_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00005302 XSetCursorState(display,windows,MagickFalse);
5303 if (crop_image == (Image *) NULL)
5304 return(MagickFalse);
5305 if (resource_info->copy_image != (Image *) NULL)
5306 resource_info->copy_image=DestroyImage(resource_info->copy_image);
5307 resource_info->copy_image=crop_image;
5308 if (mode == CopyMode)
5309 {
cristy051718b2011-08-28 22:49:25 +00005310 (void) XConfigureImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005311 return(MagickTrue);
5312 }
5313 /*
5314 Cut image.
5315 */
dirk5d0a4412016-01-05 22:28:51 +01005316 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005317 return(MagickFalse);
cristy8a46d822012-08-28 23:32:39 +00005318 image->alpha_trait=BlendPixelTrait;
cristy46ff2672012-12-14 15:32:26 +00005319 image_view=AcquireAuthenticCacheView(image,exception);
cristy49e2d862010-11-12 02:50:30 +00005320 for (y=0; y < (int) crop_info.height; y++)
cristy3ed852e2009-09-05 21:47:34 +00005321 {
cristy49e2d862010-11-12 02:50:30 +00005322 q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5323 crop_info.width,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00005324 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005325 break;
5326 for (x=0; x < (int) crop_info.width; x++)
5327 {
cristy4c08aed2011-07-01 19:47:50 +00005328 SetPixelAlpha(image,TransparentAlpha,q);
cristyed231572011-07-14 02:18:59 +00005329 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00005330 }
dirk5d0a4412016-01-05 22:28:51 +01005331 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005332 break;
5333 }
cristy49e2d862010-11-12 02:50:30 +00005334 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005335 /*
5336 Update image configuration.
5337 */
cristy6710d842011-10-20 23:23:00 +00005338 XConfigureImageColormap(display,resource_info,windows,image,exception);
cristy051718b2011-08-28 22:49:25 +00005339 (void) XConfigureImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005340 return(MagickTrue);
5341}
5342
5343/*
5344%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5345% %
5346% %
5347% %
5348+ X D r a w I m a g e %
5349% %
5350% %
5351% %
5352%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5353%
5354% XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5355% the image.
5356%
5357% The format of the XDrawEditImage method is:
5358%
5359% MagickBooleanType XDrawEditImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00005360% XResourceInfo *resource_info,XWindows *windows,Image **image,
5361% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005362%
5363% A description of each parameter follows:
5364%
5365% o display: Specifies a connection to an X server; returned from
5366% XOpenDisplay.
5367%
5368% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5369%
5370% o windows: Specifies a pointer to a XWindows structure.
5371%
5372% o image: the image.
5373%
cristy051718b2011-08-28 22:49:25 +00005374% o exception: return any errors or warnings in this structure.
5375%
cristy3ed852e2009-09-05 21:47:34 +00005376*/
5377static MagickBooleanType XDrawEditImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00005378 XResourceInfo *resource_info,XWindows *windows,Image **image,
5379 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005380{
Cristyd93b2e62019-05-22 19:45:01 -04005381 const char
5382 *const DrawMenu[] =
cristy3ed852e2009-09-05 21:47:34 +00005383 {
5384 "Element",
5385 "Color",
5386 "Stipple",
5387 "Width",
5388 "Undo",
5389 "Help",
5390 "Dismiss",
5391 (char *) NULL
5392 };
5393
5394 static ElementType
5395 element = PointElement;
5396
5397 static const ModeType
5398 DrawCommands[] =
5399 {
5400 DrawElementCommand,
5401 DrawColorCommand,
5402 DrawStippleCommand,
5403 DrawWidthCommand,
5404 DrawUndoCommand,
5405 DrawHelpCommand,
5406 DrawDismissCommand
5407 };
5408
5409 static Pixmap
5410 stipple = (Pixmap) NULL;
5411
5412 static unsigned int
5413 pen_id = 0,
5414 line_width = 1;
5415
5416 char
cristy151b66d2015-04-15 10:50:31 +00005417 command[MagickPathExtent],
5418 text[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00005419
5420 Cursor
5421 cursor;
5422
5423 int
5424 entry,
5425 id,
5426 number_coordinates,
5427 x,
5428 y;
5429
cristya19f1d72012-08-07 18:24:38 +00005430 double
cristy3ed852e2009-09-05 21:47:34 +00005431 degrees;
5432
5433 MagickStatusType
5434 status;
5435
5436 RectangleInfo
5437 rectangle_info;
5438
Cristyf2dc1dd2020-12-28 13:59:26 -05005439 int
cristy3ed852e2009-09-05 21:47:34 +00005440 i;
5441
5442 unsigned int
5443 distance,
5444 height,
5445 max_coordinates,
5446 width;
5447
cristybb503372010-05-27 20:51:26 +00005448 size_t
cristy3ed852e2009-09-05 21:47:34 +00005449 state;
5450
5451 Window
5452 root_window;
5453
5454 XDrawInfo
5455 draw_info;
5456
5457 XEvent
5458 event;
5459
5460 XPoint
5461 *coordinate_info;
5462
5463 XSegment
5464 line_info;
5465
5466 /*
5467 Allocate polygon info.
5468 */
5469 max_coordinates=2048;
5470 coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5471 sizeof(*coordinate_info));
5472 if (coordinate_info == (XPoint *) NULL)
5473 {
cristy051718b2011-08-28 22:49:25 +00005474 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00005475 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
cristy3ed852e2009-09-05 21:47:34 +00005476 return(MagickFalse);
5477 }
5478 /*
5479 Map Command widget.
5480 */
5481 (void) CloneString(&windows->command.name,"Draw");
5482 windows->command.data=4;
5483 (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5484 (void) XMapRaised(display,windows->command.id);
5485 XClientMessage(display,windows->image.id,windows->im_protocols,
5486 windows->im_update_widget,CurrentTime);
5487 /*
5488 Wait for first button press.
5489 */
5490 root_window=XRootWindow(display,XDefaultScreen(display));
5491 draw_info.stencil=OpaqueStencil;
5492 status=MagickTrue;
5493 cursor=XCreateFontCursor(display,XC_tcross);
5494 for ( ; ; )
5495 {
5496 XQueryPosition(display,windows->image.id,&x,&y);
5497 (void) XSelectInput(display,windows->image.id,
5498 windows->image.attributes.event_mask | PointerMotionMask);
5499 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5500 state=DefaultState;
5501 do
5502 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -07005503 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005504 {
5505 /*
5506 Display pointer position.
5507 */
cristy151b66d2015-04-15 10:50:31 +00005508 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
cristy3ed852e2009-09-05 21:47:34 +00005509 x+windows->image.x,y+windows->image.y);
5510 XInfoWidget(display,windows,text);
5511 }
5512 /*
5513 Wait for next event.
5514 */
cristy6710d842011-10-20 23:23:00 +00005515 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +00005516 if (event.xany.window == windows->command.id)
5517 {
5518 /*
5519 Select a command from the Command widget.
5520 */
5521 id=XCommandWidget(display,windows,DrawMenu,&event);
5522 if (id < 0)
5523 continue;
5524 switch (DrawCommands[id])
5525 {
5526 case DrawElementCommand:
5527 {
Cristyd93b2e62019-05-22 19:45:01 -04005528 const char
5529 *const Elements[] =
cristy3ed852e2009-09-05 21:47:34 +00005530 {
5531 "point",
5532 "line",
5533 "rectangle",
5534 "fill rectangle",
5535 "circle",
5536 "fill circle",
5537 "ellipse",
5538 "fill ellipse",
5539 "polygon",
5540 "fill polygon",
5541 (char *) NULL,
5542 };
5543
5544 /*
5545 Select a command from the pop-up menu.
5546 */
5547 element=(ElementType) (XMenuWidget(display,windows,
5548 DrawMenu[id],Elements,command)+1);
5549 break;
5550 }
5551 case DrawColorCommand:
5552 {
5553 const char
5554 *ColorMenu[MaxNumberPens+1];
5555
5556 int
5557 pen_number;
5558
5559 MagickBooleanType
5560 transparent;
5561
5562 XColor
5563 color;
5564
5565 /*
5566 Initialize menu selections.
5567 */
5568 for (i=0; i < (int) (MaxNumberPens-2); i++)
5569 ColorMenu[i]=resource_info->pen_colors[i];
5570 ColorMenu[MaxNumberPens-2]="transparent";
5571 ColorMenu[MaxNumberPens-1]="Browser...";
5572 ColorMenu[MaxNumberPens]=(char *) NULL;
5573 /*
5574 Select a pen color from the pop-up menu.
5575 */
5576 pen_number=XMenuWidget(display,windows,DrawMenu[id],
5577 (const char **) ColorMenu,command);
5578 if (pen_number < 0)
5579 break;
5580 transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5581 MagickFalse;
Elliott Hughes5d41fba2021-04-12 16:36:42 -07005582 if (transparent != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005583 {
5584 draw_info.stencil=TransparentStencil;
5585 break;
5586 }
5587 if (pen_number == (MaxNumberPens-1))
5588 {
5589 static char
cristy151b66d2015-04-15 10:50:31 +00005590 color_name[MagickPathExtent] = "gray";
cristy3ed852e2009-09-05 21:47:34 +00005591
5592 /*
5593 Select a pen color from a dialog.
5594 */
5595 resource_info->pen_colors[pen_number]=color_name;
5596 XColorBrowserWidget(display,windows,"Select",color_name);
5597 if (*color_name == '\0')
5598 break;
5599 }
5600 /*
5601 Set pen color.
5602 */
5603 (void) XParseColor(display,windows->map_info->colormap,
5604 resource_info->pen_colors[pen_number],&color);
5605 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5606 (unsigned int) MaxColors,&color);
5607 windows->pixel_info->pen_colors[pen_number]=color;
5608 pen_id=(unsigned int) pen_number;
5609 draw_info.stencil=OpaqueStencil;
5610 break;
5611 }
5612 case DrawStippleCommand:
5613 {
Cristyd93b2e62019-05-22 19:45:01 -04005614 const char
cristy3ed852e2009-09-05 21:47:34 +00005615 *StipplesMenu[] =
5616 {
5617 "Brick",
5618 "Diagonal",
5619 "Scales",
5620 "Vertical",
5621 "Wavy",
5622 "Translucent",
5623 "Opaque",
5624 (char *) NULL,
5625 (char *) NULL,
5626 };
5627
Cristyd93b2e62019-05-22 19:45:01 -04005628 Image
5629 *stipple_image;
5630
5631 ImageInfo
5632 *image_info;
5633
5634 int
5635 status;
5636
5637 static char
5638 filename[MagickPathExtent] = "\0";
5639
cristy3ed852e2009-09-05 21:47:34 +00005640 /*
5641 Select a command from the pop-up menu.
5642 */
5643 StipplesMenu[7]="Open...";
5644 entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5645 command);
5646 if (entry < 0)
5647 break;
5648 if (stipple != (Pixmap) NULL)
5649 (void) XFreePixmap(display,stipple);
5650 stipple=(Pixmap) NULL;
cristy3ed852e2009-09-05 21:47:34 +00005651 if (entry != 7)
5652 {
5653 switch (entry)
5654 {
5655 case 0:
5656 {
5657 stipple=XCreateBitmapFromData(display,root_window,
5658 (char *) BricksBitmap,BricksWidth,BricksHeight);
5659 break;
5660 }
5661 case 1:
5662 {
5663 stipple=XCreateBitmapFromData(display,root_window,
5664 (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5665 break;
5666 }
5667 case 2:
5668 {
5669 stipple=XCreateBitmapFromData(display,root_window,
5670 (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5671 break;
5672 }
5673 case 3:
5674 {
5675 stipple=XCreateBitmapFromData(display,root_window,
5676 (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5677 break;
5678 }
5679 case 4:
5680 {
5681 stipple=XCreateBitmapFromData(display,root_window,
5682 (char *) WavyBitmap,WavyWidth,WavyHeight);
5683 break;
5684 }
5685 case 5:
cristy3ed852e2009-09-05 21:47:34 +00005686 {
5687 stipple=XCreateBitmapFromData(display,root_window,
5688 (char *) HighlightBitmap,HighlightWidth,
5689 HighlightHeight);
5690 break;
5691 }
cristydd05beb2010-11-21 21:23:39 +00005692 case 6:
5693 default:
5694 {
5695 stipple=XCreateBitmapFromData(display,root_window,
5696 (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5697 break;
5698 }
cristy3ed852e2009-09-05 21:47:34 +00005699 }
5700 break;
5701 }
5702 XFileBrowserWidget(display,windows,"Stipple",filename);
5703 if (*filename == '\0')
5704 break;
5705 /*
5706 Read image.
5707 */
5708 XSetCursorState(display,windows,MagickTrue);
5709 XCheckRefreshWindows(display,windows);
5710 image_info=AcquireImageInfo();
5711 (void) CopyMagickString(image_info->filename,filename,
cristy151b66d2015-04-15 10:50:31 +00005712 MagickPathExtent);
cristy051718b2011-08-28 22:49:25 +00005713 stipple_image=ReadImage(image_info,exception);
5714 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00005715 XSetCursorState(display,windows,MagickFalse);
5716 if (stipple_image == (Image *) NULL)
5717 break;
5718 (void) AcquireUniqueFileResource(filename);
Cristy859511c2018-04-22 14:50:36 -04005719 (void) FormatLocaleString(stipple_image->filename,
5720 MagickPathExtent,"xbm:%s",filename);
cristy051718b2011-08-28 22:49:25 +00005721 (void) WriteImage(image_info,stipple_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005722 stipple_image=DestroyImage(stipple_image);
5723 image_info=DestroyImageInfo(image_info);
5724 status=XReadBitmapFile(display,root_window,filename,&width,
5725 &height,&stipple,&x,&y);
5726 (void) RelinquishUniqueFileResource(filename);
5727 if ((status != BitmapSuccess) != 0)
5728 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5729 filename);
5730 break;
5731 }
5732 case DrawWidthCommand:
5733 {
Cristyd93b2e62019-05-22 19:45:01 -04005734 const char
5735 *const WidthsMenu[] =
cristy3ed852e2009-09-05 21:47:34 +00005736 {
5737 "1",
5738 "2",
5739 "4",
5740 "8",
5741 "16",
5742 "Dialog...",
5743 (char *) NULL,
5744 };
5745
Cristyd93b2e62019-05-22 19:45:01 -04005746 static char
5747 width[MagickPathExtent] = "0";
5748
cristy3ed852e2009-09-05 21:47:34 +00005749 /*
5750 Select a command from the pop-up menu.
5751 */
5752 entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5753 command);
5754 if (entry < 0)
5755 break;
5756 if (entry != 5)
5757 {
cristydd05beb2010-11-21 21:23:39 +00005758 line_width=(unsigned int) StringToUnsignedLong(
5759 WidthsMenu[entry]);
cristy3ed852e2009-09-05 21:47:34 +00005760 break;
5761 }
5762 (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5763 width);
5764 if (*width == '\0')
5765 break;
cristye27293e2009-12-18 02:53:20 +00005766 line_width=(unsigned int) StringToUnsignedLong(width);
cristy3ed852e2009-09-05 21:47:34 +00005767 break;
5768 }
5769 case DrawUndoCommand:
5770 {
5771 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
cristy051718b2011-08-28 22:49:25 +00005772 image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005773 break;
5774 }
5775 case DrawHelpCommand:
5776 {
Cristy6970baa2019-04-20 21:23:54 -04005777 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00005778 "Help Viewer - Image Rotation",ImageDrawHelp);
5779 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5780 break;
5781 }
5782 case DrawDismissCommand:
5783 {
5784 /*
5785 Prematurely exit.
5786 */
5787 state|=EscapeState;
5788 state|=ExitState;
5789 break;
5790 }
5791 default:
5792 break;
5793 }
5794 (void) XCheckDefineCursor(display,windows->image.id,cursor);
5795 continue;
5796 }
5797 switch (event.type)
5798 {
5799 case ButtonPress:
5800 {
5801 if (event.xbutton.button != Button1)
5802 break;
5803 if (event.xbutton.window != windows->image.id)
5804 break;
5805 /*
5806 exit loop.
5807 */
5808 x=event.xbutton.x;
5809 y=event.xbutton.y;
5810 state|=ExitState;
5811 break;
5812 }
5813 case ButtonRelease:
5814 break;
5815 case Expose:
5816 break;
5817 case KeyPress:
5818 {
5819 KeySym
5820 key_symbol;
5821
5822 if (event.xkey.window != windows->image.id)
5823 break;
5824 /*
5825 Respond to a user key press.
5826 */
5827 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5828 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5829 switch ((int) key_symbol)
5830 {
5831 case XK_Escape:
5832 case XK_F20:
5833 {
5834 /*
5835 Prematurely exit.
5836 */
5837 state|=EscapeState;
5838 state|=ExitState;
5839 break;
5840 }
5841 case XK_F1:
5842 case XK_Help:
5843 {
Cristy6970baa2019-04-20 21:23:54 -04005844 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00005845 "Help Viewer - Image Rotation",ImageDrawHelp);
5846 break;
5847 }
5848 default:
5849 {
5850 (void) XBell(display,0);
5851 break;
5852 }
5853 }
5854 break;
5855 }
5856 case MotionNotify:
5857 {
5858 /*
5859 Map and unmap Info widget as text cursor crosses its boundaries.
5860 */
5861 x=event.xmotion.x;
5862 y=event.xmotion.y;
Elliott Hughes5d41fba2021-04-12 16:36:42 -07005863 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005864 {
5865 if ((x < (int) (windows->info.x+windows->info.width)) &&
5866 (y < (int) (windows->info.y+windows->info.height)))
5867 (void) XWithdrawWindow(display,windows->info.id,
5868 windows->info.screen);
5869 }
5870 else
5871 if ((x > (int) (windows->info.x+windows->info.width)) ||
5872 (y > (int) (windows->info.y+windows->info.height)))
5873 (void) XMapWindow(display,windows->info.id);
5874 break;
5875 }
5876 }
5877 } while ((state & ExitState) == 0);
5878 (void) XSelectInput(display,windows->image.id,
5879 windows->image.attributes.event_mask);
5880 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5881 if ((state & EscapeState) != 0)
5882 break;
5883 /*
5884 Draw element as pointer moves until the button is released.
5885 */
5886 distance=0;
5887 degrees=0.0;
5888 line_info.x1=x;
5889 line_info.y1=y;
5890 line_info.x2=x;
5891 line_info.y2=y;
cristy49e2d862010-11-12 02:50:30 +00005892 rectangle_info.x=(ssize_t) x;
5893 rectangle_info.y=(ssize_t) y;
cristy3ed852e2009-09-05 21:47:34 +00005894 rectangle_info.width=0;
5895 rectangle_info.height=0;
5896 number_coordinates=1;
5897 coordinate_info->x=x;
5898 coordinate_info->y=y;
5899 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5900 state=DefaultState;
5901 do
5902 {
5903 switch (element)
5904 {
5905 case PointElement:
5906 default:
5907 {
5908 if (number_coordinates > 1)
5909 {
5910 (void) XDrawLines(display,windows->image.id,
5911 windows->image.highlight_context,coordinate_info,
5912 number_coordinates,CoordModeOrigin);
cristy151b66d2015-04-15 10:50:31 +00005913 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d",
cristy3ed852e2009-09-05 21:47:34 +00005914 coordinate_info[number_coordinates-1].x,
5915 coordinate_info[number_coordinates-1].y);
5916 XInfoWidget(display,windows,text);
5917 }
5918 break;
5919 }
5920 case LineElement:
5921 {
5922 if (distance > 9)
5923 {
5924 /*
5925 Display angle of the line.
5926 */
5927 degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5928 line_info.y1),(double) (line_info.x2-line_info.x1)));
cristy151b66d2015-04-15 10:50:31 +00005929 (void) FormatLocaleString(text,MagickPathExtent," %g",
cristy3ed852e2009-09-05 21:47:34 +00005930 (double) degrees);
5931 XInfoWidget(display,windows,text);
5932 XHighlightLine(display,windows->image.id,
5933 windows->image.highlight_context,&line_info);
5934 }
5935 else
Elliott Hughes5d41fba2021-04-12 16:36:42 -07005936 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005937 (void) XWithdrawWindow(display,windows->info.id,
5938 windows->info.screen);
5939 break;
5940 }
5941 case RectangleElement:
5942 case FillRectangleElement:
5943 {
5944 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5945 {
5946 /*
5947 Display info and draw drawing rectangle.
5948 */
cristy151b66d2015-04-15 10:50:31 +00005949 (void) FormatLocaleString(text,MagickPathExtent,
cristy6d8abba2010-06-03 01:10:47 +00005950 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
cristye8c25f92010-06-03 00:53:06 +00005951 (double) rectangle_info.height,(double) rectangle_info.x,
5952 (double) rectangle_info.y);
cristy3ed852e2009-09-05 21:47:34 +00005953 XInfoWidget(display,windows,text);
5954 XHighlightRectangle(display,windows->image.id,
5955 windows->image.highlight_context,&rectangle_info);
5956 }
5957 else
Elliott Hughes5d41fba2021-04-12 16:36:42 -07005958 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005959 (void) XWithdrawWindow(display,windows->info.id,
5960 windows->info.screen);
5961 break;
5962 }
5963 case CircleElement:
5964 case FillCircleElement:
5965 case EllipseElement:
5966 case FillEllipseElement:
5967 {
5968 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5969 {
5970 /*
5971 Display info and draw drawing rectangle.
5972 */
cristy151b66d2015-04-15 10:50:31 +00005973 (void) FormatLocaleString(text,MagickPathExtent,
cristy6d8abba2010-06-03 01:10:47 +00005974 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
cristye8c25f92010-06-03 00:53:06 +00005975 (double) rectangle_info.height,(double) rectangle_info.x,
5976 (double) rectangle_info.y);
cristy3ed852e2009-09-05 21:47:34 +00005977 XInfoWidget(display,windows,text);
5978 XHighlightEllipse(display,windows->image.id,
5979 windows->image.highlight_context,&rectangle_info);
5980 }
5981 else
Elliott Hughes5d41fba2021-04-12 16:36:42 -07005982 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005983 (void) XWithdrawWindow(display,windows->info.id,
5984 windows->info.screen);
5985 break;
5986 }
5987 case PolygonElement:
5988 case FillPolygonElement:
5989 {
5990 if (number_coordinates > 1)
5991 (void) XDrawLines(display,windows->image.id,
5992 windows->image.highlight_context,coordinate_info,
5993 number_coordinates,CoordModeOrigin);
5994 if (distance > 9)
5995 {
5996 /*
5997 Display angle of the line.
5998 */
5999 degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6000 line_info.y1),(double) (line_info.x2-line_info.x1)));
cristy151b66d2015-04-15 10:50:31 +00006001 (void) FormatLocaleString(text,MagickPathExtent," %g",
cristy3ed852e2009-09-05 21:47:34 +00006002 (double) degrees);
6003 XInfoWidget(display,windows,text);
6004 XHighlightLine(display,windows->image.id,
6005 windows->image.highlight_context,&line_info);
6006 }
6007 else
Elliott Hughes5d41fba2021-04-12 16:36:42 -07006008 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00006009 (void) XWithdrawWindow(display,windows->info.id,
6010 windows->info.screen);
6011 break;
6012 }
6013 }
6014 /*
6015 Wait for next event.
6016 */
cristy6710d842011-10-20 23:23:00 +00006017 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +00006018 switch (element)
6019 {
6020 case PointElement:
6021 default:
6022 {
6023 if (number_coordinates > 1)
6024 (void) XDrawLines(display,windows->image.id,
6025 windows->image.highlight_context,coordinate_info,
6026 number_coordinates,CoordModeOrigin);
6027 break;
6028 }
6029 case LineElement:
6030 {
6031 if (distance > 9)
6032 XHighlightLine(display,windows->image.id,
6033 windows->image.highlight_context,&line_info);
6034 break;
6035 }
6036 case RectangleElement:
6037 case FillRectangleElement:
6038 {
6039 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6040 XHighlightRectangle(display,windows->image.id,
6041 windows->image.highlight_context,&rectangle_info);
6042 break;
6043 }
6044 case CircleElement:
6045 case FillCircleElement:
6046 case EllipseElement:
6047 case FillEllipseElement:
6048 {
6049 if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6050 XHighlightEllipse(display,windows->image.id,
6051 windows->image.highlight_context,&rectangle_info);
6052 break;
6053 }
6054 case PolygonElement:
6055 case FillPolygonElement:
6056 {
6057 if (number_coordinates > 1)
6058 (void) XDrawLines(display,windows->image.id,
6059 windows->image.highlight_context,coordinate_info,
6060 number_coordinates,CoordModeOrigin);
6061 if (distance > 9)
6062 XHighlightLine(display,windows->image.id,
6063 windows->image.highlight_context,&line_info);
6064 break;
6065 }
6066 }
6067 switch (event.type)
6068 {
6069 case ButtonPress:
6070 break;
6071 case ButtonRelease:
6072 {
6073 /*
6074 User has committed to element.
6075 */
6076 line_info.x2=event.xbutton.x;
6077 line_info.y2=event.xbutton.y;
cristy49e2d862010-11-12 02:50:30 +00006078 rectangle_info.x=(ssize_t) event.xbutton.x;
6079 rectangle_info.y=(ssize_t) event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +00006080 coordinate_info[number_coordinates].x=event.xbutton.x;
6081 coordinate_info[number_coordinates].y=event.xbutton.y;
6082 if (((element != PolygonElement) &&
6083 (element != FillPolygonElement)) || (distance <= 9))
6084 {
6085 state|=ExitState;
6086 break;
6087 }
6088 number_coordinates++;
6089 if (number_coordinates < (int) max_coordinates)
6090 {
6091 line_info.x1=event.xbutton.x;
6092 line_info.y1=event.xbutton.y;
6093 break;
6094 }
6095 max_coordinates<<=1;
6096 coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6097 max_coordinates,sizeof(*coordinate_info));
6098 if (coordinate_info == (XPoint *) NULL)
cristy051718b2011-08-28 22:49:25 +00006099 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00006100 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
cristy3ed852e2009-09-05 21:47:34 +00006101 break;
6102 }
6103 case Expose:
6104 break;
6105 case MotionNotify:
6106 {
6107 if (event.xmotion.window != windows->image.id)
6108 break;
6109 if (element != PointElement)
6110 {
6111 line_info.x2=event.xmotion.x;
6112 line_info.y2=event.xmotion.y;
cristy49e2d862010-11-12 02:50:30 +00006113 rectangle_info.x=(ssize_t) event.xmotion.x;
6114 rectangle_info.y=(ssize_t) event.xmotion.y;
cristy3ed852e2009-09-05 21:47:34 +00006115 break;
6116 }
6117 coordinate_info[number_coordinates].x=event.xbutton.x;
6118 coordinate_info[number_coordinates].y=event.xbutton.y;
6119 number_coordinates++;
6120 if (number_coordinates < (int) max_coordinates)
6121 break;
6122 max_coordinates<<=1;
6123 coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6124 max_coordinates,sizeof(*coordinate_info));
6125 if (coordinate_info == (XPoint *) NULL)
cristy051718b2011-08-28 22:49:25 +00006126 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00006127 ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
cristy3ed852e2009-09-05 21:47:34 +00006128 break;
6129 }
6130 default:
6131 break;
6132 }
6133 /*
6134 Check boundary conditions.
6135 */
6136 if (line_info.x2 < 0)
6137 line_info.x2=0;
6138 else
6139 if (line_info.x2 > (int) windows->image.width)
6140 line_info.x2=(short) windows->image.width;
6141 if (line_info.y2 < 0)
6142 line_info.y2=0;
6143 else
6144 if (line_info.y2 > (int) windows->image.height)
6145 line_info.y2=(short) windows->image.height;
6146 distance=(unsigned int)
6147 (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6148 ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6149 if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6150 ((state & ExitState) != 0))
6151 {
6152 if (rectangle_info.x < 0)
6153 rectangle_info.x=0;
6154 else
cristy49e2d862010-11-12 02:50:30 +00006155 if (rectangle_info.x > (ssize_t) windows->image.width)
cristybb503372010-05-27 20:51:26 +00006156 rectangle_info.x=(ssize_t) windows->image.width;
cristy3ed852e2009-09-05 21:47:34 +00006157 if ((int) rectangle_info.x < x)
6158 rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6159 else
6160 {
6161 rectangle_info.width=(unsigned int) (rectangle_info.x-x);
cristy49e2d862010-11-12 02:50:30 +00006162 rectangle_info.x=(ssize_t) x;
cristy3ed852e2009-09-05 21:47:34 +00006163 }
6164 if (rectangle_info.y < 0)
6165 rectangle_info.y=0;
6166 else
cristy49e2d862010-11-12 02:50:30 +00006167 if (rectangle_info.y > (ssize_t) windows->image.height)
cristybb503372010-05-27 20:51:26 +00006168 rectangle_info.y=(ssize_t) windows->image.height;
cristy3ed852e2009-09-05 21:47:34 +00006169 if ((int) rectangle_info.y < y)
6170 rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6171 else
6172 {
6173 rectangle_info.height=(unsigned int) (rectangle_info.y-y);
cristy49e2d862010-11-12 02:50:30 +00006174 rectangle_info.y=(ssize_t) y;
cristy3ed852e2009-09-05 21:47:34 +00006175 }
6176 }
6177 } while ((state & ExitState) == 0);
6178 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6179 if ((element == PointElement) || (element == PolygonElement) ||
6180 (element == FillPolygonElement))
6181 {
6182 /*
6183 Determine polygon bounding box.
6184 */
cristy49e2d862010-11-12 02:50:30 +00006185 rectangle_info.x=(ssize_t) coordinate_info->x;
6186 rectangle_info.y=(ssize_t) coordinate_info->y;
cristy3ed852e2009-09-05 21:47:34 +00006187 x=coordinate_info->x;
6188 y=coordinate_info->y;
6189 for (i=1; i < number_coordinates; i++)
6190 {
6191 if (coordinate_info[i].x > x)
6192 x=coordinate_info[i].x;
6193 if (coordinate_info[i].y > y)
6194 y=coordinate_info[i].y;
cristy49e2d862010-11-12 02:50:30 +00006195 if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6196 rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6197 if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6198 rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
cristy3ed852e2009-09-05 21:47:34 +00006199 }
cristybb503372010-05-27 20:51:26 +00006200 rectangle_info.width=(size_t) (x-rectangle_info.x);
6201 rectangle_info.height=(size_t) (y-rectangle_info.y);
cristy3ed852e2009-09-05 21:47:34 +00006202 for (i=0; i < number_coordinates; i++)
6203 {
6204 coordinate_info[i].x-=rectangle_info.x;
6205 coordinate_info[i].y-=rectangle_info.y;
6206 }
6207 }
6208 else
6209 if (distance <= 9)
6210 continue;
6211 else
6212 if ((element == RectangleElement) ||
6213 (element == CircleElement) || (element == EllipseElement))
6214 {
6215 rectangle_info.width--;
6216 rectangle_info.height--;
6217 }
6218 /*
6219 Drawing is relative to image configuration.
6220 */
6221 draw_info.x=(int) rectangle_info.x;
6222 draw_info.y=(int) rectangle_info.y;
6223 (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
cristy051718b2011-08-28 22:49:25 +00006224 image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006225 width=(unsigned int) (*image)->columns;
6226 height=(unsigned int) (*image)->rows;
6227 x=0;
6228 y=0;
6229 if (windows->image.crop_geometry != (char *) NULL)
6230 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6231 draw_info.x+=windows->image.x-(line_width/2);
6232 if (draw_info.x < 0)
6233 draw_info.x=0;
6234 draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6235 draw_info.y+=windows->image.y-(line_width/2);
6236 if (draw_info.y < 0)
6237 draw_info.y=0;
6238 draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6239 draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6240 if (draw_info.width > (unsigned int) (*image)->columns)
6241 draw_info.width=(unsigned int) (*image)->columns;
6242 draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6243 if (draw_info.height > (unsigned int) (*image)->rows)
6244 draw_info.height=(unsigned int) (*image)->rows;
cristy151b66d2015-04-15 10:50:31 +00006245 (void) FormatLocaleString(draw_info.geometry,MagickPathExtent,"%ux%u%+d%+d",
cristy3ed852e2009-09-05 21:47:34 +00006246 width*draw_info.width/windows->image.ximage->width,
6247 height*draw_info.height/windows->image.ximage->height,
6248 draw_info.x+x,draw_info.y+y);
6249 /*
6250 Initialize drawing attributes.
6251 */
6252 draw_info.degrees=0.0;
6253 draw_info.element=element;
6254 draw_info.stipple=stipple;
6255 draw_info.line_width=line_width;
6256 draw_info.line_info=line_info;
6257 if (line_info.x1 > (int) (line_width/2))
6258 draw_info.line_info.x1=(short) line_width/2;
6259 if (line_info.y1 > (int) (line_width/2))
6260 draw_info.line_info.y1=(short) line_width/2;
6261 draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6262 draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6263 if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6264 {
6265 draw_info.line_info.x2=(-draw_info.line_info.x2);
6266 draw_info.line_info.y2=(-draw_info.line_info.y2);
6267 }
6268 if (draw_info.line_info.x2 < 0)
6269 {
6270 draw_info.line_info.x2=(-draw_info.line_info.x2);
6271 Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6272 }
6273 if (draw_info.line_info.y2 < 0)
6274 {
6275 draw_info.line_info.y2=(-draw_info.line_info.y2);
6276 Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6277 }
6278 draw_info.rectangle_info=rectangle_info;
cristy49e2d862010-11-12 02:50:30 +00006279 if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
cristybb503372010-05-27 20:51:26 +00006280 draw_info.rectangle_info.x=(ssize_t) line_width/2;
cristy49e2d862010-11-12 02:50:30 +00006281 if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
cristybb503372010-05-27 20:51:26 +00006282 draw_info.rectangle_info.y=(ssize_t) line_width/2;
cristy3ed852e2009-09-05 21:47:34 +00006283 draw_info.number_coordinates=(unsigned int) number_coordinates;
6284 draw_info.coordinate_info=coordinate_info;
6285 windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6286 /*
6287 Draw element on image.
6288 */
6289 XSetCursorState(display,windows,MagickTrue);
6290 XCheckRefreshWindows(display,windows);
cristy6710d842011-10-20 23:23:00 +00006291 status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006292 XSetCursorState(display,windows,MagickFalse);
6293 /*
6294 Update image colormap and return to image drawing.
6295 */
cristy6710d842011-10-20 23:23:00 +00006296 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00006297 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006298 }
6299 XSetCursorState(display,windows,MagickFalse);
6300 coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
dirkb9dbc292015-07-26 09:50:00 +00006301 return(status != 0 ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00006302}
6303
6304/*
6305%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6306% %
6307% %
6308% %
6309+ X D r a w P a n R e c t a n g l e %
6310% %
6311% %
6312% %
6313%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6314%
6315% XDrawPanRectangle() draws a rectangle in the pan window. The pan window
6316% displays a zoom image and the rectangle shows which portion of the image is
6317% displayed in the Image window.
6318%
6319% The format of the XDrawPanRectangle method is:
6320%
6321% XDrawPanRectangle(Display *display,XWindows *windows)
6322%
6323% A description of each parameter follows:
6324%
6325% o display: Specifies a connection to an X server; returned from
6326% XOpenDisplay.
6327%
6328% o windows: Specifies a pointer to a XWindows structure.
6329%
6330*/
6331static void XDrawPanRectangle(Display *display,XWindows *windows)
6332{
cristya19f1d72012-08-07 18:24:38 +00006333 double
cristy3ed852e2009-09-05 21:47:34 +00006334 scale_factor;
6335
6336 RectangleInfo
6337 highlight_info;
6338
6339 /*
6340 Determine dimensions of the panning rectangle.
6341 */
cristya19f1d72012-08-07 18:24:38 +00006342 scale_factor=(double) windows->pan.width/windows->image.ximage->width;
cristy49e2d862010-11-12 02:50:30 +00006343 highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +00006344 highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
cristya19f1d72012-08-07 18:24:38 +00006345 scale_factor=(double)
cristy3ed852e2009-09-05 21:47:34 +00006346 windows->pan.height/windows->image.ximage->height;
cristy49e2d862010-11-12 02:50:30 +00006347 highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00006348 highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6349 /*
6350 Display the panning rectangle.
6351 */
6352 (void) XClearWindow(display,windows->pan.id);
6353 XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6354 &highlight_info);
6355}
6356
6357/*
6358%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6359% %
6360% %
6361% %
6362+ X I m a g e C a c h e %
6363% %
6364% %
6365% %
6366%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6367%
6368% XImageCache() handles the creation, manipulation, and destruction of the
6369% image cache (undo and redo buffers).
6370%
6371% The format of the XImageCache method is:
6372%
6373% void XImageCache(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +00006374% XWindows *windows,const CommandType command,Image **image,
6375% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00006376%
6377% A description of each parameter follows:
6378%
6379% o display: Specifies a connection to an X server; returned from
6380% XOpenDisplay.
6381%
6382% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6383%
6384% o windows: Specifies a pointer to a XWindows structure.
6385%
6386% o command: Specifies a command to perform.
6387%
cristya9a86bb2011-01-13 01:11:00 +00006388% o image: the image; XImageCache may transform the image and return a new
6389% image pointer.
cristy3ed852e2009-09-05 21:47:34 +00006390%
cristy051718b2011-08-28 22:49:25 +00006391% o exception: return any errors or warnings in this structure.
6392%
cristy3ed852e2009-09-05 21:47:34 +00006393*/
6394static void XImageCache(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +00006395 XWindows *windows,const CommandType command,Image **image,
6396 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00006397{
6398 Image
6399 *cache_image;
6400
6401 static Image
6402 *redo_image = (Image *) NULL,
6403 *undo_image = (Image *) NULL;
6404
6405 switch (command)
6406 {
6407 case FreeBuffersCommand:
6408 {
6409 /*
6410 Free memory from the undo and redo cache.
6411 */
6412 while (undo_image != (Image *) NULL)
6413 {
6414 cache_image=undo_image;
6415 undo_image=GetPreviousImageInList(undo_image);
6416 cache_image->list=DestroyImage(cache_image->list);
6417 cache_image=DestroyImage(cache_image);
6418 }
6419 undo_image=NewImageList();
6420 if (redo_image != (Image *) NULL)
6421 redo_image=DestroyImage(redo_image);
6422 redo_image=NewImageList();
6423 return;
6424 }
6425 case UndoCommand:
6426 {
cristya9a86bb2011-01-13 01:11:00 +00006427 char
cristy151b66d2015-04-15 10:50:31 +00006428 image_geometry[MagickPathExtent];
cristya9a86bb2011-01-13 01:11:00 +00006429
cristy3ed852e2009-09-05 21:47:34 +00006430 /*
6431 Undo the last image transformation.
6432 */
6433 if (undo_image == (Image *) NULL)
6434 {
6435 (void) XBell(display,0);
6436 return;
6437 }
6438 cache_image=undo_image;
6439 undo_image=GetPreviousImageInList(undo_image);
6440 windows->image.window_changes.width=(int) cache_image->columns;
6441 windows->image.window_changes.height=(int) cache_image->rows;
cristy151b66d2015-04-15 10:50:31 +00006442 (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
cristya9a86bb2011-01-13 01:11:00 +00006443 windows->image.ximage->width,windows->image.ximage->height);
cristye941a752011-10-15 01:52:48 +00006444 (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6445 exception);
cristy3ed852e2009-09-05 21:47:34 +00006446 if (windows->image.crop_geometry != (char *) NULL)
cristye941a752011-10-15 01:52:48 +00006447 windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6448 windows->image.crop_geometry);
cristy3ed852e2009-09-05 21:47:34 +00006449 windows->image.crop_geometry=cache_image->geometry;
6450 if (redo_image != (Image *) NULL)
6451 redo_image=DestroyImage(redo_image);
6452 redo_image=(*image);
6453 *image=cache_image->list;
6454 cache_image=DestroyImage(cache_image);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07006455 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00006456 return;
cristy6710d842011-10-20 23:23:00 +00006457 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00006458 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006459 return;
6460 }
6461 case CutCommand:
6462 case PasteCommand:
6463 case ApplyCommand:
6464 case HalfSizeCommand:
6465 case OriginalSizeCommand:
6466 case DoubleSizeCommand:
6467 case ResizeCommand:
6468 case TrimCommand:
6469 case CropCommand:
6470 case ChopCommand:
6471 case FlipCommand:
6472 case FlopCommand:
6473 case RotateRightCommand:
6474 case RotateLeftCommand:
6475 case RotateCommand:
6476 case ShearCommand:
6477 case RollCommand:
6478 case NegateCommand:
6479 case ContrastStretchCommand:
6480 case SigmoidalContrastCommand:
6481 case NormalizeCommand:
6482 case EqualizeCommand:
6483 case HueCommand:
6484 case SaturationCommand:
6485 case BrightnessCommand:
6486 case GammaCommand:
6487 case SpiffCommand:
6488 case DullCommand:
6489 case GrayscaleCommand:
6490 case MapCommand:
6491 case QuantizeCommand:
6492 case DespeckleCommand:
6493 case EmbossCommand:
6494 case ReduceNoiseCommand:
6495 case AddNoiseCommand:
6496 case SharpenCommand:
6497 case BlurCommand:
6498 case ThresholdCommand:
6499 case EdgeDetectCommand:
6500 case SpreadCommand:
6501 case ShadeCommand:
6502 case RaiseCommand:
6503 case SegmentCommand:
6504 case SolarizeCommand:
6505 case SepiaToneCommand:
6506 case SwirlCommand:
6507 case ImplodeCommand:
6508 case VignetteCommand:
6509 case WaveCommand:
6510 case OilPaintCommand:
6511 case CharcoalDrawCommand:
6512 case AnnotateCommand:
6513 case AddBorderCommand:
6514 case AddFrameCommand:
6515 case CompositeCommand:
6516 case CommentCommand:
6517 case LaunchCommand:
6518 case RegionofInterestCommand:
6519 case SaveToUndoBufferCommand:
6520 case RedoCommand:
6521 {
6522 Image
6523 *previous_image;
6524
cristybb503372010-05-27 20:51:26 +00006525 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006526 bytes;
6527
cristy101ab702011-10-13 13:06:32 +00006528 bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
cristy3ed852e2009-09-05 21:47:34 +00006529 if (undo_image != (Image *) NULL)
6530 {
6531 /*
cristya9a86bb2011-01-13 01:11:00 +00006532 Ensure the undo cache has enough memory available.
cristy3ed852e2009-09-05 21:47:34 +00006533 */
6534 previous_image=undo_image;
6535 while (previous_image != (Image *) NULL)
6536 {
6537 bytes+=previous_image->list->columns*previous_image->list->rows*
cristy101ab702011-10-13 13:06:32 +00006538 sizeof(PixelInfo);
cristybb503372010-05-27 20:51:26 +00006539 if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
cristy3ed852e2009-09-05 21:47:34 +00006540 {
6541 previous_image=GetPreviousImageInList(previous_image);
6542 continue;
6543 }
6544 bytes-=previous_image->list->columns*previous_image->list->rows*
cristy101ab702011-10-13 13:06:32 +00006545 sizeof(PixelInfo);
cristy3ed852e2009-09-05 21:47:34 +00006546 if (previous_image == undo_image)
6547 undo_image=NewImageList();
6548 else
6549 previous_image->next->previous=NewImageList();
6550 break;
6551 }
6552 while (previous_image != (Image *) NULL)
6553 {
6554 /*
6555 Delete any excess memory from undo cache.
6556 */
6557 cache_image=previous_image;
6558 previous_image=GetPreviousImageInList(previous_image);
6559 cache_image->list=DestroyImage(cache_image->list);
6560 cache_image=DestroyImage(cache_image);
6561 }
6562 }
cristybb503372010-05-27 20:51:26 +00006563 if (bytes > (ssize_t) (resource_info->undo_cache << 20))
cristy3ed852e2009-09-05 21:47:34 +00006564 break;
6565 /*
6566 Save image before transformations are applied.
6567 */
cristy9950d572011-10-01 18:22:35 +00006568 cache_image=AcquireImage((ImageInfo *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +00006569 if (cache_image == (Image *) NULL)
6570 break;
6571 XSetCursorState(display,windows,MagickTrue);
6572 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00006573 cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00006574 XSetCursorState(display,windows,MagickFalse);
6575 if (cache_image->list == (Image *) NULL)
6576 {
6577 cache_image=DestroyImage(cache_image);
6578 break;
6579 }
cristybb503372010-05-27 20:51:26 +00006580 cache_image->columns=(size_t) windows->image.ximage->width;
6581 cache_image->rows=(size_t) windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +00006582 cache_image->geometry=windows->image.crop_geometry;
6583 if (windows->image.crop_geometry != (char *) NULL)
6584 {
6585 cache_image->geometry=AcquireString((char *) NULL);
6586 (void) CopyMagickString(cache_image->geometry,
cristy151b66d2015-04-15 10:50:31 +00006587 windows->image.crop_geometry,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00006588 }
6589 if (undo_image == (Image *) NULL)
6590 {
6591 undo_image=cache_image;
6592 break;
6593 }
6594 undo_image->next=cache_image;
6595 undo_image->next->previous=undo_image;
6596 undo_image=undo_image->next;
6597 break;
6598 }
6599 default:
6600 break;
6601 }
6602 if (command == RedoCommand)
6603 {
6604 /*
6605 Redo the last image transformation.
6606 */
6607 if (redo_image == (Image *) NULL)
6608 {
6609 (void) XBell(display,0);
6610 return;
6611 }
6612 windows->image.window_changes.width=(int) redo_image->columns;
6613 windows->image.window_changes.height=(int) redo_image->rows;
6614 if (windows->image.crop_geometry != (char *) NULL)
6615 windows->image.crop_geometry=(char *)
6616 RelinquishMagickMemory(windows->image.crop_geometry);
6617 windows->image.crop_geometry=redo_image->geometry;
6618 *image=DestroyImage(*image);
6619 *image=redo_image;
6620 redo_image=NewImageList();
Elliott Hughes5d41fba2021-04-12 16:36:42 -07006621 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00006622 return;
cristy6710d842011-10-20 23:23:00 +00006623 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00006624 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006625 return;
6626 }
6627 if (command != InfoCommand)
6628 return;
6629 /*
6630 Display image info.
6631 */
6632 XSetCursorState(display,windows,MagickTrue);
6633 XCheckRefreshWindows(display,windows);
cristy6710d842011-10-20 23:23:00 +00006634 XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006635 XSetCursorState(display,windows,MagickFalse);
6636}
6637
6638/*
6639%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6640% %
6641% %
6642% %
6643+ X I m a g e W i n d o w C o m m a n d %
6644% %
6645% %
6646% %
6647%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6648%
6649% XImageWindowCommand() makes a transform to the image or Image window as
6650% specified by a user menu button or keyboard command.
6651%
cristy051718b2011-08-28 22:49:25 +00006652% The format of the XImageWindowCommand method is:
cristy3ed852e2009-09-05 21:47:34 +00006653%
6654% CommandType XImageWindowCommand(Display *display,
6655% XResourceInfo *resource_info,XWindows *windows,
cristy051718b2011-08-28 22:49:25 +00006656% const MagickStatusType state,KeySym key_symbol,Image **image,
6657% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00006658%
6659% A description of each parameter follows:
6660%
6661% o nexus: Method XImageWindowCommand returns an image when the
6662% user chooses 'Open Image' from the command menu. Otherwise a null
6663% image is returned.
6664%
6665% o display: Specifies a connection to an X server; returned from
6666% XOpenDisplay.
6667%
6668% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6669%
6670% o windows: Specifies a pointer to a XWindows structure.
6671%
6672% o state: key mask.
6673%
6674% o key_symbol: Specifies a command to perform.
6675%
cristy051718b2011-08-28 22:49:25 +00006676% o image: the image; XImageWIndowCommand may transform the image and
6677% return a new image pointer.
6678%
6679% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00006680%
6681*/
6682static CommandType XImageWindowCommand(Display *display,
6683 XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
cristy051718b2011-08-28 22:49:25 +00006684 KeySym key_symbol,Image **image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00006685{
6686 static char
cristy151b66d2015-04-15 10:50:31 +00006687 delta[MagickPathExtent] = "";
cristy3ed852e2009-09-05 21:47:34 +00006688
6689 static const char
6690 Digits[] = "01234567890";
6691
6692 static KeySym
6693 last_symbol = XK_0;
6694
6695 if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6696 {
6697 if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6698 {
6699 *delta='\0';
6700 resource_info->quantum=1;
6701 }
6702 last_symbol=key_symbol;
6703 delta[strlen(delta)+1]='\0';
6704 delta[strlen(delta)]=Digits[key_symbol-XK_0];
cristyf2f27272009-12-17 14:48:46 +00006705 resource_info->quantum=StringToLong(delta);
cristy3ed852e2009-09-05 21:47:34 +00006706 return(NullCommand);
6707 }
6708 last_symbol=key_symbol;
6709 if (resource_info->immutable)
6710 {
6711 /*
6712 Virtual image window has a restricted command set.
6713 */
6714 switch (key_symbol)
6715 {
6716 case XK_question:
6717 return(InfoCommand);
6718 case XK_p:
6719 case XK_Print:
6720 return(PrintCommand);
6721 case XK_space:
6722 return(NextCommand);
6723 case XK_q:
6724 case XK_Escape:
6725 return(QuitCommand);
6726 default:
6727 break;
6728 }
6729 return(NullCommand);
6730 }
6731 switch ((int) key_symbol)
6732 {
6733 case XK_o:
6734 {
6735 if ((state & ControlMask) == 0)
6736 break;
6737 return(OpenCommand);
6738 }
6739 case XK_space:
6740 return(NextCommand);
6741 case XK_BackSpace:
6742 return(FormerCommand);
6743 case XK_s:
6744 {
6745 if ((state & Mod1Mask) != 0)
6746 return(SwirlCommand);
6747 if ((state & ControlMask) == 0)
6748 return(ShearCommand);
6749 return(SaveCommand);
6750 }
6751 case XK_p:
6752 case XK_Print:
6753 {
6754 if ((state & Mod1Mask) != 0)
6755 return(OilPaintCommand);
6756 if ((state & Mod4Mask) != 0)
6757 return(ColorCommand);
6758 if ((state & ControlMask) == 0)
6759 return(NullCommand);
6760 return(PrintCommand);
6761 }
6762 case XK_d:
6763 {
6764 if ((state & Mod4Mask) != 0)
6765 return(DrawCommand);
6766 if ((state & ControlMask) == 0)
6767 return(NullCommand);
6768 return(DeleteCommand);
6769 }
6770 case XK_Select:
6771 {
6772 if ((state & ControlMask) == 0)
6773 return(NullCommand);
6774 return(SelectCommand);
6775 }
6776 case XK_n:
6777 {
6778 if ((state & ControlMask) == 0)
6779 return(NullCommand);
6780 return(NewCommand);
6781 }
6782 case XK_q:
6783 case XK_Escape:
6784 return(QuitCommand);
6785 case XK_z:
6786 case XK_Undo:
6787 {
6788 if ((state & ControlMask) == 0)
6789 return(NullCommand);
6790 return(UndoCommand);
6791 }
6792 case XK_r:
6793 case XK_Redo:
6794 {
6795 if ((state & ControlMask) == 0)
6796 return(RollCommand);
6797 return(RedoCommand);
6798 }
6799 case XK_x:
6800 {
6801 if ((state & ControlMask) == 0)
6802 return(NullCommand);
6803 return(CutCommand);
6804 }
6805 case XK_c:
6806 {
6807 if ((state & Mod1Mask) != 0)
6808 return(CharcoalDrawCommand);
6809 if ((state & ControlMask) == 0)
6810 return(CropCommand);
6811 return(CopyCommand);
6812 }
6813 case XK_v:
6814 case XK_Insert:
6815 {
6816 if ((state & Mod4Mask) != 0)
6817 return(CompositeCommand);
6818 if ((state & ControlMask) == 0)
6819 return(FlipCommand);
6820 return(PasteCommand);
6821 }
6822 case XK_less:
6823 return(HalfSizeCommand);
6824 case XK_minus:
6825 return(OriginalSizeCommand);
6826 case XK_greater:
6827 return(DoubleSizeCommand);
6828 case XK_percent:
6829 return(ResizeCommand);
6830 case XK_at:
6831 return(RefreshCommand);
6832 case XK_bracketleft:
6833 return(ChopCommand);
6834 case XK_h:
6835 return(FlopCommand);
6836 case XK_slash:
6837 return(RotateRightCommand);
6838 case XK_backslash:
6839 return(RotateLeftCommand);
6840 case XK_asterisk:
6841 return(RotateCommand);
6842 case XK_t:
6843 return(TrimCommand);
6844 case XK_H:
6845 return(HueCommand);
6846 case XK_S:
6847 return(SaturationCommand);
6848 case XK_L:
6849 return(BrightnessCommand);
6850 case XK_G:
6851 return(GammaCommand);
6852 case XK_C:
6853 return(SpiffCommand);
6854 case XK_Z:
6855 return(DullCommand);
6856 case XK_N:
6857 return(NormalizeCommand);
6858 case XK_equal:
6859 return(EqualizeCommand);
6860 case XK_asciitilde:
6861 return(NegateCommand);
6862 case XK_period:
6863 return(GrayscaleCommand);
6864 case XK_numbersign:
6865 return(QuantizeCommand);
6866 case XK_F2:
6867 return(DespeckleCommand);
6868 case XK_F3:
6869 return(EmbossCommand);
6870 case XK_F4:
6871 return(ReduceNoiseCommand);
6872 case XK_F5:
6873 return(AddNoiseCommand);
6874 case XK_F6:
6875 return(SharpenCommand);
6876 case XK_F7:
6877 return(BlurCommand);
6878 case XK_F8:
6879 return(ThresholdCommand);
6880 case XK_F9:
6881 return(EdgeDetectCommand);
6882 case XK_F10:
6883 return(SpreadCommand);
6884 case XK_F11:
6885 return(ShadeCommand);
6886 case XK_F12:
6887 return(RaiseCommand);
6888 case XK_F13:
6889 return(SegmentCommand);
6890 case XK_i:
6891 {
6892 if ((state & Mod1Mask) == 0)
6893 return(NullCommand);
6894 return(ImplodeCommand);
6895 }
6896 case XK_w:
6897 {
6898 if ((state & Mod1Mask) == 0)
6899 return(NullCommand);
6900 return(WaveCommand);
6901 }
6902 case XK_m:
6903 {
6904 if ((state & Mod4Mask) == 0)
6905 return(NullCommand);
6906 return(MatteCommand);
6907 }
6908 case XK_b:
6909 {
6910 if ((state & Mod4Mask) == 0)
6911 return(NullCommand);
6912 return(AddBorderCommand);
6913 }
6914 case XK_f:
6915 {
6916 if ((state & Mod4Mask) == 0)
6917 return(NullCommand);
6918 return(AddFrameCommand);
6919 }
6920 case XK_exclam:
6921 {
6922 if ((state & Mod4Mask) == 0)
6923 return(NullCommand);
6924 return(CommentCommand);
6925 }
6926 case XK_a:
6927 {
6928 if ((state & Mod1Mask) != 0)
6929 return(ApplyCommand);
6930 if ((state & Mod4Mask) != 0)
6931 return(AnnotateCommand);
6932 if ((state & ControlMask) == 0)
6933 return(NullCommand);
6934 return(RegionofInterestCommand);
6935 }
6936 case XK_question:
6937 return(InfoCommand);
6938 case XK_plus:
6939 return(ZoomCommand);
6940 case XK_P:
6941 {
6942 if ((state & ShiftMask) == 0)
6943 return(NullCommand);
6944 return(ShowPreviewCommand);
6945 }
6946 case XK_Execute:
6947 return(LaunchCommand);
6948 case XK_F1:
6949 return(HelpCommand);
6950 case XK_Find:
6951 return(BrowseDocumentationCommand);
6952 case XK_Menu:
6953 {
6954 (void) XMapRaised(display,windows->command.id);
6955 return(NullCommand);
6956 }
6957 case XK_Next:
6958 case XK_Prior:
6959 case XK_Home:
6960 case XK_KP_Home:
6961 {
6962 XTranslateImage(display,windows,*image,key_symbol);
6963 return(NullCommand);
6964 }
6965 case XK_Up:
6966 case XK_KP_Up:
6967 case XK_Down:
6968 case XK_KP_Down:
6969 case XK_Left:
6970 case XK_KP_Left:
6971 case XK_Right:
6972 case XK_KP_Right:
6973 {
6974 if ((state & Mod1Mask) != 0)
6975 {
6976 RectangleInfo
6977 crop_info;
6978
6979 /*
6980 Trim one pixel from edge of image.
6981 */
6982 crop_info.x=0;
6983 crop_info.y=0;
cristybb503372010-05-27 20:51:26 +00006984 crop_info.width=(size_t) windows->image.ximage->width;
6985 crop_info.height=(size_t) windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +00006986 if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
6987 {
6988 if (resource_info->quantum >= (int) crop_info.height)
6989 resource_info->quantum=(int) crop_info.height-1;
6990 crop_info.height-=resource_info->quantum;
6991 }
6992 if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
6993 {
6994 if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
6995 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
6996 crop_info.y+=resource_info->quantum;
6997 crop_info.height-=resource_info->quantum;
6998 }
6999 if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7000 {
7001 if (resource_info->quantum >= (int) crop_info.width)
7002 resource_info->quantum=(int) crop_info.width-1;
7003 crop_info.width-=resource_info->quantum;
7004 }
7005 if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7006 {
7007 if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7008 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7009 crop_info.x+=resource_info->quantum;
7010 crop_info.width-=resource_info->quantum;
7011 }
7012 if ((int) (windows->image.x+windows->image.width) >
7013 (int) crop_info.width)
7014 windows->image.x=(int) (crop_info.width-windows->image.width);
7015 if ((int) (windows->image.y+windows->image.height) >
7016 (int) crop_info.height)
7017 windows->image.y=(int) (crop_info.height-windows->image.height);
7018 XSetCropGeometry(display,windows,&crop_info,*image);
7019 windows->image.window_changes.width=(int) crop_info.width;
7020 windows->image.window_changes.height=(int) crop_info.height;
7021 (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
cristy051718b2011-08-28 22:49:25 +00007022 (void) XConfigureImage(display,resource_info,windows,*image,
7023 exception);
cristy3ed852e2009-09-05 21:47:34 +00007024 return(NullCommand);
7025 }
7026 XTranslateImage(display,windows,*image,key_symbol);
7027 return(NullCommand);
7028 }
7029 default:
7030 return(NullCommand);
7031 }
7032 return(NullCommand);
7033}
7034
7035/*
7036%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7037% %
7038% %
7039% %
7040+ X M a g i c k C o m m a n d %
7041% %
7042% %
7043% %
7044%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7045%
7046% XMagickCommand() makes a transform to the image or Image window as
7047% specified by a user menu button or keyboard command.
7048%
7049% The format of the XMagickCommand method is:
7050%
7051% Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +00007052% XWindows *windows,const CommandType command,Image **image,
7053% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007054%
7055% A description of each parameter follows:
7056%
cristy3ed852e2009-09-05 21:47:34 +00007057% o display: Specifies a connection to an X server; returned from
7058% XOpenDisplay.
7059%
7060% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7061%
7062% o windows: Specifies a pointer to a XWindows structure.
7063%
7064% o command: Specifies a command to perform.
7065%
cristy051718b2011-08-28 22:49:25 +00007066% o image: the image; XMagickCommand may transform the image and return a
7067% new image pointer.
7068%
7069% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007070%
7071*/
7072static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +00007073 XWindows *windows,const CommandType command,Image **image,
7074 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007075{
7076 char
cristy151b66d2015-04-15 10:50:31 +00007077 filename[MagickPathExtent],
7078 geometry[MagickPathExtent],
7079 modulate_factors[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00007080
7081 GeometryInfo
7082 geometry_info;
7083
7084 Image
7085 *nexus;
7086
7087 ImageInfo
7088 *image_info;
7089
7090 int
7091 x,
7092 y;
7093
7094 MagickStatusType
7095 flags,
7096 status;
7097
7098 QuantizeInfo
7099 quantize_info;
7100
7101 RectangleInfo
7102 page_geometry;
7103
Cristyf2dc1dd2020-12-28 13:59:26 -05007104 int
cristy3ed852e2009-09-05 21:47:34 +00007105 i;
7106
7107 static char
cristy151b66d2015-04-15 10:50:31 +00007108 color[MagickPathExtent] = "gray";
cristy3ed852e2009-09-05 21:47:34 +00007109
7110 unsigned int
7111 height,
7112 width;
7113
7114 /*
7115 Process user command.
7116 */
7117 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007118 XImageCache(display,resource_info,windows,command,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007119 nexus=NewImageList();
7120 windows->image.window_changes.width=windows->image.ximage->width;
7121 windows->image.window_changes.height=windows->image.ximage->height;
7122 image_info=CloneImageInfo(resource_info->image_info);
7123 SetGeometryInfo(&geometry_info);
7124 GetQuantizeInfo(&quantize_info);
7125 switch (command)
7126 {
7127 case OpenCommand:
7128 {
7129 /*
7130 Load image.
7131 */
7132 nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7133 break;
7134 }
7135 case NextCommand:
7136 {
7137 /*
7138 Display next image.
7139 */
7140 for (i=0; i < resource_info->quantum; i++)
7141 XClientMessage(display,windows->image.id,windows->im_protocols,
7142 windows->im_next_image,CurrentTime);
7143 break;
7144 }
7145 case FormerCommand:
7146 {
7147 /*
7148 Display former image.
7149 */
7150 for (i=0; i < resource_info->quantum; i++)
7151 XClientMessage(display,windows->image.id,windows->im_protocols,
7152 windows->im_former_image,CurrentTime);
7153 break;
7154 }
7155 case SelectCommand:
7156 {
7157 int
7158 status;
7159
7160 /*
7161 Select image.
7162 */
cristy8a5d7f42013-01-06 15:24:33 +00007163 if (*resource_info->home_directory == '\0')
7164 (void) CopyMagickString(resource_info->home_directory,".",
cristy151b66d2015-04-15 10:50:31 +00007165 MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00007166 status=chdir(resource_info->home_directory);
7167 if (status == -1)
cristy051718b2011-08-28 22:49:25 +00007168 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7169 "UnableToOpenFile","%s",resource_info->home_directory);
cristy3ed852e2009-09-05 21:47:34 +00007170 nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7171 break;
7172 }
7173 case SaveCommand:
7174 {
7175 /*
7176 Save image.
7177 */
cristy051718b2011-08-28 22:49:25 +00007178 status=XSaveImage(display,resource_info,windows,*image,exception);
dirk5d0a4412016-01-05 22:28:51 +01007179 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007180 {
cristyc663dbd2011-09-16 19:43:14 +00007181 char
cristy151b66d2015-04-15 10:50:31 +00007182 message[MagickPathExtent];
cristyc663dbd2011-09-16 19:43:14 +00007183
cristy151b66d2015-04-15 10:50:31 +00007184 (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
cristyc663dbd2011-09-16 19:43:14 +00007185 exception->reason != (char *) NULL ? exception->reason : "",
7186 exception->description != (char *) NULL ? exception->description :
7187 "");
7188 XNoticeWidget(display,windows,"Unable to save file:",message);
cristy3ed852e2009-09-05 21:47:34 +00007189 break;
7190 }
7191 break;
7192 }
7193 case PrintCommand:
7194 {
7195 /*
7196 Print image.
7197 */
cristy051718b2011-08-28 22:49:25 +00007198 status=XPrintImage(display,resource_info,windows,*image,exception);
dirk5d0a4412016-01-05 22:28:51 +01007199 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007200 {
cristyc663dbd2011-09-16 19:43:14 +00007201 char
cristy151b66d2015-04-15 10:50:31 +00007202 message[MagickPathExtent];
cristyc663dbd2011-09-16 19:43:14 +00007203
cristy151b66d2015-04-15 10:50:31 +00007204 (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
cristyc663dbd2011-09-16 19:43:14 +00007205 exception->reason != (char *) NULL ? exception->reason : "",
7206 exception->description != (char *) NULL ? exception->description :
7207 "");
7208 XNoticeWidget(display,windows,"Unable to print file:",message);
cristy3ed852e2009-09-05 21:47:34 +00007209 break;
7210 }
7211 break;
7212 }
7213 case DeleteCommand:
7214 {
7215 static char
cristy151b66d2015-04-15 10:50:31 +00007216 filename[MagickPathExtent] = "\0";
cristy3ed852e2009-09-05 21:47:34 +00007217
7218 /*
7219 Delete image file.
7220 */
7221 XFileBrowserWidget(display,windows,"Delete",filename);
7222 if (*filename == '\0')
7223 break;
cristy59864562013-04-18 11:47:41 +00007224 status=ShredFile(filename);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07007225 if (status != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007226 XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7227 break;
7228 }
7229 case NewCommand:
7230 {
7231 int
7232 status;
7233
7234 static char
cristy151b66d2015-04-15 10:50:31 +00007235 color[MagickPathExtent] = "gray",
7236 geometry[MagickPathExtent] = "640x480";
cristy3ed852e2009-09-05 21:47:34 +00007237
7238 static const char
7239 *format = "gradient";
7240
7241 /*
7242 Query user for canvas geometry.
7243 */
7244 status=XDialogWidget(display,windows,"New","Enter image geometry:",
7245 geometry);
7246 if (*geometry == '\0')
7247 break;
7248 if (status == 0)
7249 format="xc";
7250 XColorBrowserWidget(display,windows,"Select",color);
7251 if (*color == '\0')
7252 break;
7253 /*
7254 Create canvas.
7255 */
cristy151b66d2015-04-15 10:50:31 +00007256 (void) FormatLocaleString(image_info->filename,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00007257 "%s:%s",format,color);
7258 (void) CloneString(&image_info->size,geometry);
cristy051718b2011-08-28 22:49:25 +00007259 nexus=ReadImage(image_info,exception);
7260 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00007261 XClientMessage(display,windows->image.id,windows->im_protocols,
7262 windows->im_next_image,CurrentTime);
7263 break;
7264 }
7265 case VisualDirectoryCommand:
7266 {
7267 /*
7268 Visual Image directory.
7269 */
cristy051718b2011-08-28 22:49:25 +00007270 nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
cristy3ed852e2009-09-05 21:47:34 +00007271 break;
7272 }
7273 case QuitCommand:
7274 {
7275 /*
7276 exit program.
7277 */
dirk5d0a4412016-01-05 22:28:51 +01007278 if (resource_info->confirm_exit == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007279 XClientMessage(display,windows->image.id,windows->im_protocols,
7280 windows->im_exit,CurrentTime);
7281 else
7282 {
7283 int
7284 status;
7285
7286 /*
7287 Confirm program exit.
7288 */
7289 status=XConfirmWidget(display,windows,"Do you really want to exit",
7290 resource_info->client_name);
7291 if (status > 0)
7292 XClientMessage(display,windows->image.id,windows->im_protocols,
7293 windows->im_exit,CurrentTime);
7294 }
7295 break;
7296 }
7297 case CutCommand:
7298 {
7299 /*
7300 Cut image.
7301 */
cristy051718b2011-08-28 22:49:25 +00007302 (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00007303 break;
7304 }
7305 case CopyCommand:
7306 {
7307 /*
7308 Copy image.
7309 */
cristy051718b2011-08-28 22:49:25 +00007310 (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7311 exception);
cristy3ed852e2009-09-05 21:47:34 +00007312 break;
7313 }
7314 case PasteCommand:
7315 {
7316 /*
7317 Paste image.
7318 */
cristy051718b2011-08-28 22:49:25 +00007319 status=XPasteImage(display,resource_info,windows,*image,exception);
dirk5d0a4412016-01-05 22:28:51 +01007320 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007321 {
7322 XNoticeWidget(display,windows,"Unable to paste X image",
7323 (*image)->filename);
7324 break;
7325 }
7326 break;
7327 }
7328 case HalfSizeCommand:
7329 {
7330 /*
7331 Half image size.
7332 */
7333 windows->image.window_changes.width=windows->image.ximage->width/2;
7334 windows->image.window_changes.height=windows->image.ximage->height/2;
cristy051718b2011-08-28 22:49:25 +00007335 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007336 break;
7337 }
7338 case OriginalSizeCommand:
7339 {
7340 /*
7341 Original image size.
7342 */
7343 windows->image.window_changes.width=(int) (*image)->columns;
7344 windows->image.window_changes.height=(int) (*image)->rows;
cristy051718b2011-08-28 22:49:25 +00007345 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007346 break;
7347 }
7348 case DoubleSizeCommand:
7349 {
7350 /*
7351 Double the image size.
7352 */
7353 windows->image.window_changes.width=windows->image.ximage->width << 1;
7354 windows->image.window_changes.height=windows->image.ximage->height << 1;
cristy051718b2011-08-28 22:49:25 +00007355 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007356 break;
7357 }
7358 case ResizeCommand:
7359 {
7360 int
7361 status;
7362
cristybb503372010-05-27 20:51:26 +00007363 size_t
cristy3ed852e2009-09-05 21:47:34 +00007364 height,
7365 width;
7366
cristy9d314ff2011-03-09 01:30:28 +00007367 ssize_t
7368 x,
7369 y;
7370
cristy3ed852e2009-09-05 21:47:34 +00007371 /*
7372 Resize image.
7373 */
cristybb503372010-05-27 20:51:26 +00007374 width=(size_t) windows->image.ximage->width;
7375 height=(size_t) windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +00007376 x=0;
7377 y=0;
cristy151b66d2015-04-15 10:50:31 +00007378 (void) FormatLocaleString(geometry,MagickPathExtent,"%.20gx%.20g+0+0",
cristye8c25f92010-06-03 00:53:06 +00007379 (double) width,(double) height);
cristy3ed852e2009-09-05 21:47:34 +00007380 status=XDialogWidget(display,windows,"Resize",
7381 "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7382 if (*geometry == '\0')
7383 break;
7384 if (status == 0)
cristy151b66d2015-04-15 10:50:31 +00007385 (void) ConcatenateMagickString(geometry,"!",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00007386 (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7387 windows->image.window_changes.width=(int) width;
7388 windows->image.window_changes.height=(int) height;
cristy051718b2011-08-28 22:49:25 +00007389 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007390 break;
7391 }
7392 case ApplyCommand:
7393 {
7394 char
cristy151b66d2015-04-15 10:50:31 +00007395 image_geometry[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00007396
7397 if ((windows->image.crop_geometry == (char *) NULL) &&
7398 ((int) (*image)->columns == windows->image.ximage->width) &&
7399 ((int) (*image)->rows == windows->image.ximage->height))
7400 break;
7401 /*
7402 Apply size transforms to image.
7403 */
7404 XSetCursorState(display,windows,MagickTrue);
7405 XCheckRefreshWindows(display,windows);
7406 /*
7407 Crop and/or scale displayed image.
7408 */
cristy151b66d2015-04-15 10:50:31 +00007409 (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
cristy3ed852e2009-09-05 21:47:34 +00007410 windows->image.ximage->width,windows->image.ximage->height);
cristye941a752011-10-15 01:52:48 +00007411 (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7412 exception);
cristy3ed852e2009-09-05 21:47:34 +00007413 if (windows->image.crop_geometry != (char *) NULL)
cristye941a752011-10-15 01:52:48 +00007414 windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7415 windows->image.crop_geometry);
cristy3ed852e2009-09-05 21:47:34 +00007416 windows->image.x=0;
7417 windows->image.y=0;
cristy6710d842011-10-20 23:23:00 +00007418 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007419 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007420 break;
7421 }
7422 case RefreshCommand:
7423 {
cristy051718b2011-08-28 22:49:25 +00007424 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007425 break;
7426 }
7427 case RestoreCommand:
7428 {
7429 /*
7430 Restore Image window to its original size.
7431 */
7432 if ((windows->image.width == (unsigned int) (*image)->columns) &&
7433 (windows->image.height == (unsigned int) (*image)->rows) &&
7434 (windows->image.crop_geometry == (char *) NULL))
7435 {
7436 (void) XBell(display,0);
7437 break;
7438 }
7439 windows->image.window_changes.width=(int) (*image)->columns;
7440 windows->image.window_changes.height=(int) (*image)->rows;
7441 if (windows->image.crop_geometry != (char *) NULL)
7442 {
7443 windows->image.crop_geometry=(char *)
7444 RelinquishMagickMemory(windows->image.crop_geometry);
7445 windows->image.crop_geometry=(char *) NULL;
7446 windows->image.x=0;
7447 windows->image.y=0;
7448 }
cristy6710d842011-10-20 23:23:00 +00007449 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007450 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007451 break;
7452 }
7453 case CropCommand:
7454 {
7455 /*
7456 Crop image.
7457 */
cristy051718b2011-08-28 22:49:25 +00007458 (void) XCropImage(display,resource_info,windows,*image,CropMode,
7459 exception);
cristy3ed852e2009-09-05 21:47:34 +00007460 break;
7461 }
7462 case ChopCommand:
7463 {
7464 /*
7465 Chop image.
7466 */
cristy051718b2011-08-28 22:49:25 +00007467 status=XChopImage(display,resource_info,windows,image,exception);
dirk5d0a4412016-01-05 22:28:51 +01007468 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007469 {
7470 XNoticeWidget(display,windows,"Unable to cut X image",
7471 (*image)->filename);
7472 break;
7473 }
7474 break;
7475 }
7476 case FlopCommand:
7477 {
7478 Image
7479 *flop_image;
7480
7481 /*
7482 Flop image scanlines.
7483 */
7484 XSetCursorState(display,windows,MagickTrue);
7485 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007486 flop_image=FlopImage(*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007487 if (flop_image != (Image *) NULL)
7488 {
7489 *image=DestroyImage(*image);
7490 *image=flop_image;
7491 }
cristy051718b2011-08-28 22:49:25 +00007492 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00007493 XSetCursorState(display,windows,MagickFalse);
7494 if (windows->image.crop_geometry != (char *) NULL)
7495 {
7496 /*
7497 Flop crop geometry.
7498 */
7499 width=(unsigned int) (*image)->columns;
7500 height=(unsigned int) (*image)->rows;
7501 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7502 &width,&height);
Cristy859511c2018-04-22 14:50:36 -04007503 (void) FormatLocaleString(windows->image.crop_geometry,
7504 MagickPathExtent,"%ux%u%+d%+d",width,height,(int) (*image)->columns-
7505 (int) width-x,y);
cristy3ed852e2009-09-05 21:47:34 +00007506 }
Elliott Hughes5d41fba2021-04-12 16:36:42 -07007507 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007508 break;
cristy051718b2011-08-28 22:49:25 +00007509 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007510 break;
7511 }
7512 case FlipCommand:
7513 {
7514 Image
7515 *flip_image;
7516
7517 /*
7518 Flip image scanlines.
7519 */
7520 XSetCursorState(display,windows,MagickTrue);
7521 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007522 flip_image=FlipImage(*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007523 if (flip_image != (Image *) NULL)
7524 {
7525 *image=DestroyImage(*image);
7526 *image=flip_image;
7527 }
cristy051718b2011-08-28 22:49:25 +00007528 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00007529 XSetCursorState(display,windows,MagickFalse);
7530 if (windows->image.crop_geometry != (char *) NULL)
7531 {
7532 /*
7533 Flip crop geometry.
7534 */
7535 width=(unsigned int) (*image)->columns;
7536 height=(unsigned int) (*image)->rows;
7537 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7538 &width,&height);
Cristy859511c2018-04-22 14:50:36 -04007539 (void) FormatLocaleString(windows->image.crop_geometry,
7540 MagickPathExtent,"%ux%u%+d%+d",width,height,x,(int) (*image)->rows-
7541 (int) height-y);
cristy3ed852e2009-09-05 21:47:34 +00007542 }
Elliott Hughes5d41fba2021-04-12 16:36:42 -07007543 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007544 break;
cristy051718b2011-08-28 22:49:25 +00007545 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007546 break;
7547 }
7548 case RotateRightCommand:
7549 {
7550 /*
7551 Rotate image 90 degrees clockwise.
7552 */
cristy051718b2011-08-28 22:49:25 +00007553 status=XRotateImage(display,resource_info,windows,90.0,image,exception);
dirk5d0a4412016-01-05 22:28:51 +01007554 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007555 {
7556 XNoticeWidget(display,windows,"Unable to rotate X image",
7557 (*image)->filename);
7558 break;
7559 }
7560 break;
7561 }
7562 case RotateLeftCommand:
7563 {
7564 /*
7565 Rotate image 90 degrees counter-clockwise.
7566 */
cristy051718b2011-08-28 22:49:25 +00007567 status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
dirk5d0a4412016-01-05 22:28:51 +01007568 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007569 {
7570 XNoticeWidget(display,windows,"Unable to rotate X image",
7571 (*image)->filename);
7572 break;
7573 }
7574 break;
7575 }
7576 case RotateCommand:
7577 {
7578 /*
7579 Rotate image.
7580 */
cristy051718b2011-08-28 22:49:25 +00007581 status=XRotateImage(display,resource_info,windows,0.0,image,exception);
dirk5d0a4412016-01-05 22:28:51 +01007582 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007583 {
7584 XNoticeWidget(display,windows,"Unable to rotate X image",
7585 (*image)->filename);
7586 break;
7587 }
7588 break;
7589 }
7590 case ShearCommand:
7591 {
7592 Image
7593 *shear_image;
7594
7595 static char
cristy151b66d2015-04-15 10:50:31 +00007596 geometry[MagickPathExtent] = "45.0x45.0";
cristy3ed852e2009-09-05 21:47:34 +00007597
7598 /*
7599 Query user for shear color and geometry.
7600 */
7601 XColorBrowserWidget(display,windows,"Select",color);
7602 if (*color == '\0')
7603 break;
7604 (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7605 geometry);
7606 if (*geometry == '\0')
7607 break;
7608 /*
7609 Shear image.
7610 */
cristy051718b2011-08-28 22:49:25 +00007611 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7612 exception);
cristy3ed852e2009-09-05 21:47:34 +00007613 XSetCursorState(display,windows,MagickTrue);
7614 XCheckRefreshWindows(display,windows);
cristy9950d572011-10-01 18:22:35 +00007615 (void) QueryColorCompliance(color,AllCompliance,
7616 &(*image)->background_color,exception);
cristy3ed852e2009-09-05 21:47:34 +00007617 flags=ParseGeometry(geometry,&geometry_info);
7618 if ((flags & SigmaValue) == 0)
7619 geometry_info.sigma=geometry_info.rho;
7620 shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
cristy051718b2011-08-28 22:49:25 +00007621 exception);
cristy3ed852e2009-09-05 21:47:34 +00007622 if (shear_image != (Image *) NULL)
7623 {
7624 *image=DestroyImage(*image);
7625 *image=shear_image;
7626 }
cristy051718b2011-08-28 22:49:25 +00007627 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00007628 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07007629 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007630 break;
7631 windows->image.window_changes.width=(int) (*image)->columns;
7632 windows->image.window_changes.height=(int) (*image)->rows;
cristy6710d842011-10-20 23:23:00 +00007633 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007634 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007635 break;
7636 }
7637 case RollCommand:
7638 {
7639 Image
7640 *roll_image;
7641
7642 static char
cristy151b66d2015-04-15 10:50:31 +00007643 geometry[MagickPathExtent] = "+2+2";
cristy3ed852e2009-09-05 21:47:34 +00007644
7645 /*
7646 Query user for the roll geometry.
7647 */
7648 (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7649 geometry);
7650 if (*geometry == '\0')
7651 break;
7652 /*
7653 Roll image.
7654 */
cristy051718b2011-08-28 22:49:25 +00007655 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7656 exception);
cristy3ed852e2009-09-05 21:47:34 +00007657 XSetCursorState(display,windows,MagickTrue);
7658 XCheckRefreshWindows(display,windows);
7659 (void) ParsePageGeometry(*image,geometry,&page_geometry,
cristy051718b2011-08-28 22:49:25 +00007660 exception);
cristy3ed852e2009-09-05 21:47:34 +00007661 roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
cristy051718b2011-08-28 22:49:25 +00007662 exception);
cristy3ed852e2009-09-05 21:47:34 +00007663 if (roll_image != (Image *) NULL)
7664 {
7665 *image=DestroyImage(*image);
7666 *image=roll_image;
7667 }
cristy051718b2011-08-28 22:49:25 +00007668 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00007669 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07007670 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007671 break;
7672 windows->image.window_changes.width=(int) (*image)->columns;
7673 windows->image.window_changes.height=(int) (*image)->rows;
cristy6710d842011-10-20 23:23:00 +00007674 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007675 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007676 break;
7677 }
7678 case TrimCommand:
7679 {
7680 static char
cristy151b66d2015-04-15 10:50:31 +00007681 fuzz[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00007682
7683 /*
7684 Query user for the fuzz factor.
7685 */
cristy151b66d2015-04-15 10:50:31 +00007686 (void) FormatLocaleString(fuzz,MagickPathExtent,"%g%%",100.0*
cristy8cd5b312010-01-07 01:10:24 +00007687 (*image)->fuzz/(QuantumRange+1.0));
cristy3ed852e2009-09-05 21:47:34 +00007688 (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7689 if (*fuzz == '\0')
7690 break;
cristydbdd0e32011-11-04 23:29:40 +00007691 (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
cristy3ed852e2009-09-05 21:47:34 +00007692 /*
7693 Trim image.
7694 */
cristy051718b2011-08-28 22:49:25 +00007695 status=XTrimImage(display,resource_info,windows,*image,exception);
dirk5d0a4412016-01-05 22:28:51 +01007696 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007697 {
7698 XNoticeWidget(display,windows,"Unable to trim X image",
7699 (*image)->filename);
7700 break;
7701 }
7702 break;
7703 }
7704 case HueCommand:
7705 {
7706 static char
cristy151b66d2015-04-15 10:50:31 +00007707 hue_percent[MagickPathExtent] = "110";
cristy3ed852e2009-09-05 21:47:34 +00007708
7709 /*
7710 Query user for percent hue change.
7711 */
7712 (void) XDialogWidget(display,windows,"Apply",
7713 "Enter percent change in image hue (0-200):",hue_percent);
7714 if (*hue_percent == '\0')
7715 break;
7716 /*
7717 Vary the image hue.
7718 */
7719 XSetCursorState(display,windows,MagickTrue);
7720 XCheckRefreshWindows(display,windows);
cristy151b66d2015-04-15 10:50:31 +00007721 (void) CopyMagickString(modulate_factors,"100.0/100.0/",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00007722 (void) ConcatenateMagickString(modulate_factors,hue_percent,
cristy151b66d2015-04-15 10:50:31 +00007723 MagickPathExtent);
cristy051718b2011-08-28 22:49:25 +00007724 (void) ModulateImage(*image,modulate_factors,exception);
cristy3ed852e2009-09-05 21:47:34 +00007725 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07007726 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007727 break;
cristy6710d842011-10-20 23:23:00 +00007728 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007729 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007730 break;
7731 }
7732 case SaturationCommand:
7733 {
7734 static char
cristy151b66d2015-04-15 10:50:31 +00007735 saturation_percent[MagickPathExtent] = "110";
cristy3ed852e2009-09-05 21:47:34 +00007736
7737 /*
7738 Query user for percent saturation change.
7739 */
7740 (void) XDialogWidget(display,windows,"Apply",
7741 "Enter percent change in color saturation (0-200):",saturation_percent);
7742 if (*saturation_percent == '\0')
7743 break;
7744 /*
7745 Vary color saturation.
7746 */
7747 XSetCursorState(display,windows,MagickTrue);
7748 XCheckRefreshWindows(display,windows);
cristy151b66d2015-04-15 10:50:31 +00007749 (void) CopyMagickString(modulate_factors,"100.0/",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00007750 (void) ConcatenateMagickString(modulate_factors,saturation_percent,
cristy151b66d2015-04-15 10:50:31 +00007751 MagickPathExtent);
cristy051718b2011-08-28 22:49:25 +00007752 (void) ModulateImage(*image,modulate_factors,exception);
cristy3ed852e2009-09-05 21:47:34 +00007753 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07007754 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007755 break;
cristy6710d842011-10-20 23:23:00 +00007756 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007757 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007758 break;
7759 }
7760 case BrightnessCommand:
7761 {
7762 static char
cristy151b66d2015-04-15 10:50:31 +00007763 brightness_percent[MagickPathExtent] = "110";
cristy3ed852e2009-09-05 21:47:34 +00007764
7765 /*
7766 Query user for percent brightness change.
7767 */
7768 (void) XDialogWidget(display,windows,"Apply",
7769 "Enter percent change in color brightness (0-200):",brightness_percent);
7770 if (*brightness_percent == '\0')
7771 break;
7772 /*
7773 Vary the color brightness.
7774 */
7775 XSetCursorState(display,windows,MagickTrue);
7776 XCheckRefreshWindows(display,windows);
7777 (void) CopyMagickString(modulate_factors,brightness_percent,
cristy151b66d2015-04-15 10:50:31 +00007778 MagickPathExtent);
cristy051718b2011-08-28 22:49:25 +00007779 (void) ModulateImage(*image,modulate_factors,exception);
cristy3ed852e2009-09-05 21:47:34 +00007780 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07007781 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007782 break;
cristy6710d842011-10-20 23:23:00 +00007783 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007784 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007785 break;
7786 }
7787 case GammaCommand:
7788 {
7789 static char
cristy151b66d2015-04-15 10:50:31 +00007790 factor[MagickPathExtent] = "1.6";
cristy3ed852e2009-09-05 21:47:34 +00007791
7792 /*
7793 Query user for gamma value.
7794 */
7795 (void) XDialogWidget(display,windows,"Gamma",
cristy50fbc382011-07-07 02:19:17 +00007796 "Enter gamma value (e.g. 1.2):",factor);
cristy3ed852e2009-09-05 21:47:34 +00007797 if (*factor == '\0')
7798 break;
7799 /*
7800 Gamma correct image.
7801 */
7802 XSetCursorState(display,windows,MagickTrue);
7803 XCheckRefreshWindows(display,windows);
cristy79d05312014-12-25 18:13:29 +00007804 (void) GammaImage(*image,strtod(factor,(char **) NULL),exception);
cristy3ed852e2009-09-05 21:47:34 +00007805 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07007806 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007807 break;
cristy6710d842011-10-20 23:23:00 +00007808 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007809 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007810 break;
7811 }
7812 case SpiffCommand:
7813 {
7814 /*
7815 Sharpen the image contrast.
7816 */
7817 XSetCursorState(display,windows,MagickTrue);
7818 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007819 (void) ContrastImage(*image,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00007820 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07007821 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007822 break;
cristy6710d842011-10-20 23:23:00 +00007823 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007824 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007825 break;
7826 }
7827 case DullCommand:
7828 {
7829 /*
7830 Dull the image contrast.
7831 */
7832 XSetCursorState(display,windows,MagickTrue);
7833 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007834 (void) ContrastImage(*image,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00007835 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07007836 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007837 break;
cristy6710d842011-10-20 23:23:00 +00007838 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007839 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007840 break;
7841 }
7842 case ContrastStretchCommand:
7843 {
7844 double
7845 black_point,
7846 white_point;
7847
7848 static char
cristy151b66d2015-04-15 10:50:31 +00007849 levels[MagickPathExtent] = "1%";
cristy3ed852e2009-09-05 21:47:34 +00007850
7851 /*
7852 Query user for gamma value.
7853 */
7854 (void) XDialogWidget(display,windows,"Contrast Stretch",
7855 "Enter black and white points:",levels);
7856 if (*levels == '\0')
7857 break;
7858 /*
7859 Contrast stretch image.
7860 */
7861 XSetCursorState(display,windows,MagickTrue);
7862 XCheckRefreshWindows(display,windows);
7863 flags=ParseGeometry(levels,&geometry_info);
7864 black_point=geometry_info.rho;
7865 white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7866 if ((flags & PercentValue) != 0)
7867 {
7868 black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7869 white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7870 }
cristya19f1d72012-08-07 18:24:38 +00007871 white_point=(double) (*image)->columns*(*image)->rows-white_point;
cristye23ec9d2011-08-16 18:15:40 +00007872 (void) ContrastStretchImage(*image,black_point,white_point,
cristy051718b2011-08-28 22:49:25 +00007873 exception);
cristy3ed852e2009-09-05 21:47:34 +00007874 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07007875 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007876 break;
cristy6710d842011-10-20 23:23:00 +00007877 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007878 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007879 break;
7880 }
7881 case SigmoidalContrastCommand:
7882 {
cristy9ee60942011-07-06 14:54:38 +00007883 GeometryInfo
7884 geometry_info;
7885
7886 MagickStatusType
7887 flags;
7888
cristy3ed852e2009-09-05 21:47:34 +00007889 static char
cristy151b66d2015-04-15 10:50:31 +00007890 levels[MagickPathExtent] = "3x50%";
cristy3ed852e2009-09-05 21:47:34 +00007891
7892 /*
7893 Query user for gamma value.
7894 */
7895 (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7896 "Enter contrast and midpoint:",levels);
7897 if (*levels == '\0')
7898 break;
7899 /*
7900 Contrast stretch image.
7901 */
7902 XSetCursorState(display,windows,MagickTrue);
7903 XCheckRefreshWindows(display,windows);
cristy9ee60942011-07-06 14:54:38 +00007904 flags=ParseGeometry(levels,&geometry_info);
7905 if ((flags & SigmaValue) == 0)
7906 geometry_info.sigma=1.0*QuantumRange/2.0;
7907 if ((flags & PercentValue) != 0)
7908 geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7909 (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
cristy051718b2011-08-28 22:49:25 +00007910 geometry_info.sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +00007911 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07007912 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007913 break;
cristy6710d842011-10-20 23:23:00 +00007914 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007915 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007916 break;
7917 }
7918 case NormalizeCommand:
7919 {
7920 /*
7921 Perform histogram normalization on the image.
7922 */
7923 XSetCursorState(display,windows,MagickTrue);
7924 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007925 (void) NormalizeImage(*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007926 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07007927 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007928 break;
cristy6710d842011-10-20 23:23:00 +00007929 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007930 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007931 break;
7932 }
7933 case EqualizeCommand:
7934 {
7935 /*
7936 Perform histogram equalization on the image.
7937 */
7938 XSetCursorState(display,windows,MagickTrue);
7939 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007940 (void) EqualizeImage(*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007941 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07007942 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007943 break;
cristy6710d842011-10-20 23:23:00 +00007944 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007945 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007946 break;
7947 }
7948 case NegateCommand:
7949 {
7950 /*
7951 Negate colors in image.
7952 */
7953 XSetCursorState(display,windows,MagickTrue);
7954 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00007955 (void) NegateImage(*image,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00007956 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07007957 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007958 break;
cristy6710d842011-10-20 23:23:00 +00007959 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007960 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007961 break;
7962 }
7963 case GrayscaleCommand:
7964 {
7965 /*
7966 Convert image to grayscale.
7967 */
7968 XSetCursorState(display,windows,MagickTrue);
7969 XCheckRefreshWindows(display,windows);
cristy17f11b02014-12-20 19:37:04 +00007970 (void) SetImageType(*image,(*image)->alpha_trait == UndefinedPixelTrait ?
cristydef23e52015-01-22 11:52:01 +00007971 GrayscaleType : GrayscaleAlphaType,exception);
cristy3ed852e2009-09-05 21:47:34 +00007972 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07007973 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007974 break;
cristy6710d842011-10-20 23:23:00 +00007975 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00007976 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007977 break;
7978 }
7979 case MapCommand:
7980 {
7981 Image
7982 *affinity_image;
7983
7984 static char
cristy151b66d2015-04-15 10:50:31 +00007985 filename[MagickPathExtent] = "\0";
cristy3ed852e2009-09-05 21:47:34 +00007986
7987 /*
7988 Request image file name from user.
7989 */
7990 XFileBrowserWidget(display,windows,"Map",filename);
7991 if (*filename == '\0')
7992 break;
7993 /*
7994 Map image.
7995 */
7996 XSetCursorState(display,windows,MagickTrue);
7997 XCheckRefreshWindows(display,windows);
cristy151b66d2015-04-15 10:50:31 +00007998 (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
cristy051718b2011-08-28 22:49:25 +00007999 affinity_image=ReadImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00008000 if (affinity_image != (Image *) NULL)
8001 {
cristy018f07f2011-09-04 21:15:19 +00008002 (void) RemapImage(&quantize_info,*image,affinity_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008003 affinity_image=DestroyImage(affinity_image);
8004 }
cristy051718b2011-08-28 22:49:25 +00008005 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008006 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07008007 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008008 break;
cristy6710d842011-10-20 23:23:00 +00008009 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008010 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008011 break;
8012 }
8013 case QuantizeCommand:
8014 {
8015 int
8016 status;
8017
8018 static char
cristy151b66d2015-04-15 10:50:31 +00008019 colors[MagickPathExtent] = "256";
cristy3ed852e2009-09-05 21:47:34 +00008020
8021 /*
8022 Query user for maximum number of colors.
8023 */
8024 status=XDialogWidget(display,windows,"Quantize",
8025 "Maximum number of colors:",colors);
8026 if (*colors == '\0')
8027 break;
8028 /*
8029 Color reduce the image.
8030 */
8031 XSetCursorState(display,windows,MagickTrue);
8032 XCheckRefreshWindows(display,windows);
cristye27293e2009-12-18 02:53:20 +00008033 quantize_info.number_colors=StringToUnsignedLong(colors);
cristycbda6112012-05-27 20:57:16 +00008034 quantize_info.dither_method=status != 0 ? RiemersmaDitherMethod :
8035 NoDitherMethod;
cristy018f07f2011-09-04 21:15:19 +00008036 (void) QuantizeImage(&quantize_info,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008037 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07008038 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008039 break;
cristy6710d842011-10-20 23:23:00 +00008040 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008041 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008042 break;
8043 }
8044 case DespeckleCommand:
8045 {
8046 Image
8047 *despeckle_image;
8048
8049 /*
8050 Despeckle image.
8051 */
8052 XSetCursorState(display,windows,MagickTrue);
8053 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +00008054 despeckle_image=DespeckleImage(*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008055 if (despeckle_image != (Image *) NULL)
8056 {
8057 *image=DestroyImage(*image);
8058 *image=despeckle_image;
8059 }
cristy051718b2011-08-28 22:49:25 +00008060 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008061 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07008062 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008063 break;
cristy6710d842011-10-20 23:23:00 +00008064 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008065 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008066 break;
8067 }
8068 case EmbossCommand:
8069 {
8070 Image
8071 *emboss_image;
8072
8073 static char
cristy151b66d2015-04-15 10:50:31 +00008074 radius[MagickPathExtent] = "0.0x1.0";
cristy3ed852e2009-09-05 21:47:34 +00008075
8076 /*
8077 Query user for emboss radius.
8078 */
8079 (void) XDialogWidget(display,windows,"Emboss",
8080 "Enter the emboss radius and standard deviation:",radius);
8081 if (*radius == '\0')
8082 break;
8083 /*
8084 Reduce noise in the image.
8085 */
8086 XSetCursorState(display,windows,MagickTrue);
8087 XCheckRefreshWindows(display,windows);
8088 flags=ParseGeometry(radius,&geometry_info);
8089 if ((flags & SigmaValue) == 0)
8090 geometry_info.sigma=1.0;
8091 emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
cristy051718b2011-08-28 22:49:25 +00008092 exception);
cristy3ed852e2009-09-05 21:47:34 +00008093 if (emboss_image != (Image *) NULL)
8094 {
8095 *image=DestroyImage(*image);
8096 *image=emboss_image;
8097 }
cristy051718b2011-08-28 22:49:25 +00008098 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008099 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07008100 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008101 break;
cristy6710d842011-10-20 23:23:00 +00008102 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008103 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008104 break;
8105 }
8106 case ReduceNoiseCommand:
8107 {
8108 Image
8109 *noise_image;
8110
8111 static char
cristy151b66d2015-04-15 10:50:31 +00008112 radius[MagickPathExtent] = "0";
cristy3ed852e2009-09-05 21:47:34 +00008113
8114 /*
8115 Query user for noise radius.
8116 */
8117 (void) XDialogWidget(display,windows,"Reduce Noise",
8118 "Enter the noise radius:",radius);
8119 if (*radius == '\0')
8120 break;
8121 /*
8122 Reduce noise in the image.
8123 */
8124 XSetCursorState(display,windows,MagickTrue);
8125 XCheckRefreshWindows(display,windows);
8126 flags=ParseGeometry(radius,&geometry_info);
cristy95c38342011-03-18 22:39:51 +00008127 noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
cristy051718b2011-08-28 22:49:25 +00008128 geometry_info.rho,(size_t) geometry_info.rho,exception);
cristy3ed852e2009-09-05 21:47:34 +00008129 if (noise_image != (Image *) NULL)
8130 {
8131 *image=DestroyImage(*image);
8132 *image=noise_image;
8133 }
cristy051718b2011-08-28 22:49:25 +00008134 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008135 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07008136 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008137 break;
cristy6710d842011-10-20 23:23:00 +00008138 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008139 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008140 break;
8141 }
8142 case AddNoiseCommand:
8143 {
8144 char
8145 **noises;
8146
8147 Image
8148 *noise_image;
8149
8150 static char
cristy151b66d2015-04-15 10:50:31 +00008151 noise_type[MagickPathExtent] = "Gaussian";
cristy3ed852e2009-09-05 21:47:34 +00008152
8153 /*
8154 Add noise to the image.
8155 */
cristy042ee782011-04-22 18:48:30 +00008156 noises=GetCommandOptions(MagickNoiseOptions);
cristy3ed852e2009-09-05 21:47:34 +00008157 if (noises == (char **) NULL)
8158 break;
8159 XListBrowserWidget(display,windows,&windows->widget,
8160 (const char **) noises,"Add Noise",
8161 "Select a type of noise to add to your image:",noise_type);
8162 noises=DestroyStringList(noises);
8163 if (*noise_type == '\0')
8164 break;
8165 XSetCursorState(display,windows,MagickTrue);
8166 XCheckRefreshWindows(display,windows);
cristy042ee782011-04-22 18:48:30 +00008167 noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
cristy9ed1f812011-10-08 02:00:08 +00008168 MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
cristy3ed852e2009-09-05 21:47:34 +00008169 if (noise_image != (Image *) NULL)
8170 {
8171 *image=DestroyImage(*image);
8172 *image=noise_image;
8173 }
cristy051718b2011-08-28 22:49:25 +00008174 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008175 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07008176 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008177 break;
cristy6710d842011-10-20 23:23:00 +00008178 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008179 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008180 break;
8181 }
8182 case SharpenCommand:
8183 {
8184 Image
8185 *sharp_image;
8186
8187 static char
cristy151b66d2015-04-15 10:50:31 +00008188 radius[MagickPathExtent] = "0.0x1.0";
cristy3ed852e2009-09-05 21:47:34 +00008189
8190 /*
8191 Query user for sharpen radius.
8192 */
8193 (void) XDialogWidget(display,windows,"Sharpen",
8194 "Enter the sharpen radius and standard deviation:",radius);
8195 if (*radius == '\0')
8196 break;
8197 /*
8198 Sharpen image scanlines.
8199 */
8200 XSetCursorState(display,windows,MagickTrue);
8201 XCheckRefreshWindows(display,windows);
8202 flags=ParseGeometry(radius,&geometry_info);
8203 sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
cristyaa2c16c2012-03-25 22:21:35 +00008204 exception);
cristy3ed852e2009-09-05 21:47:34 +00008205 if (sharp_image != (Image *) NULL)
8206 {
8207 *image=DestroyImage(*image);
8208 *image=sharp_image;
8209 }
cristy051718b2011-08-28 22:49:25 +00008210 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008211 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07008212 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008213 break;
cristy6710d842011-10-20 23:23:00 +00008214 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008215 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008216 break;
8217 }
8218 case BlurCommand:
8219 {
8220 Image
8221 *blur_image;
8222
8223 static char
cristy151b66d2015-04-15 10:50:31 +00008224 radius[MagickPathExtent] = "0.0x1.0";
cristy3ed852e2009-09-05 21:47:34 +00008225
8226 /*
8227 Query user for blur radius.
8228 */
8229 (void) XDialogWidget(display,windows,"Blur",
8230 "Enter the blur radius and standard deviation:",radius);
8231 if (*radius == '\0')
8232 break;
8233 /*
8234 Blur an image.
8235 */
8236 XSetCursorState(display,windows,MagickTrue);
8237 XCheckRefreshWindows(display,windows);
8238 flags=ParseGeometry(radius,&geometry_info);
8239 blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
cristyaa2c16c2012-03-25 22:21:35 +00008240 exception);
cristy3ed852e2009-09-05 21:47:34 +00008241 if (blur_image != (Image *) NULL)
8242 {
8243 *image=DestroyImage(*image);
8244 *image=blur_image;
8245 }
cristy051718b2011-08-28 22:49:25 +00008246 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008247 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07008248 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008249 break;
cristy6710d842011-10-20 23:23:00 +00008250 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008251 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008252 break;
8253 }
8254 case ThresholdCommand:
8255 {
8256 double
8257 threshold;
8258
8259 static char
cristy151b66d2015-04-15 10:50:31 +00008260 factor[MagickPathExtent] = "128";
cristy3ed852e2009-09-05 21:47:34 +00008261
8262 /*
8263 Query user for threshold value.
8264 */
8265 (void) XDialogWidget(display,windows,"Threshold",
8266 "Enter threshold value:",factor);
8267 if (*factor == '\0')
8268 break;
8269 /*
8270 Gamma correct image.
8271 */
8272 XSetCursorState(display,windows,MagickTrue);
8273 XCheckRefreshWindows(display,windows);
cristy9b34e302011-11-05 02:15:45 +00008274 threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
cristye941a752011-10-15 01:52:48 +00008275 (void) BilevelImage(*image,threshold,exception);
cristy3ed852e2009-09-05 21:47:34 +00008276 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07008277 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008278 break;
cristy6710d842011-10-20 23:23:00 +00008279 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008280 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008281 break;
8282 }
8283 case EdgeDetectCommand:
8284 {
8285 Image
8286 *edge_image;
8287
8288 static char
cristy151b66d2015-04-15 10:50:31 +00008289 radius[MagickPathExtent] = "0";
cristy3ed852e2009-09-05 21:47:34 +00008290
8291 /*
8292 Query user for edge factor.
8293 */
8294 (void) XDialogWidget(display,windows,"Detect Edges",
8295 "Enter the edge detect radius:",radius);
8296 if (*radius == '\0')
8297 break;
8298 /*
8299 Detect edge in image.
8300 */
8301 XSetCursorState(display,windows,MagickTrue);
8302 XCheckRefreshWindows(display,windows);
8303 flags=ParseGeometry(radius,&geometry_info);
cristy9dc4c512013-03-24 01:38:00 +00008304 edge_image=EdgeImage(*image,geometry_info.rho,exception);
cristy3ed852e2009-09-05 21:47:34 +00008305 if (edge_image != (Image *) NULL)
8306 {
8307 *image=DestroyImage(*image);
8308 *image=edge_image;
8309 }
cristy051718b2011-08-28 22:49:25 +00008310 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008311 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07008312 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008313 break;
cristy6710d842011-10-20 23:23:00 +00008314 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008315 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008316 break;
8317 }
8318 case SpreadCommand:
8319 {
8320 Image
8321 *spread_image;
8322
8323 static char
cristy151b66d2015-04-15 10:50:31 +00008324 amount[MagickPathExtent] = "2";
cristy3ed852e2009-09-05 21:47:34 +00008325
8326 /*
8327 Query user for spread amount.
8328 */
8329 (void) XDialogWidget(display,windows,"Spread",
8330 "Enter the displacement amount:",amount);
8331 if (*amount == '\0')
8332 break;
8333 /*
8334 Displace image pixels by a random amount.
8335 */
8336 XSetCursorState(display,windows,MagickTrue);
8337 XCheckRefreshWindows(display,windows);
8338 flags=ParseGeometry(amount,&geometry_info);
cristy9dc4c512013-03-24 01:38:00 +00008339 spread_image=EdgeImage(*image,geometry_info.rho,exception);
cristy3ed852e2009-09-05 21:47:34 +00008340 if (spread_image != (Image *) NULL)
8341 {
8342 *image=DestroyImage(*image);
8343 *image=spread_image;
8344 }
cristy051718b2011-08-28 22:49:25 +00008345 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008346 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07008347 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008348 break;
cristy6710d842011-10-20 23:23:00 +00008349 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008350 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008351 break;
8352 }
8353 case ShadeCommand:
8354 {
8355 Image
8356 *shade_image;
8357
8358 int
8359 status;
8360
8361 static char
cristy151b66d2015-04-15 10:50:31 +00008362 geometry[MagickPathExtent] = "30x30";
cristy3ed852e2009-09-05 21:47:34 +00008363
8364 /*
8365 Query user for the shade geometry.
8366 */
8367 status=XDialogWidget(display,windows,"Shade",
8368 "Enter the azimuth and elevation of the light source:",geometry);
8369 if (*geometry == '\0')
8370 break;
8371 /*
8372 Shade image pixels.
8373 */
8374 XSetCursorState(display,windows,MagickTrue);
8375 XCheckRefreshWindows(display,windows);
8376 flags=ParseGeometry(geometry,&geometry_info);
8377 if ((flags & SigmaValue) == 0)
8378 geometry_info.sigma=1.0;
dirkb9dbc292015-07-26 09:50:00 +00008379 shade_image=ShadeImage(*image,status != 0 ? MagickTrue : MagickFalse,
cristy051718b2011-08-28 22:49:25 +00008380 geometry_info.rho,geometry_info.sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +00008381 if (shade_image != (Image *) NULL)
8382 {
8383 *image=DestroyImage(*image);
8384 *image=shade_image;
8385 }
cristy051718b2011-08-28 22:49:25 +00008386 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008387 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07008388 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008389 break;
cristy6710d842011-10-20 23:23:00 +00008390 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008391 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008392 break;
8393 }
8394 case RaiseCommand:
8395 {
8396 static char
cristy151b66d2015-04-15 10:50:31 +00008397 bevel_width[MagickPathExtent] = "10";
cristy3ed852e2009-09-05 21:47:34 +00008398
8399 /*
8400 Query user for bevel width.
8401 */
8402 (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8403 if (*bevel_width == '\0')
8404 break;
8405 /*
8406 Raise an image.
8407 */
cristy051718b2011-08-28 22:49:25 +00008408 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8409 exception);
cristy3ed852e2009-09-05 21:47:34 +00008410 XSetCursorState(display,windows,MagickTrue);
8411 XCheckRefreshWindows(display,windows);
8412 (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
cristy051718b2011-08-28 22:49:25 +00008413 exception);
8414 (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00008415 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07008416 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008417 break;
cristy6710d842011-10-20 23:23:00 +00008418 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008419 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008420 break;
8421 }
8422 case SegmentCommand:
8423 {
8424 static char
cristy151b66d2015-04-15 10:50:31 +00008425 threshold[MagickPathExtent] = "1.0x1.5";
cristy3ed852e2009-09-05 21:47:34 +00008426
8427 /*
8428 Query user for smoothing threshold.
8429 */
8430 (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8431 threshold);
8432 if (*threshold == '\0')
8433 break;
8434 /*
8435 Segment an image.
8436 */
8437 XSetCursorState(display,windows,MagickTrue);
8438 XCheckRefreshWindows(display,windows);
8439 flags=ParseGeometry(threshold,&geometry_info);
8440 if ((flags & SigmaValue) == 0)
8441 geometry_info.sigma=1.0;
cristyd3d9c5d2012-02-15 22:58:57 +00008442 (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
cristy018f07f2011-09-04 21:15:19 +00008443 geometry_info.sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +00008444 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07008445 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008446 break;
cristy6710d842011-10-20 23:23:00 +00008447 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008448 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008449 break;
8450 }
8451 case SepiaToneCommand:
8452 {
8453 double
8454 threshold;
8455
8456 Image
8457 *sepia_image;
8458
8459 static char
cristy151b66d2015-04-15 10:50:31 +00008460 factor[MagickPathExtent] = "80%";
cristy3ed852e2009-09-05 21:47:34 +00008461
8462 /*
8463 Query user for sepia-tone factor.
8464 */
8465 (void) XDialogWidget(display,windows,"Sepia Tone",
8466 "Enter the sepia tone factor (0 - 99.9%):",factor);
8467 if (*factor == '\0')
8468 break;
8469 /*
8470 Sepia tone image pixels.
8471 */
8472 XSetCursorState(display,windows,MagickTrue);
8473 XCheckRefreshWindows(display,windows);
cristy9b34e302011-11-05 02:15:45 +00008474 threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
cristy051718b2011-08-28 22:49:25 +00008475 sepia_image=SepiaToneImage(*image,threshold,exception);
cristy3ed852e2009-09-05 21:47:34 +00008476 if (sepia_image != (Image *) NULL)
8477 {
8478 *image=DestroyImage(*image);
8479 *image=sepia_image;
8480 }
cristy051718b2011-08-28 22:49:25 +00008481 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008482 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07008483 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008484 break;
cristy6710d842011-10-20 23:23:00 +00008485 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008486 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008487 break;
8488 }
8489 case SolarizeCommand:
8490 {
8491 double
8492 threshold;
8493
8494 static char
cristy151b66d2015-04-15 10:50:31 +00008495 factor[MagickPathExtent] = "60%";
cristy3ed852e2009-09-05 21:47:34 +00008496
8497 /*
8498 Query user for solarize factor.
8499 */
8500 (void) XDialogWidget(display,windows,"Solarize",
8501 "Enter the solarize factor (0 - 99.9%):",factor);
8502 if (*factor == '\0')
8503 break;
8504 /*
8505 Solarize image pixels.
8506 */
8507 XSetCursorState(display,windows,MagickTrue);
8508 XCheckRefreshWindows(display,windows);
cristy9b34e302011-11-05 02:15:45 +00008509 threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
cristy5cbc0162011-08-29 00:36:28 +00008510 (void) SolarizeImage(*image,threshold,exception);
cristy3ed852e2009-09-05 21:47:34 +00008511 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07008512 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008513 break;
cristy6710d842011-10-20 23:23:00 +00008514 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008515 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008516 break;
8517 }
8518 case SwirlCommand:
8519 {
8520 Image
8521 *swirl_image;
8522
8523 static char
cristy151b66d2015-04-15 10:50:31 +00008524 degrees[MagickPathExtent] = "60";
cristy3ed852e2009-09-05 21:47:34 +00008525
8526 /*
8527 Query user for swirl angle.
8528 */
8529 (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8530 degrees);
8531 if (*degrees == '\0')
8532 break;
8533 /*
8534 Swirl image pixels about the center.
8535 */
8536 XSetCursorState(display,windows,MagickTrue);
8537 XCheckRefreshWindows(display,windows);
8538 flags=ParseGeometry(degrees,&geometry_info);
cristy76f512e2011-09-12 01:26:56 +00008539 swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8540 exception);
cristy3ed852e2009-09-05 21:47:34 +00008541 if (swirl_image != (Image *) NULL)
8542 {
8543 *image=DestroyImage(*image);
8544 *image=swirl_image;
8545 }
cristy051718b2011-08-28 22:49:25 +00008546 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008547 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07008548 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008549 break;
cristy6710d842011-10-20 23:23:00 +00008550 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008551 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008552 break;
8553 }
8554 case ImplodeCommand:
8555 {
8556 Image
8557 *implode_image;
8558
8559 static char
cristy151b66d2015-04-15 10:50:31 +00008560 factor[MagickPathExtent] = "0.3";
cristy3ed852e2009-09-05 21:47:34 +00008561
8562 /*
8563 Query user for implode factor.
8564 */
8565 (void) XDialogWidget(display,windows,"Implode",
8566 "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8567 if (*factor == '\0')
8568 break;
8569 /*
8570 Implode image pixels about the center.
8571 */
8572 XSetCursorState(display,windows,MagickTrue);
8573 XCheckRefreshWindows(display,windows);
8574 flags=ParseGeometry(factor,&geometry_info);
cristy76f512e2011-09-12 01:26:56 +00008575 implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8576 exception);
cristy3ed852e2009-09-05 21:47:34 +00008577 if (implode_image != (Image *) NULL)
8578 {
8579 *image=DestroyImage(*image);
8580 *image=implode_image;
8581 }
cristy051718b2011-08-28 22:49:25 +00008582 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008583 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07008584 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008585 break;
cristy6710d842011-10-20 23:23:00 +00008586 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008587 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008588 break;
8589 }
8590 case VignetteCommand:
8591 {
8592 Image
8593 *vignette_image;
8594
8595 static char
cristy151b66d2015-04-15 10:50:31 +00008596 geometry[MagickPathExtent] = "0x20";
cristy3ed852e2009-09-05 21:47:34 +00008597
8598 /*
8599 Query user for the vignette geometry.
8600 */
8601 (void) XDialogWidget(display,windows,"Vignette",
8602 "Enter the radius, sigma, and x and y offsets:",geometry);
8603 if (*geometry == '\0')
8604 break;
8605 /*
8606 Soften the edges of the image in vignette style
8607 */
8608 XSetCursorState(display,windows,MagickTrue);
8609 XCheckRefreshWindows(display,windows);
8610 flags=ParseGeometry(geometry,&geometry_info);
8611 if ((flags & SigmaValue) == 0)
8612 geometry_info.sigma=1.0;
8613 if ((flags & XiValue) == 0)
8614 geometry_info.xi=0.1*(*image)->columns;
8615 if ((flags & PsiValue) == 0)
8616 geometry_info.psi=0.1*(*image)->rows;
cristyaa2c16c2012-03-25 22:21:35 +00008617 vignette_image=VignetteImage(*image,geometry_info.rho,0.0,(ssize_t)
8618 ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-0.5),
8619 exception);
cristy3ed852e2009-09-05 21:47:34 +00008620 if (vignette_image != (Image *) NULL)
8621 {
8622 *image=DestroyImage(*image);
8623 *image=vignette_image;
8624 }
cristy051718b2011-08-28 22:49:25 +00008625 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008626 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07008627 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008628 break;
cristy6710d842011-10-20 23:23:00 +00008629 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008630 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008631 break;
8632 }
8633 case WaveCommand:
8634 {
8635 Image
8636 *wave_image;
8637
8638 static char
cristy151b66d2015-04-15 10:50:31 +00008639 geometry[MagickPathExtent] = "25x150";
cristy3ed852e2009-09-05 21:47:34 +00008640
8641 /*
8642 Query user for the wave geometry.
8643 */
8644 (void) XDialogWidget(display,windows,"Wave",
8645 "Enter the amplitude and length of the wave:",geometry);
8646 if (*geometry == '\0')
8647 break;
8648 /*
cristycee97112010-05-28 00:44:52 +00008649 Alter an image along a sine wave.
cristy3ed852e2009-09-05 21:47:34 +00008650 */
8651 XSetCursorState(display,windows,MagickTrue);
8652 XCheckRefreshWindows(display,windows);
8653 flags=ParseGeometry(geometry,&geometry_info);
8654 if ((flags & SigmaValue) == 0)
8655 geometry_info.sigma=1.0;
8656 wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
cristy5c4e2582011-09-11 19:21:03 +00008657 (*image)->interpolate,exception);
cristy3ed852e2009-09-05 21:47:34 +00008658 if (wave_image != (Image *) NULL)
8659 {
8660 *image=DestroyImage(*image);
8661 *image=wave_image;
8662 }
cristy051718b2011-08-28 22:49:25 +00008663 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008664 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07008665 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008666 break;
cristy6710d842011-10-20 23:23:00 +00008667 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008668 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008669 break;
8670 }
8671 case OilPaintCommand:
8672 {
8673 Image
8674 *paint_image;
8675
8676 static char
cristy151b66d2015-04-15 10:50:31 +00008677 radius[MagickPathExtent] = "0";
cristy3ed852e2009-09-05 21:47:34 +00008678
8679 /*
8680 Query user for circular neighborhood radius.
8681 */
8682 (void) XDialogWidget(display,windows,"Oil Paint",
8683 "Enter the mask radius:",radius);
8684 if (*radius == '\0')
8685 break;
8686 /*
8687 OilPaint image scanlines.
8688 */
8689 XSetCursorState(display,windows,MagickTrue);
8690 XCheckRefreshWindows(display,windows);
8691 flags=ParseGeometry(radius,&geometry_info);
cristy14973ba2011-08-27 23:48:07 +00008692 paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
cristy051718b2011-08-28 22:49:25 +00008693 exception);
cristy3ed852e2009-09-05 21:47:34 +00008694 if (paint_image != (Image *) NULL)
8695 {
8696 *image=DestroyImage(*image);
8697 *image=paint_image;
8698 }
cristy051718b2011-08-28 22:49:25 +00008699 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008700 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07008701 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008702 break;
cristy6710d842011-10-20 23:23:00 +00008703 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008704 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008705 break;
8706 }
8707 case CharcoalDrawCommand:
8708 {
8709 Image
8710 *charcoal_image;
8711
8712 static char
cristy151b66d2015-04-15 10:50:31 +00008713 radius[MagickPathExtent] = "0x1";
cristy3ed852e2009-09-05 21:47:34 +00008714
8715 /*
8716 Query user for charcoal radius.
8717 */
8718 (void) XDialogWidget(display,windows,"Charcoal Draw",
8719 "Enter the charcoal radius and sigma:",radius);
8720 if (*radius == '\0')
8721 break;
8722 /*
8723 Charcoal the image.
8724 */
cristy051718b2011-08-28 22:49:25 +00008725 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8726 exception);
cristy3ed852e2009-09-05 21:47:34 +00008727 XSetCursorState(display,windows,MagickTrue);
8728 XCheckRefreshWindows(display,windows);
8729 flags=ParseGeometry(radius,&geometry_info);
8730 if ((flags & SigmaValue) == 0)
8731 geometry_info.sigma=geometry_info.rho;
8732 charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
cristyaa2c16c2012-03-25 22:21:35 +00008733 exception);
cristy3ed852e2009-09-05 21:47:34 +00008734 if (charcoal_image != (Image *) NULL)
8735 {
8736 *image=DestroyImage(*image);
8737 *image=charcoal_image;
8738 }
cristy051718b2011-08-28 22:49:25 +00008739 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008740 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07008741 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008742 break;
cristy6710d842011-10-20 23:23:00 +00008743 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008744 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008745 break;
8746 }
8747 case AnnotateCommand:
8748 {
8749 /*
8750 Annotate the image with text.
8751 */
cristy051718b2011-08-28 22:49:25 +00008752 status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
dirk5d0a4412016-01-05 22:28:51 +01008753 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008754 {
8755 XNoticeWidget(display,windows,"Unable to annotate X image",
8756 (*image)->filename);
8757 break;
8758 }
8759 break;
8760 }
8761 case DrawCommand:
8762 {
8763 /*
8764 Draw image.
8765 */
cristy051718b2011-08-28 22:49:25 +00008766 status=XDrawEditImage(display,resource_info,windows,image,exception);
dirk5d0a4412016-01-05 22:28:51 +01008767 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008768 {
8769 XNoticeWidget(display,windows,"Unable to draw on the X image",
8770 (*image)->filename);
8771 break;
8772 }
8773 break;
8774 }
8775 case ColorCommand:
8776 {
8777 /*
8778 Color edit.
8779 */
cristy051718b2011-08-28 22:49:25 +00008780 status=XColorEditImage(display,resource_info,windows,image,exception);
dirk5d0a4412016-01-05 22:28:51 +01008781 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008782 {
8783 XNoticeWidget(display,windows,"Unable to pixel edit X image",
8784 (*image)->filename);
8785 break;
8786 }
8787 break;
8788 }
8789 case MatteCommand:
8790 {
8791 /*
8792 Matte edit.
8793 */
cristy051718b2011-08-28 22:49:25 +00008794 status=XMatteEditImage(display,resource_info,windows,image,exception);
dirk5d0a4412016-01-05 22:28:51 +01008795 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008796 {
8797 XNoticeWidget(display,windows,"Unable to matte edit X image",
8798 (*image)->filename);
8799 break;
8800 }
8801 break;
8802 }
8803 case CompositeCommand:
8804 {
8805 /*
8806 Composite image.
8807 */
cristy051718b2011-08-28 22:49:25 +00008808 status=XCompositeImage(display,resource_info,windows,*image,
8809 exception);
dirk5d0a4412016-01-05 22:28:51 +01008810 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008811 {
8812 XNoticeWidget(display,windows,"Unable to composite X image",
8813 (*image)->filename);
8814 break;
8815 }
8816 break;
8817 }
8818 case AddBorderCommand:
8819 {
8820 Image
8821 *border_image;
8822
8823 static char
cristy151b66d2015-04-15 10:50:31 +00008824 geometry[MagickPathExtent] = "6x6";
cristy3ed852e2009-09-05 21:47:34 +00008825
8826 /*
8827 Query user for border color and geometry.
8828 */
8829 XColorBrowserWidget(display,windows,"Select",color);
8830 if (*color == '\0')
8831 break;
8832 (void) XDialogWidget(display,windows,"Add Border",
8833 "Enter border geometry:",geometry);
8834 if (*geometry == '\0')
8835 break;
8836 /*
8837 Add a border to the image.
8838 */
cristy051718b2011-08-28 22:49:25 +00008839 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8840 exception);
cristy3ed852e2009-09-05 21:47:34 +00008841 XSetCursorState(display,windows,MagickTrue);
8842 XCheckRefreshWindows(display,windows);
cristy9950d572011-10-01 18:22:35 +00008843 (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
cristy051718b2011-08-28 22:49:25 +00008844 exception);
cristy3ed852e2009-09-05 21:47:34 +00008845 (void) ParsePageGeometry(*image,geometry,&page_geometry,
cristy051718b2011-08-28 22:49:25 +00008846 exception);
cristy633f0c62011-09-15 13:27:36 +00008847 border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8848 exception);
cristy3ed852e2009-09-05 21:47:34 +00008849 if (border_image != (Image *) NULL)
8850 {
8851 *image=DestroyImage(*image);
8852 *image=border_image;
8853 }
cristy051718b2011-08-28 22:49:25 +00008854 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008855 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07008856 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008857 break;
8858 windows->image.window_changes.width=(int) (*image)->columns;
8859 windows->image.window_changes.height=(int) (*image)->rows;
cristy6710d842011-10-20 23:23:00 +00008860 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008861 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008862 break;
8863 }
8864 case AddFrameCommand:
8865 {
8866 FrameInfo
8867 frame_info;
8868
8869 Image
8870 *frame_image;
8871
8872 static char
cristy151b66d2015-04-15 10:50:31 +00008873 geometry[MagickPathExtent] = "6x6";
cristy3ed852e2009-09-05 21:47:34 +00008874
8875 /*
8876 Query user for frame color and geometry.
8877 */
8878 XColorBrowserWidget(display,windows,"Select",color);
8879 if (*color == '\0')
8880 break;
8881 (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8882 geometry);
8883 if (*geometry == '\0')
8884 break;
8885 /*
8886 Surround image with an ornamental border.
8887 */
cristy051718b2011-08-28 22:49:25 +00008888 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8889 exception);
cristy3ed852e2009-09-05 21:47:34 +00008890 XSetCursorState(display,windows,MagickTrue);
8891 XCheckRefreshWindows(display,windows);
Cristy18b27502017-02-16 07:29:19 -05008892 (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
cristy051718b2011-08-28 22:49:25 +00008893 exception);
cristy3ed852e2009-09-05 21:47:34 +00008894 (void) ParsePageGeometry(*image,geometry,&page_geometry,
cristy051718b2011-08-28 22:49:25 +00008895 exception);
cristy3ed852e2009-09-05 21:47:34 +00008896 frame_info.width=page_geometry.width;
8897 frame_info.height=page_geometry.height;
8898 frame_info.outer_bevel=page_geometry.x;
8899 frame_info.inner_bevel=page_geometry.y;
cristybb503372010-05-27 20:51:26 +00008900 frame_info.x=(ssize_t) frame_info.width;
8901 frame_info.y=(ssize_t) frame_info.height;
cristy3ed852e2009-09-05 21:47:34 +00008902 frame_info.width=(*image)->columns+2*frame_info.width;
8903 frame_info.height=(*image)->rows+2*frame_info.height;
cristy633f0c62011-09-15 13:27:36 +00008904 frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
cristy3ed852e2009-09-05 21:47:34 +00008905 if (frame_image != (Image *) NULL)
8906 {
8907 *image=DestroyImage(*image);
8908 *image=frame_image;
8909 }
cristy051718b2011-08-28 22:49:25 +00008910 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008911 XSetCursorState(display,windows,MagickFalse);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07008912 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008913 break;
8914 windows->image.window_changes.width=(int) (*image)->columns;
8915 windows->image.window_changes.height=(int) (*image)->rows;
cristy6710d842011-10-20 23:23:00 +00008916 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00008917 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00008918 break;
8919 }
8920 case CommentCommand:
8921 {
8922 const char
8923 *value;
8924
8925 FILE
8926 *file;
8927
8928 int
8929 unique_file;
8930
8931 /*
8932 Edit image comment.
8933 */
8934 unique_file=AcquireUniqueFileResource(image_info->filename);
8935 if (unique_file == -1)
8936 XNoticeWidget(display,windows,"Unable to edit image comment",
8937 image_info->filename);
cristyd15e6592011-10-15 00:13:06 +00008938 value=GetImageProperty(*image,"comment",exception);
cristy3ed852e2009-09-05 21:47:34 +00008939 if (value == (char *) NULL)
8940 unique_file=close(unique_file)-1;
8941 else
8942 {
Cristyf2dc1dd2020-12-28 13:59:26 -05008943 const char
cristy3ed852e2009-09-05 21:47:34 +00008944 *p;
8945
8946 file=fdopen(unique_file,"w");
8947 if (file == (FILE *) NULL)
8948 {
8949 XNoticeWidget(display,windows,"Unable to edit image comment",
8950 image_info->filename);
8951 break;
8952 }
8953 for (p=value; *p != '\0'; p++)
8954 (void) fputc((int) *p,file);
8955 (void) fputc('\n',file);
8956 (void) fclose(file);
8957 }
8958 XSetCursorState(display,windows,MagickTrue);
8959 XCheckRefreshWindows(display,windows);
8960 status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
cristy051718b2011-08-28 22:49:25 +00008961 exception);
dirk5d0a4412016-01-05 22:28:51 +01008962 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008963 XNoticeWidget(display,windows,"Unable to edit image comment",
8964 (char *) NULL);
8965 else
8966 {
8967 char
8968 *comment;
8969
cristy051718b2011-08-28 22:49:25 +00008970 comment=FileToString(image_info->filename,~0UL,exception);
cristy3ed852e2009-09-05 21:47:34 +00008971 if (comment != (char *) NULL)
8972 {
cristyd15e6592011-10-15 00:13:06 +00008973 (void) SetImageProperty(*image,"comment",comment,exception);
cristy3ed852e2009-09-05 21:47:34 +00008974 (*image)->taint=MagickTrue;
8975 }
8976 }
8977 (void) RelinquishUniqueFileResource(image_info->filename);
8978 XSetCursorState(display,windows,MagickFalse);
8979 break;
8980 }
8981 case LaunchCommand:
8982 {
8983 /*
8984 Launch program.
8985 */
8986 XSetCursorState(display,windows,MagickTrue);
8987 XCheckRefreshWindows(display,windows);
8988 (void) AcquireUniqueFilename(filename);
cristy151b66d2015-04-15 10:50:31 +00008989 (void) FormatLocaleString((*image)->filename,MagickPathExtent,"launch:%s",
cristy3ed852e2009-09-05 21:47:34 +00008990 filename);
cristy051718b2011-08-28 22:49:25 +00008991 status=WriteImage(image_info,*image,exception);
dirk5d0a4412016-01-05 22:28:51 +01008992 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008993 XNoticeWidget(display,windows,"Unable to launch image editor",
8994 (char *) NULL);
8995 else
8996 {
cristy051718b2011-08-28 22:49:25 +00008997 nexus=ReadImage(resource_info->image_info,exception);
8998 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00008999 XClientMessage(display,windows->image.id,windows->im_protocols,
9000 windows->im_next_image,CurrentTime);
9001 }
9002 (void) RelinquishUniqueFileResource(filename);
9003 XSetCursorState(display,windows,MagickFalse);
9004 break;
9005 }
9006 case RegionofInterestCommand:
9007 {
9008 /*
9009 Apply an image processing technique to a region of interest.
9010 */
cristy051718b2011-08-28 22:49:25 +00009011 (void) XROIImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009012 break;
9013 }
9014 case InfoCommand:
9015 break;
9016 case ZoomCommand:
9017 {
9018 /*
9019 Zoom image.
9020 */
Elliott Hughes5d41fba2021-04-12 16:36:42 -07009021 if (windows->magnify.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009022 (void) XRaiseWindow(display,windows->magnify.id);
9023 else
9024 {
9025 /*
9026 Make magnify image.
9027 */
9028 XSetCursorState(display,windows,MagickTrue);
9029 (void) XMapRaised(display,windows->magnify.id);
9030 XSetCursorState(display,windows,MagickFalse);
9031 }
9032 break;
9033 }
9034 case ShowPreviewCommand:
9035 {
9036 char
Cristy8a36f4e2020-03-23 20:10:18 -04009037 **previews;
cristy3ed852e2009-09-05 21:47:34 +00009038
9039 Image
9040 *preview_image;
9041
dirkfe3754c2016-03-26 17:33:09 +01009042 PreviewType
9043 preview;
9044
cristy3ed852e2009-09-05 21:47:34 +00009045 static char
cristy151b66d2015-04-15 10:50:31 +00009046 preview_type[MagickPathExtent] = "Gamma";
cristy3ed852e2009-09-05 21:47:34 +00009047
9048 /*
9049 Select preview type from menu.
9050 */
cristy042ee782011-04-22 18:48:30 +00009051 previews=GetCommandOptions(MagickPreviewOptions);
cristy3ed852e2009-09-05 21:47:34 +00009052 if (previews == (char **) NULL)
9053 break;
9054 XListBrowserWidget(display,windows,&windows->widget,
9055 (const char **) previews,"Preview",
9056 "Select an enhancement, effect, or F/X:",preview_type);
9057 previews=DestroyStringList(previews);
9058 if (*preview_type == '\0')
9059 break;
9060 /*
9061 Show image preview.
9062 */
9063 XSetCursorState(display,windows,MagickTrue);
9064 XCheckRefreshWindows(display,windows);
dirkfe3754c2016-03-26 17:33:09 +01009065 preview=(PreviewType) ParseCommandOption(MagickPreviewOptions,
9066 MagickFalse,preview_type);
Cristy8a36f4e2020-03-23 20:10:18 -04009067 (void) FormatImageProperty(*image,"group","%.20g",(double)
dirkb83e0e32016-03-26 12:39:40 +01009068 windows->image.id);
cristy3ed852e2009-09-05 21:47:34 +00009069 (void) DeleteImageProperty(*image,"label");
cristyd15e6592011-10-15 00:13:06 +00009070 (void) SetImageProperty(*image,"label","Preview",exception);
dirkddc3a972016-03-26 17:39:47 +01009071 preview_image=PreviewImage(*image,preview,exception);
cristy3ed852e2009-09-05 21:47:34 +00009072 if (preview_image == (Image *) NULL)
9073 break;
dirkfe3754c2016-03-26 17:33:09 +01009074 (void) AcquireUniqueFilename(filename);
9075 (void) FormatLocaleString(preview_image->filename,MagickPathExtent,
9076 "show:%s",filename);
cristy051718b2011-08-28 22:49:25 +00009077 status=WriteImage(image_info,preview_image,exception);
dirkfe3754c2016-03-26 17:33:09 +01009078 (void) RelinquishUniqueFileResource(filename);
cristy3ed852e2009-09-05 21:47:34 +00009079 preview_image=DestroyImage(preview_image);
dirk5d0a4412016-01-05 22:28:51 +01009080 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009081 XNoticeWidget(display,windows,"Unable to show image preview",
9082 (*image)->filename);
9083 XDelay(display,1500);
9084 XSetCursorState(display,windows,MagickFalse);
9085 break;
9086 }
9087 case ShowHistogramCommand:
9088 {
9089 Image
9090 *histogram_image;
9091
9092 /*
9093 Show image histogram.
9094 */
9095 XSetCursorState(display,windows,MagickTrue);
9096 XCheckRefreshWindows(display,windows);
cristy3ed852e2009-09-05 21:47:34 +00009097 (void) DeleteImageProperty(*image,"label");
Cristy8a36f4e2020-03-23 20:10:18 -04009098 (void) FormatImageProperty(*image,"group","%.20g",(double)
9099 windows->image.id);
cristyd15e6592011-10-15 00:13:06 +00009100 (void) SetImageProperty(*image,"label","Histogram",exception);
cristy3ed852e2009-09-05 21:47:34 +00009101 (void) AcquireUniqueFilename(filename);
Cristy859511c2018-04-22 14:50:36 -04009102 (void) FormatLocaleString((*image)->filename,MagickPathExtent,
9103 "histogram:%s",filename);
cristy051718b2011-08-28 22:49:25 +00009104 status=WriteImage(image_info,*image,exception);
cristy151b66d2015-04-15 10:50:31 +00009105 (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
cristy051718b2011-08-28 22:49:25 +00009106 histogram_image=ReadImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00009107 (void) RelinquishUniqueFileResource(filename);
9108 if (histogram_image == (Image *) NULL)
9109 break;
cristy151b66d2015-04-15 10:50:31 +00009110 (void) FormatLocaleString(histogram_image->filename,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00009111 "show:%s",filename);
cristy051718b2011-08-28 22:49:25 +00009112 status=WriteImage(image_info,histogram_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009113 histogram_image=DestroyImage(histogram_image);
dirk5d0a4412016-01-05 22:28:51 +01009114 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009115 XNoticeWidget(display,windows,"Unable to show histogram",
9116 (*image)->filename);
9117 XDelay(display,1500);
9118 XSetCursorState(display,windows,MagickFalse);
9119 break;
9120 }
9121 case ShowMatteCommand:
9122 {
9123 Image
9124 *matte_image;
9125
cristy17f11b02014-12-20 19:37:04 +00009126 if ((*image)->alpha_trait == UndefinedPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +00009127 {
9128 XNoticeWidget(display,windows,
9129 "Image does not have any matte information",(*image)->filename);
9130 break;
9131 }
9132 /*
9133 Show image matte.
9134 */
9135 XSetCursorState(display,windows,MagickTrue);
9136 XCheckRefreshWindows(display,windows);
Cristy8a36f4e2020-03-23 20:10:18 -04009137 (void) FormatImageProperty(*image,"group","%.20g",(double)
dirkb83e0e32016-03-26 12:39:40 +01009138 windows->image.id);
cristy3ed852e2009-09-05 21:47:34 +00009139 (void) DeleteImageProperty(*image,"label");
cristyd15e6592011-10-15 00:13:06 +00009140 (void) SetImageProperty(*image,"label","Matte",exception);
cristy3ed852e2009-09-05 21:47:34 +00009141 (void) AcquireUniqueFilename(filename);
cristy151b66d2015-04-15 10:50:31 +00009142 (void) FormatLocaleString((*image)->filename,MagickPathExtent,"matte:%s",
cristy3ed852e2009-09-05 21:47:34 +00009143 filename);
cristy051718b2011-08-28 22:49:25 +00009144 status=WriteImage(image_info,*image,exception);
cristy151b66d2015-04-15 10:50:31 +00009145 (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
cristy051718b2011-08-28 22:49:25 +00009146 matte_image=ReadImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00009147 (void) RelinquishUniqueFileResource(filename);
9148 if (matte_image == (Image *) NULL)
9149 break;
Cristy8a36f4e2020-03-23 20:10:18 -04009150 (void) FormatLocaleString(matte_image->filename,MagickPathExtent,
9151 "show:%s",filename);
cristy051718b2011-08-28 22:49:25 +00009152 status=WriteImage(image_info,matte_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009153 matte_image=DestroyImage(matte_image);
dirk5d0a4412016-01-05 22:28:51 +01009154 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009155 XNoticeWidget(display,windows,"Unable to show matte",
9156 (*image)->filename);
9157 XDelay(display,1500);
9158 XSetCursorState(display,windows,MagickFalse);
9159 break;
9160 }
9161 case BackgroundCommand:
9162 {
9163 /*
9164 Background image.
9165 */
cristy051718b2011-08-28 22:49:25 +00009166 status=XBackgroundImage(display,resource_info,windows,image,exception);
dirk5d0a4412016-01-05 22:28:51 +01009167 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009168 break;
cristy051718b2011-08-28 22:49:25 +00009169 nexus=CloneImage(*image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00009170 if (nexus != (Image *) NULL)
9171 XClientMessage(display,windows->image.id,windows->im_protocols,
9172 windows->im_next_image,CurrentTime);
9173 break;
9174 }
9175 case SlideShowCommand:
9176 {
9177 static char
cristy151b66d2015-04-15 10:50:31 +00009178 delay[MagickPathExtent] = "5";
cristy3ed852e2009-09-05 21:47:34 +00009179
9180 /*
9181 Display next image after pausing.
9182 */
9183 (void) XDialogWidget(display,windows,"Slide Show",
9184 "Pause how many 1/100ths of a second between images:",delay);
9185 if (*delay == '\0')
9186 break;
cristye27293e2009-12-18 02:53:20 +00009187 resource_info->delay=StringToUnsignedLong(delay);
cristy3ed852e2009-09-05 21:47:34 +00009188 XClientMessage(display,windows->image.id,windows->im_protocols,
9189 windows->im_next_image,CurrentTime);
9190 break;
9191 }
9192 case PreferencesCommand:
9193 {
9194 /*
9195 Set user preferences.
9196 */
9197 status=XPreferencesWidget(display,resource_info,windows);
dirk5d0a4412016-01-05 22:28:51 +01009198 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009199 break;
cristy051718b2011-08-28 22:49:25 +00009200 nexus=CloneImage(*image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00009201 if (nexus != (Image *) NULL)
9202 XClientMessage(display,windows->image.id,windows->im_protocols,
9203 windows->im_next_image,CurrentTime);
9204 break;
9205 }
9206 case HelpCommand:
9207 {
9208 /*
9209 User requested help.
9210 */
Cristy6970baa2019-04-20 21:23:54 -04009211 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00009212 "Help Viewer - Display",DisplayHelp);
9213 break;
9214 }
9215 case BrowseDocumentationCommand:
9216 {
9217 Atom
9218 mozilla_atom;
9219
9220 Window
9221 mozilla_window,
9222 root_window;
9223
9224 /*
9225 Browse the ImageMagick documentation.
9226 */
9227 root_window=XRootWindow(display,XDefaultScreen(display));
9228 mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9229 mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9230 if (mozilla_window != (Window) NULL)
9231 {
9232 char
Elliott Hughes5d41fba2021-04-12 16:36:42 -07009233 command[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00009234
9235 /*
9236 Display documentation using Netscape remote control.
9237 */
cristy151b66d2015-04-15 10:50:31 +00009238 (void) FormatLocaleString(command,MagickPathExtent,
Elliott Hughes5d41fba2021-04-12 16:36:42 -07009239 "openurl(%s,new-tab)",MagickAuthoritativeURL);
cristy3ed852e2009-09-05 21:47:34 +00009240 mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9241 (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9242 8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9243 XSetCursorState(display,windows,MagickFalse);
9244 break;
9245 }
9246 XSetCursorState(display,windows,MagickTrue);
9247 XCheckRefreshWindows(display,windows);
9248 status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
cristy051718b2011-08-28 22:49:25 +00009249 exception);
dirk5d0a4412016-01-05 22:28:51 +01009250 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009251 XNoticeWidget(display,windows,"Unable to browse documentation",
9252 (char *) NULL);
9253 XDelay(display,1500);
9254 XSetCursorState(display,windows,MagickFalse);
9255 break;
9256 }
9257 case VersionCommand:
9258 {
cristybb503372010-05-27 20:51:26 +00009259 XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
cristy3ed852e2009-09-05 21:47:34 +00009260 GetMagickCopyright());
9261 break;
9262 }
9263 case SaveToUndoBufferCommand:
9264 break;
9265 default:
9266 {
9267 (void) XBell(display,0);
9268 break;
9269 }
9270 }
9271 image_info=DestroyImageInfo(image_info);
9272 return(nexus);
9273}
9274
9275/*
9276%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9277% %
9278% %
9279% %
9280+ X M a g n i f y I m a g e %
9281% %
9282% %
9283% %
9284%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9285%
9286% XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9287% The magnified portion is displayed in a separate window.
9288%
9289% The format of the XMagnifyImage method is:
9290%
cristy6710d842011-10-20 23:23:00 +00009291% void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9292% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00009293%
9294% A description of each parameter follows:
9295%
9296% o display: Specifies a connection to an X server; returned from
9297% XOpenDisplay.
9298%
9299% o windows: Specifies a pointer to a XWindows structure.
9300%
9301% o event: Specifies a pointer to a XEvent structure. If it is NULL,
9302% the entire image is refreshed.
9303%
cristy6710d842011-10-20 23:23:00 +00009304% o exception: return any errors or warnings in this structure.
9305%
cristy3ed852e2009-09-05 21:47:34 +00009306*/
cristy6710d842011-10-20 23:23:00 +00009307static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9308 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00009309{
9310 char
cristy151b66d2015-04-15 10:50:31 +00009311 text[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00009312
Cristyf2dc1dd2020-12-28 13:59:26 -05009313 int
cristy3ed852e2009-09-05 21:47:34 +00009314 x,
9315 y;
9316
cristybb503372010-05-27 20:51:26 +00009317 size_t
cristy3ed852e2009-09-05 21:47:34 +00009318 state;
9319
9320 /*
9321 Update magnified image until the mouse button is released.
9322 */
9323 (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9324 state=DefaultState;
9325 x=event->xbutton.x;
9326 y=event->xbutton.y;
cristy49e2d862010-11-12 02:50:30 +00009327 windows->magnify.x=(int) windows->image.x+x;
9328 windows->magnify.y=(int) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +00009329 do
9330 {
9331 /*
9332 Map and unmap Info widget as text cursor crosses its boundaries.
9333 */
Elliott Hughes5d41fba2021-04-12 16:36:42 -07009334 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009335 {
9336 if ((x < (int) (windows->info.x+windows->info.width)) &&
9337 (y < (int) (windows->info.y+windows->info.height)))
9338 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9339 }
9340 else
9341 if ((x > (int) (windows->info.x+windows->info.width)) ||
9342 (y > (int) (windows->info.y+windows->info.height)))
9343 (void) XMapWindow(display,windows->info.id);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07009344 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009345 {
9346 /*
9347 Display pointer position.
9348 */
cristy151b66d2015-04-15 10:50:31 +00009349 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
cristy3ed852e2009-09-05 21:47:34 +00009350 windows->magnify.x,windows->magnify.y);
9351 XInfoWidget(display,windows,text);
9352 }
9353 /*
9354 Wait for next event.
9355 */
cristy6710d842011-10-20 23:23:00 +00009356 XScreenEvent(display,windows,event,exception);
cristy3ed852e2009-09-05 21:47:34 +00009357 switch (event->type)
9358 {
9359 case ButtonPress:
9360 break;
9361 case ButtonRelease:
9362 {
9363 /*
9364 User has finished magnifying image.
9365 */
9366 x=event->xbutton.x;
9367 y=event->xbutton.y;
9368 state|=ExitState;
9369 break;
9370 }
9371 case Expose:
9372 break;
9373 case MotionNotify:
9374 {
9375 x=event->xmotion.x;
9376 y=event->xmotion.y;
9377 break;
9378 }
9379 default:
9380 break;
9381 }
9382 /*
9383 Check boundary conditions.
9384 */
9385 if (x < 0)
9386 x=0;
9387 else
9388 if (x >= (int) windows->image.width)
9389 x=(int) windows->image.width-1;
9390 if (y < 0)
9391 y=0;
9392 else
9393 if (y >= (int) windows->image.height)
9394 y=(int) windows->image.height-1;
9395 } while ((state & ExitState) == 0);
9396 /*
9397 Display magnified image.
9398 */
9399 XSetCursorState(display,windows,MagickFalse);
9400}
9401
9402/*
9403%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9404% %
9405% %
9406% %
9407+ X M a g n i f y W i n d o w C o m m a n d %
9408% %
9409% %
9410% %
9411%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9412%
9413% XMagnifyWindowCommand() moves the image within an Magnify window by one
9414% pixel as specified by the key symbol.
9415%
9416% The format of the XMagnifyWindowCommand method is:
9417%
9418% void XMagnifyWindowCommand(Display *display,XWindows *windows,
cristy6710d842011-10-20 23:23:00 +00009419% const MagickStatusType state,const KeySym key_symbol,
9420% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00009421%
9422% A description of each parameter follows:
9423%
9424% o display: Specifies a connection to an X server; returned from
9425% XOpenDisplay.
9426%
9427% o windows: Specifies a pointer to a XWindows structure.
9428%
9429% o state: key mask.
9430%
9431% o key_symbol: Specifies a KeySym which indicates which side of the image
9432% to trim.
9433%
cristy6710d842011-10-20 23:23:00 +00009434% o exception: return any errors or warnings in this structure.
9435%
cristy3ed852e2009-09-05 21:47:34 +00009436*/
9437static void XMagnifyWindowCommand(Display *display,XWindows *windows,
cristy6710d842011-10-20 23:23:00 +00009438 const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00009439{
9440 unsigned int
9441 quantum;
9442
9443 /*
9444 User specified a magnify factor or position.
9445 */
9446 quantum=1;
9447 if ((state & Mod1Mask) != 0)
9448 quantum=10;
9449 switch ((int) key_symbol)
9450 {
9451 case QuitCommand:
9452 {
9453 (void) XWithdrawWindow(display,windows->magnify.id,
9454 windows->magnify.screen);
9455 break;
9456 }
9457 case XK_Home:
9458 case XK_KP_Home:
9459 {
9460 windows->magnify.x=(int) windows->image.width/2;
9461 windows->magnify.y=(int) windows->image.height/2;
9462 break;
9463 }
9464 case XK_Left:
9465 case XK_KP_Left:
9466 {
9467 if (windows->magnify.x > 0)
9468 windows->magnify.x-=quantum;
9469 break;
9470 }
9471 case XK_Up:
9472 case XK_KP_Up:
9473 {
9474 if (windows->magnify.y > 0)
9475 windows->magnify.y-=quantum;
9476 break;
9477 }
9478 case XK_Right:
9479 case XK_KP_Right:
9480 {
9481 if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9482 windows->magnify.x+=quantum;
9483 break;
9484 }
9485 case XK_Down:
9486 case XK_KP_Down:
9487 {
9488 if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9489 windows->magnify.y+=quantum;
9490 break;
9491 }
9492 case XK_0:
9493 case XK_1:
9494 case XK_2:
9495 case XK_3:
9496 case XK_4:
9497 case XK_5:
9498 case XK_6:
9499 case XK_7:
9500 case XK_8:
9501 case XK_9:
9502 {
9503 windows->magnify.data=(key_symbol-XK_0);
9504 break;
9505 }
9506 case XK_KP_0:
9507 case XK_KP_1:
9508 case XK_KP_2:
9509 case XK_KP_3:
9510 case XK_KP_4:
9511 case XK_KP_5:
9512 case XK_KP_6:
9513 case XK_KP_7:
9514 case XK_KP_8:
9515 case XK_KP_9:
9516 {
9517 windows->magnify.data=(key_symbol-XK_KP_0);
9518 break;
9519 }
9520 default:
9521 break;
9522 }
cristy6710d842011-10-20 23:23:00 +00009523 XMakeMagnifyImage(display,windows,exception);
cristy3ed852e2009-09-05 21:47:34 +00009524}
9525
9526/*
9527%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9528% %
9529% %
9530% %
9531+ X M a k e P a n I m a g e %
9532% %
9533% %
9534% %
9535%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9536%
9537% XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9538% icon window.
9539%
9540% The format of the XMakePanImage method is:
9541%
9542% void XMakePanImage(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +00009543% XWindows *windows,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00009544%
9545% A description of each parameter follows:
9546%
9547% o display: Specifies a connection to an X server; returned from
9548% XOpenDisplay.
9549%
9550% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9551%
9552% o windows: Specifies a pointer to a XWindows structure.
9553%
9554% o image: the image.
9555%
cristy051718b2011-08-28 22:49:25 +00009556% o exception: return any errors or warnings in this structure.
9557%
cristy3ed852e2009-09-05 21:47:34 +00009558*/
9559static void XMakePanImage(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +00009560 XWindows *windows,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00009561{
9562 MagickStatusType
9563 status;
9564
9565 /*
9566 Create and display image for panning icon.
9567 */
9568 XSetCursorState(display,windows,MagickTrue);
9569 XCheckRefreshWindows(display,windows);
cristy49e2d862010-11-12 02:50:30 +00009570 windows->pan.x=(int) windows->image.x;
9571 windows->pan.y=(int) windows->image.y;
cristy3ed852e2009-09-05 21:47:34 +00009572 status=XMakeImage(display,resource_info,&windows->pan,image,
cristy051718b2011-08-28 22:49:25 +00009573 windows->pan.width,windows->pan.height,exception);
dirk5d0a4412016-01-05 22:28:51 +01009574 if (status == MagickFalse)
cristybeb1a6b2013-11-04 12:08:18 +00009575 ThrowXWindowException(ResourceLimitError,
cristy051718b2011-08-28 22:49:25 +00009576 "MemoryAllocationFailed",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00009577 (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9578 windows->pan.pixmap);
9579 (void) XClearWindow(display,windows->pan.id);
9580 XDrawPanRectangle(display,windows);
9581 XSetCursorState(display,windows,MagickFalse);
9582}
9583
9584/*
9585%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9586% %
9587% %
9588% %
9589+ X M a t t a E d i t I m a g e %
9590% %
9591% %
9592% %
9593%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9594%
9595% XMatteEditImage() allows the user to interactively change the Matte channel
9596% of an image. If the image is PseudoClass it is promoted to DirectClass
9597% before the matte information is stored.
9598%
9599% The format of the XMatteEditImage method is:
9600%
9601% MagickBooleanType XMatteEditImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00009602% XResourceInfo *resource_info,XWindows *windows,Image **image,
9603% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00009604%
9605% A description of each parameter follows:
9606%
9607% o display: Specifies a connection to an X server; returned from
9608% XOpenDisplay.
9609%
9610% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9611%
9612% o windows: Specifies a pointer to a XWindows structure.
9613%
9614% o image: the image; returned from ReadImage.
9615%
cristy051718b2011-08-28 22:49:25 +00009616% o exception: return any errors or warnings in this structure.
9617%
cristy3ed852e2009-09-05 21:47:34 +00009618*/
9619static MagickBooleanType XMatteEditImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00009620 XResourceInfo *resource_info,XWindows *windows,Image **image,
9621 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00009622{
Cristyd93b2e62019-05-22 19:45:01 -04009623 const char
9624 *const MatteEditMenu[] =
cristy3ed852e2009-09-05 21:47:34 +00009625 {
9626 "Method",
9627 "Border Color",
9628 "Fuzz",
9629 "Matte Value",
9630 "Undo",
9631 "Help",
9632 "Dismiss",
9633 (char *) NULL
9634 };
9635
Cristyd93b2e62019-05-22 19:45:01 -04009636 static char
9637 matte[MagickPathExtent] = "0";
9638
cristy3ed852e2009-09-05 21:47:34 +00009639 static const ModeType
9640 MatteEditCommands[] =
9641 {
9642 MatteEditMethod,
9643 MatteEditBorderCommand,
9644 MatteEditFuzzCommand,
9645 MatteEditValueCommand,
9646 MatteEditUndoCommand,
9647 MatteEditHelpCommand,
9648 MatteEditDismissCommand
9649 };
9650
9651 static PaintMethod
9652 method = PointMethod;
9653
9654 static XColor
9655 border_color = { 0, 0, 0, 0, 0, 0 };
9656
9657 char
cristy151b66d2015-04-15 10:50:31 +00009658 command[MagickPathExtent],
9659 text[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00009660
9661 Cursor
9662 cursor;
9663
9664 int
9665 entry,
9666 id,
9667 x,
9668 x_offset,
9669 y,
9670 y_offset;
9671
Cristyf2dc1dd2020-12-28 13:59:26 -05009672 int
cristy3ed852e2009-09-05 21:47:34 +00009673 i;
9674
Cristyf2dc1dd2020-12-28 13:59:26 -05009675 Quantum
cristy3ed852e2009-09-05 21:47:34 +00009676 *q;
9677
9678 unsigned int
9679 height,
9680 width;
9681
cristybb503372010-05-27 20:51:26 +00009682 size_t
cristy3ed852e2009-09-05 21:47:34 +00009683 state;
9684
9685 XEvent
9686 event;
9687
9688 /*
9689 Map Command widget.
9690 */
9691 (void) CloneString(&windows->command.name,"Matte Edit");
9692 windows->command.data=4;
9693 (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9694 (void) XMapRaised(display,windows->command.id);
9695 XClientMessage(display,windows->image.id,windows->im_protocols,
9696 windows->im_update_widget,CurrentTime);
9697 /*
9698 Make cursor.
9699 */
9700 cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9701 resource_info->background_color,resource_info->foreground_color);
9702 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9703 /*
9704 Track pointer until button 1 is pressed.
9705 */
9706 XQueryPosition(display,windows->image.id,&x,&y);
9707 (void) XSelectInput(display,windows->image.id,
9708 windows->image.attributes.event_mask | PointerMotionMask);
9709 state=DefaultState;
9710 do
9711 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -07009712 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009713 {
9714 /*
9715 Display pointer position.
9716 */
cristy151b66d2015-04-15 10:50:31 +00009717 (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
cristy3ed852e2009-09-05 21:47:34 +00009718 x+windows->image.x,y+windows->image.y);
9719 XInfoWidget(display,windows,text);
9720 }
9721 /*
9722 Wait for next event.
9723 */
cristy6710d842011-10-20 23:23:00 +00009724 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +00009725 if (event.xany.window == windows->command.id)
9726 {
9727 /*
9728 Select a command from the Command widget.
9729 */
9730 id=XCommandWidget(display,windows,MatteEditMenu,&event);
9731 if (id < 0)
9732 {
9733 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9734 continue;
9735 }
9736 switch (MatteEditCommands[id])
9737 {
9738 case MatteEditMethod:
9739 {
9740 char
9741 **methods;
9742
9743 /*
9744 Select a method from the pop-up menu.
9745 */
cristy042ee782011-04-22 18:48:30 +00009746 methods=GetCommandOptions(MagickMethodOptions);
cristy3ed852e2009-09-05 21:47:34 +00009747 if (methods == (char **) NULL)
9748 break;
9749 entry=XMenuWidget(display,windows,MatteEditMenu[id],
9750 (const char **) methods,command);
9751 if (entry >= 0)
cristy042ee782011-04-22 18:48:30 +00009752 method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
cristy3ed852e2009-09-05 21:47:34 +00009753 MagickFalse,methods[entry]);
9754 methods=DestroyStringList(methods);
9755 break;
9756 }
9757 case MatteEditBorderCommand:
9758 {
9759 const char
9760 *ColorMenu[MaxNumberPens];
9761
9762 int
9763 pen_number;
9764
9765 /*
9766 Initialize menu selections.
9767 */
9768 for (i=0; i < (int) (MaxNumberPens-2); i++)
9769 ColorMenu[i]=resource_info->pen_colors[i];
9770 ColorMenu[MaxNumberPens-2]="Browser...";
9771 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9772 /*
9773 Select a pen color from the pop-up menu.
9774 */
9775 pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9776 (const char **) ColorMenu,command);
9777 if (pen_number < 0)
9778 break;
9779 if (pen_number == (MaxNumberPens-2))
9780 {
9781 static char
cristy151b66d2015-04-15 10:50:31 +00009782 color_name[MagickPathExtent] = "gray";
cristy3ed852e2009-09-05 21:47:34 +00009783
9784 /*
9785 Select a pen color from a dialog.
9786 */
9787 resource_info->pen_colors[pen_number]=color_name;
9788 XColorBrowserWidget(display,windows,"Select",color_name);
9789 if (*color_name == '\0')
9790 break;
9791 }
9792 /*
9793 Set border color.
9794 */
9795 (void) XParseColor(display,windows->map_info->colormap,
9796 resource_info->pen_colors[pen_number],&border_color);
9797 break;
9798 }
9799 case MatteEditFuzzCommand:
9800 {
Cristyd93b2e62019-05-22 19:45:01 -04009801 const char
9802 *const FuzzMenu[] =
cristy3ed852e2009-09-05 21:47:34 +00009803 {
9804 "0%",
9805 "2%",
9806 "5%",
9807 "10%",
9808 "15%",
9809 "Dialog...",
9810 (char *) NULL,
9811 };
9812
Cristyd93b2e62019-05-22 19:45:01 -04009813 static char
9814 fuzz[MagickPathExtent];
9815
cristy3ed852e2009-09-05 21:47:34 +00009816 /*
9817 Select a command from the pop-up menu.
9818 */
9819 entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9820 command);
9821 if (entry < 0)
9822 break;
9823 if (entry != 5)
9824 {
cristydbdd0e32011-11-04 23:29:40 +00009825 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
cristy4c08aed2011-07-01 19:47:50 +00009826 QuantumRange+1.0);
cristy3ed852e2009-09-05 21:47:34 +00009827 break;
9828 }
cristy151b66d2015-04-15 10:50:31 +00009829 (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00009830 (void) XDialogWidget(display,windows,"Ok",
9831 "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9832 if (*fuzz == '\0')
9833 break;
cristy151b66d2015-04-15 10:50:31 +00009834 (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
cristy9b34e302011-11-05 02:15:45 +00009835 (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9836 1.0);
cristy3ed852e2009-09-05 21:47:34 +00009837 break;
9838 }
9839 case MatteEditValueCommand:
9840 {
Cristyd93b2e62019-05-22 19:45:01 -04009841 const char
9842 *const MatteMenu[] =
cristy3ed852e2009-09-05 21:47:34 +00009843 {
9844 "Opaque",
9845 "Transparent",
9846 "Dialog...",
9847 (char *) NULL,
9848 };
9849
Cristyd93b2e62019-05-22 19:45:01 -04009850 static char
9851 message[MagickPathExtent];
9852
cristy3ed852e2009-09-05 21:47:34 +00009853 /*
9854 Select a command from the pop-up menu.
9855 */
9856 entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9857 command);
9858 if (entry < 0)
9859 break;
9860 if (entry != 2)
9861 {
cristy151b66d2015-04-15 10:50:31 +00009862 (void) FormatLocaleString(matte,MagickPathExtent,QuantumFormat,
cristy4c08aed2011-07-01 19:47:50 +00009863 OpaqueAlpha);
cristy3ed852e2009-09-05 21:47:34 +00009864 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
Cristy859511c2018-04-22 14:50:36 -04009865 (void) FormatLocaleString(matte,MagickPathExtent,
9866 QuantumFormat,(Quantum) TransparentAlpha);
cristy3ed852e2009-09-05 21:47:34 +00009867 break;
9868 }
cristy151b66d2015-04-15 10:50:31 +00009869 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00009870 "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9871 QuantumRange);
9872 (void) XDialogWidget(display,windows,"Matte",message,matte);
9873 if (*matte == '\0')
9874 break;
9875 break;
9876 }
9877 case MatteEditUndoCommand:
9878 {
9879 (void) XMagickCommand(display,resource_info,windows,UndoCommand,
cristy051718b2011-08-28 22:49:25 +00009880 image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009881 break;
9882 }
9883 case MatteEditHelpCommand:
9884 {
Cristy6970baa2019-04-20 21:23:54 -04009885 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00009886 "Help Viewer - Matte Edit",ImageMatteEditHelp);
9887 break;
9888 }
9889 case MatteEditDismissCommand:
9890 {
9891 /*
9892 Prematurely exit.
9893 */
9894 state|=EscapeState;
9895 state|=ExitState;
9896 break;
9897 }
9898 default:
9899 break;
9900 }
9901 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9902 continue;
9903 }
9904 switch (event.type)
9905 {
9906 case ButtonPress:
9907 {
9908 if (event.xbutton.button != Button1)
9909 break;
9910 if ((event.xbutton.window != windows->image.id) &&
9911 (event.xbutton.window != windows->magnify.id))
9912 break;
9913 /*
9914 Update matte data.
9915 */
9916 x=event.xbutton.x;
9917 y=event.xbutton.y;
9918 (void) XMagickCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +00009919 SaveToUndoBufferCommand,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009920 state|=UpdateConfigurationState;
9921 break;
9922 }
9923 case ButtonRelease:
9924 {
9925 if (event.xbutton.button != Button1)
9926 break;
9927 if ((event.xbutton.window != windows->image.id) &&
9928 (event.xbutton.window != windows->magnify.id))
9929 break;
9930 /*
9931 Update colormap information.
9932 */
9933 x=event.xbutton.x;
9934 y=event.xbutton.y;
cristy6710d842011-10-20 23:23:00 +00009935 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy051718b2011-08-28 22:49:25 +00009936 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +00009937 XInfoWidget(display,windows,text);
9938 (void) XCheckDefineCursor(display,windows->image.id,cursor);
9939 state&=(~UpdateConfigurationState);
9940 break;
9941 }
9942 case Expose:
9943 break;
9944 case KeyPress:
9945 {
9946 char
cristy151b66d2015-04-15 10:50:31 +00009947 command[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00009948
9949 KeySym
9950 key_symbol;
9951
9952 if (event.xkey.window == windows->magnify.id)
9953 {
9954 Window
9955 window;
9956
9957 window=windows->magnify.id;
9958 while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9959 }
9960 if (event.xkey.window != windows->image.id)
9961 break;
9962 /*
9963 Respond to a user key press.
9964 */
9965 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9966 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9967 switch ((int) key_symbol)
9968 {
9969 case XK_Escape:
9970 case XK_F20:
9971 {
9972 /*
9973 Prematurely exit.
9974 */
9975 state|=ExitState;
9976 break;
9977 }
9978 case XK_F1:
9979 case XK_Help:
9980 {
Cristy6970baa2019-04-20 21:23:54 -04009981 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00009982 "Help Viewer - Matte Edit",ImageMatteEditHelp);
9983 break;
9984 }
9985 default:
9986 {
9987 (void) XBell(display,0);
9988 break;
9989 }
9990 }
9991 break;
9992 }
9993 case MotionNotify:
9994 {
9995 /*
9996 Map and unmap Info widget as cursor crosses its boundaries.
9997 */
9998 x=event.xmotion.x;
9999 y=event.xmotion.y;
Elliott Hughes5d41fba2021-04-12 16:36:42 -070010000 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010001 {
10002 if ((x < (int) (windows->info.x+windows->info.width)) &&
10003 (y < (int) (windows->info.y+windows->info.height)))
10004 (void) XWithdrawWindow(display,windows->info.id,
10005 windows->info.screen);
10006 }
10007 else
10008 if ((x > (int) (windows->info.x+windows->info.width)) ||
10009 (y > (int) (windows->info.y+windows->info.height)))
10010 (void) XMapWindow(display,windows->info.id);
10011 break;
10012 }
10013 default:
10014 break;
10015 }
10016 if (event.xany.window == windows->magnify.id)
10017 {
10018 x=windows->magnify.x-windows->image.x;
10019 y=windows->magnify.y-windows->image.y;
10020 }
10021 x_offset=x;
10022 y_offset=y;
10023 if ((state & UpdateConfigurationState) != 0)
10024 {
cristy49e2d862010-11-12 02:50:30 +000010025 CacheView
10026 *image_view;
10027
cristy3ed852e2009-09-05 21:47:34 +000010028 int
10029 x,
10030 y;
10031
10032 /*
10033 Matte edit is relative to image configuration.
10034 */
10035 (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10036 MagickTrue);
10037 XPutPixel(windows->image.ximage,x_offset,y_offset,
10038 windows->pixel_info->background_color.pixel);
10039 width=(unsigned int) (*image)->columns;
10040 height=(unsigned int) (*image)->rows;
10041 x=0;
10042 y=0;
10043 if (windows->image.crop_geometry != (char *) NULL)
cristy4c08aed2011-07-01 19:47:50 +000010044 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10045 &height);
10046 x_offset=(int) (width*(windows->image.x+x_offset)/
10047 windows->image.ximage->width+x);
10048 y_offset=(int) (height*(windows->image.y+y_offset)/
10049 windows->image.ximage->height+y);
cristy3ed852e2009-09-05 21:47:34 +000010050 if ((x_offset < 0) || (y_offset < 0))
10051 continue;
10052 if ((x_offset >= (int) (*image)->columns) ||
10053 (y_offset >= (int) (*image)->rows))
10054 continue;
dirk5d0a4412016-01-05 22:28:51 +010010055 if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010056 return(MagickFalse);
cristy17f11b02014-12-20 19:37:04 +000010057 if ((*image)->alpha_trait == UndefinedPixelTrait)
cristycd515282011-12-27 01:40:49 +000010058 (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
cristy6f5395d2012-12-14 18:30:30 +000010059 image_view=AcquireAuthenticCacheView(*image,exception);
cristy3ed852e2009-09-05 21:47:34 +000010060 switch (method)
10061 {
10062 case PointMethod:
10063 default:
10064 {
10065 /*
10066 Update matte information using point algorithm.
10067 */
cristy49e2d862010-11-12 02:50:30 +000010068 q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10069 (ssize_t) y_offset,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +000010070 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010071 break;
cristy4c08aed2011-07-01 19:47:50 +000010072 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
cristy49e2d862010-11-12 02:50:30 +000010073 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +000010074 break;
10075 }
10076 case ReplaceMethod:
10077 {
cristy101ab702011-10-13 13:06:32 +000010078 PixelInfo
cristy4c08aed2011-07-01 19:47:50 +000010079 pixel,
cristy3ed852e2009-09-05 21:47:34 +000010080 target;
10081
10082 /*
10083 Update matte information using replace algorithm.
10084 */
cristyf05d4942012-03-17 16:26:09 +000010085 (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
10086 x_offset,(ssize_t) y_offset,&target,exception);
cristy49e2d862010-11-12 02:50:30 +000010087 for (y=0; y < (int) (*image)->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010088 {
cristy49e2d862010-11-12 02:50:30 +000010089 q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
cristy051718b2011-08-28 22:49:25 +000010090 (*image)->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +000010091 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010092 break;
10093 for (x=0; x < (int) (*image)->columns; x++)
10094 {
cristy101ab702011-10-13 13:06:32 +000010095 GetPixelInfoPixel(*image,q,&pixel);
10096 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
cristy4c08aed2011-07-01 19:47:50 +000010097 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
cristyed231572011-07-14 02:18:59 +000010098 q+=GetPixelChannels(*image);
cristy3ed852e2009-09-05 21:47:34 +000010099 }
dirk5d0a4412016-01-05 22:28:51 +010010100 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010101 break;
10102 }
10103 break;
10104 }
10105 case FloodfillMethod:
10106 case FillToBorderMethod:
10107 {
cristybd5a96c2011-08-21 00:04:26 +000010108 ChannelType
10109 channel_mask;
10110
cristy3ed852e2009-09-05 21:47:34 +000010111 DrawInfo
10112 *draw_info;
10113
cristy4c08aed2011-07-01 19:47:50 +000010114 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +000010115 target;
10116
10117 /*
10118 Update matte information using floodfill algorithm.
10119 */
cristy3aa93752011-12-18 15:54:24 +000010120 (void) GetOneVirtualPixelInfo(*image,
cristy52010022011-10-21 18:07:37 +000010121 GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10122 y_offset,&target,exception);
cristy3ed852e2009-09-05 21:47:34 +000010123 if (method == FillToBorderMethod)
10124 {
cristya19f1d72012-08-07 18:24:38 +000010125 target.red=(double) ScaleShortToQuantum(
cristy4c08aed2011-07-01 19:47:50 +000010126 border_color.red);
cristya19f1d72012-08-07 18:24:38 +000010127 target.green=(double) ScaleShortToQuantum(
cristy4c08aed2011-07-01 19:47:50 +000010128 border_color.green);
cristya19f1d72012-08-07 18:24:38 +000010129 target.blue=(double) ScaleShortToQuantum(
cristy4c08aed2011-07-01 19:47:50 +000010130 border_color.blue);
cristy3ed852e2009-09-05 21:47:34 +000010131 }
10132 draw_info=CloneDrawInfo(resource_info->image_info,
10133 (DrawInfo *) NULL);
cristya19f1d72012-08-07 18:24:38 +000010134 draw_info->fill.alpha=(double) ClampToQuantum(
cristye42f6582012-02-11 17:59:50 +000010135 StringToDouble(matte,(char **) NULL));
cristyaeded782012-09-11 23:39:36 +000010136 channel_mask=SetImageChannelMask(*image,AlphaChannel);
cristyd42d9952011-07-08 14:21:50 +000010137 (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
anthony11d32022012-11-17 05:31:33 +000010138 x_offset,(ssize_t) y_offset,
dirkb9dbc292015-07-26 09:50:00 +000010139 method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
cristycf1296e2012-08-26 23:40:49 +000010140 (void) SetPixelChannelMask(*image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +000010141 draw_info=DestroyDrawInfo(draw_info);
10142 break;
10143 }
10144 case ResetMethod:
10145 {
10146 /*
10147 Update matte information using reset algorithm.
10148 */
dirk5d0a4412016-01-05 22:28:51 +010010149 if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010150 return(MagickFalse);
cristy49e2d862010-11-12 02:50:30 +000010151 for (y=0; y < (int) (*image)->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010152 {
cristy49e2d862010-11-12 02:50:30 +000010153 q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10154 (*image)->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +000010155 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010156 break;
10157 for (x=0; x < (int) (*image)->columns; x++)
10158 {
cristy4c08aed2011-07-01 19:47:50 +000010159 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
cristyed231572011-07-14 02:18:59 +000010160 q+=GetPixelChannels(*image);
cristy3ed852e2009-09-05 21:47:34 +000010161 }
dirk5d0a4412016-01-05 22:28:51 +010010162 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010163 break;
10164 }
cristy4c08aed2011-07-01 19:47:50 +000010165 if (StringToLong(matte) == (long) OpaqueAlpha)
cristy8a46d822012-08-28 23:32:39 +000010166 (*image)->alpha_trait=UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +000010167 break;
10168 }
10169 }
cristy49e2d862010-11-12 02:50:30 +000010170 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +000010171 state&=(~UpdateConfigurationState);
10172 }
10173 } while ((state & ExitState) == 0);
10174 (void) XSelectInput(display,windows->image.id,
10175 windows->image.attributes.event_mask);
10176 XSetCursorState(display,windows,MagickFalse);
10177 (void) XFreeCursor(display,cursor);
10178 return(MagickTrue);
10179}
10180
10181/*
10182%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10183% %
10184% %
10185% %
10186+ X O p e n I m a g e %
10187% %
10188% %
10189% %
10190%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10191%
10192% XOpenImage() loads an image from a file.
10193%
10194% The format of the XOpenImage method is:
10195%
10196% Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10197% XWindows *windows,const unsigned int command)
10198%
10199% A description of each parameter follows:
10200%
10201% o display: Specifies a connection to an X server; returned from
10202% XOpenDisplay.
10203%
10204% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10205%
10206% o windows: Specifies a pointer to a XWindows structure.
10207%
10208% o command: A value other than zero indicates that the file is selected
10209% from the command line argument list.
10210%
10211*/
10212static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10213 XWindows *windows,const MagickBooleanType command)
10214{
10215 const MagickInfo
10216 *magick_info;
10217
10218 ExceptionInfo
10219 *exception;
10220
10221 Image
10222 *nexus;
10223
10224 ImageInfo
10225 *image_info;
10226
10227 static char
cristy151b66d2015-04-15 10:50:31 +000010228 filename[MagickPathExtent] = "\0";
cristy3ed852e2009-09-05 21:47:34 +000010229
10230 /*
10231 Request file name from user.
10232 */
dirkdaeeade2016-01-05 23:01:16 +010010233 if (command == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010234 XFileBrowserWidget(display,windows,"Open",filename);
10235 else
10236 {
10237 char
10238 **filelist,
10239 **files;
10240
10241 int
10242 count,
10243 status;
10244
Cristyf2dc1dd2020-12-28 13:59:26 -050010245 int
cristy3ed852e2009-09-05 21:47:34 +000010246 i,
10247 j;
10248
10249 /*
10250 Select next image from the command line.
10251 */
10252 status=XGetCommand(display,windows->image.id,&files,&count);
10253 if (status == 0)
10254 {
cristybeb1a6b2013-11-04 12:08:18 +000010255 ThrowXWindowException(XServerError,"UnableToGetProperty","...");
cristy3ed852e2009-09-05 21:47:34 +000010256 return((Image *) NULL);
10257 }
10258 filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10259 if (filelist == (char **) NULL)
10260 {
cristybeb1a6b2013-11-04 12:08:18 +000010261 ThrowXWindowException(ResourceLimitError,
cristy3ed852e2009-09-05 21:47:34 +000010262 "MemoryAllocationFailed","...");
10263 (void) XFreeStringList(files);
10264 return((Image *) NULL);
10265 }
10266 j=0;
10267 for (i=1; i < count; i++)
10268 if (*files[i] != '-')
10269 filelist[j++]=files[i];
10270 filelist[j]=(char *) NULL;
10271 XListBrowserWidget(display,windows,&windows->widget,
10272 (const char **) filelist,"Load","Select Image to Load:",filename);
10273 filelist=(char **) RelinquishMagickMemory(filelist);
10274 (void) XFreeStringList(files);
10275 }
10276 if (*filename == '\0')
10277 return((Image *) NULL);
10278 image_info=CloneImageInfo(resource_info->image_info);
10279 (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10280 (void *) NULL);
cristy151b66d2015-04-15 10:50:31 +000010281 (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +000010282 exception=AcquireExceptionInfo();
cristyd965a422010-03-03 17:47:35 +000010283 (void) SetImageInfo(image_info,0,exception);
cristy3ed852e2009-09-05 21:47:34 +000010284 if (LocaleCompare(image_info->magick,"X") == 0)
10285 {
10286 char
cristy151b66d2015-04-15 10:50:31 +000010287 seconds[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +000010288
10289 /*
10290 User may want to delay the X server screen grab.
10291 */
cristy151b66d2015-04-15 10:50:31 +000010292 (void) CopyMagickString(seconds,"0",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +000010293 (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10294 seconds);
10295 if (*seconds == '\0')
10296 return((Image *) NULL);
cristybb503372010-05-27 20:51:26 +000010297 XDelay(display,(size_t) (1000*StringToLong(seconds)));
cristy3ed852e2009-09-05 21:47:34 +000010298 }
10299 magick_info=GetMagickInfo(image_info->magick,exception);
10300 if ((magick_info != (const MagickInfo *) NULL) &&
dirke8e609a2015-02-22 00:36:34 +000010301 GetMagickRawSupport(magick_info) == MagickTrue)
cristy3ed852e2009-09-05 21:47:34 +000010302 {
10303 char
cristy151b66d2015-04-15 10:50:31 +000010304 geometry[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +000010305
10306 /*
10307 Request image size from the user.
10308 */
cristy151b66d2015-04-15 10:50:31 +000010309 (void) CopyMagickString(geometry,"512x512",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +000010310 if (image_info->size != (char *) NULL)
cristy151b66d2015-04-15 10:50:31 +000010311 (void) CopyMagickString(geometry,image_info->size,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +000010312 (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10313 geometry);
10314 (void) CloneString(&image_info->size,geometry);
10315 }
10316 /*
10317 Load the image.
10318 */
10319 XSetCursorState(display,windows,MagickTrue);
10320 XCheckRefreshWindows(display,windows);
cristy151b66d2015-04-15 10:50:31 +000010321 (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +000010322 nexus=ReadImage(image_info,exception);
10323 CatchException(exception);
10324 XSetCursorState(display,windows,MagickFalse);
10325 if (nexus != (Image *) NULL)
10326 XClientMessage(display,windows->image.id,windows->im_protocols,
10327 windows->im_next_image,CurrentTime);
10328 else
10329 {
10330 char
10331 *text,
10332 **textlist;
10333
10334 /*
10335 Unknown image format.
10336 */
cristy3a5987c2013-11-07 14:18:46 +000010337 text=FileToString(filename,~0UL,exception);
cristy3ed852e2009-09-05 21:47:34 +000010338 if (text == (char *) NULL)
10339 return((Image *) NULL);
10340 textlist=StringToList(text);
10341 if (textlist != (char **) NULL)
10342 {
10343 char
cristy151b66d2015-04-15 10:50:31 +000010344 title[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +000010345
Cristyf2dc1dd2020-12-28 13:59:26 -050010346 int
cristy3ed852e2009-09-05 21:47:34 +000010347 i;
10348
cristy151b66d2015-04-15 10:50:31 +000010349 (void) FormatLocaleString(title,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +000010350 "Unknown format: %s",filename);
10351 XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10352 (const char **) textlist);
10353 for (i=0; textlist[i] != (char *) NULL; i++)
10354 textlist[i]=DestroyString(textlist[i]);
10355 textlist=(char **) RelinquishMagickMemory(textlist);
10356 }
10357 text=DestroyString(text);
10358 }
10359 exception=DestroyExceptionInfo(exception);
10360 image_info=DestroyImageInfo(image_info);
10361 return(nexus);
10362}
10363
10364/*
10365%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10366% %
10367% %
10368% %
10369+ X P a n I m a g e %
10370% %
10371% %
10372% %
10373%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10374%
10375% XPanImage() pans the image until the mouse button is released.
10376%
10377% The format of the XPanImage method is:
10378%
cristy6710d842011-10-20 23:23:00 +000010379% void XPanImage(Display *display,XWindows *windows,XEvent *event,
10380% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010381%
10382% A description of each parameter follows:
10383%
10384% o display: Specifies a connection to an X server; returned from
10385% XOpenDisplay.
10386%
10387% o windows: Specifies a pointer to a XWindows structure.
10388%
10389% o event: Specifies a pointer to a XEvent structure. If it is NULL,
10390% the entire image is refreshed.
10391%
cristy6710d842011-10-20 23:23:00 +000010392% o exception: return any errors or warnings in this structure.
10393%
cristy3ed852e2009-09-05 21:47:34 +000010394*/
cristy6710d842011-10-20 23:23:00 +000010395static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10396 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010397{
10398 char
cristy151b66d2015-04-15 10:50:31 +000010399 text[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +000010400
10401 Cursor
10402 cursor;
10403
cristya19f1d72012-08-07 18:24:38 +000010404 double
cristy3ed852e2009-09-05 21:47:34 +000010405 x_factor,
10406 y_factor;
10407
10408 RectangleInfo
10409 pan_info;
10410
cristybb503372010-05-27 20:51:26 +000010411 size_t
cristy3ed852e2009-09-05 21:47:34 +000010412 state;
10413
10414 /*
10415 Define cursor.
10416 */
10417 if ((windows->image.ximage->width > (int) windows->image.width) &&
10418 (windows->image.ximage->height > (int) windows->image.height))
10419 cursor=XCreateFontCursor(display,XC_fleur);
10420 else
10421 if (windows->image.ximage->width > (int) windows->image.width)
10422 cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10423 else
10424 if (windows->image.ximage->height > (int) windows->image.height)
10425 cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10426 else
10427 cursor=XCreateFontCursor(display,XC_arrow);
10428 (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10429 /*
10430 Pan image as pointer moves until the mouse button is released.
10431 */
cristya19f1d72012-08-07 18:24:38 +000010432 x_factor=(double) windows->image.ximage->width/windows->pan.width;
10433 y_factor=(double) windows->image.ximage->height/windows->pan.height;
cristy3ed852e2009-09-05 21:47:34 +000010434 pan_info.width=windows->pan.width*windows->image.width/
10435 windows->image.ximage->width;
10436 pan_info.height=windows->pan.height*windows->image.height/
10437 windows->image.ximage->height;
10438 pan_info.x=0;
10439 pan_info.y=0;
10440 state=UpdateConfigurationState;
10441 do
10442 {
10443 switch (event->type)
10444 {
10445 case ButtonPress:
10446 {
10447 /*
10448 User choose an initial pan location.
10449 */
cristy49e2d862010-11-12 02:50:30 +000010450 pan_info.x=(ssize_t) event->xbutton.x;
10451 pan_info.y=(ssize_t) event->xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000010452 state|=UpdateConfigurationState;
10453 break;
10454 }
10455 case ButtonRelease:
10456 {
10457 /*
10458 User has finished panning the image.
10459 */
cristy49e2d862010-11-12 02:50:30 +000010460 pan_info.x=(ssize_t) event->xbutton.x;
10461 pan_info.y=(ssize_t) event->xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000010462 state|=UpdateConfigurationState | ExitState;
10463 break;
10464 }
10465 case MotionNotify:
10466 {
cristy49e2d862010-11-12 02:50:30 +000010467 pan_info.x=(ssize_t) event->xmotion.x;
10468 pan_info.y=(ssize_t) event->xmotion.y;
cristy3ed852e2009-09-05 21:47:34 +000010469 state|=UpdateConfigurationState;
10470 }
10471 default:
10472 break;
10473 }
10474 if ((state & UpdateConfigurationState) != 0)
10475 {
10476 /*
10477 Check boundary conditions.
10478 */
cristy49e2d862010-11-12 02:50:30 +000010479 if (pan_info.x < (ssize_t) (pan_info.width/2))
cristy3ed852e2009-09-05 21:47:34 +000010480 pan_info.x=0;
10481 else
cristy49e2d862010-11-12 02:50:30 +000010482 pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
cristy3ed852e2009-09-05 21:47:34 +000010483 if (pan_info.x < 0)
10484 pan_info.x=0;
10485 else
10486 if ((int) (pan_info.x+windows->image.width) >
10487 windows->image.ximage->width)
cristybb503372010-05-27 20:51:26 +000010488 pan_info.x=(ssize_t)
cristy3ed852e2009-09-05 21:47:34 +000010489 (windows->image.ximage->width-windows->image.width);
cristybb503372010-05-27 20:51:26 +000010490 if (pan_info.y < (ssize_t) (pan_info.height/2))
cristy3ed852e2009-09-05 21:47:34 +000010491 pan_info.y=0;
10492 else
cristybb503372010-05-27 20:51:26 +000010493 pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
cristy3ed852e2009-09-05 21:47:34 +000010494 if (pan_info.y < 0)
10495 pan_info.y=0;
10496 else
10497 if ((int) (pan_info.y+windows->image.height) >
10498 windows->image.ximage->height)
cristybb503372010-05-27 20:51:26 +000010499 pan_info.y=(ssize_t)
cristy3ed852e2009-09-05 21:47:34 +000010500 (windows->image.ximage->height-windows->image.height);
10501 if ((windows->image.x != (int) pan_info.x) ||
10502 (windows->image.y != (int) pan_info.y))
10503 {
10504 /*
10505 Display image pan offset.
10506 */
10507 windows->image.x=(int) pan_info.x;
10508 windows->image.y=(int) pan_info.y;
cristy151b66d2015-04-15 10:50:31 +000010509 (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
cristy3ed852e2009-09-05 21:47:34 +000010510 windows->image.width,windows->image.height,windows->image.x,
10511 windows->image.y);
10512 XInfoWidget(display,windows,text);
10513 /*
10514 Refresh Image window.
10515 */
10516 XDrawPanRectangle(display,windows);
10517 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10518 }
10519 state&=(~UpdateConfigurationState);
10520 }
10521 /*
10522 Wait for next event.
10523 */
10524 if ((state & ExitState) == 0)
cristy6710d842011-10-20 23:23:00 +000010525 XScreenEvent(display,windows,event,exception);
cristy3ed852e2009-09-05 21:47:34 +000010526 } while ((state & ExitState) == 0);
10527 /*
10528 Restore cursor.
10529 */
10530 (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10531 (void) XFreeCursor(display,cursor);
10532 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10533}
10534
10535/*
10536%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10537% %
10538% %
10539% %
10540+ X P a s t e I m a g e %
10541% %
10542% %
10543% %
10544%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10545%
10546% XPasteImage() pastes an image previously saved with XCropImage in the X
10547% window image at a location the user chooses with the pointer.
10548%
10549% The format of the XPasteImage method is:
10550%
10551% MagickBooleanType XPasteImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000010552% XResourceInfo *resource_info,XWindows *windows,Image *image,
10553% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010554%
10555% A description of each parameter follows:
10556%
10557% o display: Specifies a connection to an X server; returned from
10558% XOpenDisplay.
10559%
10560% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10561%
10562% o windows: Specifies a pointer to a XWindows structure.
10563%
10564% o image: the image; returned from ReadImage.
10565%
cristy051718b2011-08-28 22:49:25 +000010566% o exception: return any errors or warnings in this structure.
10567%
cristy3ed852e2009-09-05 21:47:34 +000010568*/
10569static MagickBooleanType XPasteImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000010570 XResourceInfo *resource_info,XWindows *windows,Image *image,
10571 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010572{
Cristyd93b2e62019-05-22 19:45:01 -040010573 const char
10574 *const PasteMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000010575 {
10576 "Operator",
10577 "Help",
10578 "Dismiss",
10579 (char *) NULL
10580 };
10581
10582 static const ModeType
10583 PasteCommands[] =
10584 {
10585 PasteOperatorsCommand,
10586 PasteHelpCommand,
10587 PasteDismissCommand
10588 };
10589
10590 static CompositeOperator
10591 compose = CopyCompositeOp;
10592
10593 char
cristy151b66d2015-04-15 10:50:31 +000010594 text[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +000010595
10596 Cursor
10597 cursor;
10598
10599 Image
10600 *paste_image;
10601
10602 int
10603 entry,
10604 id,
10605 x,
10606 y;
10607
cristya19f1d72012-08-07 18:24:38 +000010608 double
cristy3ed852e2009-09-05 21:47:34 +000010609 scale_factor;
10610
10611 RectangleInfo
10612 highlight_info,
10613 paste_info;
10614
10615 unsigned int
10616 height,
10617 width;
10618
cristybb503372010-05-27 20:51:26 +000010619 size_t
cristy3ed852e2009-09-05 21:47:34 +000010620 state;
10621
10622 XEvent
10623 event;
10624
10625 /*
10626 Copy image.
10627 */
10628 if (resource_info->copy_image == (Image *) NULL)
10629 return(MagickFalse);
cristy051718b2011-08-28 22:49:25 +000010630 paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
root1dc55582017-09-29 05:41:10 +000010631 if (paste_image == (Image *) NULL)
10632 return(MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +000010633 /*
10634 Map Command widget.
10635 */
10636 (void) CloneString(&windows->command.name,"Paste");
10637 windows->command.data=1;
10638 (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10639 (void) XMapRaised(display,windows->command.id);
10640 XClientMessage(display,windows->image.id,windows->im_protocols,
10641 windows->im_update_widget,CurrentTime);
10642 /*
10643 Track pointer until button 1 is pressed.
10644 */
10645 XSetCursorState(display,windows,MagickFalse);
10646 XQueryPosition(display,windows->image.id,&x,&y);
10647 (void) XSelectInput(display,windows->image.id,
10648 windows->image.attributes.event_mask | PointerMotionMask);
cristy49e2d862010-11-12 02:50:30 +000010649 paste_info.x=(ssize_t) windows->image.x+x;
10650 paste_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +000010651 paste_info.width=0;
10652 paste_info.height=0;
10653 cursor=XCreateFontCursor(display,XC_ul_angle);
10654 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10655 state=DefaultState;
10656 do
10657 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -070010658 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010659 {
10660 /*
10661 Display pointer position.
10662 */
cristy151b66d2015-04-15 10:50:31 +000010663 (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
cristyf2faecf2010-05-28 19:19:36 +000010664 (long) paste_info.x,(long) paste_info.y);
cristy3ed852e2009-09-05 21:47:34 +000010665 XInfoWidget(display,windows,text);
10666 }
10667 highlight_info=paste_info;
10668 highlight_info.x=paste_info.x-windows->image.x;
10669 highlight_info.y=paste_info.y-windows->image.y;
10670 XHighlightRectangle(display,windows->image.id,
10671 windows->image.highlight_context,&highlight_info);
10672 /*
10673 Wait for next event.
10674 */
cristy6710d842011-10-20 23:23:00 +000010675 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +000010676 XHighlightRectangle(display,windows->image.id,
10677 windows->image.highlight_context,&highlight_info);
10678 if (event.xany.window == windows->command.id)
10679 {
10680 /*
10681 Select a command from the Command widget.
10682 */
10683 id=XCommandWidget(display,windows,PasteMenu,&event);
10684 if (id < 0)
10685 continue;
10686 switch (PasteCommands[id])
10687 {
10688 case PasteOperatorsCommand:
10689 {
10690 char
cristy151b66d2015-04-15 10:50:31 +000010691 command[MagickPathExtent],
cristy3ed852e2009-09-05 21:47:34 +000010692 **operators;
10693
10694 /*
10695 Select a command from the pop-up menu.
10696 */
cristy042ee782011-04-22 18:48:30 +000010697 operators=GetCommandOptions(MagickComposeOptions);
cristy3ed852e2009-09-05 21:47:34 +000010698 if (operators == (char **) NULL)
10699 break;
10700 entry=XMenuWidget(display,windows,PasteMenu[id],
10701 (const char **) operators,command);
10702 if (entry >= 0)
cristy042ee782011-04-22 18:48:30 +000010703 compose=(CompositeOperator) ParseCommandOption(
cristy3ed852e2009-09-05 21:47:34 +000010704 MagickComposeOptions,MagickFalse,operators[entry]);
10705 operators=DestroyStringList(operators);
10706 break;
10707 }
10708 case PasteHelpCommand:
10709 {
Cristy6970baa2019-04-20 21:23:54 -040010710 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +000010711 "Help Viewer - Image Composite",ImagePasteHelp);
10712 break;
10713 }
10714 case PasteDismissCommand:
10715 {
10716 /*
10717 Prematurely exit.
10718 */
10719 state|=EscapeState;
10720 state|=ExitState;
10721 break;
10722 }
10723 default:
10724 break;
10725 }
10726 continue;
10727 }
10728 switch (event.type)
10729 {
10730 case ButtonPress:
10731 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -070010732 if (image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010733 (void) LogMagickEvent(X11Event,GetMagickModule(),
10734 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10735 event.xbutton.button,event.xbutton.x,event.xbutton.y);
10736 if (event.xbutton.button != Button1)
10737 break;
10738 if (event.xbutton.window != windows->image.id)
10739 break;
10740 /*
10741 Paste rectangle is relative to image configuration.
10742 */
10743 width=(unsigned int) image->columns;
10744 height=(unsigned int) image->rows;
10745 x=0;
10746 y=0;
10747 if (windows->image.crop_geometry != (char *) NULL)
10748 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10749 &width,&height);
cristya19f1d72012-08-07 18:24:38 +000010750 scale_factor=(double) windows->image.ximage->width/width;
cristy3ed852e2009-09-05 21:47:34 +000010751 paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
cristya19f1d72012-08-07 18:24:38 +000010752 scale_factor=(double) windows->image.ximage->height/height;
cristy3ed852e2009-09-05 21:47:34 +000010753 paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10754 (void) XCheckDefineCursor(display,windows->image.id,cursor);
cristy49e2d862010-11-12 02:50:30 +000010755 paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10756 paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000010757 break;
10758 }
10759 case ButtonRelease:
10760 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -070010761 if (image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010762 (void) LogMagickEvent(X11Event,GetMagickModule(),
10763 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10764 event.xbutton.button,event.xbutton.x,event.xbutton.y);
10765 if (event.xbutton.button != Button1)
10766 break;
10767 if (event.xbutton.window != windows->image.id)
10768 break;
10769 if ((paste_info.width != 0) && (paste_info.height != 0))
10770 {
10771 /*
10772 User has selected the location of the paste image.
10773 */
cristy49e2d862010-11-12 02:50:30 +000010774 paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10775 paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000010776 state|=ExitState;
10777 }
10778 break;
10779 }
10780 case Expose:
10781 break;
10782 case KeyPress:
10783 {
10784 char
cristy151b66d2015-04-15 10:50:31 +000010785 command[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +000010786
10787 KeySym
10788 key_symbol;
10789
10790 int
10791 length;
10792
10793 if (event.xkey.window != windows->image.id)
10794 break;
10795 /*
10796 Respond to a user key press.
10797 */
10798 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10799 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10800 *(command+length)='\0';
Elliott Hughes5d41fba2021-04-12 16:36:42 -070010801 if (image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010802 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010803 "Key press: 0x%lx (%s)",(long) key_symbol,command);
cristy3ed852e2009-09-05 21:47:34 +000010804 switch ((int) key_symbol)
10805 {
10806 case XK_Escape:
10807 case XK_F20:
10808 {
10809 /*
10810 Prematurely exit.
10811 */
10812 paste_image=DestroyImage(paste_image);
10813 state|=EscapeState;
10814 state|=ExitState;
10815 break;
10816 }
10817 case XK_F1:
10818 case XK_Help:
10819 {
10820 (void) XSetFunction(display,windows->image.highlight_context,
10821 GXcopy);
Cristy6970baa2019-04-20 21:23:54 -040010822 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +000010823 "Help Viewer - Image Composite",ImagePasteHelp);
10824 (void) XSetFunction(display,windows->image.highlight_context,
10825 GXinvert);
10826 break;
10827 }
10828 default:
10829 {
10830 (void) XBell(display,0);
10831 break;
10832 }
10833 }
10834 break;
10835 }
10836 case MotionNotify:
10837 {
10838 /*
10839 Map and unmap Info widget as text cursor crosses its boundaries.
10840 */
10841 x=event.xmotion.x;
10842 y=event.xmotion.y;
Elliott Hughes5d41fba2021-04-12 16:36:42 -070010843 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010844 {
10845 if ((x < (int) (windows->info.x+windows->info.width)) &&
10846 (y < (int) (windows->info.y+windows->info.height)))
10847 (void) XWithdrawWindow(display,windows->info.id,
10848 windows->info.screen);
10849 }
10850 else
10851 if ((x > (int) (windows->info.x+windows->info.width)) ||
10852 (y > (int) (windows->info.y+windows->info.height)))
10853 (void) XMapWindow(display,windows->info.id);
cristy49e2d862010-11-12 02:50:30 +000010854 paste_info.x=(ssize_t) windows->image.x+x;
10855 paste_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +000010856 break;
10857 }
10858 default:
10859 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -070010860 if (image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010861 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10862 event.type);
10863 break;
10864 }
10865 }
10866 } while ((state & ExitState) == 0);
10867 (void) XSelectInput(display,windows->image.id,
10868 windows->image.attributes.event_mask);
10869 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10870 XSetCursorState(display,windows,MagickFalse);
10871 (void) XFreeCursor(display,cursor);
10872 if ((state & EscapeState) != 0)
10873 return(MagickTrue);
10874 /*
10875 Image pasting is relative to image configuration.
10876 */
10877 XSetCursorState(display,windows,MagickTrue);
10878 XCheckRefreshWindows(display,windows);
10879 width=(unsigned int) image->columns;
10880 height=(unsigned int) image->rows;
10881 x=0;
10882 y=0;
10883 if (windows->image.crop_geometry != (char *) NULL)
10884 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
cristya19f1d72012-08-07 18:24:38 +000010885 scale_factor=(double) width/windows->image.ximage->width;
cristy3ed852e2009-09-05 21:47:34 +000010886 paste_info.x+=x;
cristy49e2d862010-11-12 02:50:30 +000010887 paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +000010888 paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
cristya19f1d72012-08-07 18:24:38 +000010889 scale_factor=(double) height/windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +000010890 paste_info.y+=y;
cristy49e2d862010-11-12 02:50:30 +000010891 paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
cristy3ed852e2009-09-05 21:47:34 +000010892 paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10893 /*
10894 Paste image with X Image window.
10895 */
cristy39172402012-03-30 13:04:39 +000010896 (void) CompositeImage(image,paste_image,compose,MagickTrue,paste_info.x,
cristyfeb3e962012-03-29 17:25:55 +000010897 paste_info.y,exception);
cristy3ed852e2009-09-05 21:47:34 +000010898 paste_image=DestroyImage(paste_image);
10899 XSetCursorState(display,windows,MagickFalse);
10900 /*
10901 Update image colormap.
10902 */
cristy6710d842011-10-20 23:23:00 +000010903 XConfigureImageColormap(display,resource_info,windows,image,exception);
cristy051718b2011-08-28 22:49:25 +000010904 (void) XConfigureImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000010905 return(MagickTrue);
10906}
10907
10908/*
10909%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10910% %
10911% %
10912% %
10913+ X P r i n t I m a g e %
10914% %
10915% %
10916% %
10917%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10918%
10919% XPrintImage() prints an image to a Postscript printer.
10920%
10921% The format of the XPrintImage method is:
10922%
10923% MagickBooleanType XPrintImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000010924% XResourceInfo *resource_info,XWindows *windows,Image *image,
10925% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010926%
10927% A description of each parameter follows:
10928%
10929% o display: Specifies a connection to an X server; returned from
10930% XOpenDisplay.
10931%
10932% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10933%
10934% o windows: Specifies a pointer to a XWindows structure.
10935%
10936% o image: the image.
10937%
cristy051718b2011-08-28 22:49:25 +000010938% o exception: return any errors or warnings in this structure.
10939%
cristy3ed852e2009-09-05 21:47:34 +000010940*/
10941static MagickBooleanType XPrintImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000010942 XResourceInfo *resource_info,XWindows *windows,Image *image,
10943 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010944{
10945 char
cristy151b66d2015-04-15 10:50:31 +000010946 filename[MagickPathExtent],
10947 geometry[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +000010948
Cristyd93b2e62019-05-22 19:45:01 -040010949 const char
10950 *const PageSizes[] =
10951 {
10952 "Letter",
10953 "Tabloid",
10954 "Ledger",
10955 "Legal",
10956 "Statement",
10957 "Executive",
10958 "A3",
10959 "A4",
10960 "A5",
10961 "B4",
10962 "B5",
10963 "Folio",
10964 "Quarto",
10965 "10x14",
10966 (char *) NULL
10967 };
10968
cristy3ed852e2009-09-05 21:47:34 +000010969 Image
10970 *print_image;
10971
10972 ImageInfo
10973 *image_info;
10974
10975 MagickStatusType
10976 status;
10977
10978 /*
10979 Request Postscript page geometry from user.
10980 */
10981 image_info=CloneImageInfo(resource_info->image_info);
cristy151b66d2015-04-15 10:50:31 +000010982 (void) FormatLocaleString(geometry,MagickPathExtent,"Letter");
cristy3ed852e2009-09-05 21:47:34 +000010983 if (image_info->page != (char *) NULL)
cristy151b66d2015-04-15 10:50:31 +000010984 (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +000010985 XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10986 "Select Postscript Page Geometry:",geometry);
10987 if (*geometry == '\0')
10988 return(MagickTrue);
10989 image_info->page=GetPageGeometry(geometry);
10990 /*
10991 Apply image transforms.
10992 */
10993 XSetCursorState(display,windows,MagickTrue);
10994 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +000010995 print_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000010996 if (print_image == (Image *) NULL)
10997 return(MagickFalse);
cristy151b66d2015-04-15 10:50:31 +000010998 (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
cristy3ed852e2009-09-05 21:47:34 +000010999 windows->image.ximage->width,windows->image.ximage->height);
cristye941a752011-10-15 01:52:48 +000011000 (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11001 exception);
cristy3ed852e2009-09-05 21:47:34 +000011002 /*
11003 Print image.
11004 */
11005 (void) AcquireUniqueFilename(filename);
cristy151b66d2015-04-15 10:50:31 +000011006 (void) FormatLocaleString(print_image->filename,MagickPathExtent,"print:%s",
cristy3ed852e2009-09-05 21:47:34 +000011007 filename);
cristy051718b2011-08-28 22:49:25 +000011008 status=WriteImage(image_info,print_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011009 (void) RelinquishUniqueFileResource(filename);
11010 print_image=DestroyImage(print_image);
11011 image_info=DestroyImageInfo(image_info);
11012 XSetCursorState(display,windows,MagickFalse);
dirkb9dbc292015-07-26 09:50:00 +000011013 return(status != 0 ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +000011014}
11015
11016/*
11017%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11018% %
11019% %
11020% %
11021+ X R O I I m a g e %
11022% %
11023% %
11024% %
11025%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11026%
11027% XROIImage() applies an image processing technique to a region of interest.
11028%
11029% The format of the XROIImage method is:
11030%
11031% MagickBooleanType XROIImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000011032% XResourceInfo *resource_info,XWindows *windows,Image **image,
11033% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011034%
11035% A description of each parameter follows:
11036%
11037% o display: Specifies a connection to an X server; returned from
11038% XOpenDisplay.
11039%
11040% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11041%
11042% o windows: Specifies a pointer to a XWindows structure.
11043%
11044% o image: the image; returned from ReadImage.
11045%
cristy051718b2011-08-28 22:49:25 +000011046% o exception: return any errors or warnings in this structure.
11047%
cristy3ed852e2009-09-05 21:47:34 +000011048*/
11049static MagickBooleanType XROIImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000011050 XResourceInfo *resource_info,XWindows *windows,Image **image,
11051 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011052{
11053#define ApplyMenus 7
11054
Cristyd93b2e62019-05-22 19:45:01 -040011055 const char
11056 *const ROIMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000011057 {
11058 "Help",
11059 "Dismiss",
11060 (char *) NULL
11061 },
Cristyd93b2e62019-05-22 19:45:01 -040011062 *const ApplyMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000011063 {
11064 "File",
11065 "Edit",
11066 "Transform",
11067 "Enhance",
11068 "Effects",
11069 "F/X",
11070 "Miscellany",
11071 "Help",
11072 "Dismiss",
11073 (char *) NULL
11074 },
Cristyd93b2e62019-05-22 19:45:01 -040011075 *const FileMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000011076 {
11077 "Save...",
11078 "Print...",
11079 (char *) NULL
11080 },
Cristyd93b2e62019-05-22 19:45:01 -040011081 *const EditMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000011082 {
11083 "Undo",
11084 "Redo",
11085 (char *) NULL
11086 },
Cristyd93b2e62019-05-22 19:45:01 -040011087 *const TransformMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000011088 {
11089 "Flop",
11090 "Flip",
11091 "Rotate Right",
11092 "Rotate Left",
11093 (char *) NULL
11094 },
Cristyd93b2e62019-05-22 19:45:01 -040011095 *const EnhanceMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000011096 {
11097 "Hue...",
11098 "Saturation...",
11099 "Brightness...",
11100 "Gamma...",
11101 "Spiff",
11102 "Dull",
11103 "Contrast Stretch...",
11104 "Sigmoidal Contrast...",
11105 "Normalize",
11106 "Equalize",
11107 "Negate",
11108 "Grayscale",
11109 "Map...",
11110 "Quantize...",
11111 (char *) NULL
11112 },
Cristyd93b2e62019-05-22 19:45:01 -040011113 *const EffectsMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000011114 {
11115 "Despeckle",
11116 "Emboss",
11117 "Reduce Noise",
11118 "Add Noise",
11119 "Sharpen...",
11120 "Blur...",
11121 "Threshold...",
11122 "Edge Detect...",
11123 "Spread...",
11124 "Shade...",
11125 "Raise...",
11126 "Segment...",
11127 (char *) NULL
11128 },
Cristyd93b2e62019-05-22 19:45:01 -040011129 *const FXMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000011130 {
11131 "Solarize...",
11132 "Sepia Tone...",
11133 "Swirl...",
11134 "Implode...",
11135 "Vignette...",
11136 "Wave...",
11137 "Oil Paint...",
11138 "Charcoal Draw...",
11139 (char *) NULL
11140 },
Cristyd93b2e62019-05-22 19:45:01 -040011141 *const MiscellanyMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000011142 {
11143 "Image Info",
11144 "Zoom Image",
11145 "Show Preview...",
11146 "Show Histogram",
11147 "Show Matte",
11148 (char *) NULL
11149 };
11150
Cristyd93b2e62019-05-22 19:45:01 -040011151 const char
11152 *const *Menus[ApplyMenus] =
cristy3ed852e2009-09-05 21:47:34 +000011153 {
11154 FileMenu,
11155 EditMenu,
11156 TransformMenu,
11157 EnhanceMenu,
11158 EffectsMenu,
11159 FXMenu,
11160 MiscellanyMenu
11161 };
11162
11163 static const CommandType
11164 ApplyCommands[] =
11165 {
11166 NullCommand,
11167 NullCommand,
11168 NullCommand,
11169 NullCommand,
11170 NullCommand,
11171 NullCommand,
11172 NullCommand,
11173 HelpCommand,
11174 QuitCommand
11175 },
11176 FileCommands[] =
11177 {
11178 SaveCommand,
11179 PrintCommand
11180 },
11181 EditCommands[] =
11182 {
11183 UndoCommand,
11184 RedoCommand
11185 },
11186 TransformCommands[] =
11187 {
11188 FlopCommand,
11189 FlipCommand,
11190 RotateRightCommand,
11191 RotateLeftCommand
11192 },
11193 EnhanceCommands[] =
11194 {
11195 HueCommand,
11196 SaturationCommand,
11197 BrightnessCommand,
11198 GammaCommand,
11199 SpiffCommand,
11200 DullCommand,
11201 ContrastStretchCommand,
11202 SigmoidalContrastCommand,
11203 NormalizeCommand,
11204 EqualizeCommand,
11205 NegateCommand,
11206 GrayscaleCommand,
11207 MapCommand,
11208 QuantizeCommand
11209 },
11210 EffectsCommands[] =
11211 {
11212 DespeckleCommand,
11213 EmbossCommand,
11214 ReduceNoiseCommand,
11215 AddNoiseCommand,
11216 SharpenCommand,
11217 BlurCommand,
11218 EdgeDetectCommand,
11219 SpreadCommand,
11220 ShadeCommand,
11221 RaiseCommand,
11222 SegmentCommand
11223 },
11224 FXCommands[] =
11225 {
11226 SolarizeCommand,
11227 SepiaToneCommand,
11228 SwirlCommand,
11229 ImplodeCommand,
11230 VignetteCommand,
11231 WaveCommand,
11232 OilPaintCommand,
11233 CharcoalDrawCommand
11234 },
11235 MiscellanyCommands[] =
11236 {
11237 InfoCommand,
11238 ZoomCommand,
11239 ShowPreviewCommand,
11240 ShowHistogramCommand,
11241 ShowMatteCommand
11242 },
11243 ROICommands[] =
11244 {
11245 ROIHelpCommand,
11246 ROIDismissCommand
11247 };
11248
11249 static const CommandType
11250 *Commands[ApplyMenus] =
11251 {
11252 FileCommands,
11253 EditCommands,
11254 TransformCommands,
11255 EnhanceCommands,
11256 EffectsCommands,
11257 FXCommands,
11258 MiscellanyCommands
11259 };
11260
11261 char
cristy151b66d2015-04-15 10:50:31 +000011262 command[MagickPathExtent],
11263 text[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +000011264
11265 CommandType
11266 command_type;
11267
11268 Cursor
11269 cursor;
11270
11271 Image
11272 *roi_image;
11273
11274 int
11275 entry,
11276 id,
11277 x,
11278 y;
11279
cristya19f1d72012-08-07 18:24:38 +000011280 double
cristy3ed852e2009-09-05 21:47:34 +000011281 scale_factor;
11282
11283 MagickProgressMonitor
11284 progress_monitor;
11285
11286 RectangleInfo
11287 crop_info,
11288 highlight_info,
11289 roi_info;
11290
11291 unsigned int
11292 height,
11293 width;
11294
cristybb503372010-05-27 20:51:26 +000011295 size_t
cristy3ed852e2009-09-05 21:47:34 +000011296 state;
11297
11298 XEvent
11299 event;
11300
11301 /*
11302 Map Command widget.
11303 */
11304 (void) CloneString(&windows->command.name,"ROI");
11305 windows->command.data=0;
11306 (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11307 (void) XMapRaised(display,windows->command.id);
11308 XClientMessage(display,windows->image.id,windows->im_protocols,
11309 windows->im_update_widget,CurrentTime);
11310 /*
11311 Track pointer until button 1 is pressed.
11312 */
11313 XQueryPosition(display,windows->image.id,&x,&y);
11314 (void) XSelectInput(display,windows->image.id,
11315 windows->image.attributes.event_mask | PointerMotionMask);
cristy49e2d862010-11-12 02:50:30 +000011316 roi_info.x=(ssize_t) windows->image.x+x;
11317 roi_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +000011318 roi_info.width=0;
11319 roi_info.height=0;
11320 cursor=XCreateFontCursor(display,XC_fleur);
11321 state=DefaultState;
11322 do
11323 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -070011324 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000011325 {
11326 /*
11327 Display pointer position.
11328 */
cristy151b66d2015-04-15 10:50:31 +000011329 (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
cristyf2faecf2010-05-28 19:19:36 +000011330 (long) roi_info.x,(long) roi_info.y);
cristy3ed852e2009-09-05 21:47:34 +000011331 XInfoWidget(display,windows,text);
11332 }
11333 /*
11334 Wait for next event.
11335 */
cristy6710d842011-10-20 23:23:00 +000011336 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +000011337 if (event.xany.window == windows->command.id)
11338 {
11339 /*
11340 Select a command from the Command widget.
11341 */
11342 id=XCommandWidget(display,windows,ROIMenu,&event);
11343 if (id < 0)
11344 continue;
11345 switch (ROICommands[id])
11346 {
11347 case ROIHelpCommand:
11348 {
Cristy6970baa2019-04-20 21:23:54 -040011349 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +000011350 "Help Viewer - Region of Interest",ImageROIHelp);
11351 break;
11352 }
11353 case ROIDismissCommand:
11354 {
11355 /*
11356 Prematurely exit.
11357 */
11358 state|=EscapeState;
11359 state|=ExitState;
11360 break;
11361 }
11362 default:
11363 break;
11364 }
11365 continue;
11366 }
11367 switch (event.type)
11368 {
11369 case ButtonPress:
11370 {
11371 if (event.xbutton.button != Button1)
11372 break;
11373 if (event.xbutton.window != windows->image.id)
11374 break;
11375 /*
11376 Note first corner of region of interest rectangle-- exit loop.
11377 */
11378 (void) XCheckDefineCursor(display,windows->image.id,cursor);
cristy49e2d862010-11-12 02:50:30 +000011379 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11380 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000011381 state|=ExitState;
11382 break;
11383 }
11384 case ButtonRelease:
11385 break;
11386 case Expose:
11387 break;
11388 case KeyPress:
11389 {
11390 KeySym
11391 key_symbol;
11392
11393 if (event.xkey.window != windows->image.id)
11394 break;
11395 /*
11396 Respond to a user key press.
11397 */
11398 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11399 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11400 switch ((int) key_symbol)
11401 {
11402 case XK_Escape:
11403 case XK_F20:
11404 {
11405 /*
11406 Prematurely exit.
11407 */
11408 state|=EscapeState;
11409 state|=ExitState;
11410 break;
11411 }
11412 case XK_F1:
11413 case XK_Help:
11414 {
Cristy6970baa2019-04-20 21:23:54 -040011415 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +000011416 "Help Viewer - Region of Interest",ImageROIHelp);
11417 break;
11418 }
11419 default:
11420 {
11421 (void) XBell(display,0);
11422 break;
11423 }
11424 }
11425 break;
11426 }
11427 case MotionNotify:
11428 {
11429 /*
11430 Map and unmap Info widget as text cursor crosses its boundaries.
11431 */
11432 x=event.xmotion.x;
11433 y=event.xmotion.y;
Elliott Hughes5d41fba2021-04-12 16:36:42 -070011434 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000011435 {
11436 if ((x < (int) (windows->info.x+windows->info.width)) &&
11437 (y < (int) (windows->info.y+windows->info.height)))
11438 (void) XWithdrawWindow(display,windows->info.id,
11439 windows->info.screen);
11440 }
11441 else
11442 if ((x > (int) (windows->info.x+windows->info.width)) ||
11443 (y > (int) (windows->info.y+windows->info.height)))
11444 (void) XMapWindow(display,windows->info.id);
cristy49e2d862010-11-12 02:50:30 +000011445 roi_info.x=(ssize_t) windows->image.x+x;
11446 roi_info.y=(ssize_t) windows->image.y+y;
cristy3ed852e2009-09-05 21:47:34 +000011447 break;
11448 }
11449 default:
11450 break;
11451 }
11452 } while ((state & ExitState) == 0);
11453 (void) XSelectInput(display,windows->image.id,
11454 windows->image.attributes.event_mask);
11455 if ((state & EscapeState) != 0)
11456 {
11457 /*
11458 User want to exit without region of interest.
11459 */
11460 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11461 (void) XFreeCursor(display,cursor);
11462 return(MagickTrue);
11463 }
11464 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11465 do
11466 {
11467 /*
11468 Size rectangle as pointer moves until the mouse button is released.
11469 */
11470 x=(int) roi_info.x;
11471 y=(int) roi_info.y;
11472 roi_info.width=0;
11473 roi_info.height=0;
11474 state=DefaultState;
11475 do
11476 {
11477 highlight_info=roi_info;
11478 highlight_info.x=roi_info.x-windows->image.x;
11479 highlight_info.y=roi_info.y-windows->image.y;
11480 if ((highlight_info.width > 3) && (highlight_info.height > 3))
11481 {
11482 /*
11483 Display info and draw region of interest rectangle.
11484 */
dirk5d0a4412016-01-05 22:28:51 +010011485 if (windows->info.mapped == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000011486 (void) XMapWindow(display,windows->info.id);
cristy151b66d2015-04-15 10:50:31 +000011487 (void) FormatLocaleString(text,MagickPathExtent,
cristy6d8abba2010-06-03 01:10:47 +000011488 " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +000011489 roi_info.height,(double) roi_info.x,(double) roi_info.y);
cristy3ed852e2009-09-05 21:47:34 +000011490 XInfoWidget(display,windows,text);
11491 XHighlightRectangle(display,windows->image.id,
11492 windows->image.highlight_context,&highlight_info);
11493 }
11494 else
Elliott Hughes5d41fba2021-04-12 16:36:42 -070011495 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000011496 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11497 /*
11498 Wait for next event.
11499 */
cristy6710d842011-10-20 23:23:00 +000011500 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +000011501 if ((highlight_info.width > 3) && (highlight_info.height > 3))
11502 XHighlightRectangle(display,windows->image.id,
11503 windows->image.highlight_context,&highlight_info);
11504 switch (event.type)
11505 {
11506 case ButtonPress:
11507 {
cristy49e2d862010-11-12 02:50:30 +000011508 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11509 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000011510 break;
11511 }
11512 case ButtonRelease:
11513 {
11514 /*
11515 User has committed to region of interest rectangle.
11516 */
cristy49e2d862010-11-12 02:50:30 +000011517 roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11518 roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
cristy3ed852e2009-09-05 21:47:34 +000011519 XSetCursorState(display,windows,MagickFalse);
11520 state|=ExitState;
11521 if (LocaleCompare(windows->command.name,"Apply") == 0)
11522 break;
11523 (void) CloneString(&windows->command.name,"Apply");
11524 windows->command.data=ApplyMenus;
11525 (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11526 break;
11527 }
11528 case Expose:
11529 break;
11530 case MotionNotify:
11531 {
cristy49e2d862010-11-12 02:50:30 +000011532 roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11533 roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
cristy3ed852e2009-09-05 21:47:34 +000011534 }
11535 default:
11536 break;
11537 }
11538 if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11539 ((state & ExitState) != 0))
11540 {
11541 /*
11542 Check boundary conditions.
11543 */
11544 if (roi_info.x < 0)
11545 roi_info.x=0;
11546 else
cristy49e2d862010-11-12 02:50:30 +000011547 if (roi_info.x > (ssize_t) windows->image.ximage->width)
11548 roi_info.x=(ssize_t) windows->image.ximage->width;
cristy3ed852e2009-09-05 21:47:34 +000011549 if ((int) roi_info.x < x)
11550 roi_info.width=(unsigned int) (x-roi_info.x);
11551 else
11552 {
11553 roi_info.width=(unsigned int) (roi_info.x-x);
cristy49e2d862010-11-12 02:50:30 +000011554 roi_info.x=(ssize_t) x;
cristy3ed852e2009-09-05 21:47:34 +000011555 }
11556 if (roi_info.y < 0)
11557 roi_info.y=0;
11558 else
cristy49e2d862010-11-12 02:50:30 +000011559 if (roi_info.y > (ssize_t) windows->image.ximage->height)
11560 roi_info.y=(ssize_t) windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +000011561 if ((int) roi_info.y < y)
11562 roi_info.height=(unsigned int) (y-roi_info.y);
11563 else
11564 {
11565 roi_info.height=(unsigned int) (roi_info.y-y);
cristy49e2d862010-11-12 02:50:30 +000011566 roi_info.y=(ssize_t) y;
cristy3ed852e2009-09-05 21:47:34 +000011567 }
11568 }
11569 } while ((state & ExitState) == 0);
11570 /*
11571 Wait for user to grab a corner of the rectangle or press return.
11572 */
11573 state=DefaultState;
11574 command_type=NullCommand;
cristy4d246fc2014-01-15 22:33:44 +000011575 crop_info.x=0;
11576 crop_info.y=0;
cristy3ed852e2009-09-05 21:47:34 +000011577 (void) XMapWindow(display,windows->info.id);
11578 do
11579 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -070011580 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000011581 {
11582 /*
11583 Display pointer position.
11584 */
cristy151b66d2015-04-15 10:50:31 +000011585 (void) FormatLocaleString(text,MagickPathExtent,
cristy6d8abba2010-06-03 01:10:47 +000011586 " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +000011587 roi_info.height,(double) roi_info.x,(double) roi_info.y);
cristy3ed852e2009-09-05 21:47:34 +000011588 XInfoWidget(display,windows,text);
11589 }
11590 highlight_info=roi_info;
11591 highlight_info.x=roi_info.x-windows->image.x;
11592 highlight_info.y=roi_info.y-windows->image.y;
11593 if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11594 {
11595 state|=EscapeState;
11596 state|=ExitState;
11597 break;
11598 }
11599 if ((state & UpdateRegionState) != 0)
11600 {
11601 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11602 switch (command_type)
11603 {
11604 case UndoCommand:
11605 case RedoCommand:
11606 {
11607 (void) XMagickCommand(display,resource_info,windows,command_type,
cristy051718b2011-08-28 22:49:25 +000011608 image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011609 break;
11610 }
11611 default:
11612 {
11613 /*
11614 Region of interest is relative to image configuration.
11615 */
11616 progress_monitor=SetImageProgressMonitor(*image,
11617 (MagickProgressMonitor) NULL,(*image)->client_data);
11618 crop_info=roi_info;
11619 width=(unsigned int) (*image)->columns;
11620 height=(unsigned int) (*image)->rows;
11621 x=0;
11622 y=0;
11623 if (windows->image.crop_geometry != (char *) NULL)
11624 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11625 &width,&height);
cristya19f1d72012-08-07 18:24:38 +000011626 scale_factor=(double) width/windows->image.ximage->width;
cristy3ed852e2009-09-05 21:47:34 +000011627 crop_info.x+=x;
cristy49e2d862010-11-12 02:50:30 +000011628 crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +000011629 crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
cristya19f1d72012-08-07 18:24:38 +000011630 scale_factor=(double)
cristy3ed852e2009-09-05 21:47:34 +000011631 height/windows->image.ximage->height;
11632 crop_info.y+=y;
cristy49e2d862010-11-12 02:50:30 +000011633 crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +000011634 crop_info.height=(unsigned int)
11635 (scale_factor*crop_info.height+0.5);
cristy051718b2011-08-28 22:49:25 +000011636 roi_image=CropImage(*image,&crop_info,exception);
cristy3ed852e2009-09-05 21:47:34 +000011637 (void) SetImageProgressMonitor(*image,progress_monitor,
11638 (*image)->client_data);
11639 if (roi_image == (Image *) NULL)
11640 continue;
11641 /*
11642 Apply image processing technique to the region of interest.
11643 */
11644 windows->image.orphan=MagickTrue;
11645 (void) XMagickCommand(display,resource_info,windows,command_type,
cristy051718b2011-08-28 22:49:25 +000011646 &roi_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011647 progress_monitor=SetImageProgressMonitor(*image,
11648 (MagickProgressMonitor) NULL,(*image)->client_data);
11649 (void) XMagickCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000011650 SaveToUndoBufferCommand,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011651 windows->image.orphan=MagickFalse;
cristyfeb3e962012-03-29 17:25:55 +000011652 (void) CompositeImage(*image,roi_image,CopyCompositeOp,
cristy39172402012-03-30 13:04:39 +000011653 MagickTrue,crop_info.x,crop_info.y,exception);
cristy3ed852e2009-09-05 21:47:34 +000011654 roi_image=DestroyImage(roi_image);
11655 (void) SetImageProgressMonitor(*image,progress_monitor,
11656 (*image)->client_data);
11657 break;
11658 }
11659 }
11660 if (command_type != InfoCommand)
11661 {
cristy6710d842011-10-20 23:23:00 +000011662 XConfigureImageColormap(display,resource_info,windows,*image,
11663 exception);
11664 (void) XConfigureImage(display,resource_info,windows,*image,
11665 exception);
cristy3ed852e2009-09-05 21:47:34 +000011666 }
11667 XCheckRefreshWindows(display,windows);
11668 XInfoWidget(display,windows,text);
11669 (void) XSetFunction(display,windows->image.highlight_context,
11670 GXinvert);
11671 state&=(~UpdateRegionState);
11672 }
11673 XHighlightRectangle(display,windows->image.id,
11674 windows->image.highlight_context,&highlight_info);
cristy6710d842011-10-20 23:23:00 +000011675 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +000011676 if (event.xany.window == windows->command.id)
11677 {
11678 /*
11679 Select a command from the Command widget.
11680 */
11681 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11682 command_type=NullCommand;
11683 id=XCommandWidget(display,windows,ApplyMenu,&event);
11684 if (id >= 0)
11685 {
cristy151b66d2015-04-15 10:50:31 +000011686 (void) CopyMagickString(command,ApplyMenu[id],MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +000011687 command_type=ApplyCommands[id];
11688 if (id < ApplyMenus)
11689 {
11690 /*
11691 Select a command from a pop-up menu.
11692 */
11693 entry=XMenuWidget(display,windows,ApplyMenu[id],
11694 (const char **) Menus[id],command);
11695 if (entry >= 0)
11696 {
11697 (void) CopyMagickString(command,Menus[id][entry],
cristy151b66d2015-04-15 10:50:31 +000011698 MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +000011699 command_type=Commands[id][entry];
11700 }
11701 }
11702 }
11703 (void) XSetFunction(display,windows->image.highlight_context,
11704 GXinvert);
11705 XHighlightRectangle(display,windows->image.id,
11706 windows->image.highlight_context,&highlight_info);
11707 if (command_type == HelpCommand)
11708 {
11709 (void) XSetFunction(display,windows->image.highlight_context,
11710 GXcopy);
Cristy6970baa2019-04-20 21:23:54 -040011711 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +000011712 "Help Viewer - Region of Interest",ImageROIHelp);
11713 (void) XSetFunction(display,windows->image.highlight_context,
11714 GXinvert);
11715 continue;
11716 }
11717 if (command_type == QuitCommand)
11718 {
11719 /*
11720 exit.
11721 */
11722 state|=EscapeState;
11723 state|=ExitState;
11724 continue;
11725 }
11726 if (command_type != NullCommand)
11727 state|=UpdateRegionState;
11728 continue;
11729 }
11730 XHighlightRectangle(display,windows->image.id,
11731 windows->image.highlight_context,&highlight_info);
11732 switch (event.type)
11733 {
11734 case ButtonPress:
11735 {
11736 x=windows->image.x;
11737 y=windows->image.y;
11738 if (event.xbutton.button != Button1)
11739 break;
11740 if (event.xbutton.window != windows->image.id)
11741 break;
11742 x=windows->image.x+event.xbutton.x;
11743 y=windows->image.y+event.xbutton.y;
11744 if ((x < (int) (roi_info.x+RoiDelta)) &&
11745 (x > (int) (roi_info.x-RoiDelta)) &&
11746 (y < (int) (roi_info.y+RoiDelta)) &&
11747 (y > (int) (roi_info.y-RoiDelta)))
11748 {
cristybb503372010-05-27 20:51:26 +000011749 roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11750 roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
cristy3ed852e2009-09-05 21:47:34 +000011751 state|=UpdateConfigurationState;
11752 break;
11753 }
11754 if ((x < (int) (roi_info.x+RoiDelta)) &&
11755 (x > (int) (roi_info.x-RoiDelta)) &&
11756 (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11757 (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11758 {
cristybb503372010-05-27 20:51:26 +000011759 roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
cristy3ed852e2009-09-05 21:47:34 +000011760 state|=UpdateConfigurationState;
11761 break;
11762 }
11763 if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11764 (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11765 (y < (int) (roi_info.y+RoiDelta)) &&
11766 (y > (int) (roi_info.y-RoiDelta)))
11767 {
cristybb503372010-05-27 20:51:26 +000011768 roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
cristy3ed852e2009-09-05 21:47:34 +000011769 state|=UpdateConfigurationState;
11770 break;
11771 }
11772 if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11773 (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11774 (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11775 (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11776 {
11777 state|=UpdateConfigurationState;
11778 break;
11779 }
11780 }
11781 case ButtonRelease:
11782 {
11783 if (event.xbutton.window == windows->pan.id)
11784 if ((highlight_info.x != crop_info.x-windows->image.x) ||
11785 (highlight_info.y != crop_info.y-windows->image.y))
11786 XHighlightRectangle(display,windows->image.id,
11787 windows->image.highlight_context,&highlight_info);
11788 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11789 event.xbutton.time);
11790 break;
11791 }
11792 case Expose:
11793 {
11794 if (event.xexpose.window == windows->image.id)
11795 if (event.xexpose.count == 0)
11796 {
11797 event.xexpose.x=(int) highlight_info.x;
11798 event.xexpose.y=(int) highlight_info.y;
11799 event.xexpose.width=(int) highlight_info.width;
11800 event.xexpose.height=(int) highlight_info.height;
11801 XRefreshWindow(display,&windows->image,&event);
11802 }
11803 if (event.xexpose.window == windows->info.id)
11804 if (event.xexpose.count == 0)
11805 XInfoWidget(display,windows,text);
11806 break;
11807 }
11808 case KeyPress:
11809 {
11810 KeySym
11811 key_symbol;
11812
11813 if (event.xkey.window != windows->image.id)
11814 break;
11815 /*
11816 Respond to a user key press.
11817 */
11818 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11819 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11820 switch ((int) key_symbol)
11821 {
11822 case XK_Shift_L:
11823 case XK_Shift_R:
11824 break;
11825 case XK_Escape:
11826 case XK_F20:
11827 state|=EscapeState;
11828 case XK_Return:
11829 {
11830 state|=ExitState;
11831 break;
11832 }
11833 case XK_Home:
11834 case XK_KP_Home:
11835 {
cristybb503372010-05-27 20:51:26 +000011836 roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
cristy49e2d862010-11-12 02:50:30 +000011837 roi_info.y=(ssize_t) (windows->image.height/2L-
11838 roi_info.height/2L);
cristy3ed852e2009-09-05 21:47:34 +000011839 break;
11840 }
11841 case XK_Left:
11842 case XK_KP_Left:
11843 {
11844 roi_info.x--;
11845 break;
11846 }
11847 case XK_Up:
11848 case XK_KP_Up:
11849 case XK_Next:
11850 {
11851 roi_info.y--;
11852 break;
11853 }
11854 case XK_Right:
11855 case XK_KP_Right:
11856 {
11857 roi_info.x++;
11858 break;
11859 }
11860 case XK_Prior:
11861 case XK_Down:
11862 case XK_KP_Down:
11863 {
11864 roi_info.y++;
11865 break;
11866 }
11867 case XK_F1:
11868 case XK_Help:
11869 {
11870 (void) XSetFunction(display,windows->image.highlight_context,
11871 GXcopy);
Cristy6970baa2019-04-20 21:23:54 -040011872 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +000011873 "Help Viewer - Region of Interest",ImageROIHelp);
11874 (void) XSetFunction(display,windows->image.highlight_context,
11875 GXinvert);
11876 break;
11877 }
11878 default:
11879 {
11880 command_type=XImageWindowCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000011881 event.xkey.state,key_symbol,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011882 if (command_type != NullCommand)
11883 state|=UpdateRegionState;
11884 break;
11885 }
11886 }
11887 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11888 event.xkey.time);
11889 break;
11890 }
11891 case KeyRelease:
11892 break;
11893 case MotionNotify:
11894 {
11895 if (event.xbutton.window != windows->image.id)
11896 break;
11897 /*
11898 Map and unmap Info widget as text cursor crosses its boundaries.
11899 */
11900 x=event.xmotion.x;
11901 y=event.xmotion.y;
Elliott Hughes5d41fba2021-04-12 16:36:42 -070011902 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000011903 {
11904 if ((x < (int) (windows->info.x+windows->info.width)) &&
11905 (y < (int) (windows->info.y+windows->info.height)))
11906 (void) XWithdrawWindow(display,windows->info.id,
11907 windows->info.screen);
11908 }
11909 else
11910 if ((x > (int) (windows->info.x+windows->info.width)) ||
11911 (y > (int) (windows->info.y+windows->info.height)))
11912 (void) XMapWindow(display,windows->info.id);
cristy49e2d862010-11-12 02:50:30 +000011913 roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11914 roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
cristy3ed852e2009-09-05 21:47:34 +000011915 break;
11916 }
11917 case SelectionRequest:
11918 {
11919 XSelectionEvent
11920 notify;
11921
11922 XSelectionRequestEvent
11923 *request;
11924
11925 /*
11926 Set primary selection.
11927 */
cristy151b66d2015-04-15 10:50:31 +000011928 (void) FormatLocaleString(text,MagickPathExtent,
cristy6d8abba2010-06-03 01:10:47 +000011929 "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
cristye8c25f92010-06-03 00:53:06 +000011930 roi_info.height,(double) roi_info.x,(double) roi_info.y);
cristy3ed852e2009-09-05 21:47:34 +000011931 request=(&(event.xselectionrequest));
11932 (void) XChangeProperty(request->display,request->requestor,
11933 request->property,request->target,8,PropModeReplace,
11934 (unsigned char *) text,(int) strlen(text));
11935 notify.type=SelectionNotify;
11936 notify.display=request->display;
11937 notify.requestor=request->requestor;
11938 notify.selection=request->selection;
11939 notify.target=request->target;
11940 notify.time=request->time;
11941 if (request->property == None)
11942 notify.property=request->target;
11943 else
11944 notify.property=request->property;
11945 (void) XSendEvent(request->display,request->requestor,False,0,
11946 (XEvent *) &notify);
11947 }
11948 default:
11949 break;
11950 }
11951 if ((state & UpdateConfigurationState) != 0)
11952 {
11953 (void) XPutBackEvent(display,&event);
11954 (void) XCheckDefineCursor(display,windows->image.id,cursor);
11955 break;
11956 }
11957 } while ((state & ExitState) == 0);
11958 } while ((state & ExitState) == 0);
11959 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11960 XSetCursorState(display,windows,MagickFalse);
11961 if ((state & EscapeState) != 0)
11962 return(MagickTrue);
11963 return(MagickTrue);
11964}
11965
11966/*
11967%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11968% %
11969% %
11970% %
11971+ X R o t a t e I m a g e %
11972% %
11973% %
11974% %
11975%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11976%
11977% XRotateImage() rotates the X image. If the degrees parameter if zero, the
11978% rotation angle is computed from the slope of a line drawn by the user.
11979%
11980% The format of the XRotateImage method is:
11981%
11982% MagickBooleanType XRotateImage(Display *display,
11983% XResourceInfo *resource_info,XWindows *windows,double degrees,
cristy051718b2011-08-28 22:49:25 +000011984% Image **image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011985%
11986% A description of each parameter follows:
11987%
11988% o display: Specifies a connection to an X server; returned from
11989% XOpenDisplay.
11990%
11991% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11992%
11993% o windows: Specifies a pointer to a XWindows structure.
11994%
11995% o degrees: Specifies the number of degrees to rotate the image.
11996%
11997% o image: the image.
11998%
cristy051718b2011-08-28 22:49:25 +000011999% o exception: return any errors or warnings in this structure.
12000%
cristy3ed852e2009-09-05 21:47:34 +000012001*/
12002static MagickBooleanType XRotateImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000012003 XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12004 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012005{
Cristyd93b2e62019-05-22 19:45:01 -040012006 const char
12007 *const RotateMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000012008 {
12009 "Pixel Color",
12010 "Direction",
12011 "Help",
12012 "Dismiss",
12013 (char *) NULL
12014 };
12015
12016 static ModeType
12017 direction = HorizontalRotateCommand;
12018
12019 static const ModeType
12020 DirectionCommands[] =
12021 {
12022 HorizontalRotateCommand,
12023 VerticalRotateCommand
12024 },
12025 RotateCommands[] =
12026 {
12027 RotateColorCommand,
12028 RotateDirectionCommand,
12029 RotateHelpCommand,
12030 RotateDismissCommand
12031 };
12032
12033 static unsigned int
12034 pen_id = 0;
12035
12036 char
cristy151b66d2015-04-15 10:50:31 +000012037 command[MagickPathExtent],
12038 text[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +000012039
12040 Image
12041 *rotate_image;
12042
12043 int
12044 id,
12045 x,
12046 y;
12047
cristya19f1d72012-08-07 18:24:38 +000012048 double
cristy3ed852e2009-09-05 21:47:34 +000012049 normalized_degrees;
12050
Cristyf2dc1dd2020-12-28 13:59:26 -050012051 int
cristy3ed852e2009-09-05 21:47:34 +000012052 i;
12053
12054 unsigned int
12055 height,
12056 rotations,
12057 width;
12058
12059 if (degrees == 0.0)
12060 {
12061 unsigned int
12062 distance;
12063
cristybb503372010-05-27 20:51:26 +000012064 size_t
cristy3ed852e2009-09-05 21:47:34 +000012065 state;
12066
12067 XEvent
12068 event;
12069
12070 XSegment
12071 rotate_info;
12072
12073 /*
12074 Map Command widget.
12075 */
12076 (void) CloneString(&windows->command.name,"Rotate");
12077 windows->command.data=2;
12078 (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12079 (void) XMapRaised(display,windows->command.id);
12080 XClientMessage(display,windows->image.id,windows->im_protocols,
12081 windows->im_update_widget,CurrentTime);
12082 /*
12083 Wait for first button press.
12084 */
12085 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12086 XQueryPosition(display,windows->image.id,&x,&y);
12087 rotate_info.x1=x;
12088 rotate_info.y1=y;
12089 rotate_info.x2=x;
12090 rotate_info.y2=y;
12091 state=DefaultState;
12092 do
12093 {
12094 XHighlightLine(display,windows->image.id,
12095 windows->image.highlight_context,&rotate_info);
12096 /*
12097 Wait for next event.
12098 */
cristy6710d842011-10-20 23:23:00 +000012099 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +000012100 XHighlightLine(display,windows->image.id,
12101 windows->image.highlight_context,&rotate_info);
12102 if (event.xany.window == windows->command.id)
12103 {
12104 /*
12105 Select a command from the Command widget.
12106 */
12107 id=XCommandWidget(display,windows,RotateMenu,&event);
12108 if (id < 0)
12109 continue;
12110 (void) XSetFunction(display,windows->image.highlight_context,
12111 GXcopy);
12112 switch (RotateCommands[id])
12113 {
12114 case RotateColorCommand:
12115 {
12116 const char
12117 *ColorMenu[MaxNumberPens];
12118
12119 int
12120 pen_number;
12121
12122 XColor
12123 color;
12124
12125 /*
12126 Initialize menu selections.
12127 */
12128 for (i=0; i < (int) (MaxNumberPens-2); i++)
12129 ColorMenu[i]=resource_info->pen_colors[i];
12130 ColorMenu[MaxNumberPens-2]="Browser...";
12131 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12132 /*
12133 Select a pen color from the pop-up menu.
12134 */
12135 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12136 (const char **) ColorMenu,command);
12137 if (pen_number < 0)
12138 break;
12139 if (pen_number == (MaxNumberPens-2))
12140 {
12141 static char
cristy151b66d2015-04-15 10:50:31 +000012142 color_name[MagickPathExtent] = "gray";
cristy3ed852e2009-09-05 21:47:34 +000012143
12144 /*
12145 Select a pen color from a dialog.
12146 */
12147 resource_info->pen_colors[pen_number]=color_name;
12148 XColorBrowserWidget(display,windows,"Select",color_name);
12149 if (*color_name == '\0')
12150 break;
12151 }
12152 /*
12153 Set pen color.
12154 */
12155 (void) XParseColor(display,windows->map_info->colormap,
12156 resource_info->pen_colors[pen_number],&color);
12157 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12158 (unsigned int) MaxColors,&color);
12159 windows->pixel_info->pen_colors[pen_number]=color;
12160 pen_id=(unsigned int) pen_number;
12161 break;
12162 }
12163 case RotateDirectionCommand:
12164 {
Cristyd93b2e62019-05-22 19:45:01 -040012165 const char
Cristy6970baa2019-04-20 21:23:54 -040012166 *Directions[] =
cristy3ed852e2009-09-05 21:47:34 +000012167 {
12168 "horizontal",
12169 "vertical",
12170 (char *) NULL,
12171 };
12172
12173 /*
12174 Select a command from the pop-up menu.
12175 */
12176 id=XMenuWidget(display,windows,RotateMenu[id],
12177 Directions,command);
12178 if (id >= 0)
12179 direction=DirectionCommands[id];
12180 break;
12181 }
12182 case RotateHelpCommand:
12183 {
Cristy6970baa2019-04-20 21:23:54 -040012184 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +000012185 "Help Viewer - Image Rotation",ImageRotateHelp);
12186 break;
12187 }
12188 case RotateDismissCommand:
12189 {
12190 /*
12191 Prematurely exit.
12192 */
12193 state|=EscapeState;
12194 state|=ExitState;
12195 break;
12196 }
12197 default:
12198 break;
12199 }
12200 (void) XSetFunction(display,windows->image.highlight_context,
12201 GXinvert);
12202 continue;
12203 }
12204 switch (event.type)
12205 {
12206 case ButtonPress:
12207 {
12208 if (event.xbutton.button != Button1)
12209 break;
12210 if (event.xbutton.window != windows->image.id)
12211 break;
12212 /*
12213 exit loop.
12214 */
12215 (void) XSetFunction(display,windows->image.highlight_context,
12216 GXcopy);
12217 rotate_info.x1=event.xbutton.x;
12218 rotate_info.y1=event.xbutton.y;
12219 state|=ExitState;
12220 break;
12221 }
12222 case ButtonRelease:
12223 break;
12224 case Expose:
12225 break;
12226 case KeyPress:
12227 {
12228 char
cristy151b66d2015-04-15 10:50:31 +000012229 command[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +000012230
12231 KeySym
12232 key_symbol;
12233
12234 if (event.xkey.window != windows->image.id)
12235 break;
12236 /*
12237 Respond to a user key press.
12238 */
12239 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12240 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12241 switch ((int) key_symbol)
12242 {
12243 case XK_Escape:
12244 case XK_F20:
12245 {
12246 /*
12247 Prematurely exit.
12248 */
12249 state|=EscapeState;
12250 state|=ExitState;
12251 break;
12252 }
12253 case XK_F1:
12254 case XK_Help:
12255 {
12256 (void) XSetFunction(display,windows->image.highlight_context,
12257 GXcopy);
Cristy6970baa2019-04-20 21:23:54 -040012258 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +000012259 "Help Viewer - Image Rotation",ImageRotateHelp);
12260 (void) XSetFunction(display,windows->image.highlight_context,
12261 GXinvert);
12262 break;
12263 }
12264 default:
12265 {
12266 (void) XBell(display,0);
12267 break;
12268 }
12269 }
12270 break;
12271 }
12272 case MotionNotify:
12273 {
12274 rotate_info.x1=event.xmotion.x;
12275 rotate_info.y1=event.xmotion.y;
12276 }
12277 }
12278 rotate_info.x2=rotate_info.x1;
12279 rotate_info.y2=rotate_info.y1;
12280 if (direction == HorizontalRotateCommand)
12281 rotate_info.x2+=32;
12282 else
12283 rotate_info.y2-=32;
12284 } while ((state & ExitState) == 0);
12285 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12286 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12287 if ((state & EscapeState) != 0)
12288 return(MagickTrue);
12289 /*
12290 Draw line as pointer moves until the mouse button is released.
12291 */
12292 distance=0;
12293 (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12294 state=DefaultState;
12295 do
12296 {
12297 if (distance > 9)
12298 {
12299 /*
12300 Display info and draw rotation line.
12301 */
dirk5d0a4412016-01-05 22:28:51 +010012302 if (windows->info.mapped == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000012303 (void) XMapWindow(display,windows->info.id);
cristy151b66d2015-04-15 10:50:31 +000012304 (void) FormatLocaleString(text,MagickPathExtent," %g",
cristy3ed852e2009-09-05 21:47:34 +000012305 direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12306 XInfoWidget(display,windows,text);
12307 XHighlightLine(display,windows->image.id,
12308 windows->image.highlight_context,&rotate_info);
12309 }
12310 else
Elliott Hughes5d41fba2021-04-12 16:36:42 -070012311 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000012312 (void) XWithdrawWindow(display,windows->info.id,
12313 windows->info.screen);
12314 /*
12315 Wait for next event.
12316 */
cristy6710d842011-10-20 23:23:00 +000012317 XScreenEvent(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +000012318 if (distance > 9)
12319 XHighlightLine(display,windows->image.id,
12320 windows->image.highlight_context,&rotate_info);
12321 switch (event.type)
12322 {
12323 case ButtonPress:
12324 break;
12325 case ButtonRelease:
12326 {
12327 /*
12328 User has committed to rotation line.
12329 */
12330 rotate_info.x2=event.xbutton.x;
12331 rotate_info.y2=event.xbutton.y;
12332 state|=ExitState;
12333 break;
12334 }
12335 case Expose:
12336 break;
12337 case MotionNotify:
12338 {
12339 rotate_info.x2=event.xmotion.x;
12340 rotate_info.y2=event.xmotion.y;
12341 }
12342 default:
12343 break;
12344 }
12345 /*
12346 Check boundary conditions.
12347 */
12348 if (rotate_info.x2 < 0)
12349 rotate_info.x2=0;
12350 else
12351 if (rotate_info.x2 > (int) windows->image.width)
12352 rotate_info.x2=(short) windows->image.width;
12353 if (rotate_info.y2 < 0)
12354 rotate_info.y2=0;
12355 else
12356 if (rotate_info.y2 > (int) windows->image.height)
12357 rotate_info.y2=(short) windows->image.height;
12358 /*
12359 Compute rotation angle from the slope of the line.
12360 */
12361 degrees=0.0;
12362 distance=(unsigned int)
12363 ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12364 ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12365 if (distance > 9)
12366 degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12367 rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12368 } while ((state & ExitState) == 0);
12369 (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12370 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12371 if (distance <= 9)
12372 return(MagickTrue);
12373 }
12374 if (direction == VerticalRotateCommand)
12375 degrees-=90.0;
12376 if (degrees == 0.0)
12377 return(MagickTrue);
12378 /*
12379 Rotate image.
12380 */
12381 normalized_degrees=degrees;
12382 while (normalized_degrees < -45.0)
12383 normalized_degrees+=360.0;
12384 for (rotations=0; normalized_degrees > 45.0; rotations++)
12385 normalized_degrees-=90.0;
12386 if (normalized_degrees != 0.0)
cristy051718b2011-08-28 22:49:25 +000012387 (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12388 exception);
cristy3ed852e2009-09-05 21:47:34 +000012389 XSetCursorState(display,windows,MagickTrue);
12390 XCheckRefreshWindows(display,windows);
cristye42f6582012-02-11 17:59:50 +000012391 (*image)->background_color.red=(double) ScaleShortToQuantum(
cristy3ed852e2009-09-05 21:47:34 +000012392 windows->pixel_info->pen_colors[pen_id].red);
cristye42f6582012-02-11 17:59:50 +000012393 (*image)->background_color.green=(double) ScaleShortToQuantum(
cristy3ed852e2009-09-05 21:47:34 +000012394 windows->pixel_info->pen_colors[pen_id].green);
cristye42f6582012-02-11 17:59:50 +000012395 (*image)->background_color.blue=(double) ScaleShortToQuantum(
cristy3ed852e2009-09-05 21:47:34 +000012396 windows->pixel_info->pen_colors[pen_id].blue);
cristy051718b2011-08-28 22:49:25 +000012397 rotate_image=RotateImage(*image,degrees,exception);
cristy3ed852e2009-09-05 21:47:34 +000012398 XSetCursorState(display,windows,MagickFalse);
12399 if (rotate_image == (Image *) NULL)
12400 return(MagickFalse);
12401 *image=DestroyImage(*image);
12402 *image=rotate_image;
12403 if (windows->image.crop_geometry != (char *) NULL)
12404 {
12405 /*
12406 Rotate crop geometry.
12407 */
12408 width=(unsigned int) (*image)->columns;
12409 height=(unsigned int) (*image)->rows;
12410 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12411 switch (rotations % 4)
12412 {
12413 default:
12414 case 0:
12415 break;
12416 case 1:
12417 {
12418 /*
12419 Rotate 90 degrees.
12420 */
Cristy859511c2018-04-22 14:50:36 -040012421 (void) FormatLocaleString(windows->image.crop_geometry,
12422 MagickPathExtent,"%ux%u%+d%+d",height,width,(int) (*image)->columns-
cristy3ed852e2009-09-05 21:47:34 +000012423 (int) height-y,x);
12424 break;
12425 }
12426 case 2:
12427 {
12428 /*
12429 Rotate 180 degrees.
12430 */
Cristy859511c2018-04-22 14:50:36 -040012431 (void) FormatLocaleString(windows->image.crop_geometry,
12432 MagickPathExtent,"%ux%u%+d%+d",width,height,(int) width-x,(int)
12433 height-y);
cristy3ed852e2009-09-05 21:47:34 +000012434 break;
12435 }
12436 case 3:
12437 {
12438 /*
12439 Rotate 270 degrees.
12440 */
Cristy859511c2018-04-22 14:50:36 -040012441 (void) FormatLocaleString(windows->image.crop_geometry,
12442 MagickPathExtent,"%ux%u%+d%+d",height,width,y,(int) (*image)->rows-
12443 (int) width-x);
cristy3ed852e2009-09-05 21:47:34 +000012444 break;
12445 }
12446 }
12447 }
Elliott Hughes5d41fba2021-04-12 16:36:42 -070012448 if (windows->image.orphan != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000012449 return(MagickTrue);
12450 if (normalized_degrees != 0.0)
12451 {
12452 /*
12453 Update image colormap.
12454 */
12455 windows->image.window_changes.width=(int) (*image)->columns;
12456 windows->image.window_changes.height=(int) (*image)->rows;
12457 if (windows->image.crop_geometry != (char *) NULL)
12458 {
12459 /*
12460 Obtain dimensions of image from crop geometry.
12461 */
12462 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12463 &width,&height);
12464 windows->image.window_changes.width=(int) width;
12465 windows->image.window_changes.height=(int) height;
12466 }
cristy6710d842011-10-20 23:23:00 +000012467 XConfigureImageColormap(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012468 }
12469 else
12470 if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12471 {
12472 windows->image.window_changes.width=windows->image.ximage->height;
12473 windows->image.window_changes.height=windows->image.ximage->width;
12474 }
12475 /*
12476 Update image configuration.
12477 */
cristy051718b2011-08-28 22:49:25 +000012478 (void) XConfigureImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012479 return(MagickTrue);
12480}
12481
12482/*
12483%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12484% %
12485% %
12486% %
12487+ X S a v e I m a g e %
12488% %
12489% %
12490% %
12491%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12492%
12493% XSaveImage() saves an image to a file.
12494%
12495% The format of the XSaveImage method is:
12496%
12497% MagickBooleanType XSaveImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000012498% XResourceInfo *resource_info,XWindows *windows,Image *image,
12499% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012500%
12501% A description of each parameter follows:
12502%
12503% o display: Specifies a connection to an X server; returned from
12504% XOpenDisplay.
12505%
12506% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12507%
12508% o windows: Specifies a pointer to a XWindows structure.
12509%
12510% o image: the image.
12511%
cristy051718b2011-08-28 22:49:25 +000012512% o exception: return any errors or warnings in this structure.
12513%
cristy3ed852e2009-09-05 21:47:34 +000012514*/
12515static MagickBooleanType XSaveImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000012516 XResourceInfo *resource_info,XWindows *windows,Image *image,
12517 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012518{
12519 char
cristy151b66d2015-04-15 10:50:31 +000012520 filename[MagickPathExtent],
12521 geometry[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +000012522
12523 Image
12524 *save_image;
12525
12526 ImageInfo
12527 *image_info;
12528
12529 MagickStatusType
12530 status;
12531
12532 /*
12533 Request file name from user.
12534 */
12535 if (resource_info->write_filename != (char *) NULL)
12536 (void) CopyMagickString(filename,resource_info->write_filename,
cristy151b66d2015-04-15 10:50:31 +000012537 MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +000012538 else
12539 {
12540 char
cristy151b66d2015-04-15 10:50:31 +000012541 path[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +000012542
12543 int
12544 status;
12545
12546 GetPathComponent(image->filename,HeadPath,path);
12547 GetPathComponent(image->filename,TailPath,filename);
cristy0da1d642011-08-29 16:53:16 +000012548 if (*path != '\0')
12549 {
12550 status=chdir(path);
12551 if (status == -1)
12552 (void) ThrowMagickException(exception,GetMagickModule(),
12553 FileOpenError,"UnableToOpenFile","%s",path);
12554 }
cristy3ed852e2009-09-05 21:47:34 +000012555 }
12556 XFileBrowserWidget(display,windows,"Save",filename);
12557 if (*filename == '\0')
12558 return(MagickTrue);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070012559 if (IsPathAccessible(filename) != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000012560 {
12561 int
12562 status;
12563
12564 /*
12565 File exists-- seek user's permission before overwriting.
12566 */
12567 status=XConfirmWidget(display,windows,"Overwrite",filename);
12568 if (status <= 0)
12569 return(MagickTrue);
12570 }
12571 image_info=CloneImageInfo(resource_info->image_info);
cristy151b66d2015-04-15 10:50:31 +000012572 (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
cristy051718b2011-08-28 22:49:25 +000012573 (void) SetImageInfo(image_info,1,exception);
cristy3ed852e2009-09-05 21:47:34 +000012574 if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12575 (LocaleCompare(image_info->magick,"JPG") == 0))
12576 {
12577 char
cristy151b66d2015-04-15 10:50:31 +000012578 quality[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +000012579
12580 int
12581 status;
12582
12583 /*
12584 Request JPEG quality from user.
12585 */
cristy151b66d2015-04-15 10:50:31 +000012586 (void) FormatLocaleString(quality,MagickPathExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +000012587 image->quality);
cristy3ed852e2009-09-05 21:47:34 +000012588 status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12589 quality);
12590 if (*quality == '\0')
12591 return(MagickTrue);
cristye27293e2009-12-18 02:53:20 +000012592 image->quality=StringToUnsignedLong(quality);
cristy3ed852e2009-09-05 21:47:34 +000012593 image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12594 }
12595 if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12596 (LocaleCompare(image_info->magick,"PDF") == 0) ||
12597 (LocaleCompare(image_info->magick,"PS") == 0) ||
12598 (LocaleCompare(image_info->magick,"PS2") == 0))
12599 {
12600 char
cristy151b66d2015-04-15 10:50:31 +000012601 geometry[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +000012602
Cristyd93b2e62019-05-22 19:45:01 -040012603 const char
12604 *const PageSizes[] =
12605 {
12606 "Letter",
12607 "Tabloid",
12608 "Ledger",
12609 "Legal",
12610 "Statement",
12611 "Executive",
12612 "A3",
12613 "A4",
12614 "A5",
12615 "B4",
12616 "B5",
12617 "Folio",
12618 "Quarto",
12619 "10x14",
12620 (char *) NULL
12621 };
12622
cristy3ed852e2009-09-05 21:47:34 +000012623 /*
12624 Request page geometry from user.
12625 */
cristy151b66d2015-04-15 10:50:31 +000012626 (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +000012627 if (LocaleCompare(image_info->magick,"PDF") == 0)
cristy151b66d2015-04-15 10:50:31 +000012628 (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +000012629 if (image_info->page != (char *) NULL)
cristy151b66d2015-04-15 10:50:31 +000012630 (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +000012631 XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12632 "Select page geometry:",geometry);
12633 if (*geometry != '\0')
12634 image_info->page=GetPageGeometry(geometry);
12635 }
12636 /*
12637 Apply image transforms.
12638 */
12639 XSetCursorState(display,windows,MagickTrue);
12640 XCheckRefreshWindows(display,windows);
cristy051718b2011-08-28 22:49:25 +000012641 save_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000012642 if (save_image == (Image *) NULL)
12643 return(MagickFalse);
cristy151b66d2015-04-15 10:50:31 +000012644 (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
cristy3ed852e2009-09-05 21:47:34 +000012645 windows->image.ximage->width,windows->image.ximage->height);
cristye941a752011-10-15 01:52:48 +000012646 (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12647 exception);
cristy3ed852e2009-09-05 21:47:34 +000012648 /*
12649 Write image.
12650 */
cristy151b66d2015-04-15 10:50:31 +000012651 (void) CopyMagickString(save_image->filename,filename,MagickPathExtent);
cristy051718b2011-08-28 22:49:25 +000012652 status=WriteImage(image_info,save_image,exception);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070012653 if (status != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000012654 image->taint=MagickFalse;
12655 save_image=DestroyImage(save_image);
12656 image_info=DestroyImageInfo(image_info);
12657 XSetCursorState(display,windows,MagickFalse);
dirkb9dbc292015-07-26 09:50:00 +000012658 return(status != 0 ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +000012659}
12660
12661/*
12662%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12663% %
12664% %
12665% %
12666+ X S c r e e n E v e n t %
12667% %
12668% %
12669% %
12670%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12671%
12672% XScreenEvent() handles global events associated with the Pan and Magnify
12673% windows.
12674%
12675% The format of the XScreenEvent function is:
12676%
cristy6710d842011-10-20 23:23:00 +000012677% void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12678% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012679%
12680% A description of each parameter follows:
12681%
12682% o display: Specifies a pointer to the Display structure; returned from
12683% XOpenDisplay.
12684%
12685% o windows: Specifies a pointer to a XWindows structure.
12686%
12687% o event: Specifies a pointer to a X11 XEvent structure.
12688%
cristy6710d842011-10-20 23:23:00 +000012689% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +000012690%
12691*/
12692
12693#if defined(__cplusplus) || defined(c_plusplus)
12694extern "C" {
12695#endif
12696
12697static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12698{
Cristyf2dc1dd2020-12-28 13:59:26 -050012699 XWindows
cristy3ed852e2009-09-05 21:47:34 +000012700 *windows;
12701
12702 windows=(XWindows *) data;
12703 if ((event->type == ClientMessage) &&
12704 (event->xclient.window == windows->image.id))
12705 return(MagickFalse);
12706 return(MagickTrue);
12707}
12708
12709#if defined(__cplusplus) || defined(c_plusplus)
12710}
12711#endif
12712
cristy6710d842011-10-20 23:23:00 +000012713static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12714 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012715{
Cristyf2dc1dd2020-12-28 13:59:26 -050012716 int
cristy3ed852e2009-09-05 21:47:34 +000012717 x,
12718 y;
12719
12720 (void) XIfEvent(display,event,XPredicate,(char *) windows);
12721 if (event->xany.window == windows->command.id)
12722 return;
12723 switch (event->type)
12724 {
12725 case ButtonPress:
12726 case ButtonRelease:
12727 {
12728 if ((event->xbutton.button == Button3) &&
12729 (event->xbutton.state & Mod1Mask))
12730 {
12731 /*
12732 Convert Alt-Button3 to Button2.
12733 */
12734 event->xbutton.button=Button2;
12735 event->xbutton.state&=(~Mod1Mask);
12736 }
12737 if (event->xbutton.window == windows->backdrop.id)
12738 {
12739 (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12740 event->xbutton.time);
12741 break;
12742 }
12743 if (event->xbutton.window == windows->pan.id)
12744 {
cristy6710d842011-10-20 23:23:00 +000012745 XPanImage(display,windows,event,exception);
cristy3ed852e2009-09-05 21:47:34 +000012746 break;
12747 }
12748 if (event->xbutton.window == windows->image.id)
12749 if (event->xbutton.button == Button2)
12750 {
12751 /*
12752 Update magnified image.
12753 */
12754 x=event->xbutton.x;
12755 y=event->xbutton.y;
12756 if (x < 0)
12757 x=0;
12758 else
12759 if (x >= (int) windows->image.width)
12760 x=(int) (windows->image.width-1);
cristy49e2d862010-11-12 02:50:30 +000012761 windows->magnify.x=(int) windows->image.x+x;
cristy3ed852e2009-09-05 21:47:34 +000012762 if (y < 0)
12763 y=0;
12764 else
12765 if (y >= (int) windows->image.height)
12766 y=(int) (windows->image.height-1);
12767 windows->magnify.y=windows->image.y+y;
dirk5d0a4412016-01-05 22:28:51 +010012768 if (windows->magnify.mapped == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000012769 (void) XMapRaised(display,windows->magnify.id);
cristy6710d842011-10-20 23:23:00 +000012770 XMakeMagnifyImage(display,windows,exception);
cristy3ed852e2009-09-05 21:47:34 +000012771 if (event->type == ButtonRelease)
12772 (void) XWithdrawWindow(display,windows->info.id,
12773 windows->info.screen);
12774 break;
12775 }
12776 break;
12777 }
12778 case ClientMessage:
12779 {
12780 /*
12781 If client window delete message, exit.
12782 */
12783 if (event->xclient.message_type != windows->wm_protocols)
12784 break;
cristyecd0ab52010-05-30 14:59:20 +000012785 if (*event->xclient.data.l != (long) windows->wm_delete_window)
cristy3ed852e2009-09-05 21:47:34 +000012786 break;
12787 if (event->xclient.window == windows->magnify.id)
12788 {
12789 (void) XWithdrawWindow(display,windows->magnify.id,
12790 windows->magnify.screen);
12791 break;
12792 }
12793 break;
12794 }
12795 case ConfigureNotify:
12796 {
12797 if (event->xconfigure.window == windows->magnify.id)
12798 {
12799 unsigned int
12800 magnify;
12801
12802 /*
12803 Magnify window has a new configuration.
12804 */
12805 windows->magnify.width=(unsigned int) event->xconfigure.width;
12806 windows->magnify.height=(unsigned int) event->xconfigure.height;
dirk5d0a4412016-01-05 22:28:51 +010012807 if (windows->magnify.mapped == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000012808 break;
12809 magnify=1;
12810 while ((int) magnify <= event->xconfigure.width)
12811 magnify<<=1;
12812 while ((int) magnify <= event->xconfigure.height)
12813 magnify<<=1;
12814 magnify>>=1;
12815 if (((int) magnify != event->xconfigure.width) ||
12816 ((int) magnify != event->xconfigure.height))
12817 {
12818 XWindowChanges
12819 window_changes;
12820
12821 window_changes.width=(int) magnify;
12822 window_changes.height=(int) magnify;
12823 (void) XReconfigureWMWindow(display,windows->magnify.id,
12824 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12825 &window_changes);
12826 break;
12827 }
cristy6710d842011-10-20 23:23:00 +000012828 XMakeMagnifyImage(display,windows,exception);
cristy3ed852e2009-09-05 21:47:34 +000012829 break;
12830 }
12831 break;
12832 }
12833 case Expose:
12834 {
12835 if (event->xexpose.window == windows->image.id)
12836 {
12837 XRefreshWindow(display,&windows->image,event);
12838 break;
12839 }
12840 if (event->xexpose.window == windows->pan.id)
12841 if (event->xexpose.count == 0)
12842 {
12843 XDrawPanRectangle(display,windows);
12844 break;
12845 }
12846 if (event->xexpose.window == windows->magnify.id)
12847 if (event->xexpose.count == 0)
12848 {
cristy6710d842011-10-20 23:23:00 +000012849 XMakeMagnifyImage(display,windows,exception);
cristy3ed852e2009-09-05 21:47:34 +000012850 break;
12851 }
12852 break;
12853 }
12854 case KeyPress:
12855 {
12856 char
cristy151b66d2015-04-15 10:50:31 +000012857 command[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +000012858
12859 KeySym
12860 key_symbol;
12861
12862 if (event->xkey.window != windows->magnify.id)
12863 break;
12864 /*
12865 Respond to a user key press.
12866 */
12867 (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12868 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
cristy6710d842011-10-20 23:23:00 +000012869 XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12870 exception);
cristy3ed852e2009-09-05 21:47:34 +000012871 break;
12872 }
12873 case MapNotify:
12874 {
12875 if (event->xmap.window == windows->magnify.id)
12876 {
12877 windows->magnify.mapped=MagickTrue;
12878 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12879 break;
12880 }
12881 if (event->xmap.window == windows->info.id)
12882 {
12883 windows->info.mapped=MagickTrue;
12884 break;
12885 }
12886 break;
12887 }
12888 case MotionNotify:
12889 {
12890 while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12891 if (event->xmotion.window == windows->image.id)
Elliott Hughes5d41fba2021-04-12 16:36:42 -070012892 if (windows->magnify.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000012893 {
12894 /*
12895 Update magnified image.
12896 */
12897 x=event->xmotion.x;
12898 y=event->xmotion.y;
12899 if (x < 0)
12900 x=0;
12901 else
12902 if (x >= (int) windows->image.width)
12903 x=(int) (windows->image.width-1);
cristy49e2d862010-11-12 02:50:30 +000012904 windows->magnify.x=(int) windows->image.x+x;
cristy3ed852e2009-09-05 21:47:34 +000012905 if (y < 0)
12906 y=0;
12907 else
12908 if (y >= (int) windows->image.height)
12909 y=(int) (windows->image.height-1);
12910 windows->magnify.y=windows->image.y+y;
cristy6710d842011-10-20 23:23:00 +000012911 XMakeMagnifyImage(display,windows,exception);
cristy3ed852e2009-09-05 21:47:34 +000012912 }
12913 break;
12914 }
12915 case UnmapNotify:
12916 {
12917 if (event->xunmap.window == windows->magnify.id)
12918 {
12919 windows->magnify.mapped=MagickFalse;
12920 break;
12921 }
12922 if (event->xunmap.window == windows->info.id)
12923 {
12924 windows->info.mapped=MagickFalse;
12925 break;
12926 }
12927 break;
12928 }
12929 default:
12930 break;
12931 }
12932}
12933
12934/*
12935%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12936% %
12937% %
12938% %
12939+ X S e t C r o p G e o m e t r y %
12940% %
12941% %
12942% %
12943%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12944%
12945% XSetCropGeometry() accepts a cropping geometry relative to the Image window
12946% and translates it to a cropping geometry relative to the image.
12947%
12948% The format of the XSetCropGeometry method is:
12949%
12950% void XSetCropGeometry(Display *display,XWindows *windows,
12951% RectangleInfo *crop_info,Image *image)
12952%
12953% A description of each parameter follows:
12954%
12955% o display: Specifies a connection to an X server; returned from
12956% XOpenDisplay.
12957%
12958% o windows: Specifies a pointer to a XWindows structure.
12959%
12960% o crop_info: A pointer to a RectangleInfo that defines a region of the
12961% Image window to crop.
12962%
12963% o image: the image.
12964%
12965*/
12966static void XSetCropGeometry(Display *display,XWindows *windows,
12967 RectangleInfo *crop_info,Image *image)
12968{
12969 char
cristy151b66d2015-04-15 10:50:31 +000012970 text[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +000012971
12972 int
12973 x,
12974 y;
12975
cristya19f1d72012-08-07 18:24:38 +000012976 double
cristy3ed852e2009-09-05 21:47:34 +000012977 scale_factor;
12978
12979 unsigned int
12980 height,
12981 width;
12982
Elliott Hughes5d41fba2021-04-12 16:36:42 -070012983 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000012984 {
12985 /*
12986 Display info on cropping rectangle.
12987 */
cristy151b66d2015-04-15 10:50:31 +000012988 (void) FormatLocaleString(text,MagickPathExtent," %.20gx%.20g%+.20g%+.20g",
cristye8c25f92010-06-03 00:53:06 +000012989 (double) crop_info->width,(double) crop_info->height,(double)
12990 crop_info->x,(double) crop_info->y);
cristy3ed852e2009-09-05 21:47:34 +000012991 XInfoWidget(display,windows,text);
12992 }
12993 /*
12994 Cropping geometry is relative to any previous crop geometry.
12995 */
12996 x=0;
12997 y=0;
12998 width=(unsigned int) image->columns;
12999 height=(unsigned int) image->rows;
13000 if (windows->image.crop_geometry != (char *) NULL)
13001 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13002 else
13003 windows->image.crop_geometry=AcquireString((char *) NULL);
13004 /*
13005 Define the crop geometry string from the cropping rectangle.
13006 */
cristya19f1d72012-08-07 18:24:38 +000013007 scale_factor=(double) width/windows->image.ximage->width;
cristy3ed852e2009-09-05 21:47:34 +000013008 if (crop_info->x > 0)
13009 x+=(int) (scale_factor*crop_info->x+0.5);
13010 width=(unsigned int) (scale_factor*crop_info->width+0.5);
13011 if (width == 0)
13012 width=1;
cristya19f1d72012-08-07 18:24:38 +000013013 scale_factor=(double) height/windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +000013014 if (crop_info->y > 0)
13015 y+=(int) (scale_factor*crop_info->y+0.5);
13016 height=(unsigned int) (scale_factor*crop_info->height+0.5);
13017 if (height == 0)
13018 height=1;
cristy151b66d2015-04-15 10:50:31 +000013019 (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +000013020 "%ux%u%+d%+d",width,height,x,y);
13021}
13022
13023/*
13024%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13025% %
13026% %
13027% %
13028+ X T i l e I m a g e %
13029% %
13030% %
13031% %
13032%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13033%
13034% XTileImage() loads or deletes a selected tile from a visual image directory.
13035% The load or delete command is chosen from a menu.
13036%
13037% The format of the XTileImage method is:
13038%
13039% Image *XTileImage(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +000013040% XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000013041%
13042% A description of each parameter follows:
13043%
13044% o tile_image: XTileImage reads or deletes the tile image
13045% and returns it. A null image is returned if an error occurs.
13046%
13047% o display: Specifies a connection to an X server; returned from
13048% XOpenDisplay.
13049%
13050% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13051%
13052% o windows: Specifies a pointer to a XWindows structure.
13053%
13054% o image: the image; returned from ReadImage.
13055%
13056% o event: Specifies a pointer to a XEvent structure. If it is NULL,
13057% the entire image is refreshed.
13058%
cristy051718b2011-08-28 22:49:25 +000013059% o exception: return any errors or warnings in this structure.
13060%
cristy3ed852e2009-09-05 21:47:34 +000013061*/
13062static Image *XTileImage(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +000013063 XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000013064{
Cristyd93b2e62019-05-22 19:45:01 -040013065 const char
13066 *const VerbMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000013067 {
13068 "Load",
13069 "Next",
13070 "Former",
13071 "Delete",
13072 "Update",
13073 (char *) NULL,
13074 };
13075
13076 static const ModeType
13077 TileCommands[] =
13078 {
13079 TileLoadCommand,
13080 TileNextCommand,
13081 TileFormerCommand,
13082 TileDeleteCommand,
13083 TileUpdateCommand
13084 };
13085
13086 char
cristy151b66d2015-04-15 10:50:31 +000013087 command[MagickPathExtent],
13088 filename[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +000013089
13090 Image
13091 *tile_image;
13092
13093 int
13094 id,
13095 status,
13096 tile,
13097 x,
13098 y;
13099
cristya19f1d72012-08-07 18:24:38 +000013100 double
cristy3ed852e2009-09-05 21:47:34 +000013101 scale_factor;
13102
Cristyf2dc1dd2020-12-28 13:59:26 -050013103 char
cristy3ed852e2009-09-05 21:47:34 +000013104 *p,
13105 *q;
13106
Cristyf2dc1dd2020-12-28 13:59:26 -050013107 int
cristy3ed852e2009-09-05 21:47:34 +000013108 i;
13109
13110 unsigned int
13111 height,
13112 width;
13113
13114 /*
13115 Tile image is relative to montage image configuration.
13116 */
13117 x=0;
13118 y=0;
13119 width=(unsigned int) image->columns;
13120 height=(unsigned int) image->rows;
13121 if (windows->image.crop_geometry != (char *) NULL)
13122 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
cristya19f1d72012-08-07 18:24:38 +000013123 scale_factor=(double) width/windows->image.ximage->width;
cristy3ed852e2009-09-05 21:47:34 +000013124 event->xbutton.x+=windows->image.x;
13125 event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
cristya19f1d72012-08-07 18:24:38 +000013126 scale_factor=(double) height/windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +000013127 event->xbutton.y+=windows->image.y;
13128 event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13129 /*
13130 Determine size and location of each tile in the visual image directory.
13131 */
13132 width=(unsigned int) image->columns;
13133 height=(unsigned int) image->rows;
13134 x=0;
13135 y=0;
13136 (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13137 tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13138 (event->xbutton.x-x)/width;
13139 if (tile < 0)
13140 {
13141 /*
13142 Button press is outside any tile.
13143 */
13144 (void) XBell(display,0);
13145 return((Image *) NULL);
13146 }
13147 /*
13148 Determine file name from the tile directory.
13149 */
13150 p=image->directory;
13151 for (i=tile; (i != 0) && (*p != '\0'); )
13152 {
Cristy5d293d22018-02-24 12:45:52 -050013153 if (*p == '\xff')
cristy3ed852e2009-09-05 21:47:34 +000013154 i--;
13155 p++;
13156 }
13157 if (*p == '\0')
13158 {
13159 /*
13160 Button press is outside any tile.
13161 */
13162 (void) XBell(display,0);
13163 return((Image *) NULL);
13164 }
13165 /*
13166 Select a command from the pop-up menu.
13167 */
13168 id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13169 if (id < 0)
13170 return((Image *) NULL);
13171 q=p;
Cristy0bbdf0d2019-10-13 09:07:15 -040013172 while ((*q != '\xff') && (*q != '\0'))
cristy3ed852e2009-09-05 21:47:34 +000013173 q++;
13174 (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13175 /*
13176 Perform command for the selected tile.
13177 */
13178 XSetCursorState(display,windows,MagickTrue);
13179 XCheckRefreshWindows(display,windows);
13180 tile_image=NewImageList();
13181 switch (TileCommands[id])
13182 {
13183 case TileLoadCommand:
13184 {
13185 /*
13186 Load tile image.
13187 */
13188 XCheckRefreshWindows(display,windows);
13189 (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
cristy151b66d2015-04-15 10:50:31 +000013190 MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +000013191 (void) CopyMagickString(resource_info->image_info->filename,filename,
cristy151b66d2015-04-15 10:50:31 +000013192 MagickPathExtent);
cristy051718b2011-08-28 22:49:25 +000013193 tile_image=ReadImage(resource_info->image_info,exception);
13194 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +000013195 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13196 break;
13197 }
13198 case TileNextCommand:
13199 {
13200 /*
13201 Display next image.
13202 */
13203 XClientMessage(display,windows->image.id,windows->im_protocols,
13204 windows->im_next_image,CurrentTime);
13205 break;
13206 }
13207 case TileFormerCommand:
13208 {
13209 /*
13210 Display former image.
13211 */
13212 XClientMessage(display,windows->image.id,windows->im_protocols,
13213 windows->im_former_image,CurrentTime);
13214 break;
13215 }
13216 case TileDeleteCommand:
13217 {
13218 /*
13219 Delete tile image.
13220 */
dirk5d0a4412016-01-05 22:28:51 +010013221 if (IsPathAccessible(filename) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000013222 {
13223 XNoticeWidget(display,windows,"Image file does not exist:",filename);
13224 break;
13225 }
13226 status=XConfirmWidget(display,windows,"Really delete tile",filename);
13227 if (status <= 0)
13228 break;
cristy59864562013-04-18 11:47:41 +000013229 status=ShredFile(filename);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070013230 if (status != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000013231 {
13232 XNoticeWidget(display,windows,"Unable to delete image file:",
13233 filename);
13234 break;
13235 }
13236 }
13237 case TileUpdateCommand:
13238 {
cristy3ed852e2009-09-05 21:47:34 +000013239 int
13240 x_offset,
13241 y_offset;
13242
cristy101ab702011-10-13 13:06:32 +000013243 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +000013244 pixel;
13245
Cristyf2dc1dd2020-12-28 13:59:26 -050013246 int
cristy3ed852e2009-09-05 21:47:34 +000013247 j;
13248
Cristyf2dc1dd2020-12-28 13:59:26 -050013249 Quantum
cristy3ed852e2009-09-05 21:47:34 +000013250 *s;
13251
13252 /*
13253 Ensure all the images exist.
13254 */
13255 tile=0;
cristy101ab702011-10-13 13:06:32 +000013256 GetPixelInfo(image,&pixel);
cristy3ed852e2009-09-05 21:47:34 +000013257 for (p=image->directory; *p != '\0'; p++)
13258 {
cristyf7c25522010-11-15 01:25:14 +000013259 CacheView
13260 *image_view;
13261
cristy3ed852e2009-09-05 21:47:34 +000013262 q=p;
Cristy5d293d22018-02-24 12:45:52 -050013263 while ((*q != '\xff') && (*q != '\0'))
cristy3ed852e2009-09-05 21:47:34 +000013264 q++;
13265 (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13266 p=q;
Elliott Hughes5d41fba2021-04-12 16:36:42 -070013267 if (IsPathAccessible(filename) != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000013268 {
13269 tile++;
13270 continue;
13271 }
13272 /*
13273 Overwrite tile with background color.
13274 */
13275 x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13276 y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
cristy46ff2672012-12-14 15:32:26 +000013277 image_view=AcquireAuthenticCacheView(image,exception);
cristyf05d4942012-03-17 16:26:09 +000013278 (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
cristy3ed852e2009-09-05 21:47:34 +000013279 for (i=0; i < (int) height; i++)
13280 {
cristy49e2d862010-11-12 02:50:30 +000013281 s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13282 y_offset+i,width,1,exception);
cristy4c08aed2011-07-01 19:47:50 +000013283 if (s == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000013284 break;
13285 for (j=0; j < (int) width; j++)
cristy4c08aed2011-07-01 19:47:50 +000013286 {
cristy11a06d32015-01-04 12:03:27 +000013287 SetPixelViaPixelInfo(image,&pixel,s);
cristyed231572011-07-14 02:18:59 +000013288 s+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +000013289 }
dirk5d0a4412016-01-05 22:28:51 +010013290 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000013291 break;
13292 }
cristyca1628f2010-11-15 01:17:49 +000013293 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +000013294 tile++;
13295 }
13296 windows->image.window_changes.width=(int) image->columns;
13297 windows->image.window_changes.height=(int) image->rows;
cristy6710d842011-10-20 23:23:00 +000013298 XConfigureImageColormap(display,resource_info,windows,image,exception);
cristy051718b2011-08-28 22:49:25 +000013299 (void) XConfigureImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013300 break;
13301 }
13302 default:
13303 break;
13304 }
13305 XSetCursorState(display,windows,MagickFalse);
13306 return(tile_image);
13307}
13308
13309/*
13310%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13311% %
13312% %
13313% %
13314+ X T r a n s l a t e I m a g e %
13315% %
13316% %
13317% %
13318%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13319%
13320% XTranslateImage() translates the image within an Image window by one pixel
anthonye5b39652012-04-21 05:37:29 +000013321% as specified by the key symbol. If the image has a montage string the
cristy3ed852e2009-09-05 21:47:34 +000013322% translation is respect to the width and height contained within the string.
13323%
13324% The format of the XTranslateImage method is:
13325%
13326% void XTranslateImage(Display *display,XWindows *windows,
13327% Image *image,const KeySym key_symbol)
13328%
13329% A description of each parameter follows:
13330%
13331% o display: Specifies a connection to an X server; returned from
13332% XOpenDisplay.
13333%
13334% o windows: Specifies a pointer to a XWindows structure.
13335%
13336% o image: the image.
13337%
13338% o key_symbol: Specifies a KeySym which indicates which side of the image
13339% to trim.
13340%
13341*/
13342static void XTranslateImage(Display *display,XWindows *windows,
13343 Image *image,const KeySym key_symbol)
13344{
13345 char
cristy151b66d2015-04-15 10:50:31 +000013346 text[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +000013347
13348 int
13349 x,
13350 y;
13351
13352 unsigned int
13353 x_offset,
13354 y_offset;
13355
13356 /*
13357 User specified a pan position offset.
13358 */
13359 x_offset=windows->image.width;
13360 y_offset=windows->image.height;
13361 if (image->montage != (char *) NULL)
13362 (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13363 switch ((int) key_symbol)
13364 {
13365 case XK_Home:
13366 case XK_KP_Home:
13367 {
13368 windows->image.x=(int) windows->image.width/2;
13369 windows->image.y=(int) windows->image.height/2;
13370 break;
13371 }
13372 case XK_Left:
13373 case XK_KP_Left:
13374 {
13375 windows->image.x-=x_offset;
13376 break;
13377 }
13378 case XK_Next:
13379 case XK_Up:
13380 case XK_KP_Up:
13381 {
13382 windows->image.y-=y_offset;
13383 break;
13384 }
13385 case XK_Right:
13386 case XK_KP_Right:
13387 {
13388 windows->image.x+=x_offset;
13389 break;
13390 }
13391 case XK_Prior:
13392 case XK_Down:
13393 case XK_KP_Down:
13394 {
13395 windows->image.y+=y_offset;
13396 break;
13397 }
13398 default:
13399 return;
13400 }
13401 /*
13402 Check boundary conditions.
13403 */
13404 if (windows->image.x < 0)
13405 windows->image.x=0;
13406 else
13407 if ((int) (windows->image.x+windows->image.width) >
13408 windows->image.ximage->width)
cristy49e2d862010-11-12 02:50:30 +000013409 windows->image.x=(int) windows->image.ximage->width-windows->image.width;
cristy3ed852e2009-09-05 21:47:34 +000013410 if (windows->image.y < 0)
13411 windows->image.y=0;
13412 else
13413 if ((int) (windows->image.y+windows->image.height) >
13414 windows->image.ximage->height)
cristy49e2d862010-11-12 02:50:30 +000013415 windows->image.y=(int) windows->image.ximage->height-windows->image.height;
cristy3ed852e2009-09-05 21:47:34 +000013416 /*
13417 Refresh Image window.
13418 */
cristy151b66d2015-04-15 10:50:31 +000013419 (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
cristy3ed852e2009-09-05 21:47:34 +000013420 windows->image.width,windows->image.height,windows->image.x,
13421 windows->image.y);
13422 XInfoWidget(display,windows,text);
13423 XCheckRefreshWindows(display,windows);
13424 XDrawPanRectangle(display,windows);
13425 XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13426 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13427}
13428
13429/*
13430%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13431% %
13432% %
13433% %
13434+ X T r i m I m a g e %
13435% %
13436% %
13437% %
13438%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13439%
13440% XTrimImage() trims the edges from the Image window.
13441%
13442% The format of the XTrimImage method is:
13443%
13444% MagickBooleanType XTrimImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000013445% XResourceInfo *resource_info,XWindows *windows,Image *image,
13446% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000013447%
13448% A description of each parameter follows:
13449%
13450% o display: Specifies a connection to an X server; returned from
13451% XOpenDisplay.
13452%
13453% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13454%
13455% o windows: Specifies a pointer to a XWindows structure.
13456%
13457% o image: the image.
13458%
cristy051718b2011-08-28 22:49:25 +000013459% o exception: return any errors or warnings in this structure.
13460%
cristy3ed852e2009-09-05 21:47:34 +000013461*/
13462static MagickBooleanType XTrimImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000013463 XResourceInfo *resource_info,XWindows *windows,Image *image,
13464 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000013465{
13466 RectangleInfo
13467 trim_info;
13468
Cristyf2dc1dd2020-12-28 13:59:26 -050013469 int
cristy3ed852e2009-09-05 21:47:34 +000013470 x,
13471 y;
13472
cristybb503372010-05-27 20:51:26 +000013473 size_t
cristy3ed852e2009-09-05 21:47:34 +000013474 background,
13475 pixel;
13476
13477 /*
13478 Trim edges from image.
13479 */
13480 XSetCursorState(display,windows,MagickTrue);
13481 XCheckRefreshWindows(display,windows);
13482 /*
13483 Crop the left edge.
13484 */
13485 background=XGetPixel(windows->image.ximage,0,0);
cristybb503372010-05-27 20:51:26 +000013486 trim_info.width=(size_t) windows->image.ximage->width;
cristy3ed852e2009-09-05 21:47:34 +000013487 for (x=0; x < windows->image.ximage->width; x++)
13488 {
13489 for (y=0; y < windows->image.ximage->height; y++)
13490 {
13491 pixel=XGetPixel(windows->image.ximage,x,y);
13492 if (pixel != background)
13493 break;
13494 }
13495 if (y < windows->image.ximage->height)
13496 break;
13497 }
cristy49e2d862010-11-12 02:50:30 +000013498 trim_info.x=(ssize_t) x;
13499 if (trim_info.x == (ssize_t) windows->image.ximage->width)
cristy3ed852e2009-09-05 21:47:34 +000013500 {
13501 XSetCursorState(display,windows,MagickFalse);
13502 return(MagickFalse);
13503 }
13504 /*
13505 Crop the right edge.
13506 */
13507 background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13508 for (x=windows->image.ximage->width-1; x != 0; x--)
13509 {
13510 for (y=0; y < windows->image.ximage->height; y++)
13511 {
13512 pixel=XGetPixel(windows->image.ximage,x,y);
13513 if (pixel != background)
13514 break;
13515 }
13516 if (y < windows->image.ximage->height)
13517 break;
13518 }
cristybb503372010-05-27 20:51:26 +000013519 trim_info.width=(size_t) (x-trim_info.x+1);
cristy3ed852e2009-09-05 21:47:34 +000013520 /*
13521 Crop the top edge.
13522 */
13523 background=XGetPixel(windows->image.ximage,0,0);
cristybb503372010-05-27 20:51:26 +000013524 trim_info.height=(size_t) windows->image.ximage->height;
cristy3ed852e2009-09-05 21:47:34 +000013525 for (y=0; y < windows->image.ximage->height; y++)
13526 {
13527 for (x=0; x < windows->image.ximage->width; x++)
13528 {
13529 pixel=XGetPixel(windows->image.ximage,x,y);
13530 if (pixel != background)
13531 break;
13532 }
13533 if (x < windows->image.ximage->width)
13534 break;
13535 }
cristy49e2d862010-11-12 02:50:30 +000013536 trim_info.y=(ssize_t) y;
cristy3ed852e2009-09-05 21:47:34 +000013537 /*
13538 Crop the bottom edge.
13539 */
13540 background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13541 for (y=windows->image.ximage->height-1; y != 0; 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 }
cristybb503372010-05-27 20:51:26 +000013552 trim_info.height=(size_t) y-trim_info.y+1;
cristy3ed852e2009-09-05 21:47:34 +000013553 if (((unsigned int) trim_info.width != windows->image.width) ||
13554 ((unsigned int) trim_info.height != windows->image.height))
13555 {
13556 /*
13557 Reconfigure Image window as defined by the trimming rectangle.
13558 */
13559 XSetCropGeometry(display,windows,&trim_info,image);
13560 windows->image.window_changes.width=(int) trim_info.width;
13561 windows->image.window_changes.height=(int) trim_info.height;
cristy051718b2011-08-28 22:49:25 +000013562 (void) XConfigureImage(display,resource_info,windows,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013563 }
13564 XSetCursorState(display,windows,MagickFalse);
13565 return(MagickTrue);
13566}
13567
13568/*
13569%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13570% %
13571% %
13572% %
13573+ X V i s u a l D i r e c t o r y I m a g e %
13574% %
13575% %
13576% %
13577%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13578%
13579% XVisualDirectoryImage() creates a Visual Image Directory.
13580%
13581% The format of the XVisualDirectoryImage method is:
13582%
13583% Image *XVisualDirectoryImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000013584% XResourceInfo *resource_info,XWindows *windows,
13585% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000013586%
13587% A description of each parameter follows:
13588%
cristy3ed852e2009-09-05 21:47:34 +000013589% o display: Specifies a connection to an X server; returned from
13590% XOpenDisplay.
13591%
13592% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13593%
13594% o windows: Specifies a pointer to a XWindows structure.
13595%
cristy051718b2011-08-28 22:49:25 +000013596% o exception: return any errors or warnings in this structure.
13597%
cristy3ed852e2009-09-05 21:47:34 +000013598*/
13599static Image *XVisualDirectoryImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000013600 XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000013601{
13602#define TileImageTag "Scale/Image"
13603#define XClientName "montage"
13604
13605 char
13606 **filelist;
13607
cristy3ed852e2009-09-05 21:47:34 +000013608 Image
13609 *images,
13610 *montage_image,
13611 *next_image,
13612 *thumbnail_image;
13613
13614 ImageInfo
13615 *read_info;
13616
13617 int
13618 number_files;
13619
13620 MagickBooleanType
13621 backdrop;
13622
13623 MagickStatusType
13624 status;
13625
13626 MontageInfo
13627 *montage_info;
13628
13629 RectangleInfo
13630 geometry;
13631
Cristyf2dc1dd2020-12-28 13:59:26 -050013632 int
cristy3ed852e2009-09-05 21:47:34 +000013633 i;
13634
13635 static char
cristy151b66d2015-04-15 10:50:31 +000013636 filename[MagickPathExtent] = "\0",
13637 filenames[MagickPathExtent] = "*";
cristy3ed852e2009-09-05 21:47:34 +000013638
13639 XResourceInfo
13640 background_resources;
13641
13642 /*
13643 Request file name from user.
13644 */
13645 XFileBrowserWidget(display,windows,"Directory",filenames);
13646 if (*filenames == '\0')
13647 return((Image *) NULL);
13648 /*
13649 Expand the filenames.
13650 */
Cristy8357b5d2020-11-22 12:39:10 +000013651 filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
cristy3ed852e2009-09-05 21:47:34 +000013652 if (filelist == (char **) NULL)
13653 {
cristybeb1a6b2013-11-04 12:08:18 +000013654 ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
cristy3ed852e2009-09-05 21:47:34 +000013655 filenames);
13656 return((Image *) NULL);
13657 }
13658 number_files=1;
13659 filelist[0]=filenames;
13660 status=ExpandFilenames(&number_files,&filelist);
dirk5d0a4412016-01-05 22:28:51 +010013661 if ((status == MagickFalse) || (number_files == 0))
cristy3ed852e2009-09-05 21:47:34 +000013662 {
13663 if (number_files == 0)
cristybeb1a6b2013-11-04 12:08:18 +000013664 ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
cristy3ed852e2009-09-05 21:47:34 +000013665 else
cristybeb1a6b2013-11-04 12:08:18 +000013666 ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
cristy3ed852e2009-09-05 21:47:34 +000013667 filenames);
13668 return((Image *) NULL);
13669 }
13670 /*
13671 Set image background resources.
13672 */
13673 background_resources=(*resource_info);
13674 background_resources.window_id=AcquireString("");
cristy151b66d2015-04-15 10:50:31 +000013675 (void) FormatLocaleString(background_resources.window_id,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +000013676 "0x%lx",windows->image.id);
13677 background_resources.backdrop=MagickTrue;
13678 /*
13679 Read each image and convert them to a tile.
13680 */
dirkb9dbc292015-07-26 09:50:00 +000013681 backdrop=((windows->visual_info->klass == TrueColor) ||
13682 (windows->visual_info->klass == DirectColor)) ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +000013683 read_info=CloneImageInfo(resource_info->image_info);
cristy9ce61202010-11-24 00:38:37 +000013684 (void) SetImageOption(read_info,"jpeg:size","120x120");
13685 (void) CloneString(&read_info->size,DefaultTileGeometry);
cristy3ed852e2009-09-05 21:47:34 +000013686 (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13687 (void *) NULL);
13688 images=NewImageList();
cristy3ed852e2009-09-05 21:47:34 +000013689 XSetCursorState(display,windows,MagickTrue);
13690 XCheckRefreshWindows(display,windows);
cristy49e2d862010-11-12 02:50:30 +000013691 for (i=0; i < (int) number_files; i++)
cristy3ed852e2009-09-05 21:47:34 +000013692 {
cristy151b66d2015-04-15 10:50:31 +000013693 (void) CopyMagickString(read_info->filename,filelist[i],MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +000013694 filelist[i]=DestroyString(filelist[i]);
13695 *read_info->magick='\0';
cristy3ed852e2009-09-05 21:47:34 +000013696 next_image=ReadImage(read_info,exception);
13697 CatchException(exception);
13698 if (next_image != (Image *) NULL)
13699 {
13700 (void) DeleteImageProperty(next_image,"label");
cristy77619442010-11-24 00:27:23 +000013701 (void) SetImageProperty(next_image,"label",InterpretImageProperties(
cristyd15e6592011-10-15 00:13:06 +000013702 read_info,next_image,DefaultTileLabel,exception),exception);
cristy3ed852e2009-09-05 21:47:34 +000013703 (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13704 exception);
13705 thumbnail_image=ThumbnailImage(next_image,geometry.width,
13706 geometry.height,exception);
13707 if (thumbnail_image != (Image *) NULL)
13708 {
13709 next_image=DestroyImage(next_image);
13710 next_image=thumbnail_image;
13711 }
13712 if (backdrop)
13713 {
13714 (void) XDisplayBackgroundImage(display,&background_resources,
cristy051718b2011-08-28 22:49:25 +000013715 next_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013716 XSetCursorState(display,windows,MagickTrue);
13717 }
13718 AppendImageToList(&images,next_image);
13719 if (images->progress_monitor != (MagickProgressMonitor) NULL)
13720 {
13721 MagickBooleanType
13722 proceed;
13723
13724 proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13725 (MagickSizeType) number_files);
dirk5d0a4412016-01-05 22:28:51 +010013726 if (proceed == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000013727 break;
13728 }
13729 }
13730 }
cristy3ed852e2009-09-05 21:47:34 +000013731 filelist=(char **) RelinquishMagickMemory(filelist);
cristy3ed852e2009-09-05 21:47:34 +000013732 if (images == (Image *) NULL)
13733 {
cristy8d52fca2010-11-24 00:45:05 +000013734 read_info=DestroyImageInfo(read_info);
cristy3ed852e2009-09-05 21:47:34 +000013735 XSetCursorState(display,windows,MagickFalse);
cristybeb1a6b2013-11-04 12:08:18 +000013736 ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
cristy3ed852e2009-09-05 21:47:34 +000013737 return((Image *) NULL);
13738 }
13739 /*
13740 Create the Visual Image Directory.
13741 */
cristy8d52fca2010-11-24 00:45:05 +000013742 montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13743 montage_info->pointsize=10;
cristy3ed852e2009-09-05 21:47:34 +000013744 if (resource_info->font != (char *) NULL)
13745 (void) CloneString(&montage_info->font,resource_info->font);
cristy151b66d2015-04-15 10:50:31 +000013746 (void) CopyMagickString(montage_info->filename,filename,MagickPathExtent);
cristy8d52fca2010-11-24 00:45:05 +000013747 montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
cristy051718b2011-08-28 22:49:25 +000013748 images),exception);
cristy3ed852e2009-09-05 21:47:34 +000013749 images=DestroyImageList(images);
cristy8d52fca2010-11-24 00:45:05 +000013750 montage_info=DestroyMontageInfo(montage_info);
13751 read_info=DestroyImageInfo(read_info);
cristy3ed852e2009-09-05 21:47:34 +000013752 XSetCursorState(display,windows,MagickFalse);
13753 if (montage_image == (Image *) NULL)
13754 return(montage_image);
13755 XClientMessage(display,windows->image.id,windows->im_protocols,
13756 windows->im_next_image,CurrentTime);
13757 return(montage_image);
13758}
13759
13760/*
13761%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13762% %
13763% %
13764% %
13765% X D i s p l a y B a c k g r o u n d I m a g e %
13766% %
13767% %
13768% %
13769%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13770%
13771% XDisplayBackgroundImage() displays an image in the background of a window.
13772%
13773% The format of the XDisplayBackgroundImage method is:
13774%
13775% MagickBooleanType XDisplayBackgroundImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000013776% XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000013777%
13778% A description of each parameter follows:
13779%
13780% o display: Specifies a connection to an X server; returned from
13781% XOpenDisplay.
13782%
13783% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13784%
13785% o image: the image.
13786%
cristy051718b2011-08-28 22:49:25 +000013787% o exception: return any errors or warnings in this structure.
13788%
cristy3ed852e2009-09-05 21:47:34 +000013789*/
13790MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
cristy051718b2011-08-28 22:49:25 +000013791 XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000013792{
13793 char
cristy151b66d2015-04-15 10:50:31 +000013794 geometry[MagickPathExtent],
13795 visual_type[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +000013796
13797 int
13798 height,
13799 status,
13800 width;
13801
13802 RectangleInfo
13803 geometry_info;
13804
13805 static XPixelInfo
13806 pixel;
13807
13808 static XStandardColormap
13809 *map_info;
13810
13811 static XVisualInfo
13812 *visual_info = (XVisualInfo *) NULL;
13813
13814 static XWindowInfo
13815 window_info;
13816
cristybb503372010-05-27 20:51:26 +000013817 size_t
cristy3ed852e2009-09-05 21:47:34 +000013818 delay;
13819
13820 Window
13821 root_window;
13822
13823 XGCValues
13824 context_values;
13825
13826 XResourceInfo
13827 resources;
13828
13829 XWindowAttributes
13830 window_attributes;
13831
13832 /*
13833 Determine target window.
13834 */
13835 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +000013836 assert(image->signature == MagickCoreSignature);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070013837 if (image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000013838 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13839 resources=(*resource_info);
13840 window_info.id=(Window) NULL;
13841 root_window=XRootWindow(display,XDefaultScreen(display));
13842 if (LocaleCompare(resources.window_id,"root") == 0)
13843 window_info.id=root_window;
13844 else
13845 {
cristy02e64f82013-11-14 16:20:04 +000013846 if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
cristy3ed852e2009-09-05 21:47:34 +000013847 window_info.id=XWindowByID(display,root_window,
13848 (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13849 if (window_info.id == (Window) NULL)
13850 window_info.id=XWindowByName(display,root_window,resources.window_id);
13851 }
13852 if (window_info.id == (Window) NULL)
13853 {
cristybeb1a6b2013-11-04 12:08:18 +000013854 ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
cristy3ed852e2009-09-05 21:47:34 +000013855 resources.window_id);
13856 return(MagickFalse);
13857 }
13858 /*
13859 Determine window visual id.
13860 */
13861 window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13862 window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
cristy151b66d2015-04-15 10:50:31 +000013863 (void) CopyMagickString(visual_type,"default",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +000013864 status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13865 if (status != 0)
cristy151b66d2015-04-15 10:50:31 +000013866 (void) FormatLocaleString(visual_type,MagickPathExtent,"0x%lx",
cristy3ed852e2009-09-05 21:47:34 +000013867 XVisualIDFromVisual(window_attributes.visual));
13868 if (visual_info == (XVisualInfo *) NULL)
13869 {
13870 /*
13871 Allocate standard colormap.
13872 */
13873 map_info=XAllocStandardColormap();
13874 if (map_info == (XStandardColormap *) NULL)
13875 ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13876 image->filename);
13877 map_info->colormap=(Colormap) NULL;
cristyf2faecf2010-05-28 19:19:36 +000013878 pixel.pixels=(unsigned long *) NULL;
cristy3ed852e2009-09-05 21:47:34 +000013879 /*
13880 Initialize visual info.
13881 */
13882 resources.map_type=(char *) NULL;
13883 resources.visual_type=visual_type;
13884 visual_info=XBestVisualInfo(display,map_info,&resources);
13885 if (visual_info == (XVisualInfo *) NULL)
13886 ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13887 resources.visual_type);
13888 /*
13889 Initialize window info.
13890 */
13891 window_info.ximage=(XImage *) NULL;
13892 window_info.matte_image=(XImage *) NULL;
13893 window_info.pixmap=(Pixmap) NULL;
13894 window_info.matte_pixmap=(Pixmap) NULL;
13895 }
13896 /*
13897 Free previous root colors.
13898 */
13899 if (window_info.id == root_window)
13900 (void) XDestroyWindowColors(display,root_window);
13901 /*
13902 Initialize Standard Colormap.
13903 */
13904 resources.colormap=SharedColormap;
cristy6710d842011-10-20 23:23:00 +000013905 XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13906 exception);
cristy3ed852e2009-09-05 21:47:34 +000013907 /*
13908 Graphic context superclass.
13909 */
Elliott Hughes5d41fba2021-04-12 16:36:42 -070013910 context_values.background=pixel.foreground_color.pixel;
13911 context_values.foreground=pixel.background_color.pixel;
cristy3ed852e2009-09-05 21:47:34 +000013912 pixel.annotate_context=XCreateGC(display,window_info.id,
cristybb503372010-05-27 20:51:26 +000013913 (size_t) (GCBackground | GCForeground),&context_values);
cristy3ed852e2009-09-05 21:47:34 +000013914 if (pixel.annotate_context == (GC) NULL)
13915 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13916 image->filename);
13917 /*
13918 Initialize Image window attributes.
13919 */
13920 window_info.name=AcquireString("\0");
13921 window_info.icon_name=AcquireString("\0");
13922 XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13923 &resources,&window_info);
13924 /*
13925 Create the X image.
13926 */
13927 window_info.width=(unsigned int) image->columns;
13928 window_info.height=(unsigned int) image->rows;
13929 if ((image->columns != window_info.width) ||
13930 (image->rows != window_info.height))
13931 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13932 image->filename);
cristy151b66d2015-04-15 10:50:31 +000013933 (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>",
cristy3ed852e2009-09-05 21:47:34 +000013934 window_attributes.width,window_attributes.height);
13935 geometry_info.width=window_info.width;
13936 geometry_info.height=window_info.height;
cristyecd0ab52010-05-30 14:59:20 +000013937 geometry_info.x=(ssize_t) window_info.x;
13938 geometry_info.y=(ssize_t) window_info.y;
cristy3ed852e2009-09-05 21:47:34 +000013939 (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13940 &geometry_info.width,&geometry_info.height);
13941 window_info.width=(unsigned int) geometry_info.width;
13942 window_info.height=(unsigned int) geometry_info.height;
13943 window_info.x=(int) geometry_info.x;
13944 window_info.y=(int) geometry_info.y;
13945 status=XMakeImage(display,&resources,&window_info,image,window_info.width,
cristy051718b2011-08-28 22:49:25 +000013946 window_info.height,exception);
dirk5d0a4412016-01-05 22:28:51 +010013947 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000013948 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13949 image->filename);
13950 window_info.x=0;
13951 window_info.y=0;
Elliott Hughes5d41fba2021-04-12 16:36:42 -070013952 if (image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000013953 {
13954 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000013955 "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13956 (double) image->columns,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000013957 if (image->colors != 0)
cristye8c25f92010-06-03 00:53:06 +000013958 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13959 image->colors);
cristy3ed852e2009-09-05 21:47:34 +000013960 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13961 }
13962 /*
13963 Adjust image dimensions as specified by backdrop or geometry options.
13964 */
13965 width=(int) window_info.width;
13966 height=(int) window_info.height;
Elliott Hughes5d41fba2021-04-12 16:36:42 -070013967 if (resources.backdrop != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000013968 {
13969 /*
13970 Center image on window.
13971 */
Elliott Hughes5d41fba2021-04-12 16:36:42 -070013972 window_info.x=(window_attributes.width/2)-(window_info.ximage->width/2);
13973 window_info.y=(window_attributes.height/2)-(window_info.ximage->height/2);
cristy3ed852e2009-09-05 21:47:34 +000013974 width=window_attributes.width;
13975 height=window_attributes.height;
13976 }
13977 if ((resources.image_geometry != (char *) NULL) &&
13978 (*resources.image_geometry != '\0'))
13979 {
13980 char
cristy151b66d2015-04-15 10:50:31 +000013981 default_geometry[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +000013982
13983 int
13984 flags,
13985 gravity;
13986
13987 XSizeHints
13988 *size_hints;
13989
13990 /*
13991 User specified geometry.
13992 */
13993 size_hints=XAllocSizeHints();
13994 if (size_hints == (XSizeHints *) NULL)
13995 ThrowXWindowFatalException(ResourceLimitFatalError,
13996 "MemoryAllocationFailed",image->filename);
13997 size_hints->flags=0L;
cristy151b66d2015-04-15 10:50:31 +000013998 (void) FormatLocaleString(default_geometry,MagickPathExtent,"%dx%d",
cristy3ed852e2009-09-05 21:47:34 +000013999 width,height);
14000 flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
14001 default_geometry,window_info.border_width,size_hints,&window_info.x,
14002 &window_info.y,&width,&height,&gravity);
14003 if (flags & (XValue | YValue))
14004 {
14005 width=window_attributes.width;
14006 height=window_attributes.height;
14007 }
14008 (void) XFree((void *) size_hints);
14009 }
14010 /*
14011 Create the X pixmap.
14012 */
14013 window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14014 (unsigned int) height,window_info.depth);
14015 if (window_info.pixmap == (Pixmap) NULL)
14016 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14017 image->filename);
14018 /*
14019 Display pixmap on the window.
14020 */
14021 if (((unsigned int) width > window_info.width) ||
14022 ((unsigned int) height > window_info.height))
14023 (void) XFillRectangle(display,window_info.pixmap,
14024 window_info.annotate_context,0,0,(unsigned int) width,
14025 (unsigned int) height);
14026 (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14027 window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14028 window_info.width,(unsigned int) window_info.height);
14029 (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14030 (void) XClearWindow(display,window_info.id);
14031 delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14032 XDelay(display,delay == 0UL ? 10UL : delay);
14033 (void) XSync(display,MagickFalse);
dirkb9dbc292015-07-26 09:50:00 +000014034 return(window_info.id == root_window ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +000014035}
14036
14037/*
14038%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14039% %
14040% %
14041% %
14042+ X D i s p l a y I m a g e %
14043% %
14044% %
14045% %
14046%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14047%
14048% XDisplayImage() displays an image via X11. A new image is created and
14049% returned if the user interactively transforms the displayed image.
14050%
14051% The format of the XDisplayImage method is:
14052%
14053% Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +000014054% char **argv,int argc,Image **image,size_t *state,
14055% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000014056%
14057% A description of each parameter follows:
14058%
14059% o nexus: Method XDisplayImage returns an image when the
14060% user chooses 'Open Image' from the command menu or picks a tile
14061% from the image directory. Otherwise a null image is returned.
14062%
14063% o display: Specifies a connection to an X server; returned from
14064% XOpenDisplay.
14065%
14066% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14067%
14068% o argv: Specifies the application's argument list.
14069%
14070% o argc: Specifies the number of arguments.
14071%
14072% o image: Specifies an address to an address of an Image structure;
14073%
cristy051718b2011-08-28 22:49:25 +000014074% o exception: return any errors or warnings in this structure.
14075%
cristy3ed852e2009-09-05 21:47:34 +000014076*/
14077MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +000014078 char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000014079{
14080#define MagnifySize 256 /* must be a power of 2 */
14081#define MagickMenus 10
14082#define MagickTitle "Commands"
14083
Cristyd93b2e62019-05-22 19:45:01 -040014084 const char
14085 *const CommandMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000014086 {
14087 "File",
14088 "Edit",
14089 "View",
14090 "Transform",
14091 "Enhance",
14092 "Effects",
14093 "F/X",
14094 "Image Edit",
14095 "Miscellany",
14096 "Help",
14097 (char *) NULL
14098 },
Cristyd93b2e62019-05-22 19:45:01 -040014099 *const FileMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000014100 {
14101 "Open...",
14102 "Next",
14103 "Former",
14104 "Select...",
14105 "Save...",
14106 "Print...",
14107 "Delete...",
14108 "New...",
14109 "Visual Directory...",
14110 "Quit",
14111 (char *) NULL
14112 },
Cristyd93b2e62019-05-22 19:45:01 -040014113 *const EditMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000014114 {
14115 "Undo",
14116 "Redo",
14117 "Cut",
14118 "Copy",
14119 "Paste",
14120 (char *) NULL
14121 },
Cristyd93b2e62019-05-22 19:45:01 -040014122 *const ViewMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000014123 {
14124 "Half Size",
14125 "Original Size",
14126 "Double Size",
14127 "Resize...",
14128 "Apply",
14129 "Refresh",
14130 "Restore",
14131 (char *) NULL
14132 },
Cristyd93b2e62019-05-22 19:45:01 -040014133 *const TransformMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000014134 {
14135 "Crop",
14136 "Chop",
14137 "Flop",
14138 "Flip",
14139 "Rotate Right",
14140 "Rotate Left",
14141 "Rotate...",
14142 "Shear...",
14143 "Roll...",
14144 "Trim Edges",
14145 (char *) NULL
14146 },
Cristyd93b2e62019-05-22 19:45:01 -040014147 *const EnhanceMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000014148 {
14149 "Hue...",
14150 "Saturation...",
14151 "Brightness...",
14152 "Gamma...",
14153 "Spiff",
14154 "Dull",
14155 "Contrast Stretch...",
14156 "Sigmoidal Contrast...",
14157 "Normalize",
14158 "Equalize",
14159 "Negate",
14160 "Grayscale",
14161 "Map...",
14162 "Quantize...",
14163 (char *) NULL
14164 },
Cristyd93b2e62019-05-22 19:45:01 -040014165 *const EffectsMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000014166 {
14167 "Despeckle",
14168 "Emboss",
14169 "Reduce Noise",
14170 "Add Noise...",
14171 "Sharpen...",
14172 "Blur...",
14173 "Threshold...",
14174 "Edge Detect...",
14175 "Spread...",
14176 "Shade...",
14177 "Raise...",
14178 "Segment...",
14179 (char *) NULL
14180 },
Cristyd93b2e62019-05-22 19:45:01 -040014181 *const FXMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000014182 {
14183 "Solarize...",
14184 "Sepia Tone...",
14185 "Swirl...",
14186 "Implode...",
14187 "Vignette...",
14188 "Wave...",
14189 "Oil Paint...",
14190 "Charcoal Draw...",
14191 (char *) NULL
14192 },
Cristyd93b2e62019-05-22 19:45:01 -040014193 *const ImageEditMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000014194 {
14195 "Annotate...",
14196 "Draw...",
14197 "Color...",
14198 "Matte...",
14199 "Composite...",
14200 "Add Border...",
14201 "Add Frame...",
14202 "Comment...",
14203 "Launch...",
14204 "Region of Interest...",
14205 (char *) NULL
14206 },
Cristyd93b2e62019-05-22 19:45:01 -040014207 *const MiscellanyMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000014208 {
14209 "Image Info",
14210 "Zoom Image",
14211 "Show Preview...",
14212 "Show Histogram",
14213 "Show Matte",
14214 "Background...",
14215 "Slide Show...",
14216 "Preferences...",
14217 (char *) NULL
14218 },
Cristyd93b2e62019-05-22 19:45:01 -040014219 *const HelpMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000014220 {
14221 "Overview",
14222 "Browse Documentation",
14223 "About Display",
14224 (char *) NULL
14225 },
Cristyd93b2e62019-05-22 19:45:01 -040014226 *const ShortCutsMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000014227 {
14228 "Next",
14229 "Former",
14230 "Open...",
14231 "Save...",
14232 "Print...",
14233 "Undo",
14234 "Restore",
14235 "Image Info",
14236 "Quit",
14237 (char *) NULL
14238 },
Cristyd93b2e62019-05-22 19:45:01 -040014239 *const VirtualMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000014240 {
14241 "Image Info",
14242 "Print",
14243 "Next",
14244 "Quit",
14245 (char *) NULL
14246 };
14247
Cristyd93b2e62019-05-22 19:45:01 -040014248 const char
14249 *const *Menus[MagickMenus] =
cristy3ed852e2009-09-05 21:47:34 +000014250 {
14251 FileMenu,
14252 EditMenu,
14253 ViewMenu,
14254 TransformMenu,
14255 EnhanceMenu,
14256 EffectsMenu,
14257 FXMenu,
14258 ImageEditMenu,
14259 MiscellanyMenu,
14260 HelpMenu
14261 };
14262
14263 static CommandType
14264 CommandMenus[] =
14265 {
14266 NullCommand,
14267 NullCommand,
14268 NullCommand,
14269 NullCommand,
14270 NullCommand,
14271 NullCommand,
14272 NullCommand,
14273 NullCommand,
14274 NullCommand,
14275 NullCommand,
14276 },
14277 FileCommands[] =
14278 {
14279 OpenCommand,
14280 NextCommand,
14281 FormerCommand,
14282 SelectCommand,
14283 SaveCommand,
14284 PrintCommand,
14285 DeleteCommand,
14286 NewCommand,
14287 VisualDirectoryCommand,
14288 QuitCommand
14289 },
14290 EditCommands[] =
14291 {
14292 UndoCommand,
14293 RedoCommand,
14294 CutCommand,
14295 CopyCommand,
14296 PasteCommand
14297 },
14298 ViewCommands[] =
14299 {
14300 HalfSizeCommand,
14301 OriginalSizeCommand,
14302 DoubleSizeCommand,
14303 ResizeCommand,
14304 ApplyCommand,
14305 RefreshCommand,
14306 RestoreCommand
14307 },
14308 TransformCommands[] =
14309 {
14310 CropCommand,
14311 ChopCommand,
14312 FlopCommand,
14313 FlipCommand,
14314 RotateRightCommand,
14315 RotateLeftCommand,
14316 RotateCommand,
14317 ShearCommand,
14318 RollCommand,
14319 TrimCommand
14320 },
14321 EnhanceCommands[] =
14322 {
14323 HueCommand,
14324 SaturationCommand,
14325 BrightnessCommand,
14326 GammaCommand,
14327 SpiffCommand,
14328 DullCommand,
14329 ContrastStretchCommand,
14330 SigmoidalContrastCommand,
14331 NormalizeCommand,
14332 EqualizeCommand,
14333 NegateCommand,
14334 GrayscaleCommand,
14335 MapCommand,
14336 QuantizeCommand
14337 },
14338 EffectsCommands[] =
14339 {
14340 DespeckleCommand,
14341 EmbossCommand,
14342 ReduceNoiseCommand,
14343 AddNoiseCommand,
14344 SharpenCommand,
14345 BlurCommand,
14346 ThresholdCommand,
14347 EdgeDetectCommand,
14348 SpreadCommand,
14349 ShadeCommand,
14350 RaiseCommand,
14351 SegmentCommand
14352 },
14353 FXCommands[] =
14354 {
14355 SolarizeCommand,
14356 SepiaToneCommand,
14357 SwirlCommand,
14358 ImplodeCommand,
14359 VignetteCommand,
14360 WaveCommand,
14361 OilPaintCommand,
14362 CharcoalDrawCommand
14363 },
14364 ImageEditCommands[] =
14365 {
14366 AnnotateCommand,
14367 DrawCommand,
14368 ColorCommand,
14369 MatteCommand,
14370 CompositeCommand,
14371 AddBorderCommand,
14372 AddFrameCommand,
14373 CommentCommand,
14374 LaunchCommand,
14375 RegionofInterestCommand
14376 },
14377 MiscellanyCommands[] =
14378 {
14379 InfoCommand,
14380 ZoomCommand,
14381 ShowPreviewCommand,
14382 ShowHistogramCommand,
14383 ShowMatteCommand,
14384 BackgroundCommand,
14385 SlideShowCommand,
14386 PreferencesCommand
14387 },
14388 HelpCommands[] =
14389 {
14390 HelpCommand,
14391 BrowseDocumentationCommand,
14392 VersionCommand
14393 },
14394 ShortCutsCommands[] =
14395 {
14396 NextCommand,
14397 FormerCommand,
14398 OpenCommand,
14399 SaveCommand,
14400 PrintCommand,
14401 UndoCommand,
14402 RestoreCommand,
14403 InfoCommand,
14404 QuitCommand
14405 },
14406 VirtualCommands[] =
14407 {
14408 InfoCommand,
14409 PrintCommand,
14410 NextCommand,
14411 QuitCommand
14412 };
14413
14414 static CommandType
14415 *Commands[MagickMenus] =
14416 {
14417 FileCommands,
14418 EditCommands,
14419 ViewCommands,
14420 TransformCommands,
14421 EnhanceCommands,
14422 EffectsCommands,
14423 FXCommands,
14424 ImageEditCommands,
14425 MiscellanyCommands,
14426 HelpCommands
14427 };
14428
14429 char
cristy151b66d2015-04-15 10:50:31 +000014430 command[MagickPathExtent],
cristy00976d82011-02-20 20:31:28 +000014431 *directory,
cristy151b66d2015-04-15 10:50:31 +000014432 geometry[MagickPathExtent],
14433 resource_name[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +000014434
14435 CommandType
14436 command_type;
14437
14438 Image
14439 *display_image,
14440 *nexus;
14441
14442 int
14443 entry,
14444 id;
14445
14446 KeySym
14447 key_symbol;
14448
14449 MagickStatusType
14450 context_mask,
14451 status;
14452
14453 RectangleInfo
14454 geometry_info;
14455
Cristyf2dc1dd2020-12-28 13:59:26 -050014456 int
cristy3ed852e2009-09-05 21:47:34 +000014457 i;
14458
14459 static char
cristy151b66d2015-04-15 10:50:31 +000014460 working_directory[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +000014461
14462 static XPoint
14463 vid_info;
14464
14465 static XWindowInfo
14466 *magick_windows[MaxXWindows];
14467
14468 static unsigned int
14469 number_windows;
14470
14471 struct stat
14472 attributes;
14473
14474 time_t
14475 timer,
14476 timestamp,
14477 update_time;
14478
14479 unsigned int
14480 height,
14481 width;
14482
cristybb503372010-05-27 20:51:26 +000014483 size_t
cristy3ed852e2009-09-05 21:47:34 +000014484 delay;
14485
14486 WarningHandler
14487 warning_handler;
14488
14489 Window
14490 root_window;
14491
14492 XClassHint
14493 *class_hints;
14494
14495 XEvent
14496 event;
14497
14498 XFontStruct
14499 *font_info;
14500
14501 XGCValues
14502 context_values;
14503
14504 XPixelInfo
14505 *icon_pixel,
14506 *pixel;
14507
14508 XResourceInfo
14509 *icon_resources;
14510
14511 XStandardColormap
14512 *icon_map,
14513 *map_info;
14514
14515 XVisualInfo
14516 *icon_visual,
14517 *visual_info;
14518
14519 XWindowChanges
14520 window_changes;
14521
14522 XWindows
14523 *windows;
14524
14525 XWMHints
14526 *manager_hints;
14527
14528 assert(image != (Image **) NULL);
cristye1c94d92015-06-28 12:16:33 +000014529 assert((*image)->signature == MagickCoreSignature);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070014530 if ((*image)->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000014531 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14532 display_image=(*image);
14533 warning_handler=(WarningHandler) NULL;
14534 windows=XSetWindows((XWindows *) ~0);
14535 if (windows != (XWindows *) NULL)
14536 {
14537 int
14538 status;
14539
cristy8a5d7f42013-01-06 15:24:33 +000014540 if (*working_directory == '\0')
cristy151b66d2015-04-15 10:50:31 +000014541 (void) CopyMagickString(working_directory,".",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +000014542 status=chdir(working_directory);
14543 if (status == -1)
cristy051718b2011-08-28 22:49:25 +000014544 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14545 "UnableToOpenFile","%s",working_directory);
cristy3ed852e2009-09-05 21:47:34 +000014546 warning_handler=resource_info->display_warnings ?
14547 SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14548 warning_handler=resource_info->display_warnings ?
14549 SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14550 }
14551 else
14552 {
14553 /*
14554 Allocate windows structure.
14555 */
14556 resource_info->colors=display_image->colors;
14557 windows=XSetWindows(XInitializeWindows(display,resource_info));
14558 if (windows == (XWindows *) NULL)
14559 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14560 (*image)->filename);
14561 /*
14562 Initialize window id's.
14563 */
14564 number_windows=0;
14565 magick_windows[number_windows++]=(&windows->icon);
14566 magick_windows[number_windows++]=(&windows->backdrop);
14567 magick_windows[number_windows++]=(&windows->image);
14568 magick_windows[number_windows++]=(&windows->info);
14569 magick_windows[number_windows++]=(&windows->command);
14570 magick_windows[number_windows++]=(&windows->widget);
14571 magick_windows[number_windows++]=(&windows->popup);
14572 magick_windows[number_windows++]=(&windows->magnify);
14573 magick_windows[number_windows++]=(&windows->pan);
14574 for (i=0; i < (int) number_windows; i++)
14575 magick_windows[i]->id=(Window) NULL;
14576 vid_info.x=0;
14577 vid_info.y=0;
14578 }
14579 /*
14580 Initialize font info.
14581 */
14582 if (windows->font_info != (XFontStruct *) NULL)
14583 (void) XFreeFont(display,windows->font_info);
14584 windows->font_info=XBestFont(display,resource_info,MagickFalse);
14585 if (windows->font_info == (XFontStruct *) NULL)
14586 ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14587 resource_info->font);
14588 /*
14589 Initialize Standard Colormap.
14590 */
14591 map_info=windows->map_info;
14592 icon_map=windows->icon_map;
14593 visual_info=windows->visual_info;
14594 icon_visual=windows->icon_visual;
14595 pixel=windows->pixel_info;
14596 icon_pixel=windows->icon_pixel;
14597 font_info=windows->font_info;
14598 icon_resources=windows->icon_resources;
14599 class_hints=windows->class_hints;
14600 manager_hints=windows->manager_hints;
14601 root_window=XRootWindow(display,visual_info->screen);
14602 nexus=NewImageList();
Elliott Hughes5d41fba2021-04-12 16:36:42 -070014603 if (display_image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000014604 {
14605 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000014606 "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14607 (double) display_image->scene,(double) display_image->columns,
14608 (double) display_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000014609 if (display_image->colors != 0)
cristye8c25f92010-06-03 00:53:06 +000014610 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14611 display_image->colors);
cristy3ed852e2009-09-05 21:47:34 +000014612 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14613 display_image->magick);
14614 }
14615 XMakeStandardColormap(display,visual_info,resource_info,display_image,
cristy6710d842011-10-20 23:23:00 +000014616 map_info,pixel,exception);
cristy3ed852e2009-09-05 21:47:34 +000014617 display_image->taint=MagickFalse;
14618 /*
14619 Initialize graphic context.
14620 */
14621 windows->context.id=(Window) NULL;
14622 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14623 resource_info,&windows->context);
14624 (void) CloneString(&class_hints->res_name,resource_info->client_name);
14625 (void) CloneString(&class_hints->res_class,resource_info->client_name);
Cristyd8bcdf12019-01-20 10:02:42 -050014626 class_hints->res_class[0]=(char) LocaleUppercase((int)
14627 class_hints->res_class[0]);
cristy3ed852e2009-09-05 21:47:34 +000014628 manager_hints->flags=InputHint | StateHint;
14629 manager_hints->input=MagickFalse;
14630 manager_hints->initial_state=WithdrawnState;
14631 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14632 &windows->context);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070014633 if (display_image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000014634 (void) LogMagickEvent(X11Event,GetMagickModule(),
14635 "Window id: 0x%lx (context)",windows->context.id);
14636 context_values.background=pixel->background_color.pixel;
14637 context_values.font=font_info->fid;
14638 context_values.foreground=pixel->foreground_color.pixel;
14639 context_values.graphics_exposures=MagickFalse;
14640 context_mask=(MagickStatusType)
14641 (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14642 if (pixel->annotate_context != (GC) NULL)
14643 (void) XFreeGC(display,pixel->annotate_context);
14644 pixel->annotate_context=XCreateGC(display,windows->context.id,
14645 context_mask,&context_values);
14646 if (pixel->annotate_context == (GC) NULL)
14647 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14648 display_image->filename);
14649 context_values.background=pixel->depth_color.pixel;
14650 if (pixel->widget_context != (GC) NULL)
14651 (void) XFreeGC(display,pixel->widget_context);
14652 pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14653 &context_values);
14654 if (pixel->widget_context == (GC) NULL)
14655 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14656 display_image->filename);
14657 context_values.background=pixel->foreground_color.pixel;
14658 context_values.foreground=pixel->background_color.pixel;
14659 context_values.plane_mask=context_values.background ^
14660 context_values.foreground;
14661 if (pixel->highlight_context != (GC) NULL)
14662 (void) XFreeGC(display,pixel->highlight_context);
14663 pixel->highlight_context=XCreateGC(display,windows->context.id,
cristybb503372010-05-27 20:51:26 +000014664 (size_t) (context_mask | GCPlaneMask),&context_values);
cristy3ed852e2009-09-05 21:47:34 +000014665 if (pixel->highlight_context == (GC) NULL)
14666 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14667 display_image->filename);
14668 (void) XDestroyWindow(display,windows->context.id);
14669 /*
14670 Initialize icon window.
14671 */
14672 XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14673 icon_resources,&windows->icon);
14674 windows->icon.geometry=resource_info->icon_geometry;
14675 XBestIconSize(display,&windows->icon,display_image);
14676 windows->icon.attributes.colormap=XDefaultColormap(display,
14677 icon_visual->screen);
14678 windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14679 manager_hints->flags=InputHint | StateHint;
14680 manager_hints->input=MagickFalse;
14681 manager_hints->initial_state=IconicState;
14682 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14683 &windows->icon);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070014684 if (display_image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000014685 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14686 windows->icon.id);
14687 /*
14688 Initialize graphic context for icon window.
14689 */
14690 if (icon_pixel->annotate_context != (GC) NULL)
14691 (void) XFreeGC(display,icon_pixel->annotate_context);
14692 context_values.background=icon_pixel->background_color.pixel;
14693 context_values.foreground=icon_pixel->foreground_color.pixel;
14694 icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
cristybb503372010-05-27 20:51:26 +000014695 (size_t) (GCBackground | GCForeground),&context_values);
cristy3ed852e2009-09-05 21:47:34 +000014696 if (icon_pixel->annotate_context == (GC) NULL)
14697 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14698 display_image->filename);
14699 windows->icon.annotate_context=icon_pixel->annotate_context;
14700 /*
14701 Initialize Image window.
14702 */
14703 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14704 &windows->image);
14705 windows->image.shape=MagickTrue; /* non-rectangular shape hint */
dirk5d0a4412016-01-05 22:28:51 +010014706 if (resource_info->use_shared_memory == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000014707 windows->image.shared_memory=MagickFalse;
14708 if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14709 {
14710 char
14711 *title;
14712
14713 title=InterpretImageProperties(resource_info->image_info,display_image,
cristy018f07f2011-09-04 21:15:19 +000014714 resource_info->title,exception);
Cristyec4e6442019-06-24 10:58:25 -040014715 (void) CloneString(&windows->image.name,title);
14716 (void) CloneString(&windows->image.icon_name,title);
cristy3ed852e2009-09-05 21:47:34 +000014717 title=DestroyString(title);
14718 }
14719 else
14720 {
14721 char
Cristyec4e6442019-06-24 10:58:25 -040014722 filename[MagickPathExtent],
14723 window_name[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +000014724
14725 /*
14726 Window name is the base of the filename.
14727 */
14728 GetPathComponent(display_image->magick_filename,TailPath,filename);
cristy9a48b172011-09-20 17:41:05 +000014729 if (display_image->scene == 0)
Cristyec4e6442019-06-24 10:58:25 -040014730 (void) FormatLocaleString(window_name,MagickPathExtent,"%s: %s",
14731 MagickPackageName,filename);
cristy3ed852e2009-09-05 21:47:34 +000014732 else
Cristyec4e6442019-06-24 10:58:25 -040014733 (void) FormatLocaleString(window_name,MagickPathExtent,
cristy04d55062011-03-15 18:58:50 +000014734 "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14735 (double) display_image->scene,(double) GetImageListLength(
14736 display_image));
Cristyec4e6442019-06-24 10:58:25 -040014737 (void) CloneString(&windows->image.name,window_name);
14738 (void) CloneString(&windows->image.icon_name,filename);
cristy3ed852e2009-09-05 21:47:34 +000014739 }
14740 if (resource_info->immutable)
14741 windows->image.immutable=MagickTrue;
14742 windows->image.use_pixmap=resource_info->use_pixmap;
14743 windows->image.geometry=resource_info->image_geometry;
cristy151b66d2015-04-15 10:50:31 +000014744 (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
cristy3ed852e2009-09-05 21:47:34 +000014745 XDisplayWidth(display,visual_info->screen),
14746 XDisplayHeight(display,visual_info->screen));
14747 geometry_info.width=display_image->columns;
14748 geometry_info.height=display_image->rows;
14749 geometry_info.x=0;
14750 geometry_info.y=0;
14751 (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14752 &geometry_info.width,&geometry_info.height);
14753 windows->image.width=(unsigned int) geometry_info.width;
14754 windows->image.height=(unsigned int) geometry_info.height;
14755 windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14756 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14757 KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14758 PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14759 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14760 resource_info,&windows->backdrop);
14761 if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14762 {
14763 /*
14764 Initialize backdrop window.
14765 */
14766 windows->backdrop.x=0;
14767 windows->backdrop.y=0;
cristy40a08ad2010-02-09 02:27:44 +000014768 (void) CloneString(&windows->backdrop.name,"Backdrop");
cristybb503372010-05-27 20:51:26 +000014769 windows->backdrop.flags=(size_t) (USSize | USPosition);
cristy3ed852e2009-09-05 21:47:34 +000014770 windows->backdrop.width=(unsigned int)
14771 XDisplayWidth(display,visual_info->screen);
14772 windows->backdrop.height=(unsigned int)
14773 XDisplayHeight(display,visual_info->screen);
14774 windows->backdrop.border_width=0;
14775 windows->backdrop.immutable=MagickTrue;
14776 windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14777 ButtonReleaseMask;
14778 windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14779 StructureNotifyMask;
14780 manager_hints->flags=IconWindowHint | InputHint | StateHint;
14781 manager_hints->icon_window=windows->icon.id;
14782 manager_hints->input=MagickTrue;
14783 manager_hints->initial_state=resource_info->iconic ? IconicState :
14784 NormalState;
14785 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14786 &windows->backdrop);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070014787 if (display_image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000014788 (void) LogMagickEvent(X11Event,GetMagickModule(),
14789 "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14790 (void) XMapWindow(display,windows->backdrop.id);
14791 (void) XClearWindow(display,windows->backdrop.id);
14792 if (windows->image.id != (Window) NULL)
14793 {
14794 (void) XDestroyWindow(display,windows->image.id);
14795 windows->image.id=(Window) NULL;
14796 }
14797 /*
14798 Position image in the center the backdrop.
14799 */
14800 windows->image.flags|=USPosition;
14801 windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14802 (windows->image.width/2);
14803 windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14804 (windows->image.height/2);
14805 }
14806 manager_hints->flags=IconWindowHint | InputHint | StateHint;
14807 manager_hints->icon_window=windows->icon.id;
14808 manager_hints->input=MagickTrue;
14809 manager_hints->initial_state=resource_info->iconic ? IconicState :
14810 NormalState;
14811 if (windows->group_leader.id != (Window) NULL)
14812 {
14813 /*
14814 Follow the leader.
14815 */
14816 manager_hints->flags|=WindowGroupHint;
14817 manager_hints->window_group=windows->group_leader.id;
14818 (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070014819 if (display_image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000014820 (void) LogMagickEvent(X11Event,GetMagickModule(),
14821 "Window id: 0x%lx (group leader)",windows->group_leader.id);
14822 }
14823 XMakeWindow(display,
14824 (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14825 argv,argc,class_hints,manager_hints,&windows->image);
14826 (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14827 XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14828 if (windows->group_leader.id != (Window) NULL)
14829 (void) XSetTransientForHint(display,windows->image.id,
14830 windows->group_leader.id);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070014831 if (display_image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000014832 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14833 windows->image.id);
14834 /*
14835 Initialize Info widget.
14836 */
14837 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14838 &windows->info);
14839 (void) CloneString(&windows->info.name,"Info");
14840 (void) CloneString(&windows->info.icon_name,"Info");
14841 windows->info.border_width=1;
14842 windows->info.x=2;
14843 windows->info.y=2;
14844 windows->info.flags|=PPosition;
14845 windows->info.attributes.win_gravity=UnmapGravity;
14846 windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14847 StructureNotifyMask;
14848 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14849 manager_hints->input=MagickFalse;
14850 manager_hints->initial_state=NormalState;
14851 manager_hints->window_group=windows->image.id;
14852 XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14853 &windows->info);
14854 windows->info.highlight_stipple=XCreateBitmapFromData(display,
14855 windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14856 windows->info.shadow_stipple=XCreateBitmapFromData(display,
14857 windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14858 (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070014859 if (windows->image.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000014860 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070014861 if (display_image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000014862 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14863 windows->info.id);
14864 /*
14865 Initialize Command widget.
14866 */
14867 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14868 resource_info,&windows->command);
14869 windows->command.data=MagickMenus;
14870 (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
cristy151b66d2015-04-15 10:50:31 +000014871 (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.command",
cristy3ed852e2009-09-05 21:47:34 +000014872 resource_info->client_name);
14873 windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14874 resource_name,"geometry",(char *) NULL);
14875 (void) CloneString(&windows->command.name,MagickTitle);
14876 windows->command.border_width=0;
14877 windows->command.flags|=PPosition;
14878 windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14879 ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14880 OwnerGrabButtonMask | StructureNotifyMask;
14881 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14882 manager_hints->input=MagickTrue;
14883 manager_hints->initial_state=NormalState;
14884 manager_hints->window_group=windows->image.id;
14885 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14886 &windows->command);
14887 windows->command.highlight_stipple=XCreateBitmapFromData(display,
14888 windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14889 HighlightHeight);
14890 windows->command.shadow_stipple=XCreateBitmapFromData(display,
14891 windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14892 (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070014893 if (windows->command.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000014894 (void) XMapRaised(display,windows->command.id);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070014895 if (display_image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000014896 (void) LogMagickEvent(X11Event,GetMagickModule(),
14897 "Window id: 0x%lx (command)",windows->command.id);
14898 /*
14899 Initialize Widget window.
14900 */
14901 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14902 resource_info,&windows->widget);
cristy151b66d2015-04-15 10:50:31 +000014903 (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.widget",
cristy3ed852e2009-09-05 21:47:34 +000014904 resource_info->client_name);
14905 windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14906 resource_name,"geometry",(char *) NULL);
14907 windows->widget.border_width=0;
14908 windows->widget.flags|=PPosition;
14909 windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14910 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14911 KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14912 StructureNotifyMask;
14913 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14914 manager_hints->input=MagickTrue;
14915 manager_hints->initial_state=NormalState;
14916 manager_hints->window_group=windows->image.id;
14917 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14918 &windows->widget);
14919 windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14920 windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14921 windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14922 windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14923 (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070014924 if (display_image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000014925 (void) LogMagickEvent(X11Event,GetMagickModule(),
14926 "Window id: 0x%lx (widget)",windows->widget.id);
14927 /*
14928 Initialize popup window.
14929 */
14930 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14931 resource_info,&windows->popup);
14932 windows->popup.border_width=0;
14933 windows->popup.flags|=PPosition;
14934 windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14935 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14936 KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14937 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14938 manager_hints->input=MagickTrue;
14939 manager_hints->initial_state=NormalState;
14940 manager_hints->window_group=windows->image.id;
14941 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14942 &windows->popup);
14943 windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14944 windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14945 windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14946 windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14947 (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070014948 if (display_image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000014949 (void) LogMagickEvent(X11Event,GetMagickModule(),
14950 "Window id: 0x%lx (pop up)",windows->popup.id);
14951 /*
14952 Initialize Magnify window and cursor.
14953 */
14954 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14955 resource_info,&windows->magnify);
dirk5d0a4412016-01-05 22:28:51 +010014956 if (resource_info->use_shared_memory == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000014957 windows->magnify.shared_memory=MagickFalse;
cristy151b66d2015-04-15 10:50:31 +000014958 (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.magnify",
cristy3ed852e2009-09-05 21:47:34 +000014959 resource_info->client_name);
14960 windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14961 resource_name,"geometry",(char *) NULL);
Cristyd93b2e62019-05-22 19:45:01 -040014962 (void) FormatLocaleString(windows->magnify.name,MagickPathExtent,
14963 "Magnify %uX",resource_info->magnify);
cristy3ed852e2009-09-05 21:47:34 +000014964 if (windows->magnify.cursor != (Cursor) NULL)
14965 (void) XFreeCursor(display,windows->magnify.cursor);
14966 windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14967 map_info->colormap,resource_info->background_color,
14968 resource_info->foreground_color);
14969 if (windows->magnify.cursor == (Cursor) NULL)
14970 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14971 display_image->filename);
14972 windows->magnify.width=MagnifySize;
14973 windows->magnify.height=MagnifySize;
14974 windows->magnify.flags|=PPosition;
14975 windows->magnify.min_width=MagnifySize;
14976 windows->magnify.min_height=MagnifySize;
14977 windows->magnify.width_inc=MagnifySize;
14978 windows->magnify.height_inc=MagnifySize;
14979 windows->magnify.data=resource_info->magnify;
14980 windows->magnify.attributes.cursor=windows->magnify.cursor;
14981 windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14982 ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14983 StructureNotifyMask;
14984 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14985 manager_hints->input=MagickTrue;
14986 manager_hints->initial_state=NormalState;
14987 manager_hints->window_group=windows->image.id;
14988 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14989 &windows->magnify);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070014990 if (display_image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000014991 (void) LogMagickEvent(X11Event,GetMagickModule(),
14992 "Window id: 0x%lx (magnify)",windows->magnify.id);
14993 (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14994 /*
14995 Initialize panning window.
14996 */
14997 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14998 resource_info,&windows->pan);
14999 (void) CloneString(&windows->pan.name,"Pan Icon");
15000 windows->pan.width=windows->icon.width;
15001 windows->pan.height=windows->icon.height;
cristy151b66d2015-04-15 10:50:31 +000015002 (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.pan",
cristy3ed852e2009-09-05 21:47:34 +000015003 resource_info->client_name);
15004 windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
15005 resource_name,"geometry",(char *) NULL);
15006 (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15007 &windows->pan.width,&windows->pan.height);
15008 windows->pan.flags|=PPosition;
15009 windows->pan.immutable=MagickTrue;
15010 windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15011 ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15012 StructureNotifyMask;
15013 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15014 manager_hints->input=MagickFalse;
15015 manager_hints->initial_state=NormalState;
15016 manager_hints->window_group=windows->image.id;
15017 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15018 &windows->pan);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015019 if (display_image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015020 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15021 windows->pan.id);
15022 (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015023 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015024 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
dirk5d0a4412016-01-05 22:28:51 +010015025 if ((windows->image.mapped == MagickFalse) ||
cristy3ed852e2009-09-05 21:47:34 +000015026 (windows->backdrop.id != (Window) NULL))
15027 (void) XMapWindow(display,windows->image.id);
15028 /*
15029 Set our progress monitor and warning handlers.
15030 */
15031 if (warning_handler == (WarningHandler) NULL)
15032 {
15033 warning_handler=resource_info->display_warnings ?
15034 SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15035 warning_handler=resource_info->display_warnings ?
15036 SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15037 }
15038 /*
15039 Initialize Image and Magnify X images.
15040 */
15041 windows->image.x=0;
15042 windows->image.y=0;
15043 windows->magnify.shape=MagickFalse;
15044 width=(unsigned int) display_image->columns;
15045 height=(unsigned int) display_image->rows;
15046 if ((display_image->columns != width) || (display_image->rows != height))
15047 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15048 display_image->filename);
15049 status=XMakeImage(display,resource_info,&windows->image,display_image,
cristy051718b2011-08-28 22:49:25 +000015050 width,height,exception);
dirk5d0a4412016-01-05 22:28:51 +010015051 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015052 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15053 display_image->filename);
15054 status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
cristy051718b2011-08-28 22:49:25 +000015055 windows->magnify.width,windows->magnify.height,exception);
dirk5d0a4412016-01-05 22:28:51 +010015056 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015057 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15058 display_image->filename);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015059 if (windows->magnify.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015060 (void) XMapRaised(display,windows->magnify.id);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015061 if (windows->pan.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015062 (void) XMapRaised(display,windows->pan.id);
15063 windows->image.window_changes.width=(int) display_image->columns;
15064 windows->image.window_changes.height=(int) display_image->rows;
cristy051718b2011-08-28 22:49:25 +000015065 (void) XConfigureImage(display,resource_info,windows,display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000015066 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15067 (void) XSync(display,MagickFalse);
15068 /*
15069 Respond to events.
15070 */
15071 delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
Cristyedd02d22019-04-15 09:02:06 -040015072 timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
cristy3ed852e2009-09-05 21:47:34 +000015073 update_time=0;
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015074 if (resource_info->update != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015075 {
15076 MagickBooleanType
15077 status;
15078
15079 /*
15080 Determine when file data was last modified.
15081 */
15082 status=GetPathAttributes(display_image->filename,&attributes);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015083 if (status != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015084 update_time=attributes.st_mtime;
15085 }
15086 *state&=(~FormerImageState);
15087 *state&=(~MontageImageState);
15088 *state&=(~NextImageState);
15089 do
15090 {
15091 /*
15092 Handle a window event.
15093 */
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015094 if (windows->image.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015095 if ((display_image->delay != 0) || (resource_info->update != 0))
15096 {
Cristyedd02d22019-04-15 09:02:06 -040015097 if (timer < GetMagickTime())
cristy3ed852e2009-09-05 21:47:34 +000015098 {
dirk5d0a4412016-01-05 22:28:51 +010015099 if (resource_info->update == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015100 *state|=NextImageState | ExitState;
15101 else
15102 {
15103 MagickBooleanType
15104 status;
15105
15106 /*
15107 Determine if image file was modified.
15108 */
15109 status=GetPathAttributes(display_image->filename,&attributes);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015110 if (status != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015111 if (update_time != attributes.st_mtime)
15112 {
15113 /*
15114 Redisplay image.
15115 */
cristyb51dff52011-05-19 16:55:47 +000015116 (void) FormatLocaleString(
cristy151b66d2015-04-15 10:50:31 +000015117 resource_info->image_info->filename,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +000015118 "%s:%s",display_image->magick,
15119 display_image->filename);
cristy947cb4c2011-10-20 18:41:46 +000015120 nexus=ReadImage(resource_info->image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +000015121 if (nexus != (Image *) NULL)
cristye799ee22015-06-30 14:26:05 +000015122 *state|=NextImageState | ExitState;
cristy3ed852e2009-09-05 21:47:34 +000015123 }
15124 delay=display_image->delay/MagickMax(
15125 display_image->ticks_per_second,1L);
Cristyedd02d22019-04-15 09:02:06 -040015126 timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
cristy3ed852e2009-09-05 21:47:34 +000015127 }
15128 }
15129 if (XEventsQueued(display,QueuedAfterFlush) == 0)
15130 {
15131 /*
15132 Do not block if delay > 0.
15133 */
15134 XDelay(display,SuspendTime << 2);
15135 continue;
15136 }
15137 }
Cristyedd02d22019-04-15 09:02:06 -040015138 timestamp=GetMagickTime();
cristy3ed852e2009-09-05 21:47:34 +000015139 (void) XNextEvent(display,&event);
dirkb9dbc292015-07-26 09:50:00 +000015140 if ((windows->image.stasis == MagickFalse) ||
15141 (windows->magnify.stasis == MagickFalse))
15142 {
Cristyedd02d22019-04-15 09:02:06 -040015143 if ((GetMagickTime()-timestamp) > 0)
dirkb9dbc292015-07-26 09:50:00 +000015144 {
15145 windows->image.stasis=MagickTrue;
15146 windows->magnify.stasis=MagickTrue;
15147 }
15148 }
cristy3ed852e2009-09-05 21:47:34 +000015149 if (event.xany.window == windows->command.id)
15150 {
15151 /*
15152 Select a command from the Command widget.
15153 */
15154 id=XCommandWidget(display,windows,CommandMenu,&event);
15155 if (id < 0)
15156 continue;
cristy151b66d2015-04-15 10:50:31 +000015157 (void) CopyMagickString(command,CommandMenu[id],MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +000015158 command_type=CommandMenus[id];
15159 if (id < MagickMenus)
15160 {
15161 /*
15162 Select a command from a pop-up menu.
15163 */
15164 entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15165 command);
15166 if (entry < 0)
15167 continue;
cristy151b66d2015-04-15 10:50:31 +000015168 (void) CopyMagickString(command,Menus[id][entry],MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +000015169 command_type=Commands[id][entry];
15170 }
15171 if (command_type != NullCommand)
15172 nexus=XMagickCommand(display,resource_info,windows,command_type,
cristy051718b2011-08-28 22:49:25 +000015173 &display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000015174 continue;
15175 }
15176 switch (event.type)
15177 {
15178 case ButtonPress:
15179 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015180 if (display_image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015181 (void) LogMagickEvent(X11Event,GetMagickModule(),
15182 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15183 event.xbutton.button,event.xbutton.x,event.xbutton.y);
15184 if ((event.xbutton.button == Button3) &&
15185 (event.xbutton.state & Mod1Mask))
15186 {
15187 /*
15188 Convert Alt-Button3 to Button2.
15189 */
15190 event.xbutton.button=Button2;
15191 event.xbutton.state&=(~Mod1Mask);
15192 }
15193 if (event.xbutton.window == windows->backdrop.id)
15194 {
15195 (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15196 event.xbutton.time);
15197 break;
15198 }
15199 if (event.xbutton.window == windows->image.id)
15200 {
15201 switch (event.xbutton.button)
15202 {
15203 case Button1:
15204 {
15205 if (resource_info->immutable)
15206 {
15207 /*
15208 Select a command from the Virtual menu.
15209 */
15210 entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15211 command);
15212 if (entry >= 0)
15213 nexus=XMagickCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000015214 VirtualCommands[entry],&display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000015215 break;
15216 }
15217 /*
15218 Map/unmap Command widget.
15219 */
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015220 if (windows->command.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015221 (void) XWithdrawWindow(display,windows->command.id,
15222 windows->command.screen);
15223 else
15224 {
15225 (void) XCommandWidget(display,windows,CommandMenu,
15226 (XEvent *) NULL);
15227 (void) XMapRaised(display,windows->command.id);
15228 }
15229 break;
15230 }
15231 case Button2:
15232 {
15233 /*
15234 User pressed the image magnify button.
15235 */
15236 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
cristy051718b2011-08-28 22:49:25 +000015237 &display_image,exception);
cristy6710d842011-10-20 23:23:00 +000015238 XMagnifyImage(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +000015239 break;
15240 }
15241 case Button3:
15242 {
15243 if (resource_info->immutable)
15244 {
15245 /*
15246 Select a command from the Virtual menu.
15247 */
15248 entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15249 command);
15250 if (entry >= 0)
15251 nexus=XMagickCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000015252 VirtualCommands[entry],&display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000015253 break;
15254 }
15255 if (display_image->montage != (char *) NULL)
15256 {
15257 /*
15258 Open or delete a tile from a visual image directory.
15259 */
15260 nexus=XTileImage(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000015261 display_image,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +000015262 if (nexus != (Image *) NULL)
15263 *state|=MontageImageState | NextImageState | ExitState;
cristy49e2d862010-11-12 02:50:30 +000015264 vid_info.x=(short int) windows->image.x;
15265 vid_info.y=(short int) windows->image.y;
cristy3ed852e2009-09-05 21:47:34 +000015266 break;
15267 }
15268 /*
15269 Select a command from the Short Cuts menu.
15270 */
15271 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15272 command);
15273 if (entry >= 0)
15274 nexus=XMagickCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000015275 ShortCutsCommands[entry],&display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000015276 break;
15277 }
15278 case Button4:
15279 {
15280 /*
15281 Wheel up.
15282 */
15283 XTranslateImage(display,windows,*image,XK_Up);
15284 break;
15285 }
15286 case Button5:
15287 {
15288 /*
15289 Wheel down.
15290 */
15291 XTranslateImage(display,windows,*image,XK_Down);
15292 break;
15293 }
15294 default:
15295 break;
15296 }
15297 break;
15298 }
15299 if (event.xbutton.window == windows->magnify.id)
15300 {
Cristyd93b2e62019-05-22 19:45:01 -040015301 const char
15302 *const MagnifyMenu[] =
cristy3ed852e2009-09-05 21:47:34 +000015303 {
15304 "2",
15305 "4",
15306 "5",
15307 "6",
15308 "7",
15309 "8",
15310 "9",
15311 "3",
15312 (char *) NULL,
15313 };
15314
Cristyd93b2e62019-05-22 19:45:01 -040015315 int
15316 factor;
15317
cristy3ed852e2009-09-05 21:47:34 +000015318 static KeySym
15319 MagnifyCommands[] =
15320 {
15321 XK_2,
15322 XK_4,
15323 XK_5,
15324 XK_6,
15325 XK_7,
15326 XK_8,
15327 XK_9,
15328 XK_3
15329 };
15330
15331 /*
15332 Select a magnify factor from the pop-up menu.
15333 */
15334 factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15335 if (factor >= 0)
cristy6710d842011-10-20 23:23:00 +000015336 XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15337 exception);
cristy3ed852e2009-09-05 21:47:34 +000015338 break;
15339 }
15340 if (event.xbutton.window == windows->pan.id)
15341 {
15342 switch (event.xbutton.button)
15343 {
15344 case Button4:
15345 {
15346 /*
15347 Wheel up.
15348 */
15349 XTranslateImage(display,windows,*image,XK_Up);
15350 break;
15351 }
15352 case Button5:
15353 {
15354 /*
15355 Wheel down.
15356 */
15357 XTranslateImage(display,windows,*image,XK_Down);
15358 break;
15359 }
15360 default:
15361 {
cristy6710d842011-10-20 23:23:00 +000015362 XPanImage(display,windows,&event,exception);
cristy3ed852e2009-09-05 21:47:34 +000015363 break;
15364 }
15365 }
15366 break;
15367 }
15368 delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15369 1L);
Cristyedd02d22019-04-15 09:02:06 -040015370 timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
cristy3ed852e2009-09-05 21:47:34 +000015371 break;
15372 }
15373 case ButtonRelease:
15374 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015375 if (display_image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015376 (void) LogMagickEvent(X11Event,GetMagickModule(),
15377 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15378 event.xbutton.button,event.xbutton.x,event.xbutton.y);
15379 break;
15380 }
15381 case ClientMessage:
15382 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015383 if (display_image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015384 (void) LogMagickEvent(X11Event,GetMagickModule(),
15385 "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
cristyf2faecf2010-05-28 19:19:36 +000015386 event.xclient.message_type,event.xclient.format,(unsigned long)
cristy3ed852e2009-09-05 21:47:34 +000015387 event.xclient.data.l[0]);
15388 if (event.xclient.message_type == windows->im_protocols)
15389 {
cristyecd0ab52010-05-30 14:59:20 +000015390 if (*event.xclient.data.l == (long) windows->im_update_widget)
cristy3ed852e2009-09-05 21:47:34 +000015391 {
15392 (void) CloneString(&windows->command.name,MagickTitle);
15393 windows->command.data=MagickMenus;
15394 (void) XCommandWidget(display,windows,CommandMenu,
15395 (XEvent *) NULL);
15396 break;
15397 }
cristyecd0ab52010-05-30 14:59:20 +000015398 if (*event.xclient.data.l == (long) windows->im_update_colormap)
cristy3ed852e2009-09-05 21:47:34 +000015399 {
15400 /*
15401 Update graphic context and window colormap.
15402 */
15403 for (i=0; i < (int) number_windows; i++)
15404 {
15405 if (magick_windows[i]->id == windows->icon.id)
15406 continue;
15407 context_values.background=pixel->background_color.pixel;
15408 context_values.foreground=pixel->foreground_color.pixel;
15409 (void) XChangeGC(display,magick_windows[i]->annotate_context,
15410 context_mask,&context_values);
15411 (void) XChangeGC(display,magick_windows[i]->widget_context,
15412 context_mask,&context_values);
15413 context_values.background=pixel->foreground_color.pixel;
15414 context_values.foreground=pixel->background_color.pixel;
15415 context_values.plane_mask=context_values.background ^
15416 context_values.foreground;
15417 (void) XChangeGC(display,magick_windows[i]->highlight_context,
cristybb503372010-05-27 20:51:26 +000015418 (size_t) (context_mask | GCPlaneMask),
cristy3ed852e2009-09-05 21:47:34 +000015419 &context_values);
15420 magick_windows[i]->attributes.background_pixel=
15421 pixel->background_color.pixel;
15422 magick_windows[i]->attributes.border_pixel=
15423 pixel->border_color.pixel;
15424 magick_windows[i]->attributes.colormap=map_info->colormap;
15425 (void) XChangeWindowAttributes(display,magick_windows[i]->id,
cristy49e2d862010-11-12 02:50:30 +000015426 (unsigned long) magick_windows[i]->mask,
15427 &magick_windows[i]->attributes);
cristy3ed852e2009-09-05 21:47:34 +000015428 }
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015429 if (windows->pan.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015430 {
15431 (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15432 windows->pan.pixmap);
15433 (void) XClearWindow(display,windows->pan.id);
15434 XDrawPanRectangle(display,windows);
15435 }
15436 if (windows->backdrop.id != (Window) NULL)
15437 (void) XInstallColormap(display,map_info->colormap);
15438 break;
15439 }
cristyecd0ab52010-05-30 14:59:20 +000015440 if (*event.xclient.data.l == (long) windows->im_former_image)
cristy3ed852e2009-09-05 21:47:34 +000015441 {
15442 *state|=FormerImageState | ExitState;
15443 break;
15444 }
cristyecd0ab52010-05-30 14:59:20 +000015445 if (*event.xclient.data.l == (long) windows->im_next_image)
cristy3ed852e2009-09-05 21:47:34 +000015446 {
15447 *state|=NextImageState | ExitState;
15448 break;
15449 }
cristyecd0ab52010-05-30 14:59:20 +000015450 if (*event.xclient.data.l == (long) windows->im_retain_colors)
cristy3ed852e2009-09-05 21:47:34 +000015451 {
15452 *state|=RetainColorsState;
15453 break;
15454 }
cristyecd0ab52010-05-30 14:59:20 +000015455 if (*event.xclient.data.l == (long) windows->im_exit)
cristy3ed852e2009-09-05 21:47:34 +000015456 {
15457 *state|=ExitState;
15458 break;
15459 }
15460 break;
15461 }
15462 if (event.xclient.message_type == windows->dnd_protocols)
15463 {
15464 Atom
15465 selection,
15466 type;
15467
15468 int
15469 format,
15470 status;
15471
15472 unsigned char
15473 *data;
15474
cristyf2faecf2010-05-28 19:19:36 +000015475 unsigned long
cristy3ed852e2009-09-05 21:47:34 +000015476 after,
15477 length;
15478
15479 /*
15480 Display image named by the Drag-and-Drop selection.
15481 */
15482 if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15483 break;
15484 selection=XInternAtom(display,"DndSelection",MagickFalse);
cristyecd0ab52010-05-30 14:59:20 +000015485 status=XGetWindowProperty(display,root_window,selection,0L,(long)
cristy151b66d2015-04-15 10:50:31 +000015486 MagickPathExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
cristy3ed852e2009-09-05 21:47:34 +000015487 &length,&after,&data);
15488 if ((status != Success) || (length == 0))
15489 break;
15490 if (*event.xclient.data.l == 2)
15491 {
15492 /*
15493 Offix DND.
15494 */
15495 (void) CopyMagickString(resource_info->image_info->filename,
cristy151b66d2015-04-15 10:50:31 +000015496 (char *) data,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +000015497 }
15498 else
15499 {
15500 /*
15501 XDND.
15502 */
15503 if (strncmp((char *) data, "file:", 5) != 0)
15504 {
15505 (void) XFree((void *) data);
15506 break;
15507 }
15508 (void) CopyMagickString(resource_info->image_info->filename,
cristy151b66d2015-04-15 10:50:31 +000015509 ((char *) data)+5,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +000015510 }
cristy947cb4c2011-10-20 18:41:46 +000015511 nexus=ReadImage(resource_info->image_info,exception);
15512 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +000015513 if (nexus != (Image *) NULL)
15514 *state|=NextImageState | ExitState;
15515 (void) XFree((void *) data);
15516 break;
15517 }
15518 /*
15519 If client window delete message, exit.
15520 */
15521 if (event.xclient.message_type != windows->wm_protocols)
15522 break;
cristyecd0ab52010-05-30 14:59:20 +000015523 if (*event.xclient.data.l != (long) windows->wm_delete_window)
cristy3ed852e2009-09-05 21:47:34 +000015524 break;
15525 (void) XWithdrawWindow(display,event.xclient.window,
15526 visual_info->screen);
15527 if (event.xclient.window == windows->image.id)
15528 {
15529 *state|=ExitState;
15530 break;
15531 }
15532 if (event.xclient.window == windows->pan.id)
15533 {
15534 /*
15535 Restore original image size when pan window is deleted.
15536 */
15537 windows->image.window_changes.width=windows->image.ximage->width;
15538 windows->image.window_changes.height=windows->image.ximage->height;
15539 (void) XConfigureImage(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000015540 display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000015541 }
15542 break;
15543 }
15544 case ConfigureNotify:
15545 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015546 if (display_image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015547 (void) LogMagickEvent(X11Event,GetMagickModule(),
15548 "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15549 event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15550 event.xconfigure.y,event.xconfigure.send_event);
15551 if (event.xconfigure.window == windows->image.id)
15552 {
15553 /*
15554 Image window has a new configuration.
15555 */
15556 if (event.xconfigure.send_event != 0)
15557 {
15558 XWindowChanges
15559 window_changes;
15560
15561 /*
15562 Position the transient windows relative of the Image window.
15563 */
15564 if (windows->command.geometry == (char *) NULL)
dirk5d0a4412016-01-05 22:28:51 +010015565 if (windows->command.mapped == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015566 {
15567 windows->command.x=event.xconfigure.x-
15568 windows->command.width-25;
15569 windows->command.y=event.xconfigure.y;
15570 XConstrainWindowPosition(display,&windows->command);
15571 window_changes.x=windows->command.x;
15572 window_changes.y=windows->command.y;
15573 (void) XReconfigureWMWindow(display,windows->command.id,
15574 windows->command.screen,(unsigned int) (CWX | CWY),
15575 &window_changes);
15576 }
15577 if (windows->widget.geometry == (char *) NULL)
dirk5d0a4412016-01-05 22:28:51 +010015578 if (windows->widget.mapped == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015579 {
15580 windows->widget.x=event.xconfigure.x+
15581 event.xconfigure.width/10;
15582 windows->widget.y=event.xconfigure.y+
15583 event.xconfigure.height/10;
15584 XConstrainWindowPosition(display,&windows->widget);
15585 window_changes.x=windows->widget.x;
15586 window_changes.y=windows->widget.y;
15587 (void) XReconfigureWMWindow(display,windows->widget.id,
15588 windows->widget.screen,(unsigned int) (CWX | CWY),
15589 &window_changes);
15590 }
15591 if (windows->magnify.geometry == (char *) NULL)
dirk5d0a4412016-01-05 22:28:51 +010015592 if (windows->magnify.mapped == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015593 {
15594 windows->magnify.x=event.xconfigure.x+
15595 event.xconfigure.width+25;
15596 windows->magnify.y=event.xconfigure.y;
15597 XConstrainWindowPosition(display,&windows->magnify);
15598 window_changes.x=windows->magnify.x;
15599 window_changes.y=windows->magnify.y;
15600 (void) XReconfigureWMWindow(display,windows->magnify.id,
15601 windows->magnify.screen,(unsigned int) (CWX | CWY),
15602 &window_changes);
15603 }
15604 if (windows->pan.geometry == (char *) NULL)
dirk5d0a4412016-01-05 22:28:51 +010015605 if (windows->pan.mapped == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015606 {
15607 windows->pan.x=event.xconfigure.x+
15608 event.xconfigure.width+25;
15609 windows->pan.y=event.xconfigure.y+
15610 windows->magnify.height+50;
15611 XConstrainWindowPosition(display,&windows->pan);
15612 window_changes.x=windows->pan.x;
15613 window_changes.y=windows->pan.y;
15614 (void) XReconfigureWMWindow(display,windows->pan.id,
15615 windows->pan.screen,(unsigned int) (CWX | CWY),
15616 &window_changes);
15617 }
15618 }
cristyecd0ab52010-05-30 14:59:20 +000015619 if ((event.xconfigure.width == (int) windows->image.width) &&
15620 (event.xconfigure.height == (int) windows->image.height))
cristy3ed852e2009-09-05 21:47:34 +000015621 break;
15622 windows->image.width=(unsigned int) event.xconfigure.width;
15623 windows->image.height=(unsigned int) event.xconfigure.height;
15624 windows->image.x=0;
15625 windows->image.y=0;
15626 if (display_image->montage != (char *) NULL)
15627 {
15628 windows->image.x=vid_info.x;
15629 windows->image.y=vid_info.y;
15630 }
dirk11a7cb72016-01-05 14:00:04 +010015631 if (windows->image.mapped != MagickFalse &&
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015632 windows->image.stasis != MagickFalse)
cristy34b9f452010-01-06 20:04:29 +000015633 {
15634 /*
15635 Update image window configuration.
15636 */
15637 windows->image.window_changes.width=event.xconfigure.width;
15638 windows->image.window_changes.height=event.xconfigure.height;
15639 (void) XConfigureImage(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000015640 display_image,exception);
cristy34b9f452010-01-06 20:04:29 +000015641 }
cristy3ed852e2009-09-05 21:47:34 +000015642 /*
15643 Update pan window configuration.
15644 */
15645 if ((event.xconfigure.width < windows->image.ximage->width) ||
15646 (event.xconfigure.height < windows->image.ximage->height))
15647 {
15648 (void) XMapRaised(display,windows->pan.id);
15649 XDrawPanRectangle(display,windows);
15650 }
15651 else
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015652 if (windows->pan.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015653 (void) XWithdrawWindow(display,windows->pan.id,
15654 windows->pan.screen);
15655 break;
15656 }
15657 if (event.xconfigure.window == windows->magnify.id)
15658 {
15659 unsigned int
15660 magnify;
15661
15662 /*
15663 Magnify window has a new configuration.
15664 */
15665 windows->magnify.width=(unsigned int) event.xconfigure.width;
15666 windows->magnify.height=(unsigned int) event.xconfigure.height;
dirk5d0a4412016-01-05 22:28:51 +010015667 if (windows->magnify.mapped == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015668 break;
15669 magnify=1;
15670 while ((int) magnify <= event.xconfigure.width)
15671 magnify<<=1;
15672 while ((int) magnify <= event.xconfigure.height)
15673 magnify<<=1;
15674 magnify>>=1;
15675 if (((int) magnify != event.xconfigure.width) ||
15676 ((int) magnify != event.xconfigure.height))
15677 {
15678 window_changes.width=(int) magnify;
15679 window_changes.height=(int) magnify;
15680 (void) XReconfigureWMWindow(display,windows->magnify.id,
15681 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15682 &window_changes);
15683 break;
15684 }
dirk11a7cb72016-01-05 14:00:04 +010015685 if (windows->magnify.mapped != MagickFalse &&
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015686 windows->magnify.stasis != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015687 {
15688 status=XMakeImage(display,resource_info,&windows->magnify,
cristy051718b2011-08-28 22:49:25 +000015689 display_image,windows->magnify.width,windows->magnify.height,
15690 exception);
cristy6710d842011-10-20 23:23:00 +000015691 XMakeMagnifyImage(display,windows,exception);
cristy3ed852e2009-09-05 21:47:34 +000015692 }
15693 break;
15694 }
dirk11a7cb72016-01-05 14:00:04 +010015695 if (windows->magnify.mapped != MagickFalse &&
cristy3ed852e2009-09-05 21:47:34 +000015696 (event.xconfigure.window == windows->pan.id))
15697 {
15698 /*
15699 Pan icon window has a new configuration.
15700 */
15701 if (event.xconfigure.send_event != 0)
15702 {
15703 windows->pan.x=event.xconfigure.x;
15704 windows->pan.y=event.xconfigure.y;
15705 }
15706 windows->pan.width=(unsigned int) event.xconfigure.width;
15707 windows->pan.height=(unsigned int) event.xconfigure.height;
15708 break;
15709 }
15710 if (event.xconfigure.window == windows->icon.id)
15711 {
15712 /*
15713 Icon window has a new configuration.
15714 */
15715 windows->icon.width=(unsigned int) event.xconfigure.width;
15716 windows->icon.height=(unsigned int) event.xconfigure.height;
15717 break;
15718 }
15719 break;
15720 }
15721 case DestroyNotify:
15722 {
15723 /*
15724 Group leader has exited.
15725 */
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015726 if (display_image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015727 (void) LogMagickEvent(X11Event,GetMagickModule(),
15728 "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15729 if (event.xdestroywindow.window == windows->group_leader.id)
15730 {
15731 *state|=ExitState;
15732 break;
15733 }
15734 break;
15735 }
15736 case EnterNotify:
15737 {
15738 /*
15739 Selectively install colormap.
15740 */
15741 if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15742 if (event.xcrossing.mode != NotifyUngrab)
15743 XInstallColormap(display,map_info->colormap);
15744 break;
15745 }
15746 case Expose:
15747 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015748 if (display_image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015749 (void) LogMagickEvent(X11Event,GetMagickModule(),
15750 "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15751 event.xexpose.width,event.xexpose.height,event.xexpose.x,
15752 event.xexpose.y);
15753 /*
15754 Refresh windows that are now exposed.
15755 */
cristy6bee4042010-01-30 15:27:14 +000015756 if ((event.xexpose.window == windows->image.id) &&
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015757 windows->image.mapped != MagickFalse)
cristy6bee4042010-01-30 15:27:14 +000015758 {
15759 XRefreshWindow(display,&windows->image,&event);
15760 delay=display_image->delay/MagickMax(
15761 display_image->ticks_per_second,1L);
Cristyedd02d22019-04-15 09:02:06 -040015762 timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
cristy6bee4042010-01-30 15:27:14 +000015763 break;
15764 }
15765 if ((event.xexpose.window == windows->magnify.id) &&
dirk11a7cb72016-01-05 14:00:04 +010015766 windows->magnify.mapped != MagickFalse)
cristy6bee4042010-01-30 15:27:14 +000015767 {
cristy6710d842011-10-20 23:23:00 +000015768 XMakeMagnifyImage(display,windows,exception);
cristy6bee4042010-01-30 15:27:14 +000015769 break;
15770 }
cristy3ed852e2009-09-05 21:47:34 +000015771 if (event.xexpose.window == windows->pan.id)
cristy6bee4042010-01-30 15:27:14 +000015772 {
15773 XDrawPanRectangle(display,windows);
15774 break;
15775 }
cristy3ed852e2009-09-05 21:47:34 +000015776 if (event.xexpose.window == windows->icon.id)
cristy6bee4042010-01-30 15:27:14 +000015777 {
15778 XRefreshWindow(display,&windows->icon,&event);
15779 break;
15780 }
cristy3ed852e2009-09-05 21:47:34 +000015781 break;
15782 }
15783 case KeyPress:
15784 {
15785 int
15786 length;
15787
15788 /*
15789 Respond to a user key press.
15790 */
15791 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15792 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15793 *(command+length)='\0';
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015794 if (display_image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015795 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000015796 "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
cristy3ed852e2009-09-05 21:47:34 +000015797 key_symbol,command);
15798 if (event.xkey.window == windows->image.id)
15799 {
15800 command_type=XImageWindowCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +000015801 event.xkey.state,key_symbol,&display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000015802 if (command_type != NullCommand)
15803 nexus=XMagickCommand(display,resource_info,windows,command_type,
cristy051718b2011-08-28 22:49:25 +000015804 &display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000015805 }
15806 if (event.xkey.window == windows->magnify.id)
cristy6710d842011-10-20 23:23:00 +000015807 XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15808 exception);
cristy3ed852e2009-09-05 21:47:34 +000015809 if (event.xkey.window == windows->pan.id)
15810 {
15811 if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15812 (void) XWithdrawWindow(display,windows->pan.id,
15813 windows->pan.screen);
15814 else
15815 if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
Cristy6970baa2019-04-20 21:23:54 -040015816 XTextViewHelp(display,resource_info,windows,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +000015817 "Help Viewer - Image Pan",ImagePanHelp);
15818 else
15819 XTranslateImage(display,windows,*image,key_symbol);
15820 }
15821 delay=display_image->delay/MagickMax(
15822 display_image->ticks_per_second,1L);
Cristyedd02d22019-04-15 09:02:06 -040015823 timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
cristy3ed852e2009-09-05 21:47:34 +000015824 break;
15825 }
15826 case KeyRelease:
15827 {
15828 /*
15829 Respond to a user key release.
15830 */
15831 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15832 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015833 if (display_image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015834 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000015835 "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
cristy3ed852e2009-09-05 21:47:34 +000015836 break;
15837 }
15838 case LeaveNotify:
15839 {
15840 /*
15841 Selectively uninstall colormap.
15842 */
15843 if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15844 if (event.xcrossing.mode != NotifyUngrab)
15845 XUninstallColormap(display,map_info->colormap);
15846 break;
15847 }
15848 case MapNotify:
15849 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015850 if (display_image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015851 (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15852 event.xmap.window);
15853 if (event.xmap.window == windows->backdrop.id)
15854 {
15855 (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15856 CurrentTime);
15857 windows->backdrop.mapped=MagickTrue;
15858 break;
15859 }
15860 if (event.xmap.window == windows->image.id)
15861 {
15862 if (windows->backdrop.id != (Window) NULL)
15863 (void) XInstallColormap(display,map_info->colormap);
15864 if (LocaleCompare(display_image->magick,"LOGO") == 0)
15865 {
15866 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15867 nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15868 }
15869 if (((int) windows->image.width < windows->image.ximage->width) ||
15870 ((int) windows->image.height < windows->image.ximage->height))
15871 (void) XMapRaised(display,windows->pan.id);
15872 windows->image.mapped=MagickTrue;
15873 break;
15874 }
15875 if (event.xmap.window == windows->magnify.id)
15876 {
cristy6710d842011-10-20 23:23:00 +000015877 XMakeMagnifyImage(display,windows,exception);
cristy3ed852e2009-09-05 21:47:34 +000015878 windows->magnify.mapped=MagickTrue;
15879 (void) XWithdrawWindow(display,windows->info.id,
15880 windows->info.screen);
15881 break;
15882 }
15883 if (event.xmap.window == windows->pan.id)
15884 {
cristy051718b2011-08-28 22:49:25 +000015885 XMakePanImage(display,resource_info,windows,display_image,
15886 exception);
cristy3ed852e2009-09-05 21:47:34 +000015887 windows->pan.mapped=MagickTrue;
15888 break;
15889 }
15890 if (event.xmap.window == windows->info.id)
15891 {
15892 windows->info.mapped=MagickTrue;
15893 break;
15894 }
15895 if (event.xmap.window == windows->icon.id)
15896 {
15897 MagickBooleanType
15898 taint;
15899
15900 /*
15901 Create an icon image.
15902 */
15903 taint=display_image->taint;
15904 XMakeStandardColormap(display,icon_visual,icon_resources,
cristy6710d842011-10-20 23:23:00 +000015905 display_image,icon_map,icon_pixel,exception);
cristy3ed852e2009-09-05 21:47:34 +000015906 (void) XMakeImage(display,icon_resources,&windows->icon,
cristy051718b2011-08-28 22:49:25 +000015907 display_image,windows->icon.width,windows->icon.height,
15908 exception);
cristy3ed852e2009-09-05 21:47:34 +000015909 display_image->taint=taint;
15910 (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15911 windows->icon.pixmap);
15912 (void) XClearWindow(display,windows->icon.id);
15913 (void) XWithdrawWindow(display,windows->info.id,
15914 windows->info.screen);
15915 windows->icon.mapped=MagickTrue;
15916 break;
15917 }
15918 if (event.xmap.window == windows->command.id)
15919 {
15920 windows->command.mapped=MagickTrue;
15921 break;
15922 }
15923 if (event.xmap.window == windows->popup.id)
15924 {
15925 windows->popup.mapped=MagickTrue;
15926 break;
15927 }
15928 if (event.xmap.window == windows->widget.id)
15929 {
15930 windows->widget.mapped=MagickTrue;
15931 break;
15932 }
15933 break;
15934 }
15935 case MappingNotify:
15936 {
15937 (void) XRefreshKeyboardMapping(&event.xmapping);
15938 break;
15939 }
15940 case NoExpose:
15941 break;
15942 case PropertyNotify:
15943 {
15944 Atom
15945 type;
15946
15947 int
15948 format,
15949 status;
15950
15951 unsigned char
15952 *data;
15953
cristyf2faecf2010-05-28 19:19:36 +000015954 unsigned long
cristy3ed852e2009-09-05 21:47:34 +000015955 after,
15956 length;
15957
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015958 if (display_image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015959 (void) LogMagickEvent(X11Event,GetMagickModule(),
15960 "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15961 event.xproperty.atom,event.xproperty.state);
15962 if (event.xproperty.atom != windows->im_remote_command)
15963 break;
15964 /*
15965 Display image named by the remote command protocol.
15966 */
15967 status=XGetWindowProperty(display,event.xproperty.window,
cristy151b66d2015-04-15 10:50:31 +000015968 event.xproperty.atom,0L,(long) MagickPathExtent,MagickFalse,(Atom)
cristy3ed852e2009-09-05 21:47:34 +000015969 AnyPropertyType,&type,&format,&length,&after,&data);
15970 if ((status != Success) || (length == 0))
15971 break;
15972 if (LocaleCompare((char *) data,"-quit") == 0)
15973 {
15974 XClientMessage(display,windows->image.id,windows->im_protocols,
15975 windows->im_exit,CurrentTime);
15976 (void) XFree((void *) data);
15977 break;
15978 }
15979 (void) CopyMagickString(resource_info->image_info->filename,
cristy151b66d2015-04-15 10:50:31 +000015980 (char *) data,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +000015981 (void) XFree((void *) data);
cristy947cb4c2011-10-20 18:41:46 +000015982 nexus=ReadImage(resource_info->image_info,exception);
15983 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +000015984 if (nexus != (Image *) NULL)
15985 *state|=NextImageState | ExitState;
15986 break;
15987 }
15988 case ReparentNotify:
15989 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015990 if (display_image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015991 (void) LogMagickEvent(X11Event,GetMagickModule(),
15992 "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15993 event.xreparent.window);
15994 break;
15995 }
15996 case UnmapNotify:
15997 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -070015998 if (display_image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000015999 (void) LogMagickEvent(X11Event,GetMagickModule(),
16000 "Unmap Notify: 0x%lx",event.xunmap.window);
16001 if (event.xunmap.window == windows->backdrop.id)
16002 {
16003 windows->backdrop.mapped=MagickFalse;
16004 break;
16005 }
16006 if (event.xunmap.window == windows->image.id)
16007 {
16008 windows->image.mapped=MagickFalse;
16009 break;
16010 }
16011 if (event.xunmap.window == windows->magnify.id)
16012 {
16013 windows->magnify.mapped=MagickFalse;
16014 break;
16015 }
16016 if (event.xunmap.window == windows->pan.id)
16017 {
16018 windows->pan.mapped=MagickFalse;
16019 break;
16020 }
16021 if (event.xunmap.window == windows->info.id)
16022 {
16023 windows->info.mapped=MagickFalse;
16024 break;
16025 }
16026 if (event.xunmap.window == windows->icon.id)
16027 {
16028 if (map_info->colormap == icon_map->colormap)
16029 XConfigureImageColormap(display,resource_info,windows,
cristy6710d842011-10-20 23:23:00 +000016030 display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000016031 (void) XFreeStandardColormap(display,icon_visual,icon_map,
16032 icon_pixel);
16033 windows->icon.mapped=MagickFalse;
16034 break;
16035 }
16036 if (event.xunmap.window == windows->command.id)
16037 {
16038 windows->command.mapped=MagickFalse;
16039 break;
16040 }
16041 if (event.xunmap.window == windows->popup.id)
16042 {
16043 if (windows->backdrop.id != (Window) NULL)
16044 (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16045 CurrentTime);
16046 windows->popup.mapped=MagickFalse;
16047 break;
16048 }
16049 if (event.xunmap.window == windows->widget.id)
16050 {
16051 if (windows->backdrop.id != (Window) NULL)
16052 (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16053 CurrentTime);
16054 windows->widget.mapped=MagickFalse;
16055 break;
16056 }
16057 break;
16058 }
16059 default:
16060 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -070016061 if (display_image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000016062 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16063 event.type);
16064 break;
16065 }
16066 }
16067 } while (!(*state & ExitState));
16068 if ((*state & ExitState) == 0)
16069 (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
cristy051718b2011-08-28 22:49:25 +000016070 &display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000016071 else
Elliott Hughes5d41fba2021-04-12 16:36:42 -070016072 if (resource_info->confirm_edit != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000016073 {
16074 /*
16075 Query user if image has changed.
16076 */
dirk5d0a4412016-01-05 22:28:51 +010016077 if ((resource_info->immutable == MagickFalse) &&
dirk11a7cb72016-01-05 14:00:04 +010016078 display_image->taint != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000016079 {
16080 int
16081 status;
16082
16083 status=XConfirmWidget(display,windows,"Your image changed.",
16084 "Do you want to save it");
16085 if (status == 0)
16086 *state&=(~ExitState);
16087 else
16088 if (status > 0)
16089 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
cristy051718b2011-08-28 22:49:25 +000016090 &display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +000016091 }
16092 }
16093 if ((windows->visual_info->klass == GrayScale) ||
16094 (windows->visual_info->klass == PseudoColor) ||
16095 (windows->visual_info->klass == DirectColor))
16096 {
16097 /*
16098 Withdraw pan and Magnify window.
16099 */
Elliott Hughes5d41fba2021-04-12 16:36:42 -070016100 if (windows->info.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000016101 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070016102 if (windows->magnify.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000016103 (void) XWithdrawWindow(display,windows->magnify.id,
16104 windows->magnify.screen);
Elliott Hughes5d41fba2021-04-12 16:36:42 -070016105 if (windows->command.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000016106 (void) XWithdrawWindow(display,windows->command.id,
16107 windows->command.screen);
16108 }
Elliott Hughes5d41fba2021-04-12 16:36:42 -070016109 if (windows->pan.mapped != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000016110 (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
dirk5d0a4412016-01-05 22:28:51 +010016111 if (resource_info->backdrop == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000016112 if (windows->backdrop.mapped)
16113 {
16114 (void) XWithdrawWindow(display,windows->backdrop.id,
16115 windows->backdrop.screen);
16116 (void) XDestroyWindow(display,windows->backdrop.id);
16117 windows->backdrop.id=(Window) NULL;
16118 (void) XWithdrawWindow(display,windows->image.id,
16119 windows->image.screen);
16120 (void) XDestroyWindow(display,windows->image.id);
16121 windows->image.id=(Window) NULL;
16122 }
16123 XSetCursorState(display,windows,MagickTrue);
16124 XCheckRefreshWindows(display,windows);
16125 if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16126 *state&=(~ExitState);
16127 if (*state & ExitState)
16128 {
16129 /*
16130 Free Standard Colormap.
16131 */
16132 (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16133 if (resource_info->map_type == (char *) NULL)
16134 (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16135 /*
16136 Free X resources.
16137 */
16138 if (resource_info->copy_image != (Image *) NULL)
16139 {
16140 resource_info->copy_image=DestroyImage(resource_info->copy_image);
16141 resource_info->copy_image=NewImageList();
16142 }
16143 DestroyXResources();
16144 }
16145 (void) XSync(display,MagickFalse);
16146 /*
16147 Restore our progress monitor and warning handlers.
16148 */
16149 (void) SetErrorHandler(warning_handler);
16150 (void) SetWarningHandler(warning_handler);
16151 /*
16152 Change to home directory.
16153 */
cristy151b66d2015-04-15 10:50:31 +000016154 directory=getcwd(working_directory,MagickPathExtent);
cristy00976d82011-02-20 20:31:28 +000016155 (void) directory;
cristy3ed852e2009-09-05 21:47:34 +000016156 {
16157 int
16158 status;
16159
cristy8a5d7f42013-01-06 15:24:33 +000016160 if (*resource_info->home_directory == '\0')
cristy151b66d2015-04-15 10:50:31 +000016161 (void) CopyMagickString(resource_info->home_directory,".",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +000016162 status=chdir(resource_info->home_directory);
16163 if (status == -1)
cristy947cb4c2011-10-20 18:41:46 +000016164 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16165 "UnableToOpenFile","%s",resource_info->home_directory);
cristy3ed852e2009-09-05 21:47:34 +000016166 }
16167 *image=display_image;
16168 return(nexus);
16169}
16170#else
16171
16172/*
16173%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16174% %
16175% %
16176% %
16177+ D i s p l a y I m a g e s %
16178% %
16179% %
16180% %
16181%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16182%
16183% DisplayImages() displays an image sequence to any X window screen. It
16184% returns a value other than 0 if successful. Check the exception member
16185% of image to determine the reason for any failure.
16186%
16187% The format of the DisplayImages method is:
16188%
16189% MagickBooleanType DisplayImages(const ImageInfo *image_info,
cristy051718b2011-08-28 22:49:25 +000016190% Image *images,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000016191%
16192% A description of each parameter follows:
16193%
16194% o image_info: the image info.
16195%
16196% o image: the image.
16197%
cristy051718b2011-08-28 22:49:25 +000016198% o exception: return any errors or warnings in this structure.
16199%
cristy3ed852e2009-09-05 21:47:34 +000016200*/
16201MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
cristy051718b2011-08-28 22:49:25 +000016202 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000016203{
16204 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +000016205 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +000016206 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +000016207 assert(image->signature == MagickCoreSignature);
cristy5f0da772013-12-01 17:43:03 +000016208 (void) image_info;
Elliott Hughes5d41fba2021-04-12 16:36:42 -070016209 if (image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000016210 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy051718b2011-08-28 22:49:25 +000016211 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
anthonye5b39652012-04-21 05:37:29 +000016212 "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
cristy3ed852e2009-09-05 21:47:34 +000016213 return(MagickFalse);
16214}
16215
16216/*
16217%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16218% %
16219% %
16220% %
16221+ R e m o t e D i s p l a y C o m m a n d %
16222% %
16223% %
16224% %
16225%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16226%
16227% RemoteDisplayCommand() encourages a remote display program to display the
16228% specified image filename.
16229%
16230% The format of the RemoteDisplayCommand method is:
16231%
16232% MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16233% const char *window,const char *filename,ExceptionInfo *exception)
16234%
16235% A description of each parameter follows:
16236%
16237% o image_info: the image info.
16238%
16239% o window: Specifies the name or id of an X window.
16240%
16241% o filename: the name of the image filename to display.
16242%
16243% o exception: return any errors or warnings in this structure.
16244%
16245*/
16246MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16247 const char *window,const char *filename,ExceptionInfo *exception)
16248{
16249 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +000016250 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +000016251 assert(filename != (char *) NULL);
16252 (void) window;
16253 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16254 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
anthonye5b39652012-04-21 05:37:29 +000016255 "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image_info->filename);
cristy3ed852e2009-09-05 21:47:34 +000016256 return(MagickFalse);
16257}
16258#endif